{
  "markdown": "# memory-pulse\n\n[![npm](https://img.shields.io/npm/v/memory-pulse)](https://www.npmjs.com/package/memory-pulse)\n[![license](https://img.shields.io/badge/license-MIT-6366f1)](LICENSE)\n[![MCP](https://img.shields.io/badge/MCP-server-a855f7)](https://github.com/t-crew/memory-pulse)\n\n**Site:** [pulse.strategic-innovations.ai](https://pulse.strategic-innovations.ai/), where the guard runs in the browser. [Compare with Mem0, Zep, Letta and CLAUDE.md](https://pulse.strategic-innovations.ai/compare).\n\n**Corrections that outlive the session.**\n\nYour agent acknowledged the correction, then wrote the old value back next\nsession. memory-pulse records it once, puts it at the top of every session,\nand blocks the edit that reintroduces it, quoting the line that retired it.\nFor Claude Code, Codex, Cursor, any MCP client and GitHub Actions. The record\nis a file in your own repository.\n\nMost memory tools stop at showing the correction to the model. The guard is\nthe part that acts on it.\n\nAll four tool definitions come to **~3.3 KB, about 900 tokens**, and a test in\nthis repo fails the build if they grow past that. Independent measurements put a typical five to ten server MCP\nsetup at [50-67k tokens of tool definitions](https://getunblocked.com/blog/mcp-token-budget-autopsy/)\nbefore your first prompt, roughly a third of a 200k context window.\n\n## What it does\n\nA session ends and everything it learned goes with it. memory-pulse keeps a\nledger of **cause → effect** events in a local file, and gives the agent four\ntools over it:\n\n| tool | what it does | runs |\n|---|---|---|\n| `remember` | record a finding (or a **correction**) | locally, offline |\n| `pulse` | re-enter the project on a ranked brief, sized to a budget you set | hosted engine |\n| `recall` | what caused X? what did X cause? when was the link strongest? | hosted engine |\n| `execute` | run JS against memory in a sandbox, where only the return value enters context | hosted engine |\n\nTwo design decisions do the heavy lifting.\n\n**Corrections come first, always.** An event recorded with\n`kind: \"correction\"` outranks everything at every brief size and never decays.\nThe failure this prevents is your agent confidently quoting the benchmark\nnumber you withdrew three sessions ago.\n\n**A weak answer returns nothing.** When recall cannot clear its confidence\nfloor it returns an empty result, and the brief says so.\n\n## Install\n\nOne repo, two plugin hosts, any MCP client. Pick the row for your agent.\n\n**Claude Code, as a plugin.** Skill, MCP tools and the two hooks, from this repo:\n\n```\n/plugin marketplace add t-crew/memory-pulse\n/plugin install memory-pulse@memory-pulse\n```\n\n`claude plugin details memory-pulse` shows what you pay. About 120 tokens are\nalways on, which is the skill's description. The four tool schemas resolve at\nruntime and the hooks are free. The hooks run the plugin's own `server.mjs`,\nso what enforces your corrections is the version you installed.\n\n**Codex CLI, as a plugin.** Same files, read from `.codex-plugin/plugin.json`:\n\n```\ncodex plugin marketplace add t-crew/memory-pulse\ncodex plugin add memory-pulse@memory-pulse\n```\n\nThen, inside Codex, run `/hooks` and trust the two `memory-pulse` entries.\nCodex runs no hook it has not shown you, and installing a plugin does not\ntrust its hooks. That is Codex's rule and a good one.\n\n**Any MCP client, in one line.** Claude Code and Codex are shown, and Cursor\nand the rest take the same stdio command:\n\n```\nclaude mcp add memory-pulse -- npx -y memory-pulse\ncodex  mcp add memory-pulse -- npx -y memory-pulse\n```\n\n**Automatic re-entry without the plugin.** A SessionStart hook runs the brief\nbefore your first prompt and a PreToolUse guard checks edits. Both are\nidempotent, both merge into the settings file without clobbering it, and both\nstay silent in a project that has no ledger.\n\n```\nnpx memory-pulse install-hook            # Claude Code: ~/.claude/settings.json\nnpx memory-pulse install-hook --codex    # Codex:       ~/.codex/hooks.json (then /hooks to trust)\n```\n\nAdd `--project` to either and the hooks are written into the repo, at\n`.claude/settings.json` or `.codex/hooks.json`. Commit that and every clone is\nre-entered and guarded with nothing for anyone to install.\n\nA withdrawn number recorded with `kind: \"correction\"` outranks the history\nthat contained it, at every brief size and in every session.\n\n**Corrections are enforced, not only surfaced.** Showing an agent a correction\nis measurably not enough, because agents re-violate corrections they were just\nshown. The PreToolUse guard sees every `Edit` and `Write` in Claude Code and\nevery `apply_patch` in Codex, where one patch may touch several files and each\nis checked under its own path. An edit that writes back a withdrawn value is\n**blocked**, and the agent is told which ledger line retired it and when. An\nedit that names the replacement beside the old value passes, because \"was $49,\nnow $29\" is a comparison. Only a bare reintroduction is blocked. A shell\nheredoc is not an edit tool and is not guarded, so `check --ci` on the pull\nrequest is the layer that catches that case. Record corrections with the exact\nterms:\n\n```\nremember({ cause: \"pricing-shipped\", effect: \"price-corrected\", kind: \"correction\",\n           note: \"measured willingness to pay is $29\", withdrawn: [\"$49\"], replacement: [\"$29\"] })\n```\n\n### Commands\n\n```\nnpx memory-pulse brief          # the re-entry brief (what the SessionStart hook prints)\nnpx memory-pulse brief --budget 1500   # size it to the tokens you can spare; the richest tier that fits, corrections first and whole\nnpx memory-pulse guard          # PreToolUse hook: blocks edits that reintroduce withdrawn terms\n                                # (a later correction can `supersedes: [t]` an earlier one — only the latest binds)\nnpx memory-pulse check --ci     # Memory CI: one of three verdicts for a change, from files you own\nnpx memory-pulse verify         # row chain + last engine seal; exit 2 if either fails\nnpx memory-pulse brief --offline  # local render when the engine is unreachable\nnpx memory-pulse install-hook --ambient  # also record prompts shaped like corrections\nnpx memory-pulse lint [--ci]    # dry run: do CLAUDE.md / AGENTS.md / .claude/rules still state a value the ledger retired?\nnpx memory-pulse guard allow \"<term>\" --path <prefix> \"<reason>\"   # record a false block as an override\nnpx memory-pulse report         # correction re-violation scoreboard, computed locally\nnpx memory-pulse bench          # instant measured metrics on YOUR ledger\nnpx memory-pulse stats          # your telemetry capsule, signature verified by the engine\nnpx memory-pulse badge          # README badge markdown from your own signed numbers\nnpx memory-pulse install-hook   # installs both hooks (idempotent); --codex targets Codex; --project commits them to the repo\n```\n\nThe plugin also ships a **skill** at `skills/memory-pulse/SKILL.md` that\nteaches the agent when to pulse, how to record corrections with withdrawn\nterms, and how to respect the guard.\n\n## What the brief tells you before it tells you anything\n\nEvery brief opens with one line of provenance, so a session can tell whether\nits memory loaded whole, truncated, or not at all:\n\n```\nmemory-pulse: loaded 852 events from .memory-pulse/events.jsonl · sha256 1a2b3c4d5e6f · 2 binding corrections (10 withdrawn terms) · 1 superseded · ⚠ 1 malformed line skipped: 544 · memory key resumed (+3 new) · tier brief, 5,153 chars\n```\n\nEvery CORRECTIONS line cites its ledger record as `… -> effect (t12) — note`,\nso a correction is evidence the agent can point at. `recall` and the guard name the same `t`.\n\n## Lint: the rules a session loads, checked against the ledger\n\nGovernance files drift. A `CLAUDE.md` written in June still says the price is\n$49 after the ledger retired it in August, and every new session loads the\nstale rule with full confidence. `lint` runs the guard's check over the files\na session will read and gives each of them one of the three verdicts. It\ncovers `CLAUDE.md`, `AGENTS.md`, `.claude/rules/`, `.cursorrules`,\n`.cursor/rules/`, `.github/copilot-instructions.md`, `.codex/AGENTS.md`, and\nany paths you pass:\n\n```\n$ npx memory-pulse lint\nmemory-pulse: loaded 2 events from .memory-pulse/events.jsonl · sha256 8e401a39f323 · 1 binding correction (1 withdrawn term)\n  BLOCKED     CLAUDE.md\n             • \"$49\" was withdrawn at ledger t2: price-49-launched -> price-corrected-to-29 — use $29\n  verified    AGENTS.md\n  no evidence .claude/rules/style.md\nlint: 3 file(s) — 1 blocked, 1 verified, 1 no evidence — a rule your ledger retired is still being loaded into sessions\n```\n\nExit 2 on any blocked file. Under `--ci` it also exits 1 when it found nothing\nto check, so an empty run is never reported as a pass. `--json` is there for\nmachines. It also names the corrections that carry no `withdrawn` terms, which\nsurface in the brief but which nothing can enforce.\n\n## Tamper evidence: the ledger cannot be edited quietly\n\nSince 0.3.1 three mechanisms hold, each with its own job:\n\n- **Row chain.** Every row `remember` writes carries `prev`, the previous chained row's hash, and `hash`, the SHA-256 of its own canonical JSON. Rows that existed before the chain are never rewritten, and the first chained row seals them with a digest. An edit in place, a removed row, a reordered row or an unchained row after the chain started all fail `verifyChain()`. A failed chain blocks every `check`, `guard` and `lint` verdict, because a memory whose own history is in question cannot vouch for anything.\n- **Set head.** The engine also commits to the ledger as an order-free fold, a multiplicative group mod a 3072-bit prime, which is the MuHash construction Bitcoin Core uses for its UTXO set. Shards from several agents fold to the same head in any order, and removing a row is the group inverse, so the *state* stays exact while the *history* stays append-only. A literal XOR fold was measured forgeable, since a linear system hides an edit in 10 ms at 300 rows, and it is not used here.\n- **Seal.** Every read call returns a seal signed by the engine, carrying the row count, the watermark, the set head over every row's full content, and the chain head. The client keeps it in `seal.rain` beside the ledger and presents it on the next call. Locally, rows up to the sealed watermark must fold to the sealed head before the ledger is trusted. At the engine the signature and the fold are re-checked, and an edit below the watermark is reported as drift and blocks. A process with write access can rewrite the file and even the chain. It cannot produce the engine's signature, and it cannot make edited rows fold to the sealed head.\n\nNothing is stored server-side for any of this. The seal travels in the payload, the same way the telemetry capsule and the memory key do.\n\n## Survives compaction, works offline, captures corrections, speaks Python\n\nFour things added on 2026-09-03, each deterministic (no model in the loop):\n\n- **Compaction handoff.** `install-hook` adds a PreCompact hook. Before Claude Code compacts, `memory-pulse handoff` reads the transcript and records what the session was doing as facts: the last asks, the files edited, the last error, and the assistant's last state. The next session start prints it first, online or offline. An instruction-like message is dropped from the note and never recorded.\n- **Offline brief.** When the engine is unreachable, whether air-gapped, on a dead network or during an outage, the session no longer starts empty. `brief` prints a local render carrying every binding correction with its withdrawn and replacement terms, the last handoff and the recent rows, labelled as a local render with no ranking applied. `brief --offline` forces it. Guard, check, lint and verify never needed the network.\n- **Ambient correction capture, opt-in.** `install-hook --ambient` adds a UserPromptSubmit hook. A prompt shaped like a correction, such as `the price is $29 not $49`, `change 0.3.1 to 0.3.2` or `500 events -> 924 events`, is recorded as a correction carrying both terms, so the guard enforces it from the next edit on. A prompt that does not yield both terms is left alone. Silent unless `--verbose`.\n- **Python client.** `python/memory_pulse.py` is a single stdlib-only file with the same ledger format, the same hash chain and the same guard rule. A LangChain or CrewAI agent and a Claude Code session can share one ledger and verify each other's rows. The test suite writes rows from Python and verifies them in Node, and back.\n- **Decode-time guard for local models.** `python/span_guard.py` applies the same rule one layer down. The token that would complete a withdrawn value is masked while the model decodes, so the value cannot be generated, and the replacement's next token is offered in its place. It reads the ledger under the rules `check` uses, so a superseded correction is not enforced and a broken chain refuses to build. Stdlib only, and its suite runs without a model. Measured with mlx-lm on TinyLlama-1.1B and Qwen2.5-1.5B, the same prompt whose plain decode wrote the withdrawn price wrote the corrected one under the guard, and the sentence around it stayed intact. It blocks the spellings it was given, including the written variants you record, and a paraphrase nobody enumerated gets through.\n\n  ```python\n  from memory_pulse import Ledger\n  from span_guard import SpanGuard\n  guard = SpanGuard.from_ledger(tok, Ledger(\".memory-pulse/events.jsonl\"))\n  # mlx-lm: generate_step(..., logits_processors=[guard.logits_processor(mx, np)])\n  ```\n\n## Agent mode: a persistent agent identity that grows (opt-in)\n\nThe default is deliberate. Memory lives in the repo and capture is explicit. Agent mode covers the other thing people ask for, which is an agent that is the *same agent* tomorrow, in every project and every tool, and that grows.\n\n```\nnpx memory-pulse mode agent\nnpx memory-pulse identity \"Blue, research agent for Travis; innovate, don't debate\"\n```\n\nWhat that turns on:\n\n- **An agent ledger** at `~/.memory-pulse/agent/events.jsonl`, belonging to the agent and shared by every project and every tool that speaks MCP. Same format, same hash chain, same seal.\n- **A self block, first in every brief**, online or offline. It carries who the agent is as a pinned line, the standing rules and preferences it has learned, the lessons it carries, and a fingerprint made of the chain head and the engine's seal, so the agent can state which memory it is running on and show it was not swapped or edited overnight.\n- **Growth, after every turn.** A Stop hook deterministically records a stated decision to the project ledger, a user preference or stated lesson to the agent ledger, and any correction shaped like one. It is capped at four rows a turn, tagged `ambient`, never pinned, and never taken from instruction-like text. Identity itself is only ever set by you or superseded by a correction.\n- **Corrections that follow the agent.** A correction on the agent ledger blocks the same edit in any project.\n\n`npx memory-pulse mode deliberate` turns the hooks off again and leaves the ledgers in place. `remember` takes `scope: \"agent\"` from any tool.\n\n## What runs where (the privacy contract)\n\n- Your ledger is a **local file** at `.memory-pulse/events.jsonl` in your\n  project. You can commit it, grep it or delete it.\n- `remember` writes to it directly and **works offline**.\n- Read operations send the ledger's events to the hosted engine over TLS,\n  which computes the answer and forgets the request. **The service keeps no\n  database of your memory.** State arrives in the request and leaves in the\n  response.\n- Telemetry is a **signed capsule beside your ledger** at\n  `.memory-pulse/telemetry.rain`. The engine advances it on each read call and\n  hands it back without ever storing it. `stats` verifies the signature and\n  `badge` turns it into a README badge. Delete the file and it restarts.\n- **State persistence, no database.** After a read the engine hands back a\n  signed **memory key** (`.memory-pulse/memory.rain`, git-ignored). The next\n  read presents it and the engine resumes from it, ingesting only the events\n  recorded since. The answer is byte-identical to a full rebuild. Any mismatch,\n  whether edited history, a stepped ledger size or a bad signature, falls back\n  to a rebuild and says why. Lose the file and you lose nothing but one\n  rebuild. `MEMORY_PULSE_MEMORY_KEY=off` disables it.\n- **Memory integrity.** A note that reads like an instruction, such as \"ignore\n  previous instructions\", \"run this command\" or a fake system tag, is refused\n  by `remember`. One already sitting in a ledger is quarantined at read time\n  and reported, so memory never reaches your agent's context as an order. The\n  signed capsule also raises a **drift alert** when a ledger loses corrections,\n  shrinks, or its usage shape jumps, and the brief footer shows it. Both checks\n  are deterministic lists you can read.\n- This client is the entire client: one file, zero dependencies, readable in one sitting.\n\n## Pricing\n\n- **Free.** Ledgers up to 500 events, 200 reads a day. No account, no key.\n- **Pro, $19/mo.** Ledgers to 20,000 events, unlimited reads. One environment\n  variable, `MEMORY_PULSE_KEY`.\n\nLocal writes are free on either tier.\n\n## Measured, on our own ledger\n\nMeasured on the ledger of the project that builds memory-pulse, a 767-event\nfile of 1.08 MB, measured 2026-09-01:\n\n- A cross-referencing question answered through `execute` returned **124\n  chars** against the 1,080,983-char full dump. The intermediates never\n  entered context.\n- Re-entry briefs at the smallest tier run **~99% smaller** than reading the\n  ledger in.\n- On our recall benchmark of 351 distinct causes, the noise-floor gate returned\n  **zero wrong top answers**. When it could not clear the floor it returned\n  nothing.\n\nThe ratios depend on ledger size, and a ten-event ledger has nothing to\ncompress. The methodology lives in the engine's benchmark suite.\n\n## FAQ\n\n**Why is the engine hosted?** The ranking engine is the part that took the\nresearch. We chose a local ledger, a thin auditable client and a hosted engine\nover shipping a weaker local ranker. If a remote engine is a dealbreaker,\n`MEMORY_PULSE_API` points the client anywhere.\n\n**What about team memory?** Commit `.memory-pulse/` to the repo and your\nteammates' agents pulse the same ledger. Shared hosted ledgers are on the\nroadmap.\n\n**License?** Client: MIT. Engine: proprietary, hosted.\n\nMIT © Travis Crew\n\n## Memory CI: three verdicts, including an explicit empty one\n\n`memory-pulse check` gives a change one of three verdicts, computed locally\nfrom your ledger and your declared invariants:\n\n- **blocked.** The text writes back a value a correction withdrew, and the\n  verdict names the ledger line that retired it and what to use instead. Also\n  fires when the text trips a declared invariant. Exit 2.\n- **verified.** Recorded events bear on the text and none is contradicted.\n  Exit 0.\n- **no evidence.** The ledger has nothing to say, reported as exactly that and\n  never as a pass. Exit 1 under `--ci`. The hook stays silent on this verdict,\n  because a hook that comments on every edit livelocks the agent, and CI is\n  where it is loud.\n\n```\nnpx memory-pulse check --ci --diff origin/main      # added lines of the branch\nnpx memory-pulse check --ci --file docs/pricing.md\necho \"price is $49\" | npx memory-pulse check --ci\nnpx memory-pulse check --receipt --text \"…\"        # engine-signed receipt, keyless verify at /v1/verify\n```\n\n**Invariants** are declared, never inferred. They live one per line in\n`.memory-pulse/invariants.jsonl`, shaped like\n`{\"id\":\"receipt-wording\",\"statement\":\"say tamper-evident\",\n\"patterns\":[\"/\\\\bproof\\\\b/i\"],\"paths\":[\"site/\"],\"severity\":\"block\"}`.\nA pattern written `/…/flags` is a regular expression and anything else is a\nverbatim substring. `paths` scopes the rule to path prefixes, so a rule about\npublic wording does not fire on a proofs file. `severity: \"warn\"` reports\nwithout blocking.\n\n**Overrides** are the false-block signal. `guard allow \"$49\" --path docs/history\n\"historical table\"` records an `override` event scoped to that path prefix.\nThe hit passes there and nowhere else, and both `report` and the signed capsule\ncount it. The guard never guesses, and only explicit withdrawn terms and\ndeclared invariants can block. Measured on our own 852-event ledger, with the\nbench in the engine repo: precision 1.0, zero false blocks over 871 negatives\nincluding 694 real notes, and p95 of 1.7 ms at 1k events.\n\n## PR status check with `uses: t-crew/memory-pulse@v0`\n\nThe same three verdicts as a GitHub check on every pull request, against the\nledger and invariants committed in your repository:\n\n```yaml\n# .github/workflows/memory-ci.yml\non: pull_request\npermissions: { contents: read, checks: write, pull-requests: write }\njobs:\n  memory-ci:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with: { fetch-depth: 0 }\n      - uses: actions/setup-node@v4\n        with: { node-version: 22 }\n      - uses: t-crew/memory-pulse@v0\n```\n\nIt checks the **added lines** of the pull request. `blocked` fails the check,\nand the sticky comment cites the ledger line that retired each value.\n`verified` succeeds with \"N memories checked, M corrections enforced\".\n`no_evidence` is a **neutral** conclusion with an explanation, so an empty\nevidence set is never reported as a pass. The comment is one per pull request\nand updated in place. Nothing leaves the runner unless you pass `api-key` for\nsigned receipts. This repository runs it on itself, which is why\n`.memory-pulse/events.jsonl` is committed, and the first pull request it\nblocked is the demo.\n\n## Releasing\n\n```bash\nnpm version patch && git push --follow-tags\n```\n\nThe tag triggers `.github/workflows/release.yml`, which runs the tests and\npublishes to npm and the MCP registry using GitHub's OIDC identity. There are\nno tokens in the repo and no one-time passwords. npm attaches provenance\nautomatically, so anyone can verify the package was built from this repo.\n\nOne version has four readers: `package.json` for npm, `server.json` for the\nMCP registry, `.claude-plugin/plugin.json` for Claude Code and\n`.codex-plugin/plugin.json` for Codex. All four must agree. `npm version`\nbumps only the first, `test/manifests.test.js` fails until the other three\nfollow, and the release workflow refuses to publish while they disagree.\n",
  "bytes": 22461,
  "sha": "3d12c04c5b05306d6664ad0916cade57653b7c92414123fbb0452bd730b48c4b",
  "repo_slug": "t-crew/memory-pulse",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_t_crew_memory_pulse_902cad6e/readme"
}