v0.2.19
Release Notes for v0.2.19
This release makes MEMANTO survive an idle gap. Previously, once a session's expiry window fully lapsed, every entry point CLI, SDK, and REST, failed with 401 / "No active session. Call activate_agent()", forcing a manual re-activation. Sessions now transparently recreate themselves on the next request, gated behind the same management-access check as explicit activation, with a new auto_recreate_enabled toggle wired end-to-end through env vars, config.yaml, the CLI, and the Web UI.
New Features
- Automatic session recreation after expiry (
memanto/app/services/session_service.py,memanto/app/routes/auth_deps.py)- New
SessionService.check_and_auto_recreate()complementscheck_and_auto_renew: where renewal keeps a live session going near expiry, this issues a brand-new session (new JWT, fresh expiry window) when a caller presents the token of a session that has already fully lapsed. get_current_sessionnow catchesSessionExpiredErrorand attempts recreation before failing. Authorization reusesrequire_management_access(valid management credential or loopback caller), so a stolen stale token alone is worthless.- On success the route mirrors the auto-renewal handoff: refreshes the browser cookie when one was sent, and returns the replacement token via the
X-Session-Tokenresponse header. - Safety rails: deliberately terminated (logged-out) sessions are never resurrected; a stale token whose persisted record was already replaced by a newer session never supersedes it; foreign, malformed, or unsignable tokens fall through to normal validation; an agent id that is no longer a safe id returns
Nonerather than raising a500out of the auth dependency. - CLI and SDK direct paths (
memanto/cli/client/direct_client.py,memanto/cli/client/sdk_client.py) call the same recreation path onSessionExpiredErrorand update their cached session and token in place.InvalidSessionTokenErroris still surfaced unchanged.
- New
SESSION_AUTO_RECREATE_ENABLEDconfiguration toggle (memanto/app/config.py,.env.example,memanto/cli/config/manager.py,memanto/app/ui/static/index.html)- New setting, defaulting to
True, exposed as theSESSION_AUTO_RECREATE_ENABLEDenv var, thesession.auto_recreate_enabledkey inconfig.yaml, amemanto configboolean, and an "Auto-create a fresh session after expiry (next request)" toggle in the Web UI settings panel. - Documented in
.env.examplealongside the existingSESSION_*knobs.
- New setting, defaulting to
Improvements
- Session toggles in
config.yamlnow actually reach the server (memanto/app/config.py)SessionServicereadsauto_renew_enabled/auto_recreate_enabledoffsettings, but the Web UI andmemanto configpersist them toconfig.yaml. The startup overlay now maps both YAML keys onto their env vars, so they are no longer inert for the server and only half-honoured by the CLI. Usessetdefault, so an explicitly exportedSESSION_AUTO_*(containerised deployments) still wins.
- UI config changes apply without a restart (
memanto/app/ui/routes/ui_router.py)config.yamlis overlaid ontosettingsonly at process start, so flipping the session toggles in the UI previously did nothing until the next restart.update_ui_confignow appliesauto_renew_enabledandauto_recreate_enabledto the running server'ssettingsas well.
- Single toggle governs CLI re-activation (
memanto/cli/commands/_shared.py)get_client()no longer re-reads the YAML session config for its own gate. Expiry is handled upstream byget_active_session(), so what remains here is the rotated-MEMANTO_SECRET_KEYcase (a token that no longer verifies at all, which used to breakanalyzeLLM narratives mid-run). It is now gated onsettings.SESSION_AUTO_RECREATE_ENABLEDlike every other path, so turning auto-recreate off disables it everywhere.
Bug Fixes
- Idle CLI sessions were stranded instead of recreated (
memanto/app/services/session_service.py)get_active_session()cleared the active marker as soon as the session it named had lapsed, so every CLI entry point — which all resolve their session through that marker reported "No active session. Callactivate_agent()" regardless of the auto-recreate setting. It now attempts recreation first and returns the fresh session.- Recreation deliberately runs outside the active-marker lock: it takes the agent lifecycle lock, and
delete_sessiontakes those two in the opposite order, so holding both would invert the lock order and risk deadlock. - When recreation is declined (disabled by config, or a terminated session), the stale marker is dropped only if it still names the same lapsed session and that session is still not active, another thread may have activated in the meantime.
- Marker reading was extracted to
_read_active_marker_agent_id()so the re-check after recreation shares exactly the symlink/Windows-file handling of the original read.
Tests
tests/conftest.py: new autousereset_session_togglesfixture pinsSESSION_AUTO_RENEW_ENABLEDandSESSION_AUTO_RECREATE_ENABLEDto their defaults, so a developer who has switched either off locally no longer changes how the suite behaves.tests/test_api.py: newTestSessionAutoRecreatecovers the header path issuing a fresh token, a remote caller without a management credential getting no new session, terminated sessions not being recreated, and the behaviour with the toggle disabled.tests/test_unit.py:check_and_auto_recreateunit coverage, reviving an expired session, invalidating the old token, never reviving a terminated session, skipping still-active sessions, honouring the disabled config, and ignoring foreign/malformed tokens; plusget_active_sessionrecreating a lapsed session, clearing the marker when recreate is disabled, and refusing to revive a terminated session.tests/test_session_config_overlay.py: new file verifying that YAML session toggles reachsettings, that environment variables override YAML, and that missing or malformed config keeps the defaults.
Full Changelog
Full Changelog: v0.2.18...v0.2.19