| 2026-06-16 |
Add navi_ui form component with client-side validation
...
- Add Form UI component schema in navi/mcp/ui_server/components/form.py
- Support text, textarea, number, email, url, select, multiselect, checkbox, date fields
- Implement hidden user message mode for form submissions (is_display=False, is_context=True)
- Add WebSocket form_submit handling and _start_agent_run helper
- Render forms client-side with real-time JS validation in webclient
- Submit once guard via localStorage and replace form with read-only summary
- Provide wsSend via Vue provide/inject in ChatArea
- Add tests for backend form component, websocket form_submit, and frontend Form.vue
- Update docs/tools.md and docs/websocket.md
- Build production webclient dist
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 days ago
|
Enable navi_ui card_grid for realtor profile
...
- Add navi_ui MCP group to realtor profile tools.
- Document card_grid payload shape in realtor system prompt.
- Add card_grid schema validation in navi/mcp/ui_server.py.
- Implement CardGrid.vue: 4-card responsive grid, image/meta/description,
modal with full details and actions on click.
- Render UI components via dynamic component in UiComponentCard.vue.
- Tests: card_grid payload validation, chat store handler, CardGrid rendering
and modal open.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
9 days ago
|
Add internal navi_ui MCP server for structured UI components
...
- Add navi/mcp/ui_server.py: FastMCP streamable_http server on port 8001
exposing render_component(component_name, payload, session_id).
- Start server in main lifespan before container creation so McpManager can
connect; wire orchestrator once container is ready; clean up on shutdown.
- Add env settings NAVI_UI_MCP_ENABLED/HOST/PORT.
- Add mcp_servers.d/navi_ui.json config with the 'ui' tool group.
- Frontend: dispatch ui_component websocket event, store in chat.js, render
placeholder UiComponentCard inside AssistantMessage.vue.
- Unit tests for ui_server tool and chat.onUiComponent.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
9 days ago
|
| 2026-06-01 |
Fix frequent OAuth logouts: offline_access scope, transient error handling, fetchMe resilience
...
- Add 'offline_access' to OAuth scopes so gnexus-auth issues offline
refresh tokens instead of SSO-session-bound ones.
- Distinguish TokenRefreshException (invalid/expired refresh token)
from transient network errors during token refresh:
* TokenRefreshException → logout (token genuinely dead)
* Other exceptions → fallback to cached user or API token
- Improve refresh failure logging with exc_type and error message.
- Frontend fetchMe: swallow non-401 errors so transient 5xx/network
failures don't flash the login screen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
24 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 MCP tool spinner bug: match tool_started → tool_call by tool_call_id
...
- Add tool_call_id field to ToolStarted and ToolEvent dataclasses
- Pass tc.id as tool_call_id from agent.py, subagent_runner.py, and tool_executor.py
- Update frontend chat.js onToolStarted/onToolCall to match cards by toolCallId
with fallback to name-matching for backward compatibility
Closes spinner issue where LLM short name ("search_docs") didn't match
resolved MCP name ("mcp__gnexus_book__search_docs").
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-21 |
ArtifactsPanel: two-pass link extraction — markdown links win over bare URLs
...
Markdown links carry a title/caption, so they should always take priority
over bare URLs during deduplication. Previously a bare URL from a newer
message could shadow an older markdown link. Now markdown links are
scanned in a separate first pass, so they always win the dedup race.
Eugene Sukhodolskiy
committed
on 21 May
|
ArtifactsPanel: strip trailing punctuation from URLs for dedup
...
Bare URLs sometimes pick up a trailing ) or . from surrounding markdown
(e.g. model writes a link inside parentheses). The normalizeUrlForDedup()
helper strips trailing punctuation before checking the seen Set so
https://a/b and https://a/b) are treated as the same link.
Eugene Sukhodolskiy
committed
on 21 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
|