| 2026-11-09 |
api: split the sessions routes into a package
...
The 1064-line module becomes one /sessions router plus area modules:
common (models and helpers), listing, detail, files, messages, naming,
manage, recall. The package re-exports every view so existing imports
keep working.
Route set, order and status codes are unchanged; tests now patch
settings/asyncio where they are actually read (the submodules).
Eugene Sukhodolskiy
committed
1 day ago
|
| 2026-11-01 |
profiles: the Personal Secretary retires, and every reference goes with it
...
`secretary` had become a second general-purpose assistant: same models, same
tools, one temperature step apart from `assistant`, and admin-only for no
reason the user could see. The directory is gone and the eight remaining
profiles load clean.
A profile that no longer exists is not a harmless orphan — `Agent` resolves
`session.profile_id` on every run and raises `ProfileNotFound`, so any session
left behind would open and then fail on its first turn. Prod sessions were
moved to `assistant` first (115 rows, verified after), and the local clone
along with them (101).
The reaction fallback loses its role table: it existed because the admin
fallback was itself admin-only, which `assistant` never is. `spawn_agent`
falls back to `assistant` too, and the two prompts that recommended
`secretary` now name only `server_admin` and `developer`.
Eugene Sukhodolskiy
committed
9 days ago
|

navi: a token count that matches the model, a type for every file, and a socket that admits it is dead
...
Compression decided everything in chars // 3, and on this workload that sits
~1.8x below reality — 101k real prompt tokens against a ~56k estimate — so the
trigger, the target, the truncate budgets and the pre-call size check all ran
in a unit half the size of the real one. TokenCalibration now fits
overhead + chars / chars_per_token per model from the prompt_eval_count that
every call already reports, and a profile declares its own context_window
(sent to Ollama as num_ctx) instead of every model being held to OLLAMA_NUM_CTX.
Superseded summaries were never demoted in the database: save() only updates
flags for rows in session.messages, a summary is is_display=False, so it lived
in session.context alone and every reload resurrected it — prod sessions
carried up to 15 live summaries. PgSessionStore.set_context_flag() writes the
flip for those rows directly.
Five extension tables disagreed about what a file is. navi/content_types.py is
the one detector — signature, then extension, then a text probe — it decides
the MIME the download route serves, and the session file list now carries the
type the viewers branch on.
The idle receive loop answers client pings and sends its own heartbeat. A
half-open socket keeps readyState at OPEN and never fires close, so traffic
coming back is the only proof the connection still works.
Eugene Sukhodolskiy
committed
9 days ago
|
| 2026-10-12 |

sessions: a pasted image lands in the session directory, and the notice says where
...
An image pasted into the chat lived only in the transcript. The system prompt of
every session names `session_files/<id>/` as the place its files live, so an agent
looking for "the attached images" went there, found nothing, and spent turns on
fruitless `find` calls before giving up.
Now the agent's non-hidden turn writes each pasted image into that directory as
`image_N.<ext>` — the extension sniffed from the bytes, the bytes stored as they
arrived, so a PNG keeps its alpha channel. Numbering runs across the session, and
a picture sent twice reuses the file already holding it.
The paths go into the hidden context copy of the message: images past the context
budget are not sent to the model at all, so the notice names them for `image_view`
instead. The line is written by the agent rather than the websocket handler, which
is the layer that knows the names the files actually took — including for a message
queued behind a running turn.
A save that fails is logged and skipped: it never costs the turn.
Eugene Sukhodolskiy
committed
29 days ago
|
| 2026-06-01 |
Make shared files and published content publicly accessible
...
Remove auth requirements from:
- GET /sessions/{id}/files/{filename} — direct download links (session ID
acts as unguessable capability token)
- GET /sessions/{id}/content — published inline content list
Both endpoints still verify session exists and protect against path
traversal. File upload and file listing remain auth-gated.
Update tests to match new signatures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 1 Jun
|
| 2026-05-18 |
Make Settings immutable (frozen=True) and fix all test mutations
...
- Add frozen=True to SettingsConfigDict in navi/config.py
- Convert model_validator to mode="before" since mode="after" cannot mutate frozen instances
- Replace all field-level monkeypatches in tests with whole-Settings object replacement
- Ensure cross-module settings consistency (content_store, session_files, share_file, content_publish, filesystem)
392 passed, 1 skipped
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 18 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-04-30 |
Improve content publishing UX
Eugene Sukhodolskiy
committed
on 30 Apr
|
| 2026-04-29 |
Complete phase 7 regression test coverage
Eugene Sukhodolskiy
committed
on 29 Apr
|