syncing-memory-filesystem · git:20260826.3266698 · 2026-08-26 · sha256 41bf74a83178cc10
syncing-memory-filesystem git:20260826.3266698A
Immutable. This exact content is served forever at /api/v1/blob/41bf74a83178cc10.
---
name: syncing-memory-filesystem
description: Clone, sync, and troubleshoot git-backed agent memory repositories. Load only for memory repository setup, remote authentication or push/pull failures, cloning another agent's memory, or sync conflicts. Do not load for routine reads or edits to the current agent's memory.
---
# Git-Backed Memory Repos
Agents with the `git-memory-enabled` tag have their memory blocks stored in git repositories accessible via the Letta API. This enables version control, collaboration, and external editing of agent memory.
**Features:**
- Stored in cloud (GCS)
- Accessible via `$LETTA_BASE_URL/v1/git/<agent-id>/state.git`
- Bidirectional sync: API <-> Git (webhook-triggered, ~2-3s delay)
- Structure: `memory/system/*.md` for system blocks
## What the CLI Harness Does Automatically
When memfs is enabled, the Letta Code CLI automatically:
1. Adds the `git-memory-enabled` tag to the agent (triggers backend to create the git repo)
2. Clones the repo into `~/.letta/agents/<agent-id>/memory/` (git root is the memory directory)
3. Configures a **local** credential helper in `memory/.git/config` (so `git push`/`git pull` work without auth ceremony)
4. Installs a **pre-commit hook** that validates frontmatter before each commit (see below)
5. Installs a **post-commit hook** that pushes commits to an optional additional remote (see "Additional memory-repository remote" below)
6. Sets canonical local git identity (`letta.agentId`, `user.name`, `user.email`) so direct `git commit` from the agent's shell attributes correctly to the agent — not the operator's global git identity
7. On subsequent startups: pulls latest changes, reconfigures credentials, hooks, and identity (self-healing)
8. During sessions: periodically checks `git status` and reminds you (the agent) to commit/push if dirty
If any of these steps fail, you can replicate them manually using the sections below.
## Authentication (Preferred: Repo-Local)
The harness configures a **per-repo** credential helper during clone and refreshes it on pull/startup.
This local setup is the default and recommended approach.
Why this matters: host-level **global** credential helpers (e.g. installed by other tooling) can conflict with memfs auth and cause confusing failures.
**Important:** Always use **single-line** format for credential helpers. Multi-line helpers can break tools that parse `git config --list` line-by-line.
```bash
cd ~/.letta/agents/<agent-id>/memory
# Check local helper(s)
git config --local --get-regexp '^credential\..*\.helper$'
# Reconfigure local helper (e.g. after API key rotation) - SINGLE LINE
git config --local credential.$LETTA_BASE_URL.helper '!f() { echo "username=letta"; echo "password=$LETTA_API_KEY"; }; f'
```
If you suspect global helper conflicts, inspect and clear host-specific global entries:
```bash
# Inspect Letta-related global helpers
git config --global --get-regexp '^credential\..*letta\.com.*\.helper$'
# Example: clear a conflicting host-specific helper
git config --global --unset-all credential.https://api.letta.com.helper
```
For cloning a *different* agent's repo, prefer a one-off auth header over global credential changes:
```bash
AUTH_HEADER="Authorization: Basic $(printf 'letta:%s' "$LETTA_API_KEY" | base64 | tr -d '\n')"
git -c "http.extraHeader=$AUTH_HEADER" clone "$LETTA_BASE_URL/v1/git/<agent-id>/state.git" ~/my-agent-memory
```
## Pre-Commit Hook (Frontmatter Validation)
The harness installs a git pre-commit hook that validates `.md` files under `memory/` before each commit. This prevents pushes that the server would reject.
**Rules:**
- Every `.md` file must have YAML frontmatter (`---` header and closing `---`)
- Required fields: `description` (non-empty string)
- `read_only` is a **protected field**: you (the agent) cannot add, remove, or change it. Files with `read_only: true` cannot be modified at all. Only the server/user sets this field.
- Unknown frontmatter keys are rejected
**Valid file format:**
```markdown
---
description: What this block contains
---
Block content goes here.
```
If the hook rejects a commit, read the error message — it tells you exactly which file and which rule was violated. Fix the file and retry.
## Additional Memory-Repository Remote
In addition to pushing to the Letta server, you can push every commit to a second git remote — e.g. a private GitHub repo — so you have a backup or a copy you can browse with regular tools.
**Via the slash command (recommended):**
```
/memory-repository set git@github.com:you/my-memory.git
/memory-repository status
/memory-repository push # force a push now, e.g. after a network failure
/memory-repository unset # stop pushing
```
**How it works:**
- `/memory-repository set <url>` writes the URL to `letta.memoryRepository.url` in the memfs repo's local `.git/config` and installs a `post-commit` hook.
- After every commit, the hook reads `letta.memoryRepository.url` and asynchronously pushes to it in the background. Commits are never blocked by push failures.
- Push output and exit codes are appended to `.git/memory-repository-push.log` — visible via `/memory-repository status`.
- The setting is **per-repo**, so each agent on a machine has its own independent configuration.
**Auth:** uses your existing git credentials — SSH keys, credential helpers, or tokens in the URL. Letta does not store tokens for this feature. If you're pushing to GitHub, SSH is easiest.
**Manual equivalent (without the slash command):**
```bash
cd ~/.letta/agents/<agent-id>/memory
git config --local letta.memoryRepository.url git@github.com:you/my-memory.git
# Hook is installed automatically by the CLI on startup; no manual install needed.
```
## Clone Agent Memory
```bash
# Clone agent's memory repo
git clone "$LETTA_BASE_URL/v1/git/<agent-id>/state.git" ~/my-agent-memory
# View memory blocks
ls ~/my-agent-memory/memory/system/
cat ~/my-agent-memory/memory/system/human.md
```
## Enabling Git Memory (Manual)
If the harness `/memfs enable` failed, you can replicate it:
```bash
AGENT_ID="<your-agent-id>"
AGENT_DIR=~/.letta/agents/$AGENT_ID
MEMORY_REPO_DIR="$AGENT_DIR/memory"
# 1. Add git-memory-enabled tag (IMPORTANT: preserve existing tags!)
# First GET the agent to read current tags, then PATCH with the new tag appended.
# The harness code does: tags = [...existingTags, "git-memory-enabled"]
curl -X PATCH "$LETTA_BASE_URL/v1/agents/$AGENT_ID" \
-H "Authorization: Bearer $LETTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tags": ["origin:letta-code", "git-memory-enabled"]}'
# 2. Clone the repo into memory/
mkdir -p "$MEMORY_REPO_DIR"
git clone "$LETTA_BASE_URL/v1/git/$AGENT_ID/state.git" "$MEMORY_REPO_DIR"
# 3. Configure local credential helper (single-line format required)
cd "$MEMORY_REPO_DIR"
git config --local credential.$LETTA_BASE_URL.helper '!f() { echo "username=letta"; echo "password=$LETTA_API_KEY"; }; f'
```
## Bidirectional Sync
### API Edit -> Git Pull
```bash
# 1. Edit block via API (or use memory tools)
# 2. Pull to get changes (webhook creates commit automatically)
cd ~/.letta/agents/<agent-id>/memory
git pull
```
Changes made via the API are automatically committed to git within 2-3 seconds.
### Git Push -> API Update
```bash
cd ~/.letta/agents/<agent-id>/memory
# 1. Edit files locally
echo "Updated info" > system/human.md
# 2. Commit and push
git add system/human.md
git commit -m "fix: update human block"
git push
# 3. API automatically reflects changes (webhook-triggered, ~2-3s delay)
```
## Conflict Resolution
When both API and git have diverged:
```bash
cd ~/.letta/agents/<agent-id>/memory
# 1. Try to push (will be rejected)
git push # -> "fetch first"
# 2. Pull to create merge conflict
git pull --no-rebase
# -> CONFLICT in system/human.md
# 3. View conflict markers
cat system/human.md
# <<<<<<< HEAD
# your local changes
# =======
# server changes
# >>>>>>> <commit>
# 4. Resolve
echo "final resolved content" > system/human.md
git add system/human.md
git commit -m "fix: resolved conflict in human block"
# 5. Push resolution
git push
# -> API automatically updates with resolved content
```
## Block Management
### Create New Block
```bash
# Create file in system/ directory (automatically attached to agent)
echo "My new block content" > system/new-block.md
git add system/new-block.md
git commit -m "feat: add new block"
git push
# -> Block automatically created and attached to agent
```
### Delete/Detach Block
```bash
# Remove file from system/ directory
git rm system/persona.md
git commit -m "chore: remove persona block"
git push
# -> Block automatically detached from agent
```
## Directory Structure
```
~/.letta/agents/<agent-id>/
├── .letta/
│ └── config.json # Agent metadata
└── memory/ # Git repo root
├── .git/ # Git repo data
└── system/ # System blocks (attached to agent)
├── human.md
└── persona.md
```
**System blocks** (`memory/system/`) are attached to the agent and appear in the agent's system prompt.
## Requirements
- Agent must have `git-memory-enabled` tag
- Valid API key with agent access
- Git installed locally
## Troubleshooting
**Clone fails with "Authentication failed":**
- Check local helper(s): `git -C ~/.letta/agents/<agent-id>/memory config --local --get-regexp '^credential\..*\.helper$'`
- Check for conflicting global helper(s): `git config --global --get-regexp '^credential\..*letta\.com.*\.helper$'`
- Reconfigure local helper: see Authentication section above
- Verify the endpoint is reachable: `curl -u letta:$LETTA_API_KEY $LETTA_BASE_URL/v1/git/<agent-id>/state.git/info/refs?service=git-upload-pack`
**Push/pull doesn't update API:**
- Wait 2-3 seconds for webhook processing
- Verify agent has `git-memory-enabled` tag
- Check if you have write access to the agent
**Harness setup failed (no .git/ after /memfs enable):**
- Check debug logs (`LETTA_DEBUG=1`)
- Follow "Enabling Git Memory (Manual)" steps above
**Can't see changes immediately:**
- Bidirectional sync has a 2-3 second delay for webhook processing
- Use `git pull` to get latest API changes
- Use `git fetch` to check remote without merging