Skip to content

feat(me): user-owned memory files, MCP tool surface, Berdy integration - #67

Draft
delkc wants to merge 5 commits into
mainfrom
claydelk/me-md-integration-oss
Draft

feat(me): user-owned memory files, MCP tool surface, Berdy integration#67
delkc wants to merge 5 commits into
mainfrom
claydelk/me-md-integration-oss

Conversation

@delkc

@delkc delkc commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Draft for testing, not merge. Architecture pending DRI review.

What this is

User-owned memory for Berd: everything Berd deliberately remembers about you lives in plain markdown files you can read, edit, and delete — with consent gated in code, not prompts.

  • Storage: ~/.me/me.md (the spine — rides into every session) + topic docs under ~/.me/topics/ (loaded only when relevant)
  • Memory MCP server: bundled stdio sidecar (berd-memory-mcp), auto-registered with goose sessions. Three tools: list_topics, recall, propose_memory. The server cannot write memory — proposals queue for user approval
  • Memory noticer: in-conversation proposing depends on model judgment and proved unreliable across many test rounds, so after a conversation goes quiet a hidden zero-tool extraction pass reads the conversation and queues candidates. Deterministic — no model has to decide to offer anything
  • Consent surfaces: approval cards appear in the chat that produced the fact (above the composer), plus a "Proposed memories" queue in Settings → Memory with a badge on the Memory nav row. One queue behind all of them — resolving anywhere clears everywhere
  • Topics: new topic names are bounded to seven broad areas (Home, Social, Interests, Travel, Shopping, Work, Tools) with explicit boundaries, so facts route consistently instead of sprawling into narrow near-duplicates. Your existing topics always win
  • Settings → Memory: view/edit the spine and topics in-app (Preview/Edit), "Use memory" toggle (pause, not erase — enforced server-side on every call, mid-session included), create topics
  • Provenance: every change is attributed (you / agent-approved / agent-edit-in-chat / external) via invisible local git in ~/.me/ — no remotes, no git UI
  • Interop: the spine publishes as a managed block into agents files the user already has (~/.agents/AGENTS.md, goose global hints) — never creates them. Berd also reads the user's own global agents file into sessions
  • Berdy: the bundled onboarding agent (distro/agents/berdy.md) is memory-aware — proposes via the tool, edits directly only when told to, respects the toggle

Known gaps

  • Proposals from the noticer arrive after a lull, not instantly mid-sentence. Agents can also propose live via propose_memory, but that path depends on model judgment and is best-effort
  • Near-duplicate proposals across the two doors — dedupe is exact-match, so paraphrases ("wife works late Tuesdays" / "wife usually works late on Tuesdays") can both queue
  • Memory tools are goose-sessions-only; Claude/Codex bridge MCP injection unverified (they still get the spine via preamble)
  • No "Delete all memory" action yet (deleting ~/.me/ works and is honest)
  • Project-scoped context is not wired: the same noticing engine routing proposals into a Berd Project's context is scoped but unbuilt
  • No mobile story yet; memory help skill unwritten

Migration note

Squashed from 33 commits on the original branch (squareup/berd#1083), ported onto this repo's history after the squareup/berdblock/berd migration archived the original remote. The two repos have unrelated git histories (fresh OSS export, not a continuation), so this was a manual file-by-file port rather than a rebase/cherry-pick:

  • 30 files were net-new and copied over directly, verified byte-for-byte against the source
  • 11 modified files had identical bases between the two repos and were patch-applied cleanly, verified byte-for-byte
  • 15 modified files had real drift on main since the original branch was cut (mostly unrelated Windows process-tracking work, release tooling, and subagent-labeling changes) and were hand-ported into their current context, then verified against the semantic diff
  • Cargo.lock was regenerated with cargo generate-lockfile rather than hand-patched

Full local verification before pushing: cargo check --workspace, cargo clippy --workspace --all-targets (clean, no new warnings), cargo test --workspace (two pre-existing failure categories confirmed unrelated by reproducing them identically on a clean main checkout), pnpm typecheck, pnpm lint, pnpm test (2,705 tests passing), pnpm check:i18n.

Generated with Claude Code

@delkc
delkc force-pushed the claydelk/me-md-integration-oss branch 3 times, most recently from e9f363d to d58fe25 Compare August 18, 2026 17:49
delkc and others added 5 commits August 18, 2026 14:46
Draft for testing, not merge — architecture pending DRI review.

User-owned memory for Berd: everything Berd deliberately remembers
about you lives in plain markdown files you can read, edit, and
delete, with consent gated in code, not prompts.

- Storage: ~/.me/me.md (the spine, rides into every session) + topic
  docs under ~/.me/topics/ (loaded only when relevant)
- Memory MCP server: bundled stdio sidecar (berd-memory-mcp),
  auto-registered with goose sessions. Three tools: list_topics,
  recall, propose_memory. The server cannot write memory; proposals
  queue for user approval
- Memory noticer: a hidden zero-tool extraction pass after a
  conversation goes quiet, feeding the same consent queue as the
  in-conversation propose_memory tool
- Consent surfaces: approval cards in the chat that produced the fact,
  plus a Proposed memories queue in Settings -> Memory with a nav badge
- Topics: bounded to seven broad areas (Home, Social, Interests,
  Travel, Shopping, Work, Tools) so facts route consistently
- Settings -> Memory: view/edit the spine and topics in-app, a "Use
  memory" toggle (pause, not erase, enforced server-side per call),
  topic creation
- Provenance: every change attributed via invisible local git in
  ~/.me/ (no remotes, no git UI)
- Interop: the spine publishes into agents files the user already has
  (~/.agents/AGENTS.md, goose global hints); never creates them. Berd
  also reads the user's own global agents file into sessions
- Berdy is memory-aware: proposes via the tool, edits directly only
  when told to, respects the toggle

Squashed from 33 commits on the original branch (squareup/berd#1083),
ported onto this repo's history after the squareup/berd -> block/berd
migration archived the original remote.

Co-Authored-By: Claude <noreply@anthropic.com>
Review feedback: a click per memory is how you get an empty file, and an
empty file protects nobody. So candidates now apply as soon as Berd sees
them, and the user gets a card showing exactly what landed with a delete
button.

The queue stays the transport for both doors (the MCP server's
propose_memory and the noticer), so there's still one write path rather
than two that could drift on topic routing or attribution. What changed
is that it auto-drains instead of waiting for approval.

That trade puts the weight on undo, so:
- removeBullet deletes only an exact-match bullet, first occurrence — a
  delete that quietly took out a line the user reworded would be worse
  than one that no-ops.
- Deleting tombstones the entry in the same file propose_memory already
  checks, so an auto-add can't resurrect what was just removed.
- The removal is attributed to the user in the file history, so the trail
  shows both the add and the undo.

One record, one resolution: entries live in recent.jsonl until
acknowledged or deleted *anywhere*, so acting in chat clears the Settings
card and vice versa. Unreviewed entries age out after a week so the
section can't become a chore.

Also sweeps the copy that promised consent-before-write, which would
otherwise now be a lie: the toggle description, topics hint, preamble
reader rules, the server's tool response, and three of Berdy's rules.
The Use memory and About you descriptions read as two attempts at the
same paragraph, so each now answers a different question: what memory is
(and where it lives) up top, then what makes the spine file specific (the
one every agent reads in every chat).

Also:
- Drops 'Berdy fills this in over time' — the noticer runs on every
  conversation regardless of agent and propose_memory is available to any
  agent with the tools, so naming Berdy implied a gatekeeper that doesn't
  exist. Berdy stays in the empty-state hint, where it's a real shortcut.
- Fixes a stale consent promise the auto-add change missed: the empty
  state still said 'Nothing is added without your say-so,' which a brand
  new user reads before their file even exists.
- Corrects the template's topics note. It said topic files sit 'right
  next to this one,' but since namespacing they live in topics/ — someone
  following that literally would look in the wrong place. Example
  filenames now match the actual vocabulary (home.md, social.md).
- Trades every em dash in the Memory strings for a colon, period, or
  comma, and retunes the Topics hint for its spot beside Add topic.
Chat: added memories announce as toasts instead of inline cards, per
design feedback. Follows CompletionNotificationToast — the app's existing
two-action pattern for 'something happened, here's how to respond' — with
Delete as the secondary action and OK as primary.

Missing the toast costs nothing: unresolved entries stay in
Settings → Memory behind the nav badge, and acting in either place clears
both since they read one recent.jsonl. A shown-set keeps the 5s poll from
re-toasting the same entry. This also lets ToolChainCards go back to
treating propose_memory as an ordinary tool call, removing the hoisted-
card machinery that existed only so cards could survive chain collapse.

Settings, from testing feedback:
- The rule inside 'Added to memory' was landing between the description
  and the cards, because divide-y treats a loose <p> as a row. The
  description and cards are now one unit with the rule below the section,
  which is the boundary that matters: the whole block appears and
  disappears with context.
- Topics' description and Add topic move up under the heading with a rule
  beneath them, so Add topic sits on the other side of a line from the
  View buttons instead of reading as another topic row. Its pr-4 matches
  SettingsRow's own right padding so the buttons share a right edge. The
  'New topic' subhead is gone; its description labels the naming row.
- Delete and OK were identical ghost buttons, which gave no signal about
  which one changes a file. Delete is now destructive-ghost, OK is
  outline, both sized to match the View buttons.
Two stale lines the last rebase preserved because main edited elsewhere
in the same file:
- 'Nothing gets saved without their okay' predates auto-add. Memory is
  written immediately now, so the honest version is that anything saved
  is shown right away with a delete button.
- Berdy described the disclosure as a card in the chat; it's a toast.

Also adds pronouns to the About me hint, since it's exactly the kind of
thing you'd rather state once than repeat.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant