{"_id":"@augmem/cortext-pi-extension","_rev":"3-8f6afb279773bea590b28678b17ae54f","name":"@augmem/cortext-pi-extension","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@augmem/cortext-pi-extension","version":"0.1.0","keywords":["pi","pi-coding-agent","pi-extension","pi-package","memory","cortext","compaction"],"license":"Apache-2.0","_id":"@augmem/cortext-pi-extension@0.1.0","maintainers":[{"name":"gwillen","email":"gabewillen@gmail.com"}],"homepage":"https://github.com/augmem/cortext-pi-extension#readme","bugs":{"url":"https://github.com/augmem/cortext-pi-extension/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"c3e827bfabdf025f3cc7eafc3456fbbae71ff539","tarball":"https://registry.npmjs.org/@augmem/cortext-pi-extension/-/cortext-pi-extension-0.1.0.tgz","fileCount":21,"integrity":"sha512-kTme4c5dfeS6QL4qe3WvW586HlXA/jPvkYTuP0owYaA4tcWcf5hMis3qEoAYlJnYFrx+yjlA6R9gSUXBD2eODQ==","signatures":[{"sig":"MEUCIEUka8iPrZJv+ZaJqVWhtIBdN+xPAN4PT37gY5QIQcCEAiEA4ZtSG29KypiPahVGxxZYrAwo1g/q1HVqLekBOnb2XK8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":105827},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18"},"gitHead":"3ceb06953b5d6dd64d61fdd43288ec4153f6eea5","scripts":{"test":"npm run build && npm run build:verify && npm run notice:verify && npm run testcount:verify && node --test tests/*.test.mjs && node scripts/verify-entry.mjs","build":"tsc -p tsconfig.json && node scripts/copy-pi-types.mjs","clean":"rm -rf dist","test:unit":"npm run build && node --test tests/*.test.mjs","typecheck":"tsc -p tsconfig.json --noEmit","build:verify":"node scripts/check-dist-drift.mjs","notice:verify":"node scripts/check-notice-drift.mjs","prepublishOnly":"npm run clean && npm run build","test:integration":"npm run build && node bench/live.mjs","testcount:verify":"node scripts/check-test-count.mjs"},"_npmUser":{"name":"gwillen","email":"gabewillen@gmail.com"},"repository":{"url":"git+https://github.com/augmem/cortext-pi-extension.git","type":"git"},"_npmVersion":"11.8.0","description":"Cortext memory for the pi coding agent: per-session-isolated durable memory, live per-turn recall, zero-LLM-call compaction, and a streaming interrupt gate.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"@augmem/cortext":"^1.2.3","@opentelemetry/api":"^1.9.1"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/node":"^20.0.0"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"peerDependenciesMeta":{"@earendil-works/pi-coding-agent":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/cortext-pi-extension_0.1.0_1786937023488_0.7572960336693193","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@augmem/cortext-pi-extension","version":"0.1.1","keywords":["pi","pi-coding-agent","pi-extension","pi-package","memory","cortext","compaction"],"license":"Apache-2.0","_id":"@augmem/cortext-pi-extension@0.1.1","maintainers":[{"name":"gwillen","email":"gabewillen@gmail.com"}],"homepage":"https://github.com/augmem/cortext-pi-extension#readme","bugs":{"url":"https://github.com/augmem/cortext-pi-extension/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"be712de36c935006ceea230dedc6ddb081415e67","tarball":"https://registry.npmjs.org/@augmem/cortext-pi-extension/-/cortext-pi-extension-0.1.1.tgz","fileCount":21,"integrity":"sha512-XZUAbTqCdiPcZtElOUwAhjFoh10TRVbT0ZaMo6C53yHCYwK9FQOhaztJkju7vPYY1t7yqXkUZaGWO5iWl89Y3g==","signatures":[{"sig":"MEUCIQCkDptOvfjC77aXKJ2VEjmpBoJ4iYU5BdoCSVPD3kUu8QIgI99L0XjmHVCyq9Y3+ISa/VAc0xJEqMbrFoE5XjFOs5E=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":106209},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18"},"gitHead":"7cf208b1e6ba299ef6a0d69bc550687c14324532","scripts":{"test":"npm run build && npm run build:verify && npm run notice:verify && npm run testcount:verify && node --test tests/*.test.mjs && node scripts/verify-entry.mjs","build":"tsc -p tsconfig.json && node scripts/copy-pi-types.mjs","clean":"rm -rf dist","test:unit":"npm run build && node --test tests/*.test.mjs","typecheck":"tsc -p tsconfig.json --noEmit","build:verify":"node scripts/check-dist-drift.mjs","notice:verify":"node scripts/check-notice-drift.mjs","prepublishOnly":"npm run clean && npm run build","test:integration":"npm run build && node bench/live.mjs","testcount:verify":"node scripts/check-test-count.mjs"},"_npmUser":{"name":"gwillen","email":"gabewillen@gmail.com"},"repository":{"url":"git+https://github.com/augmem/cortext-pi-extension.git","type":"git"},"_npmVersion":"11.8.0","description":"Cortext memory for the pi coding agent: per-session-isolated durable memory, live per-turn recall, zero-LLM-call compaction, and a streaming interrupt gate.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"@augmem/cortext":"^1.3.3","@opentelemetry/api":"^1.9.1"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/node":"^20.0.0"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"peerDependenciesMeta":{"@earendil-works/pi-coding-agent":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/cortext-pi-extension_0.1.1_1787032462502_0.10319251981869448","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"pi":{"extensions":["./dist/index.js"]},"_id":"@augmem/cortext-pi-extension@0.1.2","bugs":{"url":"https://github.com/augmem/cortext-pi-extension/issues"},"dist":{"shasum":"31f6ff56824414227b6e3f111ff065a0d68feff8","tarball":"https://registry.npmjs.org/@augmem/cortext-pi-extension/-/cortext-pi-extension-0.1.2.tgz","fileCount":21,"integrity":"sha512-KGz0VgZWrocwNiJQIuWrc/4zFhkw84jxd4aeZu2q9LZa2IVjkmeoojHMUvTozMtv5u/LK0HKFe4ASEuAh9Q+1w==","signatures":[{"sig":"MEYCIQDL1V2Ov225FHMVjwMfWxiktgbYKg+4m3RN/3iXgECD4AIhAOIIoEpIEBHwOq8N/b0hLXIIXCNP9hULtSMaROr/qf8E","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGAxonH9YKpfs+PLXnYR7REiNlz6rygWCNGyRxPSLKuWAiEA01717bMO+lrLCDe7j4uKhX+LPROjAW7ux1ZUaE83GIw="}],"unpackedSize":107818},"main":"dist/index.js","name":"@augmem/cortext-pi-extension","type":"module","types":"dist/index.d.ts","engines":{"node":">=18"},"gitHead":"d15101058b67739cd78c54c2c00e9f6d217226ff","license":"Apache-2.0","scripts":{"test":"npm run build && npm run build:verify && npm run notice:verify && npm run testcount:verify && node --test tests/*.test.mjs && node scripts/verify-entry.mjs","build":"tsc -p tsconfig.json && node scripts/copy-pi-types.mjs","clean":"rm -rf dist","test:unit":"npm run build && node --test tests/*.test.mjs","typecheck":"tsc -p tsconfig.json --noEmit","build:verify":"node scripts/check-dist-drift.mjs","notice:verify":"node scripts/check-notice-drift.mjs","prepublishOnly":"npm run clean && npm run build","test:integration":"npm run build && node bench/live.mjs","testcount:verify":"node scripts/check-test-count.mjs"},"version":"0.1.2","_npmUser":{"name":"gwillen","email":"gabewillen@gmail.com"},"homepage":"https://github.com/augmem/cortext-pi-extension#readme","keywords":["pi","pi-coding-agent","pi-extension","pi-package","memory","cortext","compaction"],"repository":{"url":"git+https://github.com/augmem/cortext-pi-extension.git","type":"git"},"_npmVersion":"11.8.0","description":"Cortext memory for the pi coding agent: per-session-isolated durable memory, live per-turn recall, zero-LLM-call compaction, and a streaming interrupt gate.","directories":{},"maintainers":[{"name":"gwillen","email":"gabewillen@gmail.com"}],"_nodeVersion":"24.11.1","dependencies":{"@augmem/cortext":"^1.3.3","@opentelemetry/api":"^1.9.1"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0","@types/node":"^20.0.0"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"peerDependenciesMeta":{"@earendil-works/pi-coding-agent":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cortext-pi-extension_0.1.2_1789970761598_0.4984222733845496"}}},"time":{"created":"2026-08-17T03:23:43.279Z","modified":"2026-09-21T06:06:01.867Z","0.1.0":"2026-08-17T03:23:43.646Z","0.1.1":"2026-08-18T05:54:22.630Z","0.1.2":"2026-09-21T06:06:01.683Z"},"bugs":{"url":"https://github.com/augmem/cortext-pi-extension/issues"},"license":"Apache-2.0","homepage":"https://github.com/augmem/cortext-pi-extension#readme","keywords":["pi","pi-coding-agent","pi-extension","pi-package","memory","cortext","compaction"],"repository":{"url":"git+https://github.com/augmem/cortext-pi-extension.git","type":"git"},"description":"Cortext memory for the pi coding agent: per-session-isolated durable memory, live per-turn recall, zero-LLM-call compaction, and a streaming interrupt gate.","maintainers":[{"name":"gwillen","email":"gabewillen@gmail.com"}],"readme":"# Cortext for Pi\n\n[![license](https://img.shields.io/badge/license-Apache--2.0-blue)](./LICENSE)\n\n**Living memory for the [pi coding agent](https://pi.dev) — and compaction\nfor free.** Every message is written to a native, on-device memory engine\n([`@augmem/cortext`](https://github.com/augmem/cortext)) as the conversation\nhappens, and every turn re-queries that memory live against the current\nprompt. So when the context window fills up, nothing has to be summarized —\nnothing was being thrown away in the first place.\n\nA port of [`@augmem/cortext-openclaw-plugin`](https://github.com/augmem/cortext-openclaw-plugin)\nto pi's extension surface: same memory loop, same architecture, same test and\nproof standards — on the events the harness actually emits.\n\n```bash\n# install from npm (listed in the pi package gallery, pi.dev/packages)\npi install npm:@augmem/cortext-pi-extension\n# then restart pi\n\n# dev install from source instead\ngit clone https://github.com/augmem/cortext-pi-extension ~/.pi/agent/extensions/cortext\ncd ~/.pi/agent/extensions/cortext && npm install && npm run build\n# then restart pi — the extension is auto-discovered\n# (equivalently: pi -e /abs/path/to/dist/index.js for a single run)\n```\n\n## Why\n\n- **Recall on every turn, not just after compaction.** Each assembly queries\n  memory live with the current prompt — what gets injected is relevant to\n  what's being asked *right now*, and a correction made this turn is\n  reflected on the next (no cross-turn cache, no frozen digest). Recall runs\n  on two surfaces: the system prompt (per user prompt) and a hidden per-LLM-call\n  message (so mid-run tool loops get fresh recall too). Mid-turn, a streaming\n  gate watches the model's reasoning and answer as they stream and can stage\n  recall for the next assembly.\n- **Compaction with zero LLM calls.** Pi's native compaction pays a\n  summarizer every time the window fills and hopes the summary kept what\n  you'll need. Cortext compaction just moves a window: the extension returns\n  its own `CompactionEntry` (bridge note + exchange-aligned cut, `fromHook: true`)\n  and **no summarizer LLM call is made** — verified live (the compaction\n  response carries no `usage`). Archived content comes back through\n  query-relevant recall every turn.\n- **Measured, not vibes.** The live bench ([`bench/live.mjs`](bench/live.mjs))\n  runs a real `pi` session over RPC and proves the loop end-to-end: after an\n  extension compaction archives the exchange containing a nonsense-token\n  codename, the transcript shows the codename exists **only** in the archived\n  prefix, and the next turn answers it from memory injection alone. Run it:\n  `npm run test:integration`.\n- **Fast enough to forget it's there.** Median 2.74 ms per-message durable\n  ingest (mean 2.78 ms, p95 3.21 ms; N=200, warm store, 200-char messages,\n  `node bench/latency.mjs`), fully offline after a one-time model download,\n  no per-turn network, no API keys.\n- **Isolated by default.** One SQLite store per conversation — a shared\n  project agent can't leak one user's facts to another. Verified live with\n  positive and negative controls (a different session *cannot* read the\n  fact; the same scope *does*).\n- **The transcript is never mutated.** Compaction is a pi-native entry that\n  pi appends to the session file — the extension only chooses the cut and\n  supplies the bridge summary. No destructive surgery.\n\n## The loop\n\n```mermaid\nflowchart LR\n    subgraph turn[\"every turn\"]\n        M[message_end<br/>user / assistant / toolResult] -->|ingest, durable| S[(Cortext store<br/>on-device SQLite)]\n        P[current prompt] -->|before_agent_start + context| R{{live recall<br/>query-relevant LTM<br/>+ working memory}}\n        S --> R\n        R -->|memory block<br/>system prompt + hidden msg| CTX[model context]\n        G[message_update gate<br/>watches text + thinking deltas] -.->|stage recall| CTX\n    end\n    B[compact] -->|session_before_compact:<br/>exchange-aligned cut, 0 LLM calls| W[pi CompactionEntry<br/>verbatim tail rides along<br/>store keeps everything]\n    W --> R\n```\n\nRecall runs from turn one; compaction only changes how much verbatim tail\nrides along — the memory side never changes.\n\n## Compaction\n\nCortext answers pi's `session_before_compact` with its own compaction —\npi's own summarizer is never called. Because every message is already in the\ndurable store (`message_end` ingest), the returned `CompactionEntry` just\npicks an exchange-aligned cut (a user-message entry, so the kept tail is a\nself-contained exchange — never a tool result orphaned from its call) and\ncarries a bridge note in `summary` plus its provenance in `details`\n(`engine: \"cortext\"`, mode, dropped count). pi then natively rebuilds the\nmodel context from that entry — summary + entries from `firstKeptEntryId`\nonward — so the archived prefix simply stops riding along, and it keeps\ncoming back through query-relevant recall.\n\nThis is where the port diverges from the openclaw plugin in one deliberate\nway: pi owns the kept window natively (its `buildContextEntries` reconstructs\nfrom the compaction entry), so the openclaw plugin's own anchor/state\nself-healing machinery was **not** ported — there is no second shadow window\nto keep consistent.\n\nTwo modes (`compactionMode`):\n\n- **`hybrid`** (default) — keep the system prompt + memory injection + a\n  verbatim tail of the last `protectTail` messages, walked back to a\n  user-message boundary so the tail is a self-contained exchange.\n- **`full`** — keep the system prompt + memory injection only; the verbatim\n  window shrinks to the current exchange. Maximum savings — memory IS the\n  context.\n\nIf there is nothing before the protected window to archive, the extension\nreturns `undefined` and pi's default compaction applies — memory never\nvetoed a compaction it had no content behind.\n\n## How it plugs in\n\nFour pi surfaces (verified against the installed `pi` 0.84.2 `dist/` types,\nnot docs — see `src/pi.d.ts`):\n\n1. **Ingest** — `message_end` (user, assistant, toolResult). Tool-call\n   arguments are rendered as `[tool call] name {args}` truncated at 2,000\n   chars in the durable record (the command matters; a 100 KB patch payload\n   shouldn't dominate the store); tool *results* are ingested in full.\n2. **Recall** — `before_agent_start` appends a fenced memory block to the\n   (chained) system prompt per user prompt; `context` (fires before every\n   LLM call) appends a `display: false` custom message carrying only\n   memories not already in that run's system-prompt block, plus — once a\n   compaction entry is in context — the live working-memory snapshot\n   deduplicated against the kept window. Both drain recall the streaming\n   gate staged, under the same scope key.\n3. **Compaction** — `session_before_compact` returns the extension compaction\n   (zero LLM calls) or `undefined` (see above).\n4. **Streaming gate** — `message_update` feeds `text_delta` /\n   `thinking_delta` through Cortext's interrupt gate. On\n   `should_interrupt` / `at_boundary` the recalled memory is staged on a\n   bounded bus keyed by scope, for the next assembly. Observe-only — see\n   [limits](#design-and-limits).\n\n## Install & configure\n\npi has no per-extension config surface (verified against the installed\n0.84.2 `Settings` types), so config resolves in order:\n\n1. `CORTEX_PI_CONFIG` env var (JSON object) — wins\n2. `~/.pi/agent/cortext/config.json`\n3. defaults (a malformed source is skipped, never fatal: memory must not\n   break the agent)\n\nResolution happens once when the extension registers — there is no config\nwatching; restart pi to apply changes.\n\n| Key | Default | Meaning |\n|-----|---------|---------|\n| `dbPath` | `cortext` | Directory (under `~/.pi/agent/cortext`) for stores; `'.'`/`'..'` fall back to `cortext` (traversal guard) |\n| `memoryScope` | `session` | Isolation boundary: `session` / `agent` / `global` |\n| `focus` | `0.45` | F knob: retrieval breadth vs precision |\n| `sensitivity` | `0.5` | S knob: affective relaxation of the gate |\n| `stability` | `0.5` | T knob: gate refractory + boundary pacing |\n| `recallLimit` | `0` | extra injection cap; `0` means none (Cortext already limits `retrieved_memory`) |\n| `interruptGate` | `true` | run the streaming gate |\n| `ingestReasoning` | `true` | feed `thinking` deltas, not just answer text |\n| `autoConsolidate` | `true` | consolidate on compaction |\n| `debug` | `false` | per-message chatter (gate-observe, per-LLM-call recall) prints only when enabled; gate-fire, injection, compaction, and error logs are always on |\n| `compactionMode` | `hybrid` | `hybrid`: memory + verbatim tail; `full`: memory only |\n| `protectTail` | `6` | hybrid: trailing messages kept verbatim (exchange-aligned) |\n\nRequirements: Node.js ≥ 18; a platform with a `@augmem/cortext` native\nprebuild (installed as a dependency). On first use Cortext downloads its\nlocal model assets once; after that, memory runs fully offline.\n\n## Isolation\n\nCortext keeps one SQLite store **per isolation scope** — source ids are\nmetadata within a store, so distinct scopes are distinct databases (staged\ngate memory is keyed by the same scope, so it can't cross the boundary\neither). `memoryScope`:\n\n- **`session`** (default) — one store per session\n  (`ctx.sessionManager.getSessionId()`). Safe when an agent serves multiple\n  people: memory never crosses conversations.\n- **`agent`** — one store per project (`safe(basename(cwd))`, `main` when\n  absent) — memory persists across that project's sessions. Use only for\n  **single-user** projects. Note the identity is the cwd basename: two\n  projects with the same folder name share a store (documented trade-off;\n  pi has no stable project id in the extension context).\n- **`global`** — a single shared store.\n\nBoth session isolation (positive + negative controls) and scope-key\ndeterminism are asserted by the unit suite; live session isolation — with a\nnonsense-token needle a model can't guess — is asserted by\n[`bench/live.mjs`](bench/live.mjs).\n\n## Design and limits\n\n- **Recalled memory is untrusted input.** A prior turn could have ingested a\n  prompt-injection payload. Before injection, recalled text is stripped of\n  data-fence breakouts and fake system markers, and wrapped in a block that\n  explicitly labels it as reference data, not instructions. This is a\n  mitigation, not a guarantee.\n- **Fine-grained recall is a work in progress.** Retrieval reliably brings\n  back conversationally salient facts (decisions, named results, user\n  statements); one-off identifiers buried in bulk tool output are hit or\n  miss (same state as the openclaw plugin and the underlying engine — see\n  its release notes for needle-probe trends).\n- **The gate cannot splice into a live decode, and cannot request a\n  re-pass.** Pi's agent event stream is observe-only and has no\n  `before_agent_finalize`-equivalent hook, so on interrupt the extension\n  stages recall for the *next* assembly and that is the whole re-pass story.\n  The openclaw gateway's `revise` behavior (the `forceRepass` key) has no\n  pi counterpart and is deliberately out of scope here.\n- **Tool-call arguments are truncated at 2,000 chars** in the durable record;\n  tool *results* are ingested in full.\n- **The engine's consolidation hint is deliberately not acted on at ingest.**\n  Compact-time consolidation is the sufficient, safe cadence (openclaw\n  parity; measured retrieval is identical with or without the hint).\n- **pi owns the window.** After a Cortext compaction, the kept window is\n  whatever pi reconstructs from the entry; the extension does not (and cannot)\n  re-window on every assembly the way the openclaw context engine did. The\n  per-LLM-call recall surface compensates: once windowed, the working-memory\n  snapshot rides along with every recall call.\n- **An engine that can't open degrades its scope, not the agent.** If the\n  native store cannot be opened for a scope (missing store dir, corrupt DB\n  file, binding rejection), that scope degrades to recall-less passthrough\n  with a one-time `cortext store error` log line per scope — the agent keeps\n  running, and memory for that scope stays off for the rest of the pi process\n  lifetime: there is no automatic retry, so restarting pi re-attempts the\n  store open.\n- **Telemetry is spans only, and fully offline.** Control and failure paths\n  emit OpenTelemetry spans via `@opentelemetry/api` (a no-op proxy when no\n  collector/SDK is present), so the extension stays fully offline with no\n  per-turn network and no new diagnostics dependencies beyond the OTEL API;\n  span attributes are bounded enums/numbers only.\n\n## Verified\n\nEvery release passes, against a real `pi` 0.84.2 installation and the\nshipped engine, before publish:\n\n- **Unit + types** — `npm run typecheck` and `npm test` (90 tests, native\n  engine, offline): config resolution, store/scope-key determinism, engine\n  ingest/recall/consolidate, compaction cut logic (exchange alignment,\n  previous-boundary floor, fallback), stream-gate segmentation/staging,\n  bus bounding, plus pi-surface tests for the handler set (ingest mapping,\n  recall injection shapes, zero-LLM compaction result, shutdown flush).\n  `scripts/verify-entry.mjs` asserts the factory registers exactly the\n  expected handlers against a stub `ExtensionAPI`.\n- **Live loop** — `npm run test:integration` (`bench/live.mjs`) drives a real\n  `pi --mode rpc` session in an isolated temp project (own `--session-dir`,\n  `--no-extensions`, `--approve`'d project-level compaction settings, unique\n  `CORTEX_PI_CONFIG` dbPath — which also proves the env config path) and\n  asserts, per control:\n  - **compaction-zero** — `compact` yields an extension-provided\n    `CompactionEntry` (`details.engine=\"cortext\"`, `fromHook: true`)\n    with **no `usage`** (zero summarizer LLM calls)\n  - **compaction-window** — the needle entry is provably before\n    `firstKeptEntryId` on the branch (archived, exchange-aligned cut)\n  - **recall-post-compaction** — the next turn answers the needle, which\n    exists only in the archived prefix (memory injection is the only source)\n  - **isolation-session / isolation-store** — a different live session (and a\n    fresh handle on its store) cannot read the fact; the same scope can\n    (positive control)\n  - **ingest-durable** — a *fresh* handle on the same SQLite DB recalls the\n    needle (cross-process durability)\n  - **gate-observe / ingest-mechanism** — the streaming gate observed\n    assistant messages and processed turns with zero handler errors\n    (interrupt/boundary fires are state-dependent; observed fires are\n    reported as evidence, e.g. `gate fires=48`)\n\nVerified live twice in a row (both `8/8`), with\n`qwen3.8/Qwen3.8-27B` at `thinking=low`, pi 0.84.2:\n\n```text\nPASS  compaction-zero (extension CompactionEntry, no summarizer LLM call)\nPASS  compaction-window (needle provably archived, exchange-aligned cut)\nPASS  recall-post-compaction (archived-only needle answered from memory)\nPASS  isolation-session (different session cannot read the fact, live model check)\nPASS  ingest-durable (fresh handle on S1 store recalls the needle)\nPASS  isolation-store (fresh handle on S2 store does NOT recall the needle)\nPASS  gate-observe (streaming gate observed, no handler errors)\nPASS  ingest-mechanism (extension active, recall injected, no handler errors)\nbench: 8/8 controls passed\n```\n\n(The live bench needs a configured model for the pi session; it makes real\nLLM calls but no compaction summary call.)\n\n## Develop\n\n```bash\nnpm install\nnpm run build       # tsc → dist/\nnpm run typecheck\nnpm test            # build + unit tests (native engine; fast, offline)\nnpm run test:integration   # live bench against the installed pi\n```\n\n`npm test` includes a build-reproducibility gate (`npm run build:verify`\ndiffs a fresh tsc emit against `dist/`), a dependency-inventory gate\n(`npm run notice:verify` fails when a `package.json` runtime dependency is\nnot named in `NOTICE`), and a test-count gate\n(`npm run testcount:verify` fails when the README Verified test count\ndrifts from `tests/*.test.mjs`); a GitHub Actions CI workflow\nruns typecheck, the full test chain, and `npm audit`.\n\nThe engine-backed tests need the one-time on-device model (a pinned,\nsha256-verified 135 MB AIST GGUF plus tokenizer, fetched from Hugging\nFace and cached in the platform cache dir; `CORTEXT_MODEL_CACHE_DIR`\noverrides the location). On a fresh machine run\n`node scripts/ci-warm-model.mjs` once — CI does the same with a\nsha-keyed runner cache.\n\n`src/pi.d.ts` is transcribed from the **installed**\n`@earendil-works/pi-coding-agent` package's `dist/` types — if pi's\nextension surface changes, the transcription is re-verified against the real\npackage, not docs.\n\n## License\n\nApache-2.0. See [LICENSE](./LICENSE) and [NOTICE](./NOTICE).\n","readmeFilename":"README.md"}