| 2026-05-08 |
Compact admin/logout buttons in sidebar footer
...
In sidebar-footer-actions, replace text+icon Admin and Logout buttons
with icon-only btn-icon buttons (shield-check and sign-out icons).
Add without-hover class to suppress the default icon rotation hover
animation. Keep the Login button unchanged (text + icon as before).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 8 May
|
Add pagination, search, and sorting to admin sessions
...
Backend:
- Add count_all and search_list abstract methods to SessionStore
- Implement count_all and search_list in PgSessionStore (SQL with ILIKE)
- Implement count_all and search_list in InMemorySessionStore
- Update /admin/sessions to accept limit, offset, search, sort_by, sort_order
- Return {total, limit, offset, items} from /admin/sessions
Frontend:
- Add search input for sessions in admin panel
- Add clickable sortable column headers with asc/desc toggle
- Add pagination controls (prev/next, page size selector, item count)
- Debounce search input (300ms)
Tests:
- Add integration tests for pagination, offset, search, and sorting
- All 217 tests pass
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 8 May
|
| 2026-05-04 |
Dark login screen: #111 overlay, transparent card
...
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 4 May
|
Add full-screen login overlay for unauthenticated users
...
- Backend: new endpoint GET /auth/status returns {configured: bool}
- Webclient auth store: add authConfigured ref + fetchStatus()
- LoginScreen.vue: centered card with logo, title, and login button
- App.vue: show LoginScreen overlay when auth is configured but
user is not authenticated (z-index 9999, blocks all UI)
- App.vue onMounted: fetch auth status before trying to resolve user
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 4 May
|
Add avatar display and gnexus-auth profile link
...
Backend:
- User model: add avatar_url field
- auth/deps.py: extract avatar_url from auth_user.profile (picture/avatar_url)
- auth.py /auth/me: return avatar_url + computed profile_url
- config.py: add gnauth_profile_path setting
- .env.example: document GNAUTH_PROFILE_PATH
Frontend:
- AppSidebar.vue: show user avatar (or initial fallback) next to name
- Clicking user info opens gnexus-auth profile in new tab
- Rebuild dist/
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 4 May
|
Fix missing Annotated/Depends imports in auth.py and UI button class
...
- auth.py: add from typing import Annotated and from fastapi import Depends
to fix 422 Unprocessable Content on /auth/me and /auth/logout
- AppSidebar.vue: replace btn-ghost with btn-primary for login/logout buttons
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 4 May
|
| 2026-05-03 |
Multi-user auth via gnexus-auth OAuth + hybrid role/permission model
...
- Integrate gnexus-auth-client-py (GAuthClient) for OAuth flow, token refresh,
and webhook parsing
- Add navi/auth/ package: User model, Fernet encryptor, client singleton,
deps (get_current_user, require_admin, require_permission)
- New tables: navi_users, user_auth_sessions (auto-created on startup)
- Session/memory isolation by user_id with legacy NULL support
- Cookie-based auth proxy: /auth/login, /callback, /logout, /me
- Webhook receiver /webhooks/gnexus-auth handling user events, global logout,
session revocation, role/permission changes
- Admin endpoints (/admin/*) gated by role + permissions
- Webclient auth store with isAdmin/hasPermission guards
- Admin-only profile filtering in /agents/profiles
- 200/200 tests passing
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 3 May
|
| 2026-05-02 |
Paginate session list loading
Eugene Sukhodolskiy
committed
on 2 May
|
| 2026-04-30 |
Improve artifacts workspace controls
Eugene Sukhodolskiy
committed
on 30 Apr
|
Add artifact source previews
Eugene Sukhodolskiy
committed
on 30 Apr
|
Reduce spawn agent running indicators
Eugene Sukhodolskiy
committed
on 30 Apr
|
Improve content publishing UX
Eugene Sukhodolskiy
committed
on 30 Apr
|
| 2026-04-29 |

Stability fixes batch — tech debt review 2026-04-29
...
Critical:
- Concurrent WS run race guard (#1)
- Tool task cancellation on generator teardown (#2)
- StopAsyncIteration kills fallback chain (#3)
- Session loading race with _lastLoadId guard (#4)
- ContentCard .match() crash on non-string result (#5)
- Image data type guard in buildMessageList (#6)
High:
- Cap WS replay buffer at 500 events (#7)
- Deduplicate memory extraction task with asyncio.Lock (#9)
- TTL-based fallback blacklisting (5 min) (#10)
- Subagent tool exception isolation (#11)
- Inline image size/count validation on WS (#12)
- Clean up orphaned file on DB insert failure (#13)
- Deep watch streamingMsg for auto-scroll (#14)
- WS_SCHEME wss:// support for HTTPS (#15)
- Sending guard against duplicate message sends (#16)
- Global unhandledrejection listener in API layer (#17)
Medium:
- Cap planning_logs at 20 entries (#22)
- Store cleanup_loop task reference (#23)
- BaseException → Exception in _run_with_sentinel (#24)
- Propagate SystemExit in agent loop (#25)
- Configurable output_reserve_tokens (#26)
- Always reloadSession on session_sync (#30)
- FIFO queue for confirm dialogs (#31)
- Reset body.overflow on ImageLightbox unmount (#32)
- try/finally in fallback copy (#33)
- _isConnecting guard in WS send() (#34)
Low:
- Lazy-init deps.py singletons (#36)
- Replace __import__ with direct imports (#38)
- Preserve token count 0 in ollama.py (#39)
- Clear orphaned streamingMsg on reconnect reload (#43)
- Escape single quote in UserMessage (#44)
- Polyfill-free findLast replacement (#48)
- Match <table> tags with attributes in markdown (#49)
- Attach copy buttons only when msg.done (#50)
- Fix hasMeta falsy-0 bug (#53)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 29 Apr
|
| 2026-04-28 |
Fix system prompt leakage into chat history; polish content cards
...
Backend:
- websocket.py + agent.py: separate user-visible display_message from
LLM user_message. System hints (image/file attachments) no longer leak
into session.messages and appear after page reload.
- main.py: add ensure_tables() on startup so session_content table is
created before first publish.
- profiles: add kimi-k2.6:cloud to all model lists as fallback.
Frontend:
- ContentCard.vue: remove border-radius, add scrollbar styles, fix
metadata fallback parsing so cards survive page reload.
- content-viewers/*.html: add matching scrollbar styles.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 28 Apr
|
Add content hosting system with inline viewers
...
Backend:
- Add navi/content/ directory for published files
- Add content_store.py with publish/list/delete/cleanup functions
- Add content_publish tool for publishing files as viewable content
- Add /content static file mount in main.py
- Add /content-viewers mount for viewer pages
- Extend ToolEvent with metadata field
- Forward metadata through websocket tool_call events
- Update Agent to include metadata in ToolEvent
Frontend:
- Add ContentCard.vue component for displaying published content
- Add viewer pages: stl.html (Three.js), svg.html, html.html, pdf.html
- Update AssistantMessage.vue to render ContentCard for content_publish
- Update chat store to preserve metadata in tool cards
- Update websocket protocol docs with metadata field
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 28 Apr
|
| 2026-04-26 |
Add eval system Phase 1 — message feedback signal
...
Spec at docs/eval_system.md describes the full LLM-as-judge plan;
this commit lands only the in-app feedback layer:
- debug/eval/ Python package with EvalDB (asyncpg) and FastAPI router
exposing /eval/feedback (set / clear / list)
- message_feedback postgres table keyed by (session_id, message_index)
- thumbs up / down on each completed assistant block in the webclient,
optimistic update with rollback on failure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 26 Apr
|
| 2026-04-25 |
Collapse thinking and plan cards by default
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 25 Apr
|
| 2026-04-24 |
Add discuss profile; responsive WelcomeScreen for 6+ profiles
...
- New 'discuss' profile: creative Q&A and idea discussion, temp=1.0,
planning phase 3 only, tools: web_search/view, scratchpad, reflect,
memory, image_view, todo
- WelcomeScreen mobile: 2-column grid for profile cards, compact logo
(row layout with subtitle on second line), reduced padding/gaps
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 Apr
|
WelcomeScreen polish, root-path fix, docs update
...
- Move mobile sidebar button to top-left corner (no header bar backdrop)
- Show WelcomeScreen on / with no hash instead of auto-loading first session
- Docs: document Ollama multi-server fallback, model priority lists, OLLAMA_BACKENDS_FILE
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 Apr
|
Fix WelcomeScreen: add with-icon, mobile sidebar toggle
...
- Add with-icon class to Start button
- Add mobile-only header with sidebar toggle (reuses chat-header +
btn-sidebar-toggle styles, hidden on desktop via existing CSS)
- Wire toggle-sidebar event through App.vue
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 Apr
|
Go to WelcomeScreen when active session is deleted
...
Previously deleting the active session would load the first session from
the list. Now it calls clearSession() which resets currentId to null,
and showWelcome now triggers on currentId === null (not just empty list).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 24 Apr
|
| 2026-04-20 |

Autonomous reasoning improvements: budget, anchoring, anti-stall, validation
...
- AgentProfile: per-profile thinking mechanics flags (think_enabled,
iteration_budget_enabled, goal_anchoring, anti_stall, step_validation,
planning_reflect, adaptive_replan) — all profiles updated in config.json
- Iteration budget: inject remaining iterations into context so model knows
when to wrap up; urgency levels at ≤7 and ≤3 remaining
- Goal anchoring: inject original goal + todo state every N iterations to
prevent drift on long tasks
- Anti-stall: two signals — no todo progress for N iterations, or identical
tool calls repeated N times; warning injected into context
- Todo step validation: marking done requires a validation field describing
how result was verified; failed gets a soft nudge with tip for re-planning
- stream_complete: add think param to base class, ollama and openai backends
- Summarizer: raise max_tokens 1024→3000, expand system prompt with
user-preferences section and verbatim-value instructions
- Compression card: persist to session.messages (is_compression flag on
Message), show expandable summary in webclient with markdown body
- ToolResult.to_message_content: always include output on failure so
tracebacks and error details reach the model (fixes silent Error: None)
- Developer profile: fix subagent profile secretary→developer, add write_tool
to subagent_tools, clarify write_tool vs filesystem in system prompt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 20 Apr
|
| 2026-04-17 |

Route subagent planning events into spawn_agent card in the UI
...
Previously PlanningStatus/PlanReady had no is_subagent flag, so subagent
planning spinners and plan cards rendered as top-level Navi planning UI.
Backend:
- Add is_subagent field to PlanningStatus and PlanReady events
- _run_planning accepts is_subagent param, passes it through all yields
- run_ephemeral calls _run_planning with is_subagent=True
- websocket.py forwards is_subagent in planning_status and plan_ready messages
Frontend (chat.js):
- onPlanningStatus: if is_subagent, set planningLabel on the last spawn_agent
card instead of msg.statusLabel
- onPlanReady: if is_subagent, push plan into spawn card steps and clear
planningLabel; otherwise behave as before
Frontend (ToolCard.vue):
- Render subagent-planning-indicator (spinner + label) when planningLabel set
- Render plan cards inside subagent steps using the same plan-card pattern
Also includes leftover session changes: spawn_agent default 40 in description
and manual, updated manual content.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 17 Apr
|

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
|
Webclient UI improvements + backend fixes
...
Webclient:
- Draft persistence across page refreshes (localStorage, reactive watch)
- Image lightbox modal using UI kit classes on thumbnail click
- Copy button on user and assistant messages
- Selection reply toolbar: select assistant text → quote inserted into input
- User message rendering: proper HTML escaping, styled blockquote for > replies
- Markdown table fix: preprocessor to inject missing separator rows
- Planning status labels (rebuild dist)
Backend:
- Developer profile: enable subagent delegation, increase max_iterations to 35
- share_file: updated description + manual with absolute path requirement and URL sharing
- persona.txt: instructions for quote replies and GFM table format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 17 Apr
|
| 2026-04-16 |
Persist context token count: return from API, restore on session load
...
- GET /sessions/{id} now returns context_token_count and max_context_tokens
(max pulled from settings.ollama_num_ctx)
- loadSession() in chat store sets contextTokens/maxContextTokens from the
response so ContextBar shows the last known fill level immediately on load,
not only after the first new message
- Restore v-if guard on ContextBar (hides for brand-new sessions with 0 tokens)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 16 Apr
|
Fix metrics: net token delta, subagent aggregation, ContextBar always visible
...
- run_stream: track _prev_tokens baseline before turn loop; compute net
token cost as (context_tokens - prev) + subagent_tokens for per-message cost
- run_stream: intercept SubagentComplete in sink drain loop to accumulate
subagent token and tool-call counts into the parent turn's totals
- run_ephemeral: already emitting SubagentComplete (from prior session)
- msg-meta-row: remove margin-left:auto from .msg-meta-time so time
groups inline with elapsed/tools/tokens instead of floating right
- ContextBar: remove v-if guard so bar is always visible (not only after
first LLM response with token data)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 16 Apr
|
Add response metrics: elapsed time, tool calls, token count
...
Server:
- Message model: elapsed_seconds, tool_call_count, token_count fields
(display-only, excluded from LLM context via exclude_none)
- StreamEnd event: carries same three fields
- agent.run_stream: tracks turn start time, counts ToolEvent completions,
writes metrics onto the final assistant Message before saving to DB
- WebSocket: forwards metrics in stream_end payload
Client:
- chat.onStreamEnd: attaches elapsed_seconds, tool_call_count, token_count
to the streaming message on completion
- buildMessageList: scans each assistant group for metrics from history
- AssistantMessage: renders .msg-meta-row below the response —
timer icon + Xs · wrench icon + N tools · coins icon + Nk tokens · time
(each item only shown if present; time pushed right via margin-left: auto)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 16 Apr
|
webclient: UI density improvements for session names and header
...
Sidebar:
- session-name: 12px → 13px (more prominent with real names)
- session-preview: 13px → 12px, clamp 2 → 3 lines (more content visible)
- session-actions buttons: 26×26px, gap 2px (narrower, more room for info)
Chat header (mobile):
- chat-header-info: column layout on mobile so title and profile badge
stack vertically (flex-direction: column; align-items: flex-start)
ContextBar:
- Recomposed to single row: [track] [pct%]
- Track 100px → 60px, height 5px → 4px
- Full token counts moved to title attribute (hover tooltip)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 16 Apr
|
webclient: remove 'Planning' label from wait indicator; add scroll-to-bottom button
...
- Waiting indicator now shows only a spinner (text was misleading — model
doesn't always plan before responding)
- Add floating scroll-to-bottom button that appears when scrolled >200px
from bottom, disappears when near bottom; smooth fade+slide transition;
positioned bottom-right (under thumb on mobile)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 16 Apr
|