Skip to content

[Feature]: Persistent, retrievable memory for Professor Mari (preferences + behavior directives) #4851

Description

@kolacheee

What problem does this solve?

Professor Mari has no persistent memory of a user's preferences across sessions. Every new conversation, the user re-explains the same things: how they like lorebooks and character cards formatted, their naming/workflow conventions, and how eager (or cautious) they want Mari to be when making changes.

Two concrete gaps:

Constraint: users pay for their own model usage, so whatever we build must be token-efficient — it can't dump a pile of stored text into every prompt, and irrelevant stored memories must not dilute/poison the context.

Proposed solution

A persistent, per-install store of user "instructions/memories" (markdown), with retrieval rather than always-on injection. Scoped as v1 below; embeddings/scale left for later.

Two entry paths (both in v1):

  1. User-deposited — the user authors/uploads memory entries (reusing the existing .md upload surface Mari already has for Skills).
  2. Mari-authored — a new instructions.remember / instructions.forget app-data action so "remember that I…" persists a memory. The write is surfaced through the existing Keep/Restore review card (exact text shown before it persists), so a self-written memory is never invisible.

Retrieval — index-and-fetch (token-efficient):

  • Always inject only a compact index of available memories — title + one-line description + id — not their bodies.
  • Mari evaluates the index against the current task/turn and pulls a full memory with a new instructions.get <id> read-action only when it's relevant. Irrelevant memories cost ~nothing and can't poison the turn.
  • pinned escape hatch: a memory may opt to always inject its body. Reserved for the rare critical behavioral directive that can't risk a fetch-miss — not the default.
  • Chosen over pure semantic retrieval because the headline use case ("when I ask how, act instead of just explaining") is a behavioral directive whose trigger is a pattern of interaction, not content overlap — embeddings structurally under-retrieve exactly those. Model-driven relevance over the index catches both behavioral and reference memories because Mari reasons rather than vector-matches.

Precedence:

  • Injected memory is framed to take precedence over Mari's default behavior where the two conflict (e.g. a memory can re-enable "act on how-to questions" for a user who wants it).
  • This is prompt-level deference only. It does not alter any code-enforced safety gate: the Keep/Restore card still renders on every edit, and the hard pre-approval gate for sensitive files / dependency installs is untouched and non-overridable.

Architecture (grounded in the current code):

  • A dedicated file-native table so writes flow through the existing mutation + Keep/Restore pipeline (executeMutationcreateAppliedReview) — free undo, audit, and journaling, zero changes to the pipeline.
  • Injected via a new sibling system block next to the Skills block in buildPromptMessages() (workspace-agent.service.ts), modeled directly on the shipped Professor Mari Custom Skills system, which already does file-backed markdown → per-turn prompt injection.
  • Targets the Home workspace agent (the runtime that owns edit/approval behavior), not conversation-mode Mari.

Safety: Mari persists a memory only in response to a genuine user instruction, never as a reflex to content she just read — so a shared character / lorebook / preset can't silently plant a standing instruction (the one real injection vector a first-class self-write tool would otherwise open).

Alternatives considered

  • Always-on injection (clone Skills exactly). Rejected — wastes tokens every turn and lets irrelevant memories dilute context. (This was my first instinct; the token cost killed it.)
  • Pure semantic / embedding retrieval. Good for reference memories, but structurally weak for behavioral directives (interactional trigger, not content similarity) and a miss is silent. Kept as a v2 scale option: semantically rank the index to a top-N shortlist that Mari then reasons over — semantic in the role it's actually good at (narrowing candidates), model still making the behavioral call.
  • Literal keyword triggers (lorebook-style). Brittle; forces the user to predict trigger words for every memory.
  • Reusing the loose-file Skills store directly for self-writes. Rejected — Skills writes bypass review/undo, and the skills write-route resets Mari's session on write.
  • A single JSON settings blob. Rejected — coarse, clobber-prone undo vs. per-entry diffs on a dedicated table.

Additional context

Template check

Please uncheck (untick) the box below before submitting so we know you read the template. It is intentionally pre-checked:

  • I DID NOT read this template and provide the requested details.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestin-prHas an open pull request targeting staging

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions