pi-blackhole 0.3.1 → 0.3.3
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/README.md +66 -43
- package/package.json +1 -1
- package/src/commands/memory.ts +5 -5
- package/src/commands/pi-vcc.ts +44 -7
- package/src/core/settings.ts +17 -2
- package/src/core/unified-config.ts +178 -19
- package/src/hooks/before-compact.ts +148 -9
- package/src/om/compaction-trigger.ts +28 -6
- package/src/om/configure-overlay.ts +381 -0
- package/src/om/consolidation.ts +42 -1
- package/src/om/key-matcher.ts +72 -0
- package/src/om/model-budget.ts +22 -0
- package/src/om/status-overlay.ts +242 -0
package/README.md
CHANGED
|
@@ -1,15 +1,33 @@
|
|
|
1
1
|
# pi-blackhole
|
|
2
2
|
|
|
3
|
+
> [!IMPORTANT]
|
|
4
|
+
> **Blackhole is the default compaction engine** (`compactionEngine: "blackhole"`, `compaction: "auto"`). Auto-compaction fires at the configured threshold using blackhole's pipeline — both auto-trigger and Pi's `/compact` command use it. No additional setup needed. To work automatically, blackhole needs to register its hook so it overrides /compact with the `blackhole` compaction engine. Opting out or opting for manual compaction with blackhole can be done as below:
|
|
5
|
+
>
|
|
6
|
+
> | Setting | Auto-trigger after threshold | `/compact` (Pi built-in) |
|
|
7
|
+
> |---|---|---|
|
|
8
|
+
> | `"auto"` + `"blackhole"` (default) | blackhole handles ✓ | blackhole handles |
|
|
9
|
+
> | `"auto"` + `"pi-default"` | Pi handles | Pi handles |
|
|
10
|
+
> | `"manual"` + any | skipped | Pi handles ✓ |
|
|
11
|
+
> | `"off"` + any | skipped (Pi handles) | Pi handles ✓ |
|
|
12
|
+
>
|
|
13
|
+
> The `/blackhole` command always uses blackhole's pipeline regardless of settings.
|
|
14
|
+
>
|
|
15
|
+
> **Upgrading from an older version?** This version replaces legacy keys (`passive`, `noAutoCompact`, `overrideDefaultCompaction`) with `compaction`, `compactionEngine`, and `tailBehavior`. Automatic migration runs at startup — old configs continue to work. Best effort was made to preserve existing behavior, but review [`MIGRATION-GUIDE.md`](MIGRATION-GUIDE.md) if something behaves differently.
|
|
16
|
+
>
|
|
17
|
+
> See [`CONFIG.md`](CONFIG.md) for the full reference.
|
|
18
|
+
|
|
3
19
|
**Algorithmic compaction + session-aware observational memory for [Pi](https://github.com/badlogic/pi-mono) — in one unified extension.**
|
|
4
20
|
|
|
5
21
|
Blackhole merges the best ideas from [pi-vcc](https://github.com/sting8k/pi-vcc) and [pi-observational-memory](https://github.com/elpapi42/pi-observational-memory) into something that's become its own beast entirely. Deterministic compaction that costs nothing. A memory layer that survives compactions. Per-worker model fallback chains with persisted cooldowns. Manual flush mode. All configured from one JSON file.
|
|
6
22
|
|
|
7
23
|
> **Why this exists:** I liked both extensions but they fought each other — OM hooked into Pi's default compaction and blocked vcc from working. So I merged them, made them share a single hook and output, and added everything both were missing: fallback chains, cooldowns, a memory toggle, and a manual mode for people who want to control when context gets compressed.
|
|
8
24
|
>
|
|
9
|
-
> The codebase has since diverged heavily from both upstreams, but tries to keep up-to-date with any fixes from them
|
|
25
|
+
> The codebase has since diverged heavily from both upstreams, but tries to keep up-to-date with any fixes from them.
|
|
10
26
|
|
|
11
27
|
📖 See [`CHANGELOG.md`](CHANGELOG.md) for release history.
|
|
12
28
|
⚙️ See [`CONFIG.md`](CONFIG.md) for the full configuration reference.
|
|
29
|
+
🔄 See [`MIGRATION-GUIDE.md`](MIGRATION-GUIDE.md) if upgrading from an older version.
|
|
30
|
+
📜 See [`OLD_CONFIG.md`](OLD_CONFIG.md) for the legacy config documentation.
|
|
13
31
|
|
|
14
32
|
---
|
|
15
33
|
|
|
@@ -44,9 +62,13 @@ pi-blackhole tracks both upstream repositories via a [lockstep audit system](.pi
|
|
|
44
62
|
|
|
45
63
|
## Demo
|
|
46
64
|
|
|
47
|
-
`/blackhole` collapses ~
|
|
65
|
+
`/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 also reach via its `recall` tool and incrementally search previous conversation history.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
https://github.com/user-attachments/assets/a7dd804d-6aca-4bdb-8b6e-0dd779363a43
|
|
70
|
+
|
|
48
71
|
|
|
49
|
-
https://github.com/user-attachments/assets/ab7c5787-1bbd-466f-a231-0818e68e9f39
|
|
50
72
|
|
|
51
73
|
|
|
52
74
|
---
|
|
@@ -64,28 +86,6 @@ The two upstream projects each solve one half:
|
|
|
64
86
|
|
|
65
87
|
**pi-blackhole** puts vcc in the compaction slot and OM in the memory layer, where each does what it's designed for.
|
|
66
88
|
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## Feature comparison
|
|
71
|
-
|
|
72
|
-
| | pi-blackhole | pi-vcc | pi-obs-memory | Pi default |
|
|
73
|
-
|---|---|---|---|---|
|
|
74
|
-
| Algorithmic compaction (no LLM cost) | ✓ | ✓ | — | — |
|
|
75
|
-
| Deterministic output | ✓ | ✓ | — | — |
|
|
76
|
-
| Structured summary sections | ✓ | ✓ | — | — |
|
|
77
|
-
| Observations + reflections | ✓ | — | ✓ | — |
|
|
78
|
-
| Context survives across compactions | ✓ | — | ✓ | — |
|
|
79
|
-
| Background memory workers | ✓ | — | ✓ | — |
|
|
80
|
-
| Searchable history after compaction | ✓ | ✓ | partial | — |
|
|
81
|
-
| Per-worker model config | ✓ | — | — | — |
|
|
82
|
-
| Fallback model chains + persisted cooldowns | ✓ | — | — | — |
|
|
83
|
-
| Manual flush mode (`noAutoCompact`) | ✓ | — | — | — |
|
|
84
|
-
| Memory toggle (`/blackhole om-off`) | ✓ | — | — | — |
|
|
85
|
-
| Unified single-file config | ✓ | — | — | — |
|
|
86
|
-
| Per-session pending state | ✓ | — | — | — |
|
|
87
|
-
|
|
88
|
-
|
|
89
89
|
---
|
|
90
90
|
|
|
91
91
|
## How it works
|
|
@@ -99,7 +99,7 @@ The agent receives a deterministic recap of recent work *plus* durable facts fro
|
|
|
99
99
|
|
|
100
100
|
### The three memory workers
|
|
101
101
|
|
|
102
|
-
Three background workers run automatically during the session (when `memory: true`, which is the default):
|
|
102
|
+
Three background workers (separate LLM calls) run automatically during the session (when `memory: true`, which is the default):
|
|
103
103
|
|
|
104
104
|
- **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. Runs most frequently.
|
|
105
105
|
- **Reflector** — distills new observations into durable reflections: stable facts, patterns, and constraints that survive future compactions. Runs less often.
|
|
@@ -181,7 +181,7 @@ When exact source context is needed for precision or traceability, use the `reca
|
|
|
181
181
|
----
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
-
> **Note:** The OM injection format uses `## Reflections` and `## Observations` Markdown headers with a preamble and footer. Each observation and reflection has a 12-char hex identifier you can use with the `recall` tool to recover source evidence.
|
|
184
|
+
> **Note:** The OM injection format uses `## Reflections` and `## Observations` Markdown headers with a preamble and footer. Each observation and reflection has a 12-char hex identifier you can use with the `recall` tool to recover source evidence, as well as the agent can search based on them and get relevant context back.
|
|
185
185
|
|
|
186
186
|
---
|
|
187
187
|
|
|
@@ -190,11 +190,11 @@ When exact source context is needed for precision or traceability, use the `reca
|
|
|
190
190
|
Two modes, one shared goal: keep your agent's context sharp without manual housekeeping.
|
|
191
191
|
|
|
192
192
|
- **Auto mode (default):** install and forget. Workers run, observations are appended as invisible conversation markers, compaction fires automatically when tokens exceed threshold.
|
|
193
|
-
- **Manual mode (`
|
|
193
|
+
- **Manual mode (`compaction: "manual"` — the maintainer's daily driver):** same workers, same pipeline. But observations go to per-session disk buffers and compaction only happens when you run `/blackhole`. Cleaner conversation, manual schedule.
|
|
194
194
|
|
|
195
195
|
The tradeoff is simplicity vs cleanliness:
|
|
196
196
|
|
|
197
|
-
| | Auto (default) | Manual (`
|
|
197
|
+
| | Auto (default) | Manual (`compaction: "manual"`) |
|
|
198
198
|
|---|---|---|
|
|
199
199
|
| Workers run? | Yes | Yes |
|
|
200
200
|
| Observations go to | Conversation markers (invisible in TUI) | Disk (`<sessionId>-pending.json`) |
|
|
@@ -212,15 +212,15 @@ The difference from Pi's built-in `/compact`:
|
|
|
212
212
|
- `/compact` calls an LLM to write a free-form summary — costly, lossy, no memory layer.
|
|
213
213
|
- `/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.
|
|
214
214
|
|
|
215
|
-
###
|
|
215
|
+
### Fully disabled
|
|
216
216
|
|
|
217
|
-
Set `
|
|
217
|
+
Set `compaction: "off"` and `memory: false` (or the environment variable `PI_BLACKHOLE_PASSIVE=true` which sets both) to completely disable all background workers and blackhole's auto-compaction trigger. Pi handles auto-compaction normally. Explicit `/blackhole` still uses blackhole's pipeline. This is useful for debugging or if you want manual-only blackhole involvement.
|
|
218
218
|
|
|
219
219
|
### Without observational memory (vcc-only)
|
|
220
220
|
|
|
221
221
|
Set `memory: false` or run `/blackhole om-off` for pure vcc compaction — no background workers, no memory injection. The compaction still uses the algorithmic vcc pipeline (not Pi's LLM-based compaction). Re-enable with `/blackhole om-on` or setting `memory: true`.
|
|
222
222
|
|
|
223
|
-
This is a lighter alternative to `
|
|
223
|
+
This is a lighter alternative to `compaction: "off"`: workers are off but blackhole's compaction engine still handles compaction.
|
|
224
224
|
|
|
225
225
|
---
|
|
226
226
|
|
|
@@ -228,7 +228,7 @@ This is a lighter alternative to `passive`: workers are off but auto-compaction
|
|
|
228
228
|
|
|
229
229
|
| Command | What it does |
|
|
230
230
|
|---|---|
|
|
231
|
-
| `/blackhole` | Compact the conversation. Subcommands: `om-off` / `om-on` toggle observational memory. |
|
|
231
|
+
| `/blackhole` | Compact the conversation. Subcommands: `configure` (settings overlay), `om-off` / `om-on` toggle observational memory. |
|
|
232
232
|
| `/blackhole-memory` (or `status`) | Pipeline status: token progress, observation/reflection counts, pending data, last errors |
|
|
233
233
|
| `/blackhole-memory view` | Show visible observations and reflections (after compaction trimming), copied to clipboard |
|
|
234
234
|
| `/blackhole-memory full` | Show ALL recorded memory (including dropped observations), copied to clipboard |
|
|
@@ -254,7 +254,7 @@ The agent gets a unified `recall` tool that handles three types of input:
|
|
|
254
254
|
|
|
255
255
|
## Configuration
|
|
256
256
|
|
|
257
|
-
All settings in a single JSON file: **`~/.pi/agent/pi-blackhole/pi-blackhole-config.json`** — auto-created with defaults on first startup. See [`CONFIG.md`](CONFIG.md) for the full reference with detailed explanations for every knob. An annotated example config is at [`example-config.json`](example-config.json).
|
|
257
|
+
All settings in a single JSON file: **`~/.pi/agent/pi-blackhole/pi-blackhole-config.json`** — auto-created with defaults on first startup. See [`CONFIG.md`](CONFIG.md) for the full reference with detailed explanations for every knob. An annotated example config with the new config surface is at [`example-config-v2.json`](example-config-v2.json). The legacy example is at [`example-config.json`](example-config.json).
|
|
258
258
|
|
|
259
259
|
Quick start — just set custom models (if you want):
|
|
260
260
|
|
|
@@ -266,27 +266,28 @@ Quick start — just set custom models (if you want):
|
|
|
266
266
|
}
|
|
267
267
|
```
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
Everything else has sensible defaults.
|
|
270
270
|
|
|
271
271
|
### Settings at a glance
|
|
272
272
|
|
|
273
273
|
| Setting | Default | What it controls |
|
|
274
274
|
|---|---|---|
|
|
275
|
-
| `
|
|
276
|
-
| `
|
|
277
|
-
| `
|
|
278
|
-
| `
|
|
275
|
+
| `compaction` | `"auto"` | When compaction triggers: `"auto"` (blackhole auto-fires), `"manual"` (only `/blackhole`), `"off"` (Pi handles auto + `/compact`, `/blackhole` still works) |
|
|
276
|
+
| `compactionEngine` | `"blackhole"` | Which engine handles auto-compaction: `"blackhole"` or `"pi-default"`. Only meaningful when `compaction: "auto"` — for `"manual"`/`"off"` the hook lets Pi handle everything except `/blackhole` |
|
|
277
|
+
| `tailBehavior` | `"minimal"` | How much of the transcript stays visible after compaction: `"minimal"` (last user message only, default) or `"pi-default"` (gentle, ~20k tokens) |
|
|
278
|
+
| `memory` | `true` | `false` = OM workers off + no memory injection (compaction still runs) |
|
|
279
279
|
| `model` | — | Base fallback model for all workers (last resort before session model) |
|
|
280
280
|
| `observerModel` / `observerFallbackModels` | — / `[]` | Primary + fallback models for observer (extracts facts) |
|
|
281
281
|
| `reflectorModel` / `reflectorFallbackModels` | — / `[]` | Primary + fallback models for reflector (synthesizes reflections) |
|
|
282
282
|
| `dropperModel` / `dropperFallbackModels` | — / `[]` | Primary + fallback models for dropper (prunes observations) |
|
|
283
283
|
| *(per model)* `thinking` | `"low"` | Thinking/reasoning level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh` |
|
|
284
284
|
| *(per model)* `cooldownHours` | `1` | How long to skip this model after a retryable error |
|
|
285
|
+
| *(per model)* `contextWindow` | *(inherited from Pi)* | Override context window for this model. If unset, inherits from Pi's model registry. When set, the OM pipeline checks if the estimated input fits before calling the model — if not, the next fallback is tried. |
|
|
285
286
|
| `observeAfterTokens` | `15000` | Min accumulated tokens before observer runs |
|
|
286
287
|
| `reflectAfterTokens` | `25000` | Min accumulated tokens before reflector + dropper run |
|
|
287
|
-
| `compactAfterTokens` | `81000` | Auto-compaction threshold (when `
|
|
288
|
+
| `compactAfterTokens` | `81000` | Auto-compaction threshold (when `compaction: "auto"`) |
|
|
288
289
|
| `observerChunkMaxTokens` | `40000` | Max observer input per run (newest-first) |
|
|
289
|
-
| `observerPreambleMaxTokens` | `0` (auto) | Preamble cap for observer in `
|
|
290
|
+
| `observerPreambleMaxTokens` | `0` (auto) | Preamble cap for observer in `compaction: "manual"` mode (auto = 30% of chunk) |
|
|
290
291
|
| `observationsPoolMaxTokens` | `20000` | Max active observation pool before dropper prunes |
|
|
291
292
|
| `observationsPoolTargetTokens` | `10000` | Target size dropper aims for after pruning (derived: half of pool max) |
|
|
292
293
|
| `reflectorInputMaxTokens` | `80000` | Max reflector input budget |
|
|
@@ -295,7 +296,7 @@ That's it. Everything else has sensible defaults.
|
|
|
295
296
|
| `debug` | `false` | Pre-compaction snapshot to `/tmp/pi-blackhole-debug.json` |
|
|
296
297
|
| `debugLog` | `false` | Continuous JSONL debug log to `~/.pi/agent/pi-blackhole/debug.ndjson` |
|
|
297
298
|
|
|
298
|
-
**Environment override:** `PI_BLACKHOLE_PASSIVE=true`
|
|
299
|
+
**Environment override:** `PI_BLACKHOLE_PASSIVE=true` sets `compaction: "off"` + `memory: false` without touching the config file. Also accepts legacy `PI_VCC_OM_PASSIVE` / `PI_OBSERVATIONAL_MEMORY_PASSIVE`.
|
|
299
300
|
|
|
300
301
|
### Configuration presets
|
|
301
302
|
|
|
@@ -480,6 +481,28 @@ Results are shown as a collapsible message and auto-fed to the agent as context.
|
|
|
480
481
|
|
|
481
482
|
**Touched mode** (`mode:touched`) aggregates all files written, edited, or read across the session, grouped by path. Each entry shows which tool operation touched the file and the line count. Useful for getting a lay of the land after a long session.
|
|
482
483
|
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## Feature comparison
|
|
487
|
+
|
|
488
|
+
| | pi-blackhole | pi-vcc | pi-obs-memory | Pi default |
|
|
489
|
+
|---|---|---|---|---|
|
|
490
|
+
| Algorithmic compaction (no LLM cost) | ✓ | ✓ | — | — |
|
|
491
|
+
| Deterministic output | ✓ | ✓ | — | — |
|
|
492
|
+
| Structured summary sections | ✓ | ✓ | — | — |
|
|
493
|
+
| Observations + reflections | ✓ | — | ✓ | — |
|
|
494
|
+
| Context survives across compactions | ✓ | — | ✓ | — |
|
|
495
|
+
| Background memory workers | ✓ | — | ✓ | — |
|
|
496
|
+
| Searchable history after compaction | ✓ | ✓ | partial | — |
|
|
497
|
+
| Per-worker model config | ✓ | — | — | — |
|
|
498
|
+
| Fallback model chains + persisted cooldowns | ✓ | — | — | — |
|
|
499
|
+
| Manual flush mode (`compaction: "manual"`) | ✓ | — | — | — |
|
|
500
|
+
| Memory toggle (`/blackhole om-off`) | ✓ | — | — | — |
|
|
501
|
+
| Unified single-file config | ✓ | — | — | — |
|
|
502
|
+
| Per-session pending state | ✓ | — | — | — |
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
483
506
|
## Uninstall
|
|
484
507
|
|
|
485
508
|
```bash
|
|
@@ -500,7 +523,7 @@ What blackhole adds and reworks on top:
|
|
|
500
523
|
|
|
501
524
|
- **Unified configuration** — one JSON file, not two
|
|
502
525
|
- **Per-worker model fallback chains** with persisted cooldowns that survive Pi restarts
|
|
503
|
-
- **Manual flush mode** — `
|
|
526
|
+
- **Manual flush mode** — `compaction: "manual"` saves observations to per-session disk buffers
|
|
504
527
|
- **Conflict resolution** — OM hooks into vcc's compaction, not Pi's default
|
|
505
528
|
- **Memory toggle** (`/blackhole om-off` / `/blackhole om-on`) — disable the memory layer without uninstalling
|
|
506
529
|
- **Per-session pending state** — isolated per-session JSON files, no cross-session contamination
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-blackhole",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"packageManager": "pnpm@11.2.2",
|
|
5
5
|
"description": "Unified compaction + observational memory extension for Pi — compresses conversation context while preserving durable observations and reflections",
|
|
6
6
|
"license": "MIT",
|
package/src/commands/memory.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* /memory command — shows memory pipeline status and content.
|
|
2
|
+
* /blackhole-memory command — shows memory pipeline status and content.
|
|
3
3
|
*
|
|
4
4
|
* Created by pi-vcc-om. Replaces OM's standalone /om-status and /om-view.
|
|
5
|
-
* Usage: /memory
|
|
5
|
+
* Usage: /blackhole-memory [status|view|full]
|
|
6
6
|
*/
|
|
7
7
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { copyTextToClipboard } from "../om/clipboard.js";
|
|
@@ -79,7 +79,7 @@ export function registerMemoryCommand(pi: ExtensionAPI, runtime: Runtime): void
|
|
|
79
79
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
80
80
|
const mode = firstArg(args);
|
|
81
81
|
|
|
82
|
-
// /memory full — show full recorded memory + copy to clipboard
|
|
82
|
+
// /blackhole-memory full — show full recorded memory + copy to clipboard
|
|
83
83
|
if (mode === "full") {
|
|
84
84
|
const projection = fullProjection(entries);
|
|
85
85
|
const output = renderContentOnlyProjection(projection, "recorded");
|
|
@@ -91,7 +91,7 @@ export function registerMemoryCommand(pi: ExtensionAPI, runtime: Runtime): void
|
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
// /memory view — show visible memory + copy to clipboard
|
|
94
|
+
// /blackhole-memory view — show visible memory + copy to clipboard
|
|
95
95
|
if (mode === "view") {
|
|
96
96
|
const projection = visibleProjection(entries);
|
|
97
97
|
const output = renderContentOnlyProjection(projection, "visible");
|
|
@@ -103,7 +103,7 @@ export function registerMemoryCommand(pi: ExtensionAPI, runtime: Runtime): void
|
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
// /memory (no args) — show status
|
|
106
|
+
// /blackhole-memory (no args) — show status
|
|
107
107
|
if (mode && mode !== "status") {
|
|
108
108
|
ctx.ui.notify("Usage: /blackhole-memory [status|view|full]", "info");
|
|
109
109
|
return;
|
package/src/commands/pi-vcc.ts
CHANGED
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import type { Runtime } from "../om/runtime.js";
|
|
11
|
-
import { PI_VCC_COMPACT_INSTRUCTION } from "../hooks/before-compact";
|
|
12
|
-
import { saveUnifiedConfig } from "../core/unified-config.js";
|
|
11
|
+
import { PI_VCC_COMPACT_INSTRUCTION, notifyMigrationReminder } from "../hooks/before-compact";
|
|
12
|
+
import { saveUnifiedConfig, configPath } from "../core/unified-config.js";
|
|
13
13
|
import { readPendingState, clearPendingState, hasPendingData } from "../om/pending.js";
|
|
14
|
+
import { createConfigureOverlay } from "../om/configure-overlay.js";
|
|
14
15
|
import {
|
|
15
16
|
OM_OBSERVATIONS_DROPPED,
|
|
16
17
|
OM_OBSERVATIONS_RECORDED,
|
|
@@ -23,15 +24,50 @@ const formatTokens = (n: number): string => {
|
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
export const registerPiVccCommand = (pi: ExtensionAPI, runtime: Runtime) => {
|
|
27
|
+
const fuzzyMatch = (value: string, prefix: string): boolean => {
|
|
28
|
+
const q = prefix.toLowerCase();
|
|
29
|
+
let qi = 0;
|
|
30
|
+
for (const ch of value) {
|
|
31
|
+
if (qi < q.length && ch === q[qi]) qi++;
|
|
32
|
+
if (qi === q.length) return true;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
};
|
|
36
|
+
|
|
26
37
|
pi.registerCommand("blackhole", {
|
|
27
38
|
description:
|
|
28
39
|
"Compact conversation — structured summary (with observational memory when enabled). " +
|
|
29
|
-
"Subcommands: /blackhole
|
|
40
|
+
"Subcommands: /blackhole configure (open settings overlay), " +
|
|
41
|
+
"/blackhole om-off (disable memory), /blackhole om-on (re-enable memory).",
|
|
42
|
+
getArgumentCompletions: (prefix: string) => {
|
|
43
|
+
const subcommands = [
|
|
44
|
+
{ value: "configure", label: "Open configuration overlay to edit settings [configure]" },
|
|
45
|
+
{ value: "om-off", label: "Disable observational memory [om-off]" },
|
|
46
|
+
{ value: "om-on", label: "Enable observational memory [om-on]" },
|
|
47
|
+
];
|
|
48
|
+
if (!prefix) return subcommands;
|
|
49
|
+
return subcommands.filter((s) => fuzzyMatch(s.value, prefix));
|
|
50
|
+
},
|
|
30
51
|
handler: async (args, ctx) => {
|
|
31
52
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
32
53
|
|
|
33
|
-
// Handle
|
|
54
|
+
// Handle subcommands
|
|
34
55
|
const trimmed = (typeof args === "string" ? args : "").trim();
|
|
56
|
+
if (trimmed === "configure") {
|
|
57
|
+
// Open the config overlay
|
|
58
|
+
const result = await ctx.ui.custom<{ saved: boolean; path: string } | undefined>(
|
|
59
|
+
(tui, theme, _kb, done) => createConfigureOverlay(configPath(), theme, tui, done),
|
|
60
|
+
{ overlay: true },
|
|
61
|
+
);
|
|
62
|
+
if (result) {
|
|
63
|
+
if (result.saved) {
|
|
64
|
+
ctx.ui.notify("Configuration saved.", "info");
|
|
65
|
+
} else {
|
|
66
|
+
ctx.ui.notify("Failed to save configuration — the config file may be read-only (e.g., managed by Nix).", "warning");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
35
71
|
if (trimmed === "om-off") {
|
|
36
72
|
const saved = saveUnifiedConfig({ memory: false });
|
|
37
73
|
runtime.config.memory = false;
|
|
@@ -61,9 +97,9 @@ export const registerPiVccCommand = (pi: ExtensionAPI, runtime: Runtime) => {
|
|
|
61
97
|
return;
|
|
62
98
|
}
|
|
63
99
|
|
|
64
|
-
// If noAutoCompact: flush pending OM entries
|
|
65
|
-
// before compacting so the summary includes accumulated memory.
|
|
66
|
-
if (runtime.config.
|
|
100
|
+
// If compaction is manual (or legacy noAutoCompact): flush pending OM entries
|
|
101
|
+
// into the branch before compacting so the summary includes accumulated memory.
|
|
102
|
+
if (runtime.config.compaction === "manual" && hasPendingData(sessionId)) {
|
|
67
103
|
const pending = readPendingState(sessionId);
|
|
68
104
|
// Write all accumulated observation batches (or latest single batch
|
|
69
105
|
// as fallback for legacy pending.json without batch arrays).
|
|
@@ -105,6 +141,7 @@ export const registerPiVccCommand = (pi: ExtensionAPI, runtime: Runtime) => {
|
|
|
105
141
|
} else {
|
|
106
142
|
ctx.ui.notify("Compacted with blackhole", "info");
|
|
107
143
|
}
|
|
144
|
+
notifyMigrationReminder(sessionId, (msg, level) => ctx.ui.notify(msg, level as any));
|
|
108
145
|
},
|
|
109
146
|
onError: (err) => {
|
|
110
147
|
if (err.message === "Compaction cancelled" || err.message === "Already compacted") {
|
package/src/core/settings.ts
CHANGED
|
@@ -7,17 +7,32 @@
|
|
|
7
7
|
import { loadUnifiedConfig, scaffoldConfig } from "./unified-config.js";
|
|
8
8
|
|
|
9
9
|
export interface PiVccSettings {
|
|
10
|
-
/**
|
|
10
|
+
/** @deprecated Use compactionEngine instead. */
|
|
11
11
|
overrideDefaultCompaction: boolean;
|
|
12
|
+
/** @deprecated Use compaction instead. */
|
|
13
|
+
noAutoCompact: boolean;
|
|
14
|
+
/** @deprecated Use compaction + memory instead. */
|
|
15
|
+
passive: boolean;
|
|
12
16
|
/** Write debug snapshots to /tmp/pi-blackhole-debug.json. */
|
|
13
17
|
debug: boolean;
|
|
18
|
+
/** Unified compaction control: "auto" | "manual" | "off". */
|
|
19
|
+
compaction: "auto" | "manual" | "off";
|
|
20
|
+
/** Compaction engine: "blackhole" | "pi-default". */
|
|
21
|
+
compactionEngine: "blackhole" | "pi-default";
|
|
22
|
+
/** Visible tail behavior: "pi-default" | "minimal". */
|
|
23
|
+
tailBehavior: "pi-default" | "minimal";
|
|
14
24
|
}
|
|
15
25
|
|
|
16
26
|
export function loadSettings(): PiVccSettings {
|
|
17
27
|
const config = loadUnifiedConfig(process.cwd());
|
|
18
28
|
return {
|
|
19
|
-
overrideDefaultCompaction: config.overrideDefaultCompaction,
|
|
29
|
+
overrideDefaultCompaction: config.overrideDefaultCompaction ?? false,
|
|
30
|
+
noAutoCompact: config.noAutoCompact ?? false,
|
|
31
|
+
passive: config.passive ?? false,
|
|
20
32
|
debug: config.debug,
|
|
33
|
+
compaction: config.compaction,
|
|
34
|
+
compactionEngine: config.compactionEngine,
|
|
35
|
+
tailBehavior: config.tailBehavior,
|
|
21
36
|
};
|
|
22
37
|
}
|
|
23
38
|
|