| 2026-06-22 |
Add NAVI_AUTH_ENABLED switch for optional auth
...
- Add navi_auth_enabled setting (default true) to navi/config.py and .env.example
- When disabled, treat every request as anonymous admin user (id='anonymous')
- Create/update fixed anonymous navi_users row on startup
- Bypass OAuth/cookie/API-token resolution in navi/auth/deps.py
- Update /auth/status to return {enabled, configured}
- Log security warning on startup when auth is disabled
- Update webclient: skip fetchMe/login screen, show Local mode footer,
expose /admin link, warn in API keys panel
- Rebuild webclient production bundle
- Add unit and integration tests for no-auth mode
- Update docs: auth.md, config.md, api.md, api_tokens.md, sessions.md,
websocket.md, mechanics.md, index.md
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
2 days ago
|
| 2026-05-26 |
Fix terminal detail UX: description wrapping, status contrast, close button
...
- Remove `white-space: nowrap` from `.terminal-detail-meta` and
`.terminal-item .artifact-meta` — long descriptions now wrap instead
of being clipped with ellipsis.
- Add `-webkit-line-clamp: 2` to list-item meta so description is
limited to 2 lines with graceful overflow.
- Fix status colour in detail rows: `.terminal-detail-value.status-busy`
etc. added after `.terminal-detail-value` so their colours override
the default #C0CAF5 (was causing light-on-light text).
- Add close button (X) inside `.terminal-detail-header` to dismiss
the detail pane without leaving the tab.
- Make `selectTerminal` toggle: second click on the same item deselects.
- Build webclient and run tests — all pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 26 May
|
Add terminals tab to ArtifactsPanel with live output
...
- New "Terminals" tab in ArtifactsPanel showing active terminal sessions
- List view: name, description, status badge (busy/idle/closed)
- Detail view on click: PID, command, CWD, uptime, background flag, live output
- chat.js: capture terminal metadata from tool_start and tool_call events
- Merge terminal metadata (open/status/list/close) into reactive terminals store
- Vue reactivity: immutable updates for terminals map
- Build webclient and verify all backend tests pass
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
|
Fix reloadSession race that orphans streamingMsg
...
reloadSession checked streaming.value at entry but api.getSession is
async. While awaiting, onStreamStart could set streaming=true and push
the assistant message into messages.value. When reloadSession resumed it
replaced messages.value with the stale server snapshot, evicting the live
streaming message. All subsequent deltas and tool cards went to the orphan
and were invisible.
Fix: re-check streaming.value after the await before mutating messages.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 26 May
|
Fix delta loss during thinking-to-text transition and streaming orphaning
...
Backend:
- agent.py _consume_stream: replace elif with if so a single chunk can
carry both the final thinking fragment AND the first text delta.
Fixes the 'first token lost after tools' bug.
Frontend:
- chat.js reloadSession: bail out early when streaming.value is true.
Replacing messages.value mid-stream orphans streamingMsg, breaking
Vue reactivity for all subsequent deltas and tool cards.
- useWebSocket.js session_sync handler: add !chat.streaming guard
before calling reloadSession.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 26 May
|
| 2026-05-25 |
Add rebuilt webclient dist assets
...
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 25 May
|
Fix streaming message orphaning and duplicate message keys
...
chat.js:
- reloadSession: during an active stream, replace the last built assistant
message with the live streamingMsg instead of discarding it on text match.
This fixes the root cause of invisible first messages and flickering tool
cards when session_sync arrives mid-stream.
- buildMessageList: remove assistantCounter and use firstIdx as the message id
to avoid duplicate Vue keys (e.g. user h_0 and assistant h_0).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 25 May
|

Fix 19 issues found in full codebase review
...
Backend:
- Stop session auth bypass: require auth for owned sessions, reject anonymous with 401
- upload_file: stream chunks directly to disk instead of buffering in RAM
- MCP config: validate name against path traversal regex
- auth deps: cleanup stale refresh locks periodically
- auth routes: expire mobile auth states after 10 min to prevent unbounded growth
- compressor: meta-summarize existing summaries before compression; preserve assistant content when tool_calls present; rewrite hard_truncate to keep whole turns
- orchestrator: configurable WS replay buffer size; async cleanup/remove_websocket/clear_busy; fix run_recall ContextVar order to avoid deadlock on _build_agent failure; await cleanup in finally
- agent: persist image_msg in session.messages; remove archived messages from session after archive; remove duplicate StreamStopped yield on tool stop
- websocket: try/except around create_task with cleanup on failure; await remove_websocket
Frontend:
- App.vue: hashchange listener lifecycle in onMounted/onUnmounted
- MessageList.vue: passive scroll, flash timeout cleanup, archive scroll snapshot
- InputBar.vue: 300 ms debounce on draft save to localStorage
- SessionList.vue: remove :key from DynamicScroller to avoid remount jitter
Tests: 422 passed, 1 skipped
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 25 May
|
Add archive message pagination, configurable WS replay buffer
...
Backend:
- Add archive_threshold to Session model and getSession response
- Add next_before_seq to archive endpoint for cursor pagination
- Make WS replay buffer size configurable via WS_REPLAY_BUFFER_SIZE
Webclient:
- Add getArchivedMessages API function
- Add archive pagination state and loadArchivedMessages to chat store
- MessageList: auto-load older messages on scroll-to-top with scroll
position preservation and loading spinner
Docs: update config.md with new env vars
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 25 May
|
Add client-side image resize and server-side image token budgeting
...
Client (useFileUpload.js):
- Resize images to max 1024px on longest side via Canvas before upload
- JPEG quality 0.9, preserves EXIF orientation
- Reduces typical phone photos from 8 MB → ~400-900 KB
Server (websocket.py):
- Increase limit to 8 images, 50 MB total payload
Server (agent.py):
- Before adding user message to session.context, estimate how many images
fit in remaining context (500 tok/img, up to 80% of max_context_tokens)
- Images that don't fit are saved as resized JPEGs to session_files/
- References to saved images are appended to the user message text
so Navi can view them later via image_view tool
- session.messages keeps ALL images for full UI history
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 25 May
|
| 2026-05-24 |
Rebuild webclient with tool_call_id matching fix
...
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 May
|
Fix link deduplication in ArtifactsPanel — three root causes
...
1. normalizeUrlForDedup returned only pathname, so https://a.com/foo
and https://b.com/foo shared key /foo. Now uses host+pathname
(with www stripped).
2. Trailing punctuation was stripped for display but not before dedup,
so https://example.com/path. and https://example.com/path got
different keys /path. vs /path. Now stripTrailingPunctuation() runs
before normalizeUrlForDedup().
3. Fragile before==='(' skip logic for markdown hrefs replaced with
precise protectedRanges tracking — bare URLs whose match falls inside
a recorded markdown href range are skipped regardless of spacing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 May
|
Add MCP health check loop, auto-reconnect, and system toast notifications
...
Backend:
- McpManager: keep all configured servers in the pool even if connect()
fails at startup; health-check loop (30s) tries to reconnect dead servers
and verifies live ones with list_tools()
- McpManager: set_on_server_connected callback re-registers tools when a
dead server comes back online
- McpClient: add mark_disconnected() for silent drop detection
- McpStatusTool: skip list_tools() for disconnected servers
- Orchestrator: broadcast mcp_status_update to all WebSocket sessions
- New event type McpStatusUpdate with server_name, status, tool_count
Webclient:
- useWebSocket: handle mcp_status_update → toast.success/toast.error
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 May
|
Remove redundant success toast on token creation — modal is enough
...
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 May
|
Fix ApiKeysPanel by removing GnTable wrapper — required columns/rows props were missing
...
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 May
|
Show copied checkmark on token copy button for 1.5s
...
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 May
|
Fix settings route switching and document API token system
...
- App.vue: make route reactive via ref + getRouteFromHash() so
#settings toggles work without page reload
- docs/api_tokens.md: new comprehensive API token auth doc
- docs/api.md: add /api-tokens REST endpoints
- docs/auth.md: add token flow architecture diagram and resolution order
- docs/websocket.md: add auth section with cookie vs query-param token
- docs/architecture.md: update to AgentSessionOrchestrator + ToolContext
- docs/tools.md: add gnexus-creds MCP tools
- docs/index.md: link to api_tokens.md
- Rebuild webclient dist
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 May
|
Apply review fixes to API token auth system
...
Backend:
- navi/auth/deps.py: replace 3 DB round-trips with single JOIN query for
token resolution; update last_used_at still separate (best-effort)
- navi/api/routes/api_tokens.py: replace asyncpg-specific "UPDATE 1"
string check with RETURNING id fetchrow; increase token_prefix from
8 to 12 chars for better visual identification; add security notes
- tests/unit/auth/test_api_tokens.py: update tests for JOIN query and
RETURNING-based revoke
Frontend:
- webclient/src/components/settings/ShowTokenModal.vue: new modal that
shows the plain token in a readonly field with copy button and
explicit warning — replaces the transient toast notification
- webclient/src/components/settings/ApiKeysPanel.vue: use ShowTokenModal
- webclient/src/composables/useWebSocket.js: add security comment about
localStorage XSS risk and query param log exposure
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 May
|

Add API token auth system for headless/micro clients
...
Backend:
- navi/auth/_ddl.py: add api_tokens table with boot-time migration
- navi/auth/deps.py: _resolve_user now falls back to X-Api-Token header
and ?api_token query param for WebSocket auth
- navi/auth/__init__.py: add ApiToken pydantic model
- navi/api/routes/api_tokens.py: CRUD endpoints (POST/GET/DELETE)
- navi/main.py: wire api_tokens router
Frontend:
- webclient/src/App.vue: add #settings hash routing
- webclient/src/components/settings/: SettingsView, ApiKeysPanel,
CreateKeyModal with copy-to-clipboard flow
- webclient/src/api/index.js: token CRUD API functions
- webclient/src/stores/apiTokens.js: Pinia store
- webclient/src/components/sidebar/AppSidebar.vue: settings link
- webclient/src/composables/useWebSocket.js: append ?api_token= when
localStorage token is present
Tests:
- tests/unit/auth/test_api_tokens.py: 10 unit tests covering token
resolution (header + query param), revoke, missing/revoked tokens,
orphan users, and CRUD endpoints
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 May
|
| 2026-05-22 |
Fix link deduplication in ArtifactsPanel
...
Normalize URLs by pathname (lowercased, no query/hash, no trailing
slash) so the same file reached via different query params or
absolute/relative forms is counted once.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 22 May
|
| 2026-05-16 |
Add legacy redirect for session files and fix STL viewer error handling
...
- Add /sessions/{id}/files/{filename} → /api/sessions/{id}/files/{filename}
redirect in main.py for backward compatibility with old persisted URLs
- Fix STL viewer: wrap all renderer-dependent code in `if (renderer)`
to prevent cascading errors when WebGL is unavailable
- Rebuild dist
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 16 May
|
Fix SyntaxError: Illegal return statement in STL viewer
...
The previous fix replaced `throw err` with `return` inside a module,
but top-level `return` is a SyntaxError in ES modules.
Reverted to `throw err` — the error message is already shown on screen,
so the user still gets a graceful fallback even though the error appears
in the console.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 16 May
|
Fix session file URLs (add /api prefix) and WebGL error handling in STL viewer
...
Backend:
- _file_url in content_store.py now returns /api/sessions/... instead of /sessions/...
- share_file.py URL construction updated to include /api
- content_publish.py docstring updated to reflect correct endpoint
Frontend:
- contentLinks.js: avoid double /api prefix in dev mode when backend already returns it
- stl.html: replace throw err with return after showing WebGL fallback message
- Rebuild dist
Tests:
- Update expected URLs in test_content_store.py and test_share_file.py
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 16 May
|
Extract ContextCompressor, fix STL viewer, expand test suite, add architecture audit docs
...
- Extract ContextCompressor from agent.py (Step 1 of god-object refactor)
- Add retry + hard-truncate fallback logic to ContextCompressor
- Add unit tests: agent loop (14), compressor (18), KV store (8),
auth encrypt (3), auth deps (13), todo/scratchpad/image_view/memory
- Fix WebGL STL viewer: allow-same-origin sandbox + graceful fallback
- Add CompressionStarted event and client-side compression notice
- Add docs/architecture_weak_spots.md and plan_01_god_object_agent.md
- Update test_events.py and test_agent_context_size.py for new logic
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 16 May
|
| 2026-05-15 |

fix(recall): stabilize scheduled callback system and improve UX
...
Backend fixes:
- stop_session now stops headless recall runs via _busy_sessions dict
- _fire_recall sets user ContextVars so tools work correctly
- MaxIterationsReached treated as success, not failure
- skip_next_recall uses GREATEST(trigger_at, now) for overdue recalls
- schedule_recall rejects past trigger times
- timezone offset double-adjustment fixed for aware datetimes
- _fire_recall registers _AgentRun for reconnect/replay support
- session_sync race with stream_start fixed
Frontend improvements:
- Recall banner moved to ChatHeader with live Cancel/Skip buttons
- Recall messages styled with is_recall flag and badge
- Real-time recall updates via WebSocket (recall_update events)
- Recall filter moved to sessions-header as toggle button
- Session list shows clock icon for sessions with pending recall
- Empty state messages for empty/filtered session lists
- Fixed missing api import in ChatHeader.vue
Tests:
- Updated scheduler_loop tests for _busy_sessions dict change
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 15 May
|
fix(webclient): add missing updatePreview and updateName in sessions store
...
updatePreview and updateName were referenced in the store's return object
but never defined, causing ReferenceError at runtime after build.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 15 May
|
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
|
Add self-recall (scheduled callback) system
...
Core features:
- schedule_recall tool: once/recurring/immediate callbacks
- manage_recall tool: cancel/skip/list scheduled recalls
- Natural-language time parser (ISO, relative, "tomorrow at 09:00")
- PostgreSQL-backed RecallScheduler with lazy pool init
- Background recall_scheduler_loop with asyncio.Semaphore(3)
- _busy_sessions guard prevents user messages during headless runs
- Agent.run() preserves thinking field for session history visibility
- API endpoints: GET/DELETE/POST for session recall, admin list
- Frontend: recall badge, filter, cancel/skip in sidebar and chat header
- Tests: parser, scheduler CRUD, tools, API, scheduler loop (53 tests)
- Manuals: schedule_recall.md and manage_recall.md
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 15 May
|