| 2026-10-09 |

webclient: an App section, and a link to any settings section
...
Settings gains an App section: how to install Navi in the browser — a real
Install button where the browser offers beforeinstallprompt, per-platform
steps where it cannot, since Safari and Firefox never expose that API and iOS
goes through the share sheet — and the Android APK, with its version, size and
checksum, behind a download the app hands to the system browser. The PWA
listener is armed from main.js because beforeinstallprompt fires once, during
startup, long before the panel is ever mounted.
App is now the first tab and every section is linkable: #settings/app,
#settings/mcp, and so on. Parsing moved into utils/hash.js, which the three
places in App.vue that route on the hash now share — before it, #settings/app
was read as the id of a session and opened an empty chat. Switching tabs
rewrites the address with replaceState, so Back leaves Settings instead of
walking back through the tabs, and a bare or unknown #settings normalises to
the first tab.
Checked in Chromium against both the dev server and the built bundle: each
hash opens its section, no /api/sessions/ request is ever made for one, a tab
click leaves history.length alone, and at 390 and 320px the strip keeps five
tabs with the active one scrolled into view and no sideways overflow.
Eugene Sukhodolskiy
committed
7 hours ago
|
| 2026-10-07 |

Android app: native push notifications via JS bridge + background hold
...
WebView has no Push API, so the app shows notifications natively:
- NaviBridge JS interface (window.NaviAndroid): notify(), permission
request with a 'navi-perm-result' event callback, background-mode
start/stop and the Doze-exemption ask.
- BackgroundService: dataSync foreground service + partial wake lock —
holds the process (and the WebView WebSocket) open while the app is
minimized; quiet persistent notification with a "Отключить фон" action.
Notification channels: silent background presence, high-importance
messages.
- Manifest: POST_NOTIFICATIONS / FOREGROUND_SERVICE(_DATA_SYNC) /
WAKE_LOCK / REQUEST_IGNORE_BATTERY_OPTIMIZATIONS + service entry;
notification tap resumes MainActivity (singleTask) into open_url.
- webclient: nativeBridge composable; usePush gets a bridge mode that
replaces web-push entirely (settings toggle keeps working); chat store
raises 'Navi ответила' through the bridge on stream end when the
window is hidden. Fixed a latent undefined-variable in web syncState
(notificationsSupported -> notifSupported()).
- New unit tests for the bridge surface; 147 frontend tests + APK build
pass.
Eugene Sukhodolskiy
committed
2 days ago
|
| 2026-10-05 |

webclient: Backgrounds tab in artifacts panel + task toasts
...
- new Backgrounds tab: task list (running first, badges, per-tool icons)
with a detail view (status, timestamps, sub-agent tokens, result/progress)
- GET /sessions/{id}/tasks snapshot endpoint: task_update events are not
replayed on reconnect, so the client fetches the task list on session
load/reload; live task_update entries are merged on top (chat.fetchTasks)
- terminal task_update no longer removes the entry — it marks it finished
so the tab shows recent completions; _terminalTaskIds still blocks
resurrection by a late running update
- toasts for background task start / finish (info / success / error) in
the WS dispatch; silent for other sessions and unknown terminal ids
- persistent background-tasks chip removed (replaced by the tab); only
the queued-messages chip stays
- fix invisible status text in terminal detail rows: filled .status-*
backgrounds now scope to .terminal-status-badge only
Eugene Sukhodolskiy
committed
4 days ago
|
| 2026-09-26 |
webclient: background task chip, id-first tool matching, queued badge
...
- toolIndex (tool_call_id → card): exact matching for parallel batches and
late events after stream_end; legacy name+pending fallback kept
- bare tool_call synthesizes a card so results are never lost
- task_update: backgroundTasks chip above composer + steps appended to the
spawning tool card (parent_tool_call_id binding)
- message_queued badge, terminal_opened dispatch
- new BackgroundTasksChip component, task_update step rendering in ToolCard
Eugene Sukhodolskiy
committed
13 days ago
|

PWA: installable webclient, offline shell, web push
...
Installability:
- public/manifest.webmanifest (standalone, theme #16161E) + PNG icons
generated from logo.svg (regular + maskable, served via /images mount)
- index.html: manifest link, theme-color, apple-touch-icon
Offline shell:
- hand-rolled sw.js (no workbox): navigation = network-first (3s race)
with cached-shell fallback + background refresh (a stale cached shell
would 404 on entry chunks after a deploy); /assets/* cache-first
(content-hashed); /images/* cache-first capped; /api,/ws,/auth,/push,
/content pass-through
- vite closeBundle plugin stamps __NAVI_BUILD_VERSION__ (digest of
index.html + asset names) into dist/sw.js; sw.js served no-store so
every deploy reactivates the SW and activation evicts old caches
- SW registration in main.js, PROD only (dev HMR untouched)
- OfflineBanner (useOnline composable) over the app shell
Web push (VAPID, pywebpush):
- navi/push/ package: push_subscriptions table (postgres, boot-time DDL),
PushSubscriptionStore, PushService (async fan-out, to_thread sends,
404/410 prunes dead endpoints, per-session cooldown)
- routes: GET /push/vapid-key, POST/DELETE /push/subscribe (auth-gated)
- trigger in orchestrator run_agent + run_recall: push on StreamEnd when
no WebSocket client watches the session; fire-and-forget, never
disturbs the run; anonymous fallback only when auth is off
- client: usePush composable + Notifications settings panel (enable/
disable via PushManager.subscribe with the server VAPID key)
- notification click focuses the app at /#<session_id> (hash routing
opens the right chat); payload body is a markdown-stripped <=140-char
preview
NAVIVAPID keys empty = push fully disabled (graceful, like other optional
integrations). dist/ artifacts committed per repo convention.
Tests: pytest push store/service/routes/trigger (+23), vitest usePush
(83 webclient tests green). Full suite 1143 passed.
Eugene Sukhodolskiy
committed
13 days ago
|
| 2026-09-09 |

security: critical batch 1 — RCE/XSS/CORS/webhook hardening
...
Backend:
- auth/deps: fix refresh-lock clock race (cleanup now monotonic like the cache)
- core/registry: add missing structlog logger (NameError on fallback path)
- config: GNAUTH_WEBHOOK_SECRET, NAVI_ALLOWED_ORIGINS (+list property)
- webhooks: HMAC-SHA256 signature verification (503 unconfigured in auth mode,
unsigned+warning in no-auth mode, 403 bad/stale signature, 400 bad JSON)
- main: CORS from NAVI_ALLOWED_ORIGINS in auth mode (fail-fast on empty),
eval router behind require_admin, /debug only registered in no-auth mode
- auth routes: mobile-done sid validation (32 hex) + CSP header + safe JS
escaping (reflected XSS); Secure cookie flag via helper (https base URL)
- websocket: anonymous WS rejected in auth mode (closes legacy-session RCE);
socket registered only after access checks; stop_session auth gate
- messages: REST agent start now takes session lock + busy flag (409 on
active run), contextvars reset in finally
Webclient:
- useMarkdown: DOMPurify.sanitize on all rendered markdown (stored XSS),
image-URL scheme whitelist, delegated error listener (no inline onerror)
- html.html artifact viewer: sandbox without allow-same-origin (opaque origin)
- tests: DOMPurify runs under jsdom (happy-dom Node.prototype.nodeName getter
breaks DOMPurify); useWebSocket tests get localStorage stub + ui-kit alias
Tests: pytest 1049 passed, 1 skipped; vitest 61 passed
Eugene Sukhodolskiy
committed
on 9 Sep
|
| 2026-04-29 |
Add webclient unit tests (Vitest) — 47 tests
...
- Vitest + @vue/test-utils + happy-dom setup
- api/index.test.js: 8 tests for fetch wrapper, verbs, errors, FormData
- stores/chat.test.js: 23 tests for buildMessageList, WS handlers, load/clear
- stores/sessions.test.js: 6 tests for fetch, create, delete, pin, preview
- stores/profiles.test.js: 3 tests for fetch, auto-select, lookup
- composables/useWebSocket.test.js: 7 tests with MockWebSocket
- Export buildMessageList from chat store so tests can verify message parsing
- Update docs/testing.md with webclient stack, layout, and commands
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 29 Apr
|