omp packages

pi-blackhole0.5.15

Unified compaction + observational memory extension for Pi — compresses conversation context while preserving durable observations and reflections

k0valik Published 1 day ago MIT ~12.1K downloads/mo

Untested on omp

This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.

Did it work for you?

Result

One report per person. A new report replaces your old one.

Reports 0

No reports yet.

Readme

pi-blackhole

Deterministic compaction + session-aware observational memory for Pi — in one unified extension.

/blackhole replaces Pi's LLM-based /compact with an algorithmic structural summary — fast, and the compaction step itself is zero-cost. Three background workers (Observer, Reflector, Dropper) run as separate billed model calls to capture durable facts and decisions that survive across compactions. Per-worker model fallback chains with persisted cooldowns. Manual flush mode. One JSON file to configure it all.

A note from the maintainer: this is experimental.

It won't save you tokens by itself. Compacting is free — no model call, just structure. Remembering is not: the background workers wake up as your session grows, read what happened, and write down what seems worth keeping. Those are real billed calls. Untuned, on your main coding model, this costs more than doing nothing. It only earns its keep with the workers on something cheap or free.

It also doesn't solve compaction — nobody has, as far as I know. It takes a different approach (observational memory, via pi-observational-memory) and merges it with pi-vcc, because the two conflicted when installed together. I merged them and vibeslopped a lot on top: robustness work, new behavior, and bugfixes ported from both upstreams. The recap is pattern-matching, not understanding: it guesses at goals and preferences from their shape. The memory is model-written, so it has the opposite weakness: it can keep what sounds right but isn't. Assume both need the search tool as a backstop.

Want only one half? Use the project that does just that half. No hard feelings.

Scope stays tight on purpose: forks welcome, bug fixes welcome and reviewed, new features only if they serve conversation compaction itself — not the week's AI fad bolted on. Almost everything is a toggle, which I'll admit is tiring; I use a small slice myself. Memory on most days, off some days. Workers on free models, waking rarely, size-based auto-compaction as a backstop. Most days I don't compact at all — fresh sessions beat resumed ones.

Best fit is someone who doesn't count every token: code locally and push the workers to free hosted models, or code on a top model and run the workers on a local card — knowing each worker's different instructions wipe the local prompt cache for the next one. No measurements, no comparisons against other methods. Fast and nearly free the way I run it, good enough for what I need.


Install

# From npm (recommended)
pi install npm:pi-blackhole

# Or directly from GitHub.
# Requires npmCommand to be set in settings.json, otherwise pi runs
# `npm install --omit=dev`, devDependencies are skipped, and dist/ is not built.
# Example: "npmCommand": ["npm"] in ~/.pi/agent/settings.json
pi install git:github.com/k0valik/pi-blackhole

If you have standalone pi-vcc or pi-observational-memory installed, remove them first — they conflict and will prevent blackhole from loading:

pi uninstall npm / git:https://github.com/sting8k/pi-vcc
pi uninstall npm / git:https://github.com/elpapi42/pi-observational-memory

Then /reload or restart Pi. The config file at ~/.pi/agent/pi-blackhole/pi-blackhole-config.json is created with sensible defaults — no setup required for the default behavior. Config merges global → project → env → session (session is ephemeral). See docs/CONFIG.md for tuning or run /blackhole settings to open the interactive overlay.

Want a guided setup? Pass llms.txt to your agent — it will walk you through the interview, including picking cheap fallback models for your providers.


✨ What's new

Latest release: 0.5.15

  • Timeline and scheduled memory exports — /blackhole-export days:7 mode:timeline produces a chronological rolling snapshot. Use pi --no-session --blackhole-export "days:7 mode:timeline out:memory/weekly.md" -p for scheduled exports; distilled export remains the default.
  • Keep unobserved original context — opt into tailBehavior: "unobserved" to retain the raw history the observer has not completed. The default remains "minimal"; oversized protected tails defer compaction safely.
  • Optional context-pressure fallback — contextPressureFallback: "vcc" can bridge older retained context while keeping a recent raw suffix. It is independent of tail policy and defaults to "off".
  • Answers to question tools survive compaction — best-effort recognition covers six inspected tool families, preserving confirmed choices and scoped user notes while excluding cancellation and timeout defaults.
  • Timestamped reflections and replacement tracking — reflection evidence time is shown by default (reflectionTimestamps: true); opt into reflectionSupersedes: true to retire replaced reflections from active memory views.
  • Optional memory capture before leaving a session — observeOnShutdown: true allows one bounded observer attempt before quit, new, resume or fork. It defaults to false.

See CHANGELOG.md for detailed fixes and contribution history.


What it does

Long engineering sessions degrade. Pi's native /compact calls an LLM to write a free-form prose summary — then compacts that summary, then compacts the next. After a few cycles, load-bearing details vanish: why a decision was made, which approaches were rejected, what the user clarified early on. The session is still alive; the agent has stopped carrying the real context.

pi-blackhole solves this in two complementary ways:

  • Algorithmic compaction — a deterministic, zero-cost compile() pipeline extracts structured sections (goal, files, commits, preferences, brief transcript) and replaces the old conversation with one compact block. No LLM is called for compaction itself.
  • Observational memory — three background workers (Observer → Reflector → Dropper) run during the session, capturing timestamped facts and distilling durable reflections in a session ledger that survives every compaction.

Both halves share a single hook and a single output. Together they keep the agent's context sharp across arbitrarily long sessions — without the cost, drift, or erosion of repeated LLM-based summarization.

Blackhole also preserves confirmed answers from several common question-tool result formats in deterministic compaction and observer context. This is best-effort and shape-guarded: cancellations, drafts, timeouts, and control actions are not treated as user decisions.


Commands

Command Description & Options
/blackhole Manual compact — deterministic structural summary
/blackhole settings Open the configuration overlay (Alias: /blackhole configure)
/blackhole changelog Open the in-app changelog viewer
/blackhole cleanup Remove orphaned pending files
/blackhole om-off Disable observational memory
/blackhole om-on Enable observational memory
/blackhole-memory Memory pipeline status & token counters (Same as /blackhole-memory status)
/blackhole-memory view Show visible observations and reflections (after compaction trimming), copied to clipboard
/blackhole-memory full Show all recorded memory (including dropped observations), copied to clipboard
/blackhole-recall <query> Search session history. Supports page:N, scope:all, mode:file|touched, regex (Also available to the agent as the recall tool)
/blackhole-export Export distilled project memory (observations/reflections across past sessions + pending buffers) to import-ready markdown (Options: out:<path>.md days:<N> mode:distilled|timeline — e.g. days:7 mode:timeline for the recent chronological stream)

All commands work regardless of compaction mode — only when auto-compaction fires changes. See Compaction modes below.

days:N is a rolling snapshot, not exactly-once replication: the still-active session stays excluded and its memory can age out of the window before it becomes eligible.

Headless runs for cron/systemd timers reuse the same argument grammar: pi --no-session --blackhole-export "days:7 mode:timeline out:memory/weekly.md" -p (run from the project root). The summary prints to stdout, parse/IO failures exit non-zero, and the snapshot semantics above apply unchanged. Pass --no-session to guarantee no session file is created (message-less probe runs persisted none, but session persistence is host behavior).

The `recall` tool (agent-facing)

The agent gets one unified recall tool that handles every form of historical lookup. Searches read the raw session file directly, bypassing compaction.

Input What it does
[12-char hex] Recover source evidence for a specific observation or reflection ID from the session ledger.
#N Expand a session entry by index (show full content, bounded by the response budget).
#N:path Drill-down into file content from a tool call (e.g. #42:auth.ts shows first 30 lines; #42:auth.ts:30 shows the next 30; #42:auth.ts:full shows everything).
#N:text Drill-down into a message body (user/assistant/tool/bash text) with the same paging (#42:text, #42:text:30, #42:text:full) — the continuation path for budget-clipped entries.
Free text BM25-ranked search across transcript and/or file content. Rare terms weighted higher.
mode:file Search only write/edit file content.
mode:touched Aggregate all files written/edited across the session, grouped by path.
Regex Pattern search (e.g. fork.*pi-vcc, hook|inject).
scope:all Search across all session lineages (default: active lineage only).

When the agent expands a session entry (#N), related observations and reflections from the session ledger are automatically shown alongside the expanded content — so the agent gets the raw transcript and the durable fact layer in one call.

Every recall response is capped at recallResponseMaxChars (default 48,000 ≈ 12k tokens). Search snippet lines, expanded entries, drill-down bodies, and related observation bodies are clipped to keep a single huge stored message from flooding the context; a truncation marker names the omitted entries and how to continue (#N:text / #N:path / page:N). A capped drill-down cuts only at line boundaries and names the first line it did not show, so the next #N:path:offset:limit call continues there without skipping or repeating lines.

The /blackhole-recall command exposes the same engine to the user. Results are shown as a collapsible message and auto-fed to the agent as context.


Compaction modes

Two modes, one shared goal: keep your agent's context sharp without manual housekeeping. (compaction: "off" is a third escape hatch that hands everything back to Pi.)

Auto (default) Manual (compaction: "manual") Off (compaction: "off")
Workers run? Yes Yes Yes (unless memory: false)
Observations go to Conversation markers (invisible in TUI) Per-session disk buffers Conversation markers
Auto-compact on agent_end Yes — fires at the auto-compaction threshold (preset curve by default) No No (Pi handles it)
/compact (Pi built-in) Replaced by blackhole Pi handles Pi handles
/blackhole Optional Required to flush + compact Optional, but works
Use case "Install and forget" "I want to control when context gets compressed" "Let Pi handle it, but I want /blackhole when I need it"

Manual mode is the maintainer's daily driver: workers still run, but observations accumulate in <sessionId>-pending.json files instead of cluttering the conversation. /blackhole flushes the buffer, runs algorithmic compaction, and injects durable reflections in one shot.

compaction: "off" + memory: false (or PI_BLACKHOLE_PASSIVE=true) completely disables all background workers and blackhole's auto-compaction — useful for debugging or comparing against Pi's native path. Explicit /blackhole still works in this mode.

Who owns the compaction?

With the default compactionEngine: "blackhole", blackhole's session_before_compact hook owns every compaction Pi initiates — threshold auto-compact, overflow recovery, and /compact — replacing Pi's LLM summarizer with the deterministic pipeline. The only exception is defensive: if both the VCC summary and the OM projection come up empty (a pathological all-noise transcript), blackhole declines and Pi's native summarizer runs, so you never get a context-free replacement. With compactionEngine: "pi-default", compaction: "manual", or compaction: "off", Pi handles everything except explicit /blackhole. See docs/CONFIG.md → compactionEngine for the full interaction matrix.

How does /blackhole compare to /compact?

  • /compact calls an LLM to write a free-form summary — costly, lossy, no memory layer.
  • /blackhole uses algorithmic section extraction (goals, files, commits, preferences…) plus injects observations and reflections from the session ledger. No LLM is involved in the compaction itself. Fast, deterministic, memory-preserving - the observational memory pipeline's arrived results apply instantly on compaction.

/blackhole is essentially a single /compact that just works — especially in manual mode.


Unobserved raw tail (opt-in)

tailBehavior: "unobserved" retains original messages after trustworthy completed OM observation coverage, backing up to a complete user turn to avoid splitting tool interactions. Missing, invalid or disconnected coverage retains extra original history. Compaction and VCC never advance OM coverage. Pending observer input remains the original branch, including across restart and repeated compactions; no blocking observer flush is added.

The default remains "minimal"; "pi-default" is unchanged. This mode does not truncate retained tool outputs. If the raw tail plus summary cannot fit the model context minus Pi's reserveTokens, capacity is unknown, or no useful summary exists, Blackhole explicitly defers compaction rather than dropping the tail or invoking native summarization. Token estimates are not a provider acceptance guarantee.

Reusable VCC pressure fallback (opt-in)

contextPressureFallback: "vcc" enables a pressure fallback independently of tailBehavior; the default is "off", so existing modes retain their behavior. A preferred context that fits is left alone. Otherwise VCC bridges older retained messages and keeps a recent raw suffix bounded by Pi's keepRecentTokens, using the model window minus reserveTokens and the current system prompt. Whole user-turn boundaries conservatively keep tool interactions together. If the newest turn cannot fit, capacity is unknown, or no useful summary is available, compaction explicitly defers. No native fallback silently drops the protected backlog.

Compaction records om.preserveSourceHistory as continuity intent, not observed coverage. The observer still reads original branch source entries, not VCC summaries, across compactions and restart. Pending work remains eligible even after the opt-in is disabled. No blocking observer flush is added. Estimates do not prove provider acceptance; tool schemas and provider-specific overhead can still cause an overflow.

How it works

When /blackhole fires (manually or via the auto-trigger), two things happen in one shot:

  1. The vcc pipeline analyzes the transcript tail and produces a structured summary: session goal, file changes, commits, outstanding blockers, user preferences, and a rolling brief transcript. Deterministic — same input always produces the same output.
  2. Observational memory injection renders accumulated observations and reflections from the session ledger and appends them below the summary.

The agent receives a deterministic recap of recent work plus durable facts from the full session history — in a single replacement block. No LLM was called for the compaction itself.


Quick start config

Defaults target ~128k context models and work out of the box — no tuning required. To keep costs low, set cheap models for the background workers (the only required change for most setups):

{
  "observerModel": { "provider": "openrouter", "id": "qwen/qwen3-next-80b-a3b-instruct:free" },
  "reflectorModel": { "provider": "cerebras", "id": "gpt-oss-120b" },
  "dropperModel": { "provider": "cerebras", "id": "gpt-oss-120b" }
}

Fallbacks (optional): each worker tries stageModel → stageFallbacks → base model → session model (skipping cooled-down models). By default the session model is the last-resort fallback (sessionFallback: true), so workers keep running even with no worker models configured. If you don't want worker runs to touch your session model (extra cost and prompt-cache busting), configure cheap worker models (above) and set sessionFallback: false — or set model as a shared fallback so the session model is never reached. See docs/CONFIG.md → Model Configuration.

Config file: ~/.pi/agent/pi-blackhole/pi-blackhole-config.json

Full reference — every key, default, and env override — lives in:

  • 📘 docs/CONFIG.md — authoritative config reference. Start here for tuning.
  • 🤖 llms.txt — agent-facing interview. Pass it to your agent for a guided setup.
  • 📦 example-config.json — annotated example with fallback rationale and thinking levels.

Demo

/blackhole collapses ~143k tokens of conversation into a ~6.3k structured summary (YMMV based on your settings). /blackhole-memory shows pipeline status. /blackhole-recall searches history — the agent can do the same via its recall tool.

https://github.com/user-attachments/assets/a7dd804d-6aca-4bdb-8b6e-0dd779363a43

The three memory workers

Three background workers (separate LLM calls) run automatically during the session when memory: true (the default):

  • Observer — reads conversation since the last observation marker and extracts timestamped facts: events, decisions, preferences. Input is capped to observerChunkMaxTokens as an oldest-first prefix (overflow drains in bounded follow-up batches) to prevent context blowup on long sessions — the coverage cursor never advances past entries the model was not shown. Runs most frequently.
  • Reflector — distills new observations into durable reflections: stable facts, patterns, and constraints that survive future compactions. Runs less often.
  • Dropper — prunes low-value observations from active memory when the pool exceeds observationsPoolMaxTokens, while keeping reflections and other long-term elements safely in the session ledger.
[Conversation turn] ──> (accumulated tokens >= observeAfterTokens)
                            │
                            v
                    1. OBSERVER   (extracts timestamped observations)
                            │
                            v
                    2. REFLECTOR  (synthesizes durable reflections)
                            │
                            v
                    3. DROPPER    (prunes low-value observations)

Each worker uses an agentLoop with tool-calling capabilities — they don't just make a single LLM call. The observer, for example, can call record_observations multiple times per run to work through a chunk incrementally.

Optional observeOnShutdown (default false) makes at most one observer request on an eligible persisted session's unobserved tail before quit, new, resume or fork. The minimum is 200 raw tokens; one shared 20-second deadline covers waiting for in-flight consolidation and the request. No new reflector, dropper, retry or multi-chunk drain is launched. Reload is skipped. Forced exits and hosts with shorter shutdown deadlines may prevent completion. See shutdown observation.

If any stage fails (model error, rate limit, timeout), remaining stages are skipped and the full pipeline retries on the next agent_start or turn_end. A 30-second retry gate prevents hammering failing APIs. Within each stage, the runtime tries all configured fallback models before giving up — each failed model is cooled down and skipped in subsequent attempts.


What the agent sees after compaction

After compaction, the agent sees something like this (sections appear only when relevant — a session with no git commits won't show [Commits]):

[Session Goal]
- Fix the authentication bug in login flow
- [Scope change]
- Also update the session token refresh logic

[Files And Changes]
- Modified: src/auth/session.ts
- Created: tests/auth-refresh.test.ts

[Commits]
- a1b2c3d: fix(auth): refresh token after password reset

[Outstanding Context]
- lint check still failing on line 42

[User Preferences]
- Prefer Vietnamese responses
- Always run tests before committing

[user]
Fix the auth bug...

[assistant]
Root cause is a missing token refresh...
...transcript continues...

---
The conversation before this point has been compacted into the summary above.
Details not captured here — exact code, error messages, file paths — are only recoverable via `recall`.
Use `recall` to search the session history. Do not redo work already completed.

## Reflections
[c3d4e5f6a1b2] User is building Acme Dashboard on Next.js 15 with Supabase auth.

## Observations
[a1b2c3d4e5f6] 2026-05-23 [high] User decided to switch from REST to GraphQL; motivation was reducing over-fetching.
[b2c3d4e5f6a1] 2026-05-23 [medium] GraphQL migration completed; user confirmed working.

----
Bracketed ids in reflections and observations connect to their source session entries.
These are condensed memories from earlier in this session.
When entries conflict, the entry with the latest timestamp reflects the latest known state.
Use `recall` with an id to retrieve original context.
----

Note: The OM injection format uses ## Reflections and ## Observations Markdown headers followed by a brief footer. Each observation and reflection has a 12-char hex identifier the agent (and you, via /blackhole-recall) can use to recover source evidence. When no observations or reflections exist, only the short recall-guidance footer is appended.


Feature comparison
pi-blackhole pi-vcc pi-obs-memory Pi default
Algorithmic compaction (no LLM cost) ✓ ✓ — —
Deterministic output ✓ ✓ — —
Structured summary sections ✓ ✓ — —
Observations + reflections ✓ — ✓ —
Context survives across compactions ✓ — ✓ —
Background memory workers ✓ — ✓ —
Searchable history after compaction ✓ ✓ partial —
Per-worker model config ✓ — — —
Fallback model chains + persisted cooldowns ✓ — — —
Manual flush mode (compaction: "manual") ✓ — — —
Memory toggle (/blackhole om-off) ✓ — — —
Unified single-file config ✓ — — —
Per-session pending state ✓ — — —

Uninstall

pi uninstall git:github.com/k0valik/pi-blackhole
rm -rf ~/.pi/agent/pi-blackhole

Documentation map

Doc Audience What's in it
README.md You, now Install, commands, the pitch, the value, the demo.
CHANGELOG.md You Every release, what changed, who contributed.
CONTRIBUTING.md You, if helping Branch model, dev setup, PR description format, docs/changelog gates.
docs/CONFIG.md You, when tuning Every config key with type, default, behavior, and env-var overrides.
llms.txt Your agent Step-by-step guided setup interview, anti-patterns, exact file paths, internal constants.
docs/MIGRATION-GUIDE.md You, if upgrading Old → new config key mapping, semantic changes, automatic migration behavior.
docs/OLD_CONFIG.md Reference only The legacy pi-vcc / pi-observational-memory config surface. Kept for historical context.
example-config.json You Annotated example config with comments.
docs/APPEND_COMPACTION.md You, if curious Rules for compactionSummaryMode: "append".

Note: All docs except README.md, CHANGELOG.md (package root, read by /blackhole changelog), and llms.txt live under docs/ — product docs (architecture.md, CONFIG.md, etc.); archived_docs/ is local-only (gitignored).


Credits

pi-blackhole started as a merge of two upstream projects but has since diverged significantly. The codebase still carries DNA from both:

  • pi-vcc by @sting8k — algorithmic conversation compaction (the compile() pipeline, section extraction, recall core).
  • pi-observational-memory by @elpapi42 — session-ledger-based observation/reflection capture, memory agents, ledger folding.

What blackhole adds and reworks on top:

  • Unified configuration — one JSON file, not two.
  • Per-worker model fallback chains with persisted cooldowns that survive Pi restarts.
  • Manual flush mode — compaction: "manual" saves observations to per-session disk buffers.
  • Conflict resolution — OM hooks into vcc's compaction, not Pi's default.
  • Memory toggle (/blackhole om-off / /blackhole om-on) — disable the memory layer without uninstalling.
  • Per-session pending state — isolated per-session JSON files, no cross-session contamination.
  • Custom provider bridge — consolidation agents loaded via jiti can still use provider stream functions registered by other extensions.
  • Retryable error detection with per-model cooldowns — models that fail get cooled down, fallbacks tried automatically, 30-second retry gate prevents spam.
  • Improved observer/reflector/dropper prompts — each heavily customized with detailed extraction rules, relevance guidance, and error handling.
  • OM-recall coupling — when expanding session entries via recall, related observations and reflections are automatically shown.
  • Thinking level support — per-model thinking field for reasoning effort control, including max where supported by the provider.

Reflection evidence time

Reflection lines show their latest supporting observation timestamp by default (reflectionTimestamps: true). Legacy reflections remain unchanged. Optional reflectionSupersedes: true adds (supersedes <id>) notes on new reflections and retires the replaced reflection from compaction output (the ledger keeps it). Both keys add no model calls. Use PI_BLACKHOLE_REFLECTION_TIMESTAMPS and PI_BLACKHOLE_REFLECTION_SUPERSEDES for environment overrides. Export prefers reflection evidence time but omits notes.

License

MIT