Release Notes for v0.2.21
This release makes MEMANTO aware of which AI tool is talking to it. A new client-identity layer and append-only activity log record the tool and MEMANTO session behind every remember, recall, and answer, and the Connections page grows a live context mesh and session timeline built on top of them. Alongside it, recall/answer gain a --tool flag so agents can name themselves on reads, the MCP server attributes tool calls to its handshake client, connect templates embed each agent's own slug, and packaging moves to PyPI Trusted Publishing with a trimmed sdist.
New Features
- Tool identity and activity tracking (
memanto/app/utils/client_identity.py,memanto/app/services/activity_service.py)- New
client_identitymodule resolves the calling tool from, in order:MEMANTO_CLIENT/X-Memanto-Client, an MCPinitializehandshake'sclientInfo, or environment markers specific to a single tool (CLAUDECODE,CURSOR_TRACE_ID,CODEX_HOME,WINDSURF_HOME,GEMINI_CLI,GOOSE_PROVIDER,OPENCODE,CLINE_SESSION_ID,CONTINUE_GLOBAL_DIR,PI_AGENT,ANTIGRAVITY_HOME). Detection is conservative — an unidentified caller staysunknownrather than being guessed at. - Free-text client names are folded onto registry slugs via an alias table, so a Cursor MCP write and a Cursor CLI write land on the same connection instead of two lookalike rows.
- New
ActivityServicewrites oneO_APPENDJSON line per memory operation (remember,recall,answer) recording tool, agent, MEMANTO session, and count. Logging is best-effort and never raises — a telemetry line is never worth failing a durable write over. Files older thanRETENTION_DAYS(30) are pruned once per process. - Liveness is derived on read: a tool that touched memory within
LIVE_WINDOW_SECONDS(300) counts as connected now. - Identity is bound per-request via a
ContextVar(not a module global) so concurrent API requests cannot read each other's attribution.
- New
- Session and liveness API (
memanto/app/ui/routes/ui_router.py,memanto/app/main.py)GET /api/ui/sessionsreturns MEMANTO sessions with the tools that took part, per-tool liveness, and alive_count. Complements/api/ui/connections, which reports where MEMANTO is installed; this reports what has actually been running.GET /api/ui/sessions/{session_id}returns one session's summary plus its full event timeline,404-ing on an unknown id.- Both clamp the
dayswindow toACTIVITY_MAX_DAYS(30), matching the log's own retention. - New
attribute_calling_toolHTTP middleware bindsX-Memanto-Client(and optionalX-Memanto-Project) to the request. An anonymous HTTP call is bound toUNKNOWNrather than falling through to environment detection — otherwise an editor that startedmemanto serverwould be credited with every request it never made. - Both endpoints stay local-only behind
_require_local.
- Live context mesh and session list in the UI (
memanto/app/ui/static/index.html)- The Connections page now renders a live context mesh: a MEMANTO hub with one node per tool active in the last 7 days, live nodes and edges highlighted, and a live-count readout. The hub animates only when a tool is genuinely live, and respects
prefers-reduced-motion. - A collapsible Sessions (last 7 days) list shows each MEMANTO session, its status, and the tools that took part, expanding into a per-session event timeline.
- Live badges decorate the existing connection cards, and the mesh polls on an interval so a stale page stops claiming a tool is connected.
- The Connections page now renders a live context mesh: a MEMANTO hub with one node per tool active in the last 7 days, live nodes and edges highlighted, and a live-count readout. The hub animates only when a tool is genuinely live, and respects
--toolflag onrecallandanswer(memanto/cli/commands/memory.py)- Reads carry no
--source, so a new hidden--tooloption lets an agent name itself exactly instead of relying on environment sniffing — the only way tools that leave no distinctive marker can be attributed at all. Hidden from--helpbecause a human runningmemantoby hand has nothing to declare. remember --sourcenow defaults to the detected calling tool instead of the hardcoded"user"; an explicit--sourcealways wins, and a bare terminal still recordsuser.- Sources that name a person (
user,agent,human) fall through to environment detection rather than putting "user" on the connected-tools diagram.
- Reads carry no
Improvements
- MCP tool calls are attributed to the handshake client (
integrations/mcp/memanto_mcp/tools.py)- New
_attributed(ctx)context manager binds the MCPclientInfoname for the duration of a tool call. The MCP server runs as its own process, so its environment says nothing about the driving editor — this is the only way a recall through MCP gets credited to Cursor rather than to an anonymous caller. recall,recall_recent,recall_as_of,recall_changed_since, andanswernow acceptctx;rememberandbatch_rememberwere rewrapped to run their whole body under the attribution scope.
- New
- Activity logging wired through every read and write path (
memanto/app/services/memory_read_service.py,memanto/app/services/memory_write_service.py,memanto/cli/client/direct_client.py,memanto/cli/client/sdk_client.py,memanto/app/routes/auth_deps.py)- Semantic recall,
--recent,--as-of,--changed-since,answer, singleremember, andbatch_rememberall emit an activity event. get_current_sessionand the CLI clients'_get_validated_session_for_agentbind the session id, since the memory services below them only ever receive anagent_id.- The CLI
answerRAG path calls Moorcheh directly rather than throughMemoryReadService, so it logs its own event — otherwiseanswerwould be the one memory operation leaving no trace. - Generated instructions and
SKILL.mdnow interpolate the connected agent's real slug into--source "<slug>"and--tool "<slug>"examples, instead of a generic<your_agent_name>placeholder._install_skillpassesagent.namethrough toget_skill_content. - Recall/answer examples and the recall trigger matrix were updated to always pass
--tool, with a new anti-pattern entry explaining why reads need it.
- Semantic recall,
- Packaging and publishing (
pyproject.toml,.github/workflows/publish.yml)- PyPI publishing moved to Trusted Publishing:
PYPI_API_TOKENis gone,id-token: writeadded, and the workflow's OIDC identity is what PyPI authenticates — the action attaches PEP 740 provenance attestations by default. - New
[tool.hatch.build.targets.sdist]include list trims the sdist tomemanto/,tests/, and build metadata;assets/,examples/,integrations/,docs/, andsdks/(whose demo GIFs and architecture PNGs dominated the archive) are excluded. - Classifier bumped from
3 - Alphato5 - Production/Stable, Python 3.13 added, and project URLs repointed tomemanto.ai/docs.memanto.ai.
- PyPI publishing moved to Trusted Publishing:
Tests
tests/test_tool_sessions.py— new 500-line suite covering environment detection precedence, alias normalization, append-only event writing, failure-swallowing on broken storage, session grouping, liveness expiry, quiet-but-unexpired and zero-activity sessions, dead-session flooding, torn-line recovery, session timelines, HTTP header attribution, anonymous-caller safety, and the--toolflag onrecall/answer.tests/test_cli.py—rememberattributes the write to the calling tool, and an explicit--sourceoverrides detection.tests/test_connect_engine.py— each connected agent gets its own slug in both its instructions and its installedSKILL.md.tests/test_remaining_ui_auth.py— both new session endpoints are rejected from remote callers.tests/conftest.py— new autouse fixture resets the bound client identity and session between tests, so one test's--toolcannot decide the attribution of every test after it.
Full Changelog
Full Changelog: v0.2.20...v0.2.21