pi-blackhole 0.2.0
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.
- package/LICENSE +21 -0
- package/README.md +373 -0
- package/example-config.json +115 -0
- package/index.ts +39 -0
- package/package.json +55 -0
- package/src/commands/memory.ts +191 -0
- package/src/commands/pi-vcc.ts +94 -0
- package/src/commands/vcc-recall.ts +112 -0
- package/src/core/brief.ts +390 -0
- package/src/core/build-sections.ts +85 -0
- package/src/core/content.ts +60 -0
- package/src/core/filter-noise.ts +42 -0
- package/src/core/format-recall.ts +27 -0
- package/src/core/format.ts +76 -0
- package/src/core/lineage.ts +26 -0
- package/src/core/load-messages.ts +41 -0
- package/src/core/normalize.ts +79 -0
- package/src/core/recall-scope.ts +14 -0
- package/src/core/render-entries.ts +56 -0
- package/src/core/report.ts +237 -0
- package/src/core/sanitize.ts +5 -0
- package/src/core/search-entries.ts +227 -0
- package/src/core/settings.ts +34 -0
- package/src/core/skill-collapse.ts +35 -0
- package/src/core/summarize.ts +213 -0
- package/src/core/tool-args.ts +14 -0
- package/src/core/unified-config.ts +285 -0
- package/src/details.ts +13 -0
- package/src/extract/commits.ts +69 -0
- package/src/extract/files.ts +80 -0
- package/src/extract/goals.ts +79 -0
- package/src/extract/preferences.ts +55 -0
- package/src/hooks/before-compact.ts +345 -0
- package/src/om/agents/dropper/agent.ts +204 -0
- package/src/om/agents/dropper/prompts.ts +48 -0
- package/src/om/agents/observer/agent.ts +256 -0
- package/src/om/agents/observer/prompts.ts +119 -0
- package/src/om/agents/reflector/agent.ts +161 -0
- package/src/om/agents/reflector/prompts.ts +77 -0
- package/src/om/clipboard.ts +63 -0
- package/src/om/compaction-hook.ts +63 -0
- package/src/om/compaction-trigger.ts +92 -0
- package/src/om/config.ts +22 -0
- package/src/om/consolidation.ts +514 -0
- package/src/om/cooldown.ts +130 -0
- package/src/om/debug-log.ts +55 -0
- package/src/om/ids.ts +5 -0
- package/src/om/ledger/fold.ts +106 -0
- package/src/om/ledger/index.ts +6 -0
- package/src/om/ledger/progress.ts +225 -0
- package/src/om/ledger/projection.ts +237 -0
- package/src/om/ledger/recall.ts +243 -0
- package/src/om/ledger/render-summary.ts +44 -0
- package/src/om/ledger/types.ts +206 -0
- package/src/om/model-budget.ts +9 -0
- package/src/om/pending.ts +225 -0
- package/src/om/reverse-recall.ts +130 -0
- package/src/om/runtime.ts +241 -0
- package/src/om/serialize.ts +224 -0
- package/src/om/tokens.ts +33 -0
- package/src/sections.ts +18 -0
- package/src/tools/recall.ts +212 -0
- package/src/types.ts +19 -0
- package/vitest.config.ts +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 k0valik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# pi-blackhole
|
|
2
|
+
|
|
3
|
+
Algorithmic compaction + session-aware observational memory for [Pi](https://github.com/badlogic/pi-mono), in one extension.
|
|
4
|
+
|
|
5
|
+
Combines [pi-vcc](https://github.com/sting8k/pi-vcc) and [pi-observational-memory](https://github.com/elpapi42/pi-observational-memory) with unified configuration, per-worker model fallback chains, persisted cooldowns, and a manual flush mode.
|
|
6
|
+
|
|
7
|
+
> This is a frankenmerge. I liked both extensions but they were not compatible - observational memory hooked into Pi's default compaction and prevented pi-vcc from working. So I merged them, made them share the same hook and output, and added the things both were missing: fallback chains, cooldowns, and a toggle between them.
|
|
8
|
+
|
|
9
|
+
#### For easy setup pass the [`llms.txt`](llms.txt) llms.txt to your agent and it will guide you through the config without needing to read all the docs if you're as lazy as me
|
|
10
|
+
|
|
11
|
+
# Demo
|
|
12
|
+
|
|
13
|
+
- /blackhole collapses a 94k context-size conversation into a ~7.8k (for my settings) next input, shows the current stats for /blackhole-memory and /blackhole-recall (can be called by agent as well) augmenting the relevant neighbour observations and exact conversations so the agent can expand when it needs to remember previous conversation
|
|
14
|
+
|
|
15
|
+
https://github.com/user-attachments/assets/ab7c5787-1bbd-466f-a231-0818e68e9f39
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## The problem
|
|
20
|
+
|
|
21
|
+
Long AI engineering sessions inevitably degrade. Pi's native compaction relies on an LLM to generate free-form prose summaries recursively. Pi compacts the conversation — then compacts that summary — then compacts again. After enough cycles, small but load-bearing details disappear: why a decision was made, what approaches were already rejected, what the user clarified earlier in the session.
|
|
22
|
+
|
|
23
|
+
The session is still alive. The agent is no longer carrying the real context.
|
|
24
|
+
|
|
25
|
+
The two upstream projects solve different halves of this:
|
|
26
|
+
|
|
27
|
+
- **pi-vcc** replaces Pi's LLM-based compaction with a deterministic, zero-cost algorithmic summary. Fast, reproducible, no hallucination risk. But the compacted output is still a summary — repeated compactions still erode detail.
|
|
28
|
+
- **pi-observational-memory** captures observations and reflections in a session ledger that survives compactions. But its compaction path still calls an LLM.
|
|
29
|
+
|
|
30
|
+
**pi-blackhole** puts vcc in the compaction slot and OM in the memory layer, where each does what it's designed for.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Feature Comparison
|
|
35
|
+
|
|
36
|
+
| | pi-blackhole | pi-vcc | pi-obs-memory | Pi default |
|
|
37
|
+
|---|---|---|---|---|
|
|
38
|
+
| Algorithmic compaction (no LLM) | ✓ | ✓ | — | — |
|
|
39
|
+
| Deterministic output | ✓ | ✓ | — | — |
|
|
40
|
+
| Structured summary sections | ✓ | ✓ | — | — |
|
|
41
|
+
| Observations + reflections | ✓ | — | ✓ | — |
|
|
42
|
+
| Context survives across compactions | ✓ | — | ✓ | — |
|
|
43
|
+
| Background workers | ✓ | — | ✓ | — |
|
|
44
|
+
| Searchable history after compaction | ✓ | ✓ | partial | — |
|
|
45
|
+
| Per-worker model config | ✓ | — | — | — |
|
|
46
|
+
| Fallback model chains + persisted cooldowns | ✓ | — | — | — |
|
|
47
|
+
| Manual flush mode (`noAutoCompact`) | ✓ | — | — | — |
|
|
48
|
+
| Memory toggle (`/blackhole om-off`) | ✓ | — | — | — |
|
|
49
|
+
| Unified single-file config | ✓ | — | — | — |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## How it works
|
|
54
|
+
|
|
55
|
+
On `/blackhole`, 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. Accumulated observations and reflections from the session ledger are rendered and appended below it. The agent receives a deterministic recap of recent work and durable facts from the full session history in a single replacement block.
|
|
56
|
+
|
|
57
|
+
Three background workers from `pi-observational-memory` run during the session:
|
|
58
|
+
|
|
59
|
+
- **Observer** — reads conversation since the last observation marker and extracts timestamped facts: events, decisions, preferences. Input is capped to `observerChunkMaxTokens` newest-first to prevent context blowup on long sessions.
|
|
60
|
+
- **Reflector** — distills new observations into durable reflections: stable facts, patterns, and constraints that survive future compactions.
|
|
61
|
+
- **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.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
[Conversation turn] ──> observeAfterTokens threshold
|
|
65
|
+
│
|
|
66
|
+
v
|
|
67
|
+
1. OBSERVER
|
|
68
|
+
(extracts observations)
|
|
69
|
+
│
|
|
70
|
+
v
|
|
71
|
+
2. REFLECTOR
|
|
72
|
+
(distills reflections)
|
|
73
|
+
│
|
|
74
|
+
v
|
|
75
|
+
3. DROPPER
|
|
76
|
+
(prunes observations, keeps reflections)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The pipeline runs in order: observer -> reflector -> dropper. If any stage fails, remaining stages are skipped and the pipeline retries on the next `agent_start` or `turn_end`. A minimum 30-second interval is enforced between failed runs.
|
|
80
|
+
|
|
81
|
+
### What the agent sees after compaction
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
[Session Goal]
|
|
85
|
+
- Fix the authentication bug in login flow
|
|
86
|
+
- [Scope change]
|
|
87
|
+
- Also update the session token refresh logic
|
|
88
|
+
|
|
89
|
+
[Files And Changes]
|
|
90
|
+
- Modified: src/auth/session.ts
|
|
91
|
+
- Created: tests/auth-refresh.test.ts
|
|
92
|
+
|
|
93
|
+
[Commits]
|
|
94
|
+
- a1b2c3d: fix(auth): refresh token after password reset
|
|
95
|
+
|
|
96
|
+
[Outstanding Context]
|
|
97
|
+
- lint check still failing on line 42
|
|
98
|
+
|
|
99
|
+
[User Preferences]
|
|
100
|
+
- Prefer Vietnamese responses
|
|
101
|
+
- Always run tests before committing
|
|
102
|
+
|
|
103
|
+
[user]
|
|
104
|
+
Fix the auth bug...
|
|
105
|
+
|
|
106
|
+
[assistant]
|
|
107
|
+
Root cause is a missing token refresh...
|
|
108
|
+
...transcript continues...
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
[a1b2c3d4e5f6] 2026-05-23 [high] User decided to switch from REST to GraphQL; motivation was reducing over-fetching.
|
|
113
|
+
[b2c3d4e5f6a1] 2026-05-23 [medium] GraphQL migration completed; user confirmed working.
|
|
114
|
+
[c3d4e5f6a1b2] Reflection: User is building Acme Dashboard on Next.js 15 with Supabase auth.
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Sections appear only when relevant — a session with no git commits won't have `[Commits]`.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Compaction modes
|
|
122
|
+
|
|
123
|
+
Two modes, one shared goal: keep your agent's context sharp without manual housekeeping.
|
|
124
|
+
|
|
125
|
+
- **Auto mode (default):** install and forget. Workers run, observations are appended as invisible conversation markers, compaction fires automatically when tokens exceed threshold.
|
|
126
|
+
- **Manual mode (`noAutoCompact: true` — the maintainer's setup):** same workers, same pipeline. But observations go to a disk buffer and compaction only happens when you run `/blackhole`. Cleaner conversation, manual schedule.
|
|
127
|
+
|
|
128
|
+
The tradeoff is simplicity vs cleanliness:
|
|
129
|
+
|
|
130
|
+
| | Auto (default) | Manual (`noAutoCompact: true`) |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| Workers run? | Yes | Yes |
|
|
133
|
+
| Observations go to | Conversation markers (invisible in TUI) | Disk (`pending.json`) |
|
|
134
|
+
| Auto-compact on `agent_end` | Yes | No |
|
|
135
|
+
| `/blackhole` | Optional — use it whenever you want | Required to flush + compact |
|
|
136
|
+
| Conversation history | OM marker entries between turns (they exist but do not clutter the display) | Clean — nothing between turns |
|
|
137
|
+
| Use case | "I don't want to think about it" | "I want to control when context gets compressed" |
|
|
138
|
+
|
|
139
|
+
**Does `/blackhole` work like a single `/compact` that Just Works?**
|
|
140
|
+
|
|
141
|
+
Yes, that's exactly the idea, especially in manual mode. When you feel context is getting full or accuracy is slipping, type `/blackhole`. It flushes any accumulated observations from disk, runs algorithmic vcc compaction (zero LLM cost), and injects your durable reflections into the replacement block. One command, everything gets compressed while keeping your long-term memory alive.
|
|
142
|
+
|
|
143
|
+
The difference from Pi's built-in `/compact`:
|
|
144
|
+
- `/compact` calls an LLM to write a free-form summary — costly, lossy, no memory layer.
|
|
145
|
+
- `/blackhole` uses algorithmic section extraction (goals, files, commits, preferences...) plus injects observations/reflections from the session ledger. No LLM involved in the compaction itself. Fast, deterministic, memory-preserving.
|
|
146
|
+
|
|
147
|
+
### Without observational memory
|
|
148
|
+
|
|
149
|
+
Set `memory: false` or run `/blackhole om-off` for pure pi-vcc compaction — no background workers, no memory injection. Re-enable with `/blackhole om-on` or `memory: true`.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Commands
|
|
154
|
+
|
|
155
|
+
| Command | What it does |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `/blackhole` | Compact. `om-off` / `om-on` toggle observational memory. |
|
|
158
|
+
| `/blackhole-memory` | Pipeline status: token progress, worker counts, last errors |
|
|
159
|
+
| `/blackhole-memory view` | Visible observations and reflections, copied to clipboard |
|
|
160
|
+
| `/blackhole-memory full` | Complete recorded memory, copied to clipboard |
|
|
161
|
+
| `/blackhole-recall <query>` | Search session history. Supports `page:N`, `scope:all` |
|
|
162
|
+
|
|
163
|
+
## Tools
|
|
164
|
+
|
|
165
|
+
| Tool | Input | Returns |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| `recall` | `[12char hex]` — source evidence for an observation/reflection; `#N` — transcript entry by index; free text — BM25+regex ranked search | Source message(s) with timestamps |
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
All settings in **`~/.pi/agent/pi-blackhole-config.json`** — auto-created with defaults on first startup. See [`CONFIG.md`](CONFIG.md) for the full reference and tuning guidance.
|
|
174
|
+
|
|
175
|
+
Quick start with custom models:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"observerModel": { "provider": "openrouter", "id": "qwen/qwen3-next-80b-a3b-instruct:free" },
|
|
180
|
+
"reflectorModel": { "provider": "cerebras", "id": "gpt-oss-120b" },
|
|
181
|
+
"dropperModel": { "provider": "cerebras", "id": "gpt-oss-120b" }
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Settings at a glance
|
|
186
|
+
|
|
187
|
+
| Setting | Default (medium) | What it controls |
|
|
188
|
+
|---|---|---|
|
|
189
|
+
| `overrideDefaultCompaction` | `false` | Route all Pi compactions through blackhole, not just explicit `/blackhole` |
|
|
190
|
+
| `noAutoCompact` | `false` | Manual mode: save to disk, disable auto-compaction |
|
|
191
|
+
| `memory` | `true` | `false` = pi-vcc only, no workers, no OM injection |
|
|
192
|
+
| `passive` | `false` | Disable all workers and auto-compaction entirely |
|
|
193
|
+
| `model` | — | Base fallback model for all workers |
|
|
194
|
+
| `observerModel` / `observerFallbackModels` | — / `[]` | Primary + fallbacks for observer |
|
|
195
|
+
| `reflectorModel` / `reflectorFallbackModels` | — / `[]` | Primary + fallbacks for reflector |
|
|
196
|
+
| `dropperModel` / `dropperFallbackModels` | — / `[]` | Primary + fallbacks for dropper |
|
|
197
|
+
| `observeAfterTokens` | `10000` | Token cadence between observer runs |
|
|
198
|
+
| `reflectAfterTokens` | `20000` | Token cadence for reflector and dropper |
|
|
199
|
+
| `compactAfterTokens` | `81000` | Auto-compaction threshold |
|
|
200
|
+
| `observerChunkMaxTokens` | `40000` | Max observer input per run (newest-first) |
|
|
201
|
+
| `observationsPoolMaxTokens` | `20000` | Max active observation pool before dropper prunes |
|
|
202
|
+
| `reflectorInputMaxTokens` | `80000` | Max reflector input budget |
|
|
203
|
+
| `dropperInputMaxTokens` | `80000` | Max dropper input budget |
|
|
204
|
+
| `agentMaxTurns` | `16` | Max turns per worker agent loop |
|
|
205
|
+
| `debug` | `false` | Pre-compaction snapshot to `/tmp/pi-blackhole-debug.json` |
|
|
206
|
+
| `debugLog` | `false` | Continuous JSONL log to `~/.pi/agent/pi-blackhole/debug.ndjson` |
|
|
207
|
+
|
|
208
|
+
Env override: `PI_BLACKHOLE_PASSIVE=true` disables all workers without touching the config file.
|
|
209
|
+
|
|
210
|
+
### Configuration presets
|
|
211
|
+
|
|
212
|
+
The defaults above target a **medium-context** setup (~128k context window, e.g. GPT-4o, Claude Sonnet).
|
|
213
|
+
Paste the appropriate block into your config to match your model's context size.
|
|
214
|
+
|
|
215
|
+
#### Low context (~32k-64k — older models, fast budget models)
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"observeAfterTokens": 5000,
|
|
220
|
+
"reflectAfterTokens": 10000,
|
|
221
|
+
"compactAfterTokens": 30000,
|
|
222
|
+
"observerChunkMaxTokens": 15000,
|
|
223
|
+
"observationsPoolMaxTokens": 8000,
|
|
224
|
+
"reflectorInputMaxTokens": 30000,
|
|
225
|
+
"dropperInputMaxTokens": 30000
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### Medium context (~128k — GPT-4o, Claude Sonnet, Gemini Pro; this is the default)
|
|
230
|
+
|
|
231
|
+
Our built-in defaults already target this tier. If you reset your config, these are what you get:
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"observeAfterTokens": 10000,
|
|
236
|
+
"reflectAfterTokens": 20000,
|
|
237
|
+
"compactAfterTokens": 81000,
|
|
238
|
+
"observerChunkMaxTokens": 40000,
|
|
239
|
+
"observationsPoolMaxTokens": 20000,
|
|
240
|
+
"reflectorInputMaxTokens": 80000,
|
|
241
|
+
"dropperInputMaxTokens": 80000
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### High context (~200k+ — Claude Opus, Gemini Ultra, large local models)
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"observeAfterTokens": 20000,
|
|
250
|
+
"reflectAfterTokens": 40000,
|
|
251
|
+
"compactAfterTokens": 180000,
|
|
252
|
+
"observerChunkMaxTokens": 80000,
|
|
253
|
+
"observationsPoolMaxTokens": 40000,
|
|
254
|
+
"reflectorInputMaxTokens": 160000,
|
|
255
|
+
"dropperInputMaxTokens": 160000
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**What to tune first:** `compactAfterTokens` should be significantly below your model's total context window — aim for ~60-70%. If you find the agent loses context before compaction fires, lower it. If compaction fires too often and breaks flow, raise it. The other thresholds scale proportionally with this value.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Model fallback chains
|
|
264
|
+
|
|
265
|
+
Each worker has a primary model and an ordered fallback list. On any error — rate limit, timeout, API failure — the failed model is cooled down and the next candidate is tried. If all candidates are exhausted, the pipeline aborts and retries on the next trigger event. The session model is always the last resort and is never cooled down.
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
[Worker fails: 429 / timeout / 5xx]
|
|
269
|
+
│
|
|
270
|
+
v
|
|
271
|
+
Add model to cooldown list
|
|
272
|
+
│
|
|
273
|
+
v
|
|
274
|
+
Try next fallback candidate
|
|
275
|
+
│
|
|
276
|
+
v
|
|
277
|
+
[All candidates exhausted?]
|
|
278
|
+
│ │
|
|
279
|
+
yes no ──> try next
|
|
280
|
+
│
|
|
281
|
+
v
|
|
282
|
+
Fall back to session model (never cooled down)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Cooldowns are persisted to `~/.pi/agent/pi-blackhole/pi-blackhole-cooldown.json` and survive Pi restarts. Each entry records the model key, the triggering error, the responsible stage, and the expiry timestamp.
|
|
286
|
+
|
|
287
|
+
With a full config, resolution order looks like:
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
Observer: qwen3-next-80b → gemma4:31b-cloud → gemma-4-31b-it:free → base model → session model
|
|
291
|
+
Reflector: gpt-oss-120b (cerebras) → glm-4.7 (z.ai) → gpt-oss-120b:free → base model → session model
|
|
292
|
+
Dropper: gpt-oss-120b (cerebras) → glm-4.7 (z.ai) → gpt-oss-120b:free → base model → session model
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Recall
|
|
298
|
+
|
|
299
|
+
Pi's default compaction discards old messages permanently — after compaction, the agent only sees the summary. Blackhole preserves searchable history through two surfaces.
|
|
300
|
+
|
|
301
|
+
### `recall` tool (agent-facing)
|
|
302
|
+
|
|
303
|
+
Searches the raw session JSONL directly, bypassing compaction. Default scope is the active conversation lineage.
|
|
304
|
+
|
|
305
|
+
| Input | What it does |
|
|
306
|
+
|---|---|
|
|
307
|
+
| `[12char hex]` | Recover source evidence for an observation or reflection |
|
|
308
|
+
| `#N` | Expand a transcript entry by index |
|
|
309
|
+
| Free text | BM25-ranked OR search, rare terms weighted higher |
|
|
310
|
+
| Regex | Pattern search (e.g. `fork.*pi-vcc`, `hook\|inject`) |
|
|
311
|
+
|
|
312
|
+
### `/blackhole-recall` command (user-facing)
|
|
313
|
+
|
|
314
|
+
Results are shown as a collapsible message and auto-fed to the agent as context.
|
|
315
|
+
|
|
316
|
+
```
|
|
317
|
+
/blackhole-recall auth token # active-lineage search, ranked
|
|
318
|
+
/blackhole-recall auth token page:2 # paginated (5 results/page)
|
|
319
|
+
/blackhole-recall hook|inject # regex
|
|
320
|
+
/blackhole-recall fail.*build scope:all # regex across all lineages
|
|
321
|
+
/blackhole-recall # recent 25 entries
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Install
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
# From npm (recommended)
|
|
330
|
+
pi install npm:pi-blackhole
|
|
331
|
+
|
|
332
|
+
# Or directly from GitHub
|
|
333
|
+
pi install git:github.com/k0valik/pi-blackhole
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
If you have standalone pi-vcc or pi-observational-memory installed, remove them first — they will conflict:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
pi uninstall https://github.com/sting8k/pi-vcc
|
|
340
|
+
pi uninstall https://github.com/elpapi42/pi-observational-memory
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Then `/reload` or restart Pi.
|
|
344
|
+
|
|
345
|
+
### Standalone install (without Pi)
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
npm install pi-blackhole
|
|
349
|
+
# or
|
|
350
|
+
pnpm add pi-blackhole
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Uninstall
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
pi uninstall git:github.com/k0valik/pi-blackhole
|
|
357
|
+
rm -rf ~/.pi/agent/pi-blackhole ~/.pi/agent/pi-blackhole-config.json
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Credits
|
|
363
|
+
|
|
364
|
+
The bulk of this codebase comes from two upstream projects:
|
|
365
|
+
|
|
366
|
+
- **[pi-vcc](https://github.com/sting8k/pi-vcc)** by @sting8k — algorithmic conversation compaction
|
|
367
|
+
- **[pi-observational-memory](https://github.com/elpapi42/pi-observational-memory)** by @elpapi42 — session-ledger-based observation and reflection capture
|
|
368
|
+
|
|
369
|
+
pi-blackhole combines, configures, and extends them into a single extension and adds unified configuration, per-worker model fallback chains, persisted cooldowns, manual flush mode, and conflict resolution between the two, and better error detection.
|
|
370
|
+
|
|
371
|
+
## License
|
|
372
|
+
|
|
373
|
+
MIT
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "blackhole unified configuration — ~/.pi/agent/pi-blackhole-config.json",
|
|
3
|
+
"_all_settings_in_one_file": true,
|
|
4
|
+
|
|
5
|
+
"overrideDefaultCompaction": false,
|
|
6
|
+
"noAutoCompact": false,
|
|
7
|
+
"debug": false,
|
|
8
|
+
|
|
9
|
+
"model": {
|
|
10
|
+
"_comment": "Base model (last fallback before session model).",
|
|
11
|
+
"provider": "openrouter",
|
|
12
|
+
"id": "google/gemma-4-31b-it:free",
|
|
13
|
+
"thinking": "low",
|
|
14
|
+
"cooldownHours": 6
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
"observerModel": {
|
|
18
|
+
"_comment": "Observer primary: Qwen3 Next 80B via OpenRouter free (strong extraction with reasoning).",
|
|
19
|
+
"provider": "openrouter",
|
|
20
|
+
"id": "qwen/qwen3-next-80b-a3b-instruct:free",
|
|
21
|
+
"thinking": "low",
|
|
22
|
+
"cooldownHours": 12
|
|
23
|
+
},
|
|
24
|
+
"observerFallbackModels": [
|
|
25
|
+
{
|
|
26
|
+
"_comment": "Observer fallback 1: Gemma 4 31B via Ollama Cloud.",
|
|
27
|
+
"provider": "ollama",
|
|
28
|
+
"id": "gemma4:31b-cloud",
|
|
29
|
+
"thinking": "off",
|
|
30
|
+
"cooldownHours": 6
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"_comment": "Observer fallback 2: Gemma 4 31B via OpenRouter free tier.",
|
|
34
|
+
"provider": "openrouter",
|
|
35
|
+
"id": "google/gemma-4-31b-it:free",
|
|
36
|
+
"thinking": "off",
|
|
37
|
+
"cooldownHours": 6
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
|
|
41
|
+
"reflectorModel": {
|
|
42
|
+
"_comment": "Reflector primary: GPT OSS 120B via Cerebras (strong reasoning for synthesis).",
|
|
43
|
+
"provider": "cerebras",
|
|
44
|
+
"id": "gpt-oss-120b",
|
|
45
|
+
"thinking": "low",
|
|
46
|
+
"cooldownHours": 12
|
|
47
|
+
},
|
|
48
|
+
"reflectorFallbackModels": [
|
|
49
|
+
{
|
|
50
|
+
"_comment": "Reflector fallback 1: GLM-4.7 via z.ai with thinking enabled.",
|
|
51
|
+
"provider": "zai",
|
|
52
|
+
"id": "glm-4.7",
|
|
53
|
+
"thinking": "low",
|
|
54
|
+
"cooldownHours": 6
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"_comment": "Reflector fallback 2: GPT OSS 120B via OpenRouter free tier.",
|
|
58
|
+
"provider": "openrouter",
|
|
59
|
+
"id": "openai/gpt-oss-120b:free",
|
|
60
|
+
"thinking": "low",
|
|
61
|
+
"cooldownHours": 6
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
|
|
65
|
+
"dropperModel": {
|
|
66
|
+
"_comment": "Dropper primary: GPT OSS 120B via Cerebras (judgment/prioritization).",
|
|
67
|
+
"provider": "cerebras",
|
|
68
|
+
"id": "gpt-oss-120b",
|
|
69
|
+
"thinking": "off",
|
|
70
|
+
"cooldownHours": 12
|
|
71
|
+
},
|
|
72
|
+
"dropperFallbackModels": [
|
|
73
|
+
{
|
|
74
|
+
"_comment": "Dropper fallback 1: GLM-4.7 via z.ai.",
|
|
75
|
+
"provider": "zai",
|
|
76
|
+
"id": "glm-4.7",
|
|
77
|
+
"thinking": "low",
|
|
78
|
+
"cooldownHours": 6
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"_comment": "Dropper fallback 2: GPT OSS 120B via OpenRouter free tier.",
|
|
82
|
+
"provider": "openrouter",
|
|
83
|
+
"id": "openai/gpt-oss-120b:free",
|
|
84
|
+
"thinking": "low",
|
|
85
|
+
"cooldownHours": 6
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
|
|
89
|
+
"observeAfterTokens": 10000,
|
|
90
|
+
"reflectAfterTokens": 20000,
|
|
91
|
+
"compactAfterTokens": 81000,
|
|
92
|
+
"observationsPoolMaxTokens": 20000,
|
|
93
|
+
"reflectorInputMaxTokens": 80000,
|
|
94
|
+
"dropperInputMaxTokens": 80000,
|
|
95
|
+
"observerChunkMaxTokens": 40000,
|
|
96
|
+
"agentMaxTurns": 16,
|
|
97
|
+
|
|
98
|
+
"passive": false,
|
|
99
|
+
"noAutoCompact": false,
|
|
100
|
+
"debugLog": false,
|
|
101
|
+
|
|
102
|
+
"_notes": [
|
|
103
|
+
"Model fallback chains per worker:",
|
|
104
|
+
" Observer: qwen3-next-80b → gemma4:31b-cloud → google/gemma-4-31b-it:free → base model → session",
|
|
105
|
+
" Reflector: gpt-oss-120b (cerebras) → glm-4.7 (z.ai) → openai/gpt-oss-120b:free → base model → session",
|
|
106
|
+
" Dropper: gpt-oss-120b (cerebras) → glm-4.7 (z.ai) → openai/gpt-oss-120b:free → base model → session",
|
|
107
|
+
"",
|
|
108
|
+
"On retryable error (429, 5xx, timeout), the model is cooled down for cooldownHours.",
|
|
109
|
+
"Cooled-down models are skipped on subsequent consolidation attempts until cooldown expires.",
|
|
110
|
+
"After all candidates exhausted, the stage aborts. 30s retry gate prevents spam.",
|
|
111
|
+
"Cooldowns persist in ~/.pi/agent/pi-blackhole/pi-blackhole-cooldown.json (survives pi restarts).",
|
|
112
|
+
"Valid thinking values: off, minimal, low, medium, high, xhigh.",
|
|
113
|
+
"Env override for passive mode: PI_BLACKHOLE_PASSIVE=true."
|
|
114
|
+
]
|
|
115
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified entry point. Registers all pi-vcc + observational-memory
|
|
3
|
+
* commands, hooks, and tools.
|
|
4
|
+
*
|
|
5
|
+
* Upstream: https://github.com/elpapi42/pi-observational-memory (src/index.ts)
|
|
6
|
+
* https://github.com/sting8k/pi-vcc (index.ts)
|
|
7
|
+
* Merged and extended by pi-vcc-om.
|
|
8
|
+
*/
|
|
9
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { scaffoldSettings } from "./src/core/settings";
|
|
11
|
+
import { registerBeforeCompactHook } from "./src/hooks/before-compact";
|
|
12
|
+
import { registerPiVccCommand } from "./src/commands/pi-vcc";
|
|
13
|
+
import { registerMemoryCommand } from "./src/commands/memory";
|
|
14
|
+
import { registerVccRecallCommand } from "./src/commands/vcc-recall";
|
|
15
|
+
import { registerConsolidationTrigger } from "./src/om/consolidation.js";
|
|
16
|
+
import { registerCompactionTrigger } from "./src/om/compaction-trigger.js";
|
|
17
|
+
import { registerRecallTool } from "./src/tools/recall";
|
|
18
|
+
import { Runtime } from "./src/om/runtime.js";
|
|
19
|
+
|
|
20
|
+
export default (pi: ExtensionAPI) => {
|
|
21
|
+
scaffoldSettings();
|
|
22
|
+
|
|
23
|
+
const omRuntime = new Runtime();
|
|
24
|
+
|
|
25
|
+
// Observational memory: background consolidation pipeline
|
|
26
|
+
registerConsolidationTrigger(pi, omRuntime); // agent_start + turn_end → observer/reflector/dropper
|
|
27
|
+
registerCompactionTrigger(pi, omRuntime); // agent_end → auto-compaction
|
|
28
|
+
|
|
29
|
+
// Pi-vcc: compaction + om injection
|
|
30
|
+
registerBeforeCompactHook(pi, omRuntime); // session_before_compact → pi-vcc + om content
|
|
31
|
+
|
|
32
|
+
// Commands
|
|
33
|
+
registerPiVccCommand(pi, omRuntime); // /pi-vcc (needs runtime for noAutoCompact flush)
|
|
34
|
+
registerMemoryCommand(pi, omRuntime); // /blackhole-memory [status|view|full]
|
|
35
|
+
registerVccRecallCommand(pi); // /blackhole-recall <query>
|
|
36
|
+
|
|
37
|
+
// Tools
|
|
38
|
+
registerRecallTool(pi); // unified recall (#N + [12char])
|
|
39
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-blackhole",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Unified compaction + observational memory extension for Pi — compresses conversation context while preserving durable observations and reflections",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"*.ts",
|
|
9
|
+
"src/**/*.ts",
|
|
10
|
+
"!src/**/*.test.ts",
|
|
11
|
+
"!tests/",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md",
|
|
14
|
+
"example-config.json"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"check": "tsc --noEmit"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"pi-package",
|
|
21
|
+
"pi-extension",
|
|
22
|
+
"pi",
|
|
23
|
+
"pi-coding-agent",
|
|
24
|
+
"pi-agent",
|
|
25
|
+
"pi-blackhole",
|
|
26
|
+
"blackhole",
|
|
27
|
+
"compact",
|
|
28
|
+
"compaction",
|
|
29
|
+
"observational-memory",
|
|
30
|
+
"recall",
|
|
31
|
+
"ai",
|
|
32
|
+
"agents",
|
|
33
|
+
"cli"
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/k0valik/pi-blackhole.git"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@earendil-works/pi-agent-core": "0.75.5",
|
|
41
|
+
"@earendil-works/pi-ai": "0.75.4",
|
|
42
|
+
"@earendil-works/pi-coding-agent": "0.75.4",
|
|
43
|
+
"@earendil-works/pi-tui": "0.75.5",
|
|
44
|
+
"typebox": "^1.1.38"
|
|
45
|
+
},
|
|
46
|
+
"pi": {
|
|
47
|
+
"extensions": [
|
|
48
|
+
"./index.ts"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"typescript": "^5.7.3",
|
|
53
|
+
"vitest": "^4.1.7"
|
|
54
|
+
}
|
|
55
|
+
}
|