hippocamp 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ on:
4
4
  release:
5
5
  types:
6
6
  - published
7
+ workflow_dispatch:
7
8
 
8
9
  permissions:
9
10
  contents: read
@@ -11,6 +12,7 @@ permissions:
11
12
 
12
13
  jobs:
13
14
  publish:
15
+ if: github.event_name == 'release'
14
16
  runs-on: ubuntu-latest
15
17
  steps:
16
18
  - uses: actions/checkout@v6
@@ -25,3 +27,26 @@ jobs:
25
27
  - name: Verify release tag matches package version
26
28
  run: test "$GITHUB_REF_NAME" = "v$(node -p "require('./package.json').version")"
27
29
  - run: npm publish
30
+
31
+ github-packages:
32
+ # Mirror of the npm package on GitHub Packages, so the repository lists it.
33
+ # npmjs.com stays the install path; GitHub's npm registry needs a token to install.
34
+ if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
35
+ runs-on: ubuntu-latest
36
+ permissions:
37
+ contents: read
38
+ packages: write
39
+ steps:
40
+ - uses: actions/checkout@v6
41
+ - uses: actions/setup-node@v6
42
+ with:
43
+ node-version: "24"
44
+ registry-url: "https://npm.pkg.github.com"
45
+ package-manager-cache: false
46
+ - name: Scope the package name for GitHub Packages
47
+ run: |
48
+ npm pkg set name="@${GITHUB_REPOSITORY_OWNER}/hippocamp"
49
+ npm pkg set repository.url="https://github.com/${GITHUB_REPOSITORY}"
50
+ - run: npm publish
51
+ env:
52
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/AGENTS.md CHANGED
@@ -47,15 +47,15 @@ This repository is a local-first MCP package for Git-backed agent memory. Keep c
47
47
  - Sync paths must stay under the selected Lagoon memory root.
48
48
  - Global memory lives under `HIPPOCAMP_GLOBAL_ROOT/`.
49
49
  - Project memory lives under `HIPPOCAMP_GLOBAL_ROOT/projects/<slug>/`.
50
- - The current project slug is inferred from `HIPPOCAMP_PROJECT_ROOT` or the current working directory.
51
- - Event logs are append-only daily Markdown files under `events/YYYY-MM-DD.md`, with sibling `events/YYYY-MM-DD.index.json` cue indexes.
50
+ - The current project slug is inferred from `HIPPOCAMP_PROJECT_ROOT` or the current working directory. An existing folder with nothing but `.git` has no project; project scope then resolves to the global root.
51
+ - Event logs are append-only daily Markdown files under `events/YYYY-MM-DD.md`, with sibling `events/YYYY-MM-DD.index.json` keyword indexes.
52
52
  - Curated files such as `current_state.md` and `open_threads.md` use explicit file writes.
53
- - MCP `append_event` requires a title and 1-8 cues; MCP `write_memory_file` rejects paths under `events/`; an explicit `projectRoot` must be a directory inside a Git repository.
53
+ - MCP `append_event` requires a title and 1-8 keywords; MCP `write_memory_file` rejects paths under `events/`; an explicit `projectRoot` must be a directory inside a Git repository.
54
54
  - `append_event` auto-stamps `Agent:` / `Session:` provenance when resolvable (installer env, MCP client name, or process session id). No manual user config.
55
55
  - Default sync is automatic through the local Lagoon repo.
56
56
  - Local MCP mode must not require `GITHUB_TOKEN`, GitHub App auth, or a cloud service.
57
57
  - Memory must not duplicate facts already tracked by GitHub commits, PRs, issues, reviews, or CI. Store references plus the missing rationale, preference, assumption, or follow-up context instead.
58
- - Dream compaction sends `current_state.md`, `open_threads.md`, and bounded cue-indexed event evidence for open-thread decisions. It must not dump full event logs, cue indexes, or raw GitHub-owned facts into the model prompt.
58
+ - Dream maintains `current_state.md` and `open_threads.md` using bounded recent indexed events and open-thread evidence. It must not dump full event logs, keyword indexes, or raw GitHub-owned facts into the model prompt. Preserve the `hippocamp-dream-through` comment in current state; it tracks the last reviewed event without another file.
59
59
  - Dream must compact coherent Markdown snapshots and fail without writing when the snapshot cannot fit. It must not crop wake-up files.
60
60
  - The Dream GitHub Actions workflow is a Lagoon repo template. It should remain schedule-only and create PRs rather than pushing directly to `main`. Auto-merge is allowed only for repositories named `lagoon` after exact changed-file validation.
61
61
  - The Railway Dream deployment is optional infrastructure. It must exit after each run, keep credentials in environment variables, and preserve the same PR policy.
@@ -84,5 +84,5 @@ When changing code, use the smallest verification that proves the change:
84
84
 
85
85
  - The cloud/API version is intentionally deferred.
86
86
  - Do not reintroduce Next.js, GitHub API token auth, or GitHub App auth for the local MVP.
87
- - Keep Dream minimal: scheduled coherent compaction, current/open curated files plus bounded thread evidence, one PR per project.
87
+ - Keep Dream minimal: scheduled updates and coherent compaction, two curated files plus bounded indexed evidence, one PR per project.
88
88
  - The local install story should stay: `npx hippocamp@latest install`, a local Lagoon repo created if missing, normal Git auth. Installers never create remotes or push.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Hippocamp
2
2
 
3
- [![npm](https://img.shields.io/npm/v/hippocamp)](https://www.npmjs.com/package/hippocamp) [![CI](https://github.com/guillaumegay13/hippocamp/actions/workflows/ci.yml/badge.svg)](https://github.com/guillaumegay13/hippocamp/actions/workflows/ci.yml)
3
+ [![npm](https://img.shields.io/npm/v/hippocamp)](https://www.npmjs.com/package/hippocamp) [![npm downloads](https://img.shields.io/npm/dm/hippocamp)](https://www.npmjs.com/package/hippocamp) [![CI](https://github.com/guillaumegay13/hippocamp/actions/workflows/ci.yml/badge.svg)](https://github.com/guillaumegay13/hippocamp/actions/workflows/ci.yml)
4
4
 
5
5
  <p align="center">
6
6
  <img src="./assets/brand/hippocamp-mascot.png" alt="Hippocamp mascot" width="220" />
@@ -24,12 +24,24 @@ A replay of a real session. Neither prompt mentions Hippocamp.
24
24
 
25
25
  ## Why Hippocamp?
26
26
 
27
- - **Written by the agent itself.** The agent doing the work decides what is worth keeping, in its own words. Unlike memory services that run a second model to extract facts, nothing rewrites it behind your back: events are saved as written, and optional Dream compaction of the two summary files arrives as a pull request you review.
27
+ - **Written by the agent itself.** The agent doing the work records what is worth keeping, in its own words. Events stay as written. Optional Dream reviews recent events to update and compact the two summary files, promoting supported lessons through a pull request you review.
28
28
  - **One memory for every agent.** Claude Code, Codex, and your other agents share the same repo. Built-in memory, like Claude Code's or Codex's, stays inside one tool.
29
29
  - **History you can audit.** Every memory is a Git commit. Roll back a bad memory like bad code.
30
30
  - **Files you own.** Plain Markdown, readable without any UI, synced across machines with your normal Git credentials.
31
31
  - **Nothing to sign up for.** No vendor that can change terms or shut down.
32
32
 
33
+ ## Compared with other memory tools
34
+
35
+ | | **Hippocamp** | [Mem0](https://docs.mem0.ai) | [Zep](https://www.getzep.com) | [Graphiti](https://github.com/getzep/graphiti) | [Letta Code](https://github.com/letta-ai/letta-code) | [Basic Memory](https://github.com/basicmachines-co/basic-memory) | Claude Code / Codex built-in |
36
+ | --- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
37
+ | Agent agnostic (MCP server) | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
38
+ | Plain files you own | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
39
+ | Auditable history | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
40
+ | No extra account, API key, or model | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
41
+ | Syncs through your own Git | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
42
+ | Always free | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
43
+ | Open source | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ |
44
+
33
45
  ## Results
34
46
 
35
47
  Reproducible with the scripts in this repo.
@@ -47,7 +59,7 @@ Full tables, method, and limits: [docs/results.md](docs/results.md).
47
59
 
48
60
  - [Setup](docs/setup.md): install options, the Lagoon repo, a private remote, configuration
49
61
  - [How it works](docs/how-it-works.md): memory layout, MCP tools, memory rules
50
- - [Dream](docs/dream.md): optional overnight compaction as a reviewed pull request
62
+ - [Dream](docs/dream.md): optional overnight memory updates and compaction as a reviewed pull request
51
63
  - [Results](docs/results.md): evals and how to rerun them
52
64
  - [Development](docs/development.md): commands and releases
53
65
 
@@ -3,7 +3,6 @@ name: Hippocamp Dream
3
3
  on:
4
4
  schedule:
5
5
  - cron: "17 3 * * *"
6
- workflow_dispatch:
7
6
 
8
7
  permissions:
9
8
  contents: write
@@ -42,7 +41,7 @@ jobs:
42
41
  git config user.name "hippocamp-dream[bot]"
43
42
  git config user.email "hippocamp-dream[bot]@users.noreply.github.com"
44
43
 
45
- - name: Find projects over threshold
44
+ - name: Find projects needing Dream
46
45
  run: |
47
46
  npx -y hippocamp@latest dream \
48
47
  --all \
@@ -115,7 +114,7 @@ jobs:
115
114
  fi
116
115
 
117
116
  git add "projects/${project}/current_state.md" "projects/${project}/open_threads.md"
118
- git commit -m "Dream compact ${project} memory"
117
+ git commit -m "Dream update ${project} memory"
119
118
  git push --force-with-lease origin "$branch"
120
119
 
121
120
  node - "$project" "/tmp/hippocamp-dream-${project}.json" > "/tmp/hippocamp-dream-${project}.md" <<'NODE'
@@ -124,7 +123,7 @@ jobs:
124
123
  const payload = JSON.parse(fs.readFileSync(process.argv[3], "utf8"));
125
124
  const result = payload.results[0];
126
125
  const lines = [
127
- `Dream compacted \`${project}\` curated memory.`,
126
+ `Dream updated \`${project}\` curated memory.`,
128
127
  "",
129
128
  "Before:",
130
129
  `- wake_up: ${result.wakeUpChars} chars`,
@@ -132,6 +131,7 @@ jobs:
132
131
  `- open_threads.md: ${result.files["open_threads.md"]} bytes`,
133
132
  "",
134
133
  "Context:",
134
+ `- new event excerpts: ${result.context.recentEvents}`,
135
135
  `- open threads found: ${result.context.openThreads}`,
136
136
  `- threads with event evidence: ${result.context.threadsWithEvidence}`,
137
137
  `- event evidence snippets: ${result.context.evidenceItems}`,
@@ -144,15 +144,15 @@ jobs:
144
144
  "",
145
145
  "Policy:",
146
146
  "- touched only `current_state.md` and `open_threads.md`",
147
- "- used bounded cue-indexed event evidence for thread decisions",
148
- "- did not modify append-only events or cue indexes",
149
- "- moved historical detail out of wake-up context",
147
+ "- used bounded recent and thread evidence to update project memory",
148
+ "- did not modify append-only events or keyword indexes",
149
+ "- preserved supported lessons and remaining actions within the size budget",
150
150
  ];
151
151
  console.log(lines.join("\n"));
152
152
  NODE
153
153
 
154
154
  existing="$(gh pr list --head "$branch" --state open --json number --jq '.[0].number // empty')"
155
- title="Dream compact ${project} memory"
155
+ title="Dream update ${project} memory"
156
156
 
157
157
  if [ -n "$existing" ]; then
158
158
  gh pr edit "$existing" --title "$title" --body-file "/tmp/hippocamp-dream-${project}.md"
@@ -119,7 +119,7 @@ while IFS= read -r project; do
119
119
  fi
120
120
 
121
121
  git add "projects/${project}/current_state.md" "projects/${project}/open_threads.md"
122
- git commit -m "Dream compact ${project} memory"
122
+ git commit -m "Dream update ${project} memory"
123
123
 
124
124
  if git ls-remote --exit-code --heads origin "refs/heads/${branch}" >/dev/null 2>&1; then
125
125
  git fetch origin "refs/heads/${branch}:refs/remotes/origin/${branch}"
@@ -133,7 +133,7 @@ const project = process.argv[2];
133
133
  const payload = JSON.parse(fs.readFileSync(process.argv[3], "utf8"));
134
134
  const result = payload.results[0];
135
135
  const lines = [
136
- `Dream compacted \`${project}\` curated memory.`,
136
+ `Dream updated \`${project}\` curated memory.`,
137
137
  "",
138
138
  "Before:",
139
139
  `- wake_up: ${result.wakeUpChars} chars`,
@@ -141,6 +141,7 @@ const lines = [
141
141
  `- open_threads.md: ${result.files["open_threads.md"]} bytes`,
142
142
  "",
143
143
  "Context:",
144
+ `- new event excerpts: ${result.context.recentEvents}`,
144
145
  `- open threads found: ${result.context.openThreads}`,
145
146
  `- threads with event evidence: ${result.context.threadsWithEvidence}`,
146
147
  `- event evidence snippets: ${result.context.evidenceItems}`,
@@ -153,15 +154,15 @@ const lines = [
153
154
  "",
154
155
  "Policy:",
155
156
  "- touched only `current_state.md` and `open_threads.md`",
156
- "- used bounded cue-indexed event evidence for thread decisions",
157
- "- did not modify append-only events or cue indexes",
158
- "- moved historical detail out of wake-up context",
157
+ "- used bounded recent and thread evidence to update project memory",
158
+ "- did not modify append-only events or keyword indexes",
159
+ "- preserved supported lessons and remaining actions within the size budget",
159
160
  ];
160
161
  console.log(lines.join("\n"));
161
162
  NODE
162
163
 
163
164
  existing="$(gh pr list --repo "$LAGOON_REPOSITORY" --head "$branch" --state open --json number --jq '.[0].number // empty')"
164
- title="Dream compact ${project} memory"
165
+ title="Dream update ${project} memory"
165
166
 
166
167
  if [[ -n "$existing" ]]; then
167
168
  gh api --method PATCH "repos/${LAGOON_REPOSITORY}/pulls/${existing}" \
package/docs/dream.md CHANGED
@@ -2,16 +2,24 @@
2
2
 
3
3
  [← Back to README](../README.md)
4
4
 
5
- Dream is offline compaction for project wake-up context. It rewrites only:
5
+ Dream keeps project wake-up context current and compact. It rewrites only:
6
6
 
7
7
  ```text
8
8
  projects/<project-slug>/current_state.md
9
9
  projects/<project-slug>/open_threads.md
10
10
  ```
11
11
 
12
- It does not dump event logs into the model prompt, does not rewrite append-only events, and does not run during normal MCP wake-up. When a project is over the threshold, Dream builds a capped thread evidence pack by searching cue-indexed project events for `open_threads.md` bullets. The evidence budget follows `--target-chars`, so the same size target controls both the desired output and the supporting context.
12
+ Dream runs when a project has new indexed events or its wake-up context exceeds `--threshold-chars`. It does not run during normal MCP wake-up or modify append-only events.
13
13
 
14
- Dream uses an Eve-style compaction loop. It rewrites the complete curated snapshot, measures the result, and compacts that coherent result again when it is still over the target. It never crops a wake-up file. After three passes, it fails without writing either file if the snapshot still does not fit.
14
+ Each run reads a chronological batch of up to 20 new indexed event excerpts, independent of existing open threads. The first run starts with the last seven days. After a successful write, a small `<!-- hippocamp-dream-through: ... -->` comment in `current_state.md` records the last event included. Preserve this comment when editing the file. Later runs resume there, including after downtime; events after the last included event wait for the next run. If the first excerpt alone exceeds the recent-event budget, Dream fails without writing and asks for a larger `--target-chars`. If the comment is removed, Dream starts again with the last seven days.
15
+
16
+ Recent events use at most half of the evidence budget. The remaining budget supports searches for existing `open_threads.md` bullets. The total evidence budget follows `--target-chars`. The model receives bounded coherent excerpts with source paths and timestamps, never whole event logs or raw indexes.
17
+
18
+ Dream promotes clear decisions, explicit corrections, and reusable project lessons into `current_state.md`, with source references. It keeps remaining actions in `open_threads.md`, preserves unresolved blockers, and removes completed actions only when evidence supports it. A merged PR does not prove its deployment or follow-up work is done. Uncertain lessons remain in events for normal search.
19
+
20
+ Corrections apply across both files: a shipped feature should not remain an implementation task, while unverified deployment stays open. Extra compaction passes must keep source paths and timestamps for retained knowledge and aim below the size limit to leave room for length estimates. Dream writes the review comment itself; the model does not set review progress.
21
+
22
+ The snapshot may grow within `--target-chars` when new evidence adds useful knowledge. If it exceeds the target, Dream compacts the complete result again. It never crops a wake-up file. After three passes, it fails without writing either file or advancing the event comment if the snapshot still does not fit. A project below the threshold with no new indexed evidence is skipped without a model call.
15
23
 
16
24
  Dry-run is the default:
17
25
 
@@ -26,7 +34,7 @@ npm run dream -- --project my-project --write
26
34
  - `MANIFEST_API_KEY`
27
35
  - `HIPPOCAMP_DREAM_MODEL` (defaults to `auto`; override if you want a specific provider/model)
28
36
 
29
- The GitHub Actions template at `assets/github-actions/hippocamp-dream.yml` is meant to be copied into the private Lagoon memory repo as `.github/workflows/hippocamp-dream.yml`. It runs on a schedule only, scans projects over the wake-up threshold, and creates or updates one PR per project. With `HIPPOCAMP_DREAM_AUTO_MERGE=true`, Dream squash-merges the PR only when the repository name is `lagoon` and the PR changes only the two curated project files.
37
+ The GitHub Actions template at `assets/github-actions/hippocamp-dream.yml` is meant to be copied into the private Lagoon memory repo as `.github/workflows/hippocamp-dream.yml`. It runs on a schedule only, scans projects with new events or oversized wake-up context, and creates or updates one PR per project. With `HIPPOCAMP_DREAM_AUTO_MERGE=true`, Dream squash-merges the PR only when the repository name is `lagoon` and the PR changes only the two curated project files.
30
38
 
31
39
  ## Railway
32
40
 
@@ -67,18 +67,18 @@ Typical agent flow:
67
67
  1. Call `wake_up` at the start of a top-level task.
68
68
  2. Read the returned global and project memory.
69
69
  3. Use `search_memory` for task-specific recall after wake-up.
70
- 4. Use `append_event` for meaningful milestones, with a title and 1-8 concise cues.
70
+ 4. Use `append_event` for meaningful milestones, with a title and 1-8 concise keywords.
71
71
  5. Update curated files like `current_state.md` and `open_threads.md` before finishing.
72
72
 
73
73
  Writes sync by default. If sync fails or is skipped, call `sync_memory`.
74
74
 
75
- Event writes update a sibling `events/YYYY-MM-DD.index.json` file. The Markdown event remains the canonical memory; the sidecar is rebuildable. Search ranks indexed events and curated files with BM25 and typo tolerance ([MiniSearch](https://github.com/lucaong/minisearch)), weighting cues above titles and titles above bodies. It returns small coherent evidence blocks from indexed events. If no indexed result is strong enough, it returns no event result instead of scanning whole event logs.
75
+ Event writes update a sibling `events/YYYY-MM-DD.index.json` file. The Markdown event remains the canonical memory; the sidecar is rebuildable. Search ranks indexed events and curated files with BM25 and typo tolerance ([MiniSearch](https://github.com/lucaong/minisearch)), weighting keywords above titles and titles above bodies. It returns small coherent evidence blocks from indexed events; an event found only by its keywords or title returns its first paragraphs. If no indexed result is strong enough, it returns no event result instead of scanning whole event logs.
76
76
 
77
77
  ## Memory Rules
78
78
 
79
79
  - Keep memory concise.
80
80
  - Prefer curated summaries over raw event history.
81
- - Give events a short `Cues:` section; cues weigh the most in search ranking.
81
+ - Give events a short `Keywords:` section; keywords weigh the most in search ranking. Older events with a `Cues:` section still search the same way.
82
82
  - Do not duplicate GitHub-owned facts such as commits, PRs, issues, reviews, or CI results.
83
83
  - Store artifact references plus the missing rationale, preference, assumption, or follow-up context.
84
84
  - Use project scope for project-specific state.
@@ -92,7 +92,7 @@ Example event content:
92
92
  Agent: claude
93
93
  Session: mcp-a1b2c3d4
94
94
 
95
- Cues:
95
+ Keywords:
96
96
  - local-first
97
97
  - token-free
98
98
  - install-story
package/docs/setup.md CHANGED
@@ -57,6 +57,7 @@ Environment variables are optional for local use:
57
57
  - `HIPPOCAMP_GLOBAL_ROOT`: local Lagoon clone path. Default: `~/.lagoon`
58
58
  - `HIPPOCAMP_PROJECT_ROOT`: project root used to infer the current project slug. Default: current working directory
59
59
  - Git worktrees, including Conductor workspaces, resolve to the main repository name so every workspace shares the same project memory.
60
+ - An empty folder (nothing but `.git`, like a fresh agent session folder) has no project: project memory falls back to global memory, so these sessions start clean and stay searchable from everywhere.
60
61
  - `MANIFEST_BASE_URL`: OpenAI-compatible base URL used by `hippocamp dream --write`
61
62
  - `MANIFEST_API_KEY`: API key used by `hippocamp dream --write`
62
63
  - `HIPPOCAMP_DREAM_MODEL`: model used by Dream. Default: `auto`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hippocamp",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "private": false,
5
5
  "description": "Local Git-backed memory for AI agents.",
6
6
  "license": "MIT",
@@ -61,8 +61,9 @@ async function run(options) {
61
61
  }
62
62
 
63
63
  const { cases } = JSON.parse(await fs.readFile(options.cases, "utf8"));
64
- // searchMemory derives the project slug from the folder name, so a bare temp
65
- // folder per slug reads that project's memory without touching any real repo.
64
+ // searchMemory derives the project slug from the folder name, so a temp path per
65
+ // slug reads that project's memory without touching any real repo. The folder is
66
+ // not created: an existing empty folder has no project and reads global memory.
66
67
  const sandboxRoot = await fs.mkdtemp(path.join(os.tmpdir(), "hippocamp-eval-lagoon-"));
67
68
  const answerable = { count: 0, hit: 0, mrr: 0, chars: 0, topScores: [] };
68
69
  const empty = { count: 0, correct: 0, topScores: [] };
@@ -74,7 +75,6 @@ async function run(options) {
74
75
  }
75
76
 
76
77
  const projectRoot = path.join(sandboxRoot, item.project);
77
- await fs.mkdir(projectRoot, { recursive: true });
78
78
 
79
79
  const search = await memory.searchMemory({
80
80
  query: item.query,