pi-midcompact 0.2.1 → 0.3.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/README.md +10 -4
- package/figures/transaction-lifecycle.svg +1 -1
- package/package.json +1 -1
- package/skills/midcompact/SKILL.md +91 -31
- package/src/index.ts +41 -6
package/README.md
CHANGED
|
@@ -34,10 +34,14 @@ Start a transaction at a natural breakpoint: the current work is complete enough
|
|
|
34
34
|
Run:
|
|
35
35
|
|
|
36
36
|
```text
|
|
37
|
-
/midcompact
|
|
37
|
+
/midcompact start
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
Pi creates a temporary transaction after the anchor and tells the Agent how to plan the compression. No conversation is changed yet.
|
|
40
|
+
Pi asks for confirmation, then creates a temporary transaction after the anchor and tells the Agent how to plan the compression. No conversation is changed yet. You can include the initial scope in the same command:
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
/midcompact start Compress the early repository exploration, but keep user requirements verbatim.
|
|
44
|
+
```
|
|
41
45
|
|
|
42
46
|
### 2. Discuss what to compress with the Agent
|
|
43
47
|
|
|
@@ -109,7 +113,7 @@ A committed compression does not rewrite or delete the Pi session.
|
|
|
109
113
|
|
|
110
114
|

|
|
111
115
|
|
|
112
|
-
1. `/midcompact` freezes the current session-tree leaf as the anchor and starts a temporary maintenance branch.
|
|
116
|
+
1. `/midcompact start [instructions]` freezes the current session-tree leaf as the anchor and starts a temporary maintenance branch.
|
|
113
117
|
2. The Agent and you discuss a draft on that branch. This planning chatter is abandoned at commit.
|
|
114
118
|
3. `/midcompact commit` returns to the anchor and saves a branch-local `midcompact-state` entry containing the reviewed ranges and summaries.
|
|
115
119
|
4. Before later model requests, the extension finds the exact selected raw message sequences and projects them into summary messages.
|
|
@@ -136,7 +140,7 @@ Enter/Esc/q close
|
|
|
136
140
|
|
|
137
141
|
| Command | Result |
|
|
138
142
|
| --- | --- |
|
|
139
|
-
| `/midcompact` |
|
|
143
|
+
| `/midcompact start [instructions]` | Confirms and starts a transaction at the current session-tree leaf, optionally with an initial compression focus. |
|
|
140
144
|
| `/midcompact review` | Opens the draft review timeline. |
|
|
141
145
|
| `/midcompact commit` | Commits the reviewed draft. Human only. |
|
|
142
146
|
| `/midcompact abort` | Abandons the transaction and returns to the anchor. |
|
|
@@ -152,4 +156,6 @@ The extension shows planning status in Pi's footer only while a transaction is a
|
|
|
152
156
|
- **Tool protocol is protected.** Unknown, incomplete, or orphaned tool exchanges are not compressible.
|
|
153
157
|
- **Repeated transactions work.** Later transactions can compress newly accumulated raw context; existing summaries remain protected.
|
|
154
158
|
- **Native Pi `/compact` interaction needs more real-session validation.** Avoid relying on mixed automatic/native compaction behavior for critical work until it has been exercised in your environment.
|
|
159
|
+
- **Provider and extension interoperability needs more real-session validation.** Unusual message shapes, third-party context-transform ordering, and long-lived exact message fingerprints have not been broadly exercised.
|
|
160
|
+
- **Very long sessions are not stress-tested.** Large review snapshots and repeated block accumulation may eventually require consolidation.
|
|
155
161
|
- **Review is TUI-only.** There is no browser review interface in this version.
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
<circle cx="245" cy="505" r="23" class="anchor"/>
|
|
53
53
|
<polygon points="245,489 249,499 260,499 251,506 255,517 245,511 235,517 239,506 230,499 241,499" fill="#2563eb"/>
|
|
54
54
|
<text x="290" y="512" class="label blue">T50 · anchor</text>
|
|
55
|
-
<text x="290" y="537" class="small blue">/midcompact freezes
|
|
55
|
+
<text x="290" y="537" class="small blue">/midcompact start freezes here</text>
|
|
56
56
|
|
|
57
57
|
<!-- maintenance branch -->
|
|
58
58
|
<path d="M268 505 H 575" class="dash"/>
|
package/package.json
CHANGED
|
@@ -1,57 +1,117 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: midcompact
|
|
3
|
-
description: Use during an active /midcompact transaction to
|
|
3
|
+
description: Use during an active /midcompact transaction to plan and draft selective compression of stale middle sections of a long Pi conversation, or independently to retrieve details from previously compressed blocks. Covers how to choose compression ranges, how to negotiate compression depth with the user, and the midcompact tool interface.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Midcompact
|
|
7
7
|
|
|
8
|
-
Use this skill
|
|
8
|
+
Use this skill when a `/midcompact start` transaction is active, or when information must be retrieved from a previously compressed block.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## What compression does
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Mid-context compaction is **selective replacement** inside one linear conversation, not a restart:
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
```
|
|
15
|
+
A → B → C → ... → NOW
|
|
16
|
+
└─ selected slices become summaries; everything else stays verbatim
|
|
17
|
+
```
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
Three mechanical facts shape how you work:
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
- **KEEP by omission.** Anything outside a draft range stays verbatim. You declare only what to compress, never what to preserve.
|
|
22
|
+
- **Originals survive.** Session entries stay on disk, and `action="recall"` brings an active committed block back into view. Compression is reversible at the information-access level, not a deletion — but recall returns a readable rendering, not a byte-exact replay, so it is no substitute for keeping something verbatim.
|
|
23
|
+
- **Projection is layered.** Compression applies to what future models see, not to stored history.
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
## Who decides what
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
27
|
+
| Actor | Owns |
|
|
28
|
+
|-------|------|
|
|
29
|
+
| Extension | Session-tree mechanics, projection, protocol safety |
|
|
30
|
+
| You | Semantic judgment: which content may yield to a summary, and what each summary must carry |
|
|
31
|
+
| User | Compression scope and depth; the only actor that can commit |
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
You cannot commit. `/midcompact commit` is the user's gate. Your output is a proposal.
|
|
28
34
|
|
|
29
|
-
|
|
35
|
+
## What may be compressed
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
One conservation law decides every case:
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
2. Use `midcompact(action="locate", ...)` to resolve semantic landmarks to atom refs. Locator results include readable previews; request `detail="full"` when a boundary is ambiguous.
|
|
35
|
-
3. Build a draft with `midcompact(action="plan", op="add", start=..., end=..., summary=...)`.
|
|
36
|
-
- Use multiple ranges for non-contiguous compression.
|
|
37
|
-
- To preserve an important atom verbatim inside a broader phase, split the compression into ranges around that atom.
|
|
38
|
-
- Prefer KEEP-by-omission when uncertain.
|
|
39
|
-
- After each meaningful draft change, use the returned context telemetry to understand its scale; do not treat it as a quota.
|
|
40
|
-
4. Use `midcompact(action="plan", op="show")` and present the complete proposed plan to the user. Include what each range begins/ends with, not only atom IDs.
|
|
41
|
-
5. Recommend `/midcompact review` when the user wants to inspect the linear anchor timeline, proposed ranges, summaries, and KEEP holes. Incorporate requested changes with `op="update"`, `op="remove"`, additional ranges, or the review UI.
|
|
42
|
-
6. After the user is satisfied, ask them to run `/midcompact commit`. The Agent cannot commit itself. The explicit user command is the commit gate and returns the session tree to the anchor before persisting the projection.
|
|
39
|
+
> Compression must conserve every fact that can still affect the work **in the projected context**. After a range is replaced, each such fact must remain available with sufficient fidelity, either in text left verbatim outside the ranges or in the replacement summary.
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
Not age. Not token count. Not whether it is a tool call or prose. Two consequences follow:
|
|
42
|
+
|
|
43
|
+
- **The summary is load-bearing.** When a fact lives only inside a compressed range, omitting it from the summary removes it from the projected context. Recall is a recovery path, not a default carrier.
|
|
44
|
+
- **Some information must not be entrusted to paraphrase.** Exact requirement wording, literal error text, decision-critical evidence, protocol structure. When exact form or provenance matters, keep the source verbatim unless the replacement preserves it with equivalent fidelity.
|
|
45
|
+
|
|
46
|
+
Illustrative cases, derived from the law:
|
|
47
|
+
|
|
48
|
+
- Work that reached a conclusion or resolved result, with nothing downstream depending on how it got there → the summary states the outcome, or the concluding atom stays outside the range → compressible.
|
|
49
|
+
- Tool output fully absorbed into the answer that follows it → the answer sits outside the range → a thin summary suffices.
|
|
50
|
+
- A constraint, correction, or approval the user stated once → if the wording itself carries the constraint, keep it verbatim; otherwise the summary must state it explicitly.
|
|
51
|
+
- A rejected approach whose rejection reason still constrains current work → the summary must carry the reason; the exploration around it can go.
|
|
52
|
+
|
|
53
|
+
**Reverse failure.** Some content looks stale but is the only record of an environment quirk, a version-specific behavior, or a failure mode that will resurface. If you cannot state faithfully what a segment established, you cannot summarize it conservingly — keep it verbatim.
|
|
54
|
+
|
|
55
|
+
**Your summary becomes the successor.** The next working Agent — likely you, after commit — sees only your summary. Write what it needs to avoid redoing or breaking work: user intent and constraints, decisions and their rationale, file paths and signatures, validation state, unresolved issues, and the next useful state. Cutting repetitive exploration is the goal; shortening prose is not.
|
|
56
|
+
|
|
57
|
+
## Phase 1 — read, segment, propose, align
|
|
58
|
+
|
|
59
|
+
**Do not call `plan` until you have presented the semantic candidates and the user has confirmed or adjusted the direction**, unless the user explicitly instructs you to skip the proposal.
|
|
60
|
+
|
|
61
|
+
1. Read back over the conversation in your current context and segment it semantically — by phase of work, not by message count.
|
|
62
|
+
2. Judge each segment against the conservation law. For each candidate, be able to say where its load-bearing facts would end up.
|
|
63
|
+
3. Present candidates: where each begins and ends, roughly how large it is, and what its summary would carry. Name segments you deliberately excluded when the exclusion is non-obvious.
|
|
64
|
+
4. If scope or depth is still unresolved, ask how deep to go and which regions matter. Depth is the user's decision, not a number you optimize.
|
|
65
|
+
|
|
66
|
+
`locate` is read-only and may be used sparingly here to confirm a boundary you are about to propose, but not to scan exploratorily. A session that has reached compression is already near its limit: speculative calls, and a plan rebuilt after review, both consume what you are trying to reclaim.
|
|
67
|
+
|
|
68
|
+
If `/midcompact start` carried an instruction (it arrives as `User focus: ...`), treat it as guidance for whichever of scope and depth it specifies. Still propose, but briefly; ask only what the instruction leaves open.
|
|
69
|
+
|
|
70
|
+
## Phase 2 — locate, draft, review
|
|
71
|
+
|
|
72
|
+
1. `action="locate"` to resolve the landmarks you agreed on into atom refs. Results include previews; request `detail="full"` when a boundary is ambiguous.
|
|
73
|
+
2. `action="plan", op="add"` per range — several ranges for non-contiguous compression. To keep one important atom verbatim inside a broader phase, add ranges around it; that is KEEP by omission in practice.
|
|
74
|
+
3. `action="plan", op="show"`, then present the complete plan described by content rather than atom IDs.
|
|
75
|
+
4. Recommend `/midcompact review` when the user wants to inspect the anchor timeline, ranges, summaries, and KEEP holes visually. Apply changes with `op="update"`, `op="remove"`, or new ranges.
|
|
76
|
+
5. Ask the user to run `/midcompact commit` when satisfied.
|
|
77
|
+
|
|
78
|
+
## Tool interface
|
|
79
|
+
|
|
80
|
+
**Atoms are not messages.** An atom is the smallest compressible unit. One assistant message containing one or more tool calls, plus its immediately following matching results, forms a single `tool_exchange` atom; you cannot compress half of one.
|
|
81
|
+
|
|
82
|
+
**Two ref namespaces.** `a0001` is an atom ref, valid only within the current transaction's anchor snapshot — indices shift after every commit, so never reuse one across transactions; re-run `locate`. `c0001` is a compressed block id, stable while that block stays active on the current branch, used by `recall`.
|
|
83
|
+
|
|
84
|
+
`action="locate"` — pass either `ref` for a direct lookup, or at least one real filter: `pattern`, `tool_name`, or a `source` other than `any`. With none it returns nothing rather than an error. Optional: `direction` (`oldest`/`newest`), `limit` (default 5, max 20), `detail` (`brief`/`full`).
|
|
85
|
+
|
|
86
|
+
`action="plan"` — `op` defaults to `show`.
|
|
87
|
+
|
|
88
|
+
| op | Requires |
|
|
89
|
+
|----|----------|
|
|
90
|
+
| `show` | — |
|
|
91
|
+
| `add` | `start`, `end`, `summary`; `topic` optional |
|
|
92
|
+
| `update` | `draft_id` and at least one of `summary`, `topic` |
|
|
93
|
+
| `remove` | `draft_id` |
|
|
94
|
+
|
|
95
|
+
`op="add"` rejects a range when any of the following holds. These are mechanical constraints, independent of semantic value — a range must satisfy both.
|
|
96
|
+
|
|
97
|
+
| Condition | Meaning |
|
|
98
|
+
|-----------|---------|
|
|
99
|
+
| Range crosses a protected atom | Split the plan around it. An atom is protected when its tool-call protocol is still open (an incomplete `tool_exchange`, or an orphaned tool result), when it is an existing compressed block, when its message kind is not one the extension can compress, or when its messages have no persistent session entry to anchor to. |
|
|
100
|
+
| Range overlaps an existing draft range | Remove or update that range instead. |
|
|
101
|
+
| `start` occurs after `end` | Refs are positional; order them. |
|
|
102
|
+
| Unknown atom ref | Usually a typo or a ref carried over from an earlier transaction. Re-run `locate` against the current snapshot. |
|
|
103
|
+
|
|
104
|
+
**Telemetry** accompanies every `plan` result: anchor usage at start, approximate raw and summary tokens for the draft, and projected whole-context usage if committed now. Use it to check the draft against the depth agreed in Phase 1. It is awareness, not a target, and the projections are estimates.
|
|
45
105
|
|
|
46
106
|
## Repeated compression
|
|
47
107
|
|
|
48
|
-
A session may be
|
|
108
|
+
A session may be compacted multiple times. Committed blocks stay active and appear as protected atoms in later snapshots, so they cannot be recompressed. A later transaction compresses raw history accumulated around them.
|
|
49
109
|
|
|
50
110
|
## Recall
|
|
51
111
|
|
|
52
|
-
|
|
112
|
+
`action="recall"` works whether or not a transaction is active, does not change the projection, and reads blocks active on the current branch.
|
|
53
113
|
|
|
54
|
-
- `
|
|
55
|
-
- `
|
|
114
|
+
- `pattern="..."` searches topics and summaries of active blocks (`limit` default 8, max 20).
|
|
115
|
+
- `ref="c0001"` returns a readable, structure-flattened rendering of that block's messages, truncated if long. On a truncation marker, retry the same ref with `detail="full"`; if that still truncates, inspect the session tree. There is no paging.
|
|
56
116
|
|
|
57
|
-
|
|
117
|
+
Every summary in context states its own block id and the exact recall call for it. Use recall when a summary lacks a detail the current work needs.
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type ExtensionContext,
|
|
7
7
|
type SessionEntry,
|
|
8
8
|
} from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import type { AutocompleteItem } from "@earendil-works/pi-tui";
|
|
9
10
|
|
|
10
11
|
import { buildAtoms, formatLocatedAtom, locateAtoms } from "./atoms.js";
|
|
11
12
|
import { messageText } from "./messages.js";
|
|
@@ -89,9 +90,23 @@ export default function (pi: ExtensionAPI) {
|
|
|
89
90
|
|
|
90
91
|
pi.registerCommand("midcompact", {
|
|
91
92
|
description: "Start, review, commit, inspect, or abort a branch-isolated mid-context compression transaction",
|
|
93
|
+
getArgumentCompletions(prefix: string): AutocompleteItem[] | null {
|
|
94
|
+
const query = prefix.trimStart().toLowerCase();
|
|
95
|
+
if (/\s/.test(query)) return null;
|
|
96
|
+
const items: AutocompleteItem[] = [
|
|
97
|
+
{ value: "start", label: "start — Start a new midcompact transaction at the current anchor" },
|
|
98
|
+
{ value: "abort", label: "abort — Abort the active transaction and return to anchor" },
|
|
99
|
+
{ value: "commit", label: "commit — Commit the current draft to the branch state" },
|
|
100
|
+
{ value: "review", label: "review — Open interactive UI to inspect and edit the draft" },
|
|
101
|
+
{ value: "status", label: "status — Show current transaction and draft status" },
|
|
102
|
+
];
|
|
103
|
+
const filtered = items.filter((item) => item.value.startsWith(query));
|
|
104
|
+
return filtered.length > 0 ? filtered : null;
|
|
105
|
+
},
|
|
92
106
|
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
93
107
|
await ctx.waitForIdle();
|
|
94
|
-
const
|
|
108
|
+
const rawArgs = args.trim();
|
|
109
|
+
const sub = rawArgs.toLowerCase();
|
|
95
110
|
|
|
96
111
|
if (sub === "abort") {
|
|
97
112
|
const restored = restoreTransaction(ctx.sessionManager.getBranch() as SessionEntry[]);
|
|
@@ -164,8 +179,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
164
179
|
return;
|
|
165
180
|
}
|
|
166
181
|
|
|
167
|
-
|
|
168
|
-
|
|
182
|
+
const startMatch = rawArgs.match(/^start\s+/i);
|
|
183
|
+
const isStart = sub === "start" || !!startMatch;
|
|
184
|
+
const customInstructions = startMatch ? rawArgs.slice(startMatch[0].length).trim() : undefined;
|
|
185
|
+
|
|
186
|
+
if (!isStart) {
|
|
187
|
+
ctx.ui.notify("Usage: /midcompact start [instructions] | /midcompact review | /midcompact commit | /midcompact status | /midcompact abort", "warning");
|
|
169
188
|
return;
|
|
170
189
|
}
|
|
171
190
|
|
|
@@ -179,6 +198,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
179
198
|
ctx.ui.notify("Cannot start midcompact without a session leaf.", "error");
|
|
180
199
|
return;
|
|
181
200
|
}
|
|
201
|
+
if (ctx.hasUI) {
|
|
202
|
+
const ok = await ctx.ui.confirm(
|
|
203
|
+
"Start midcompact transaction?",
|
|
204
|
+
"The current context snapshot will be frozen as an anchor. You will need to review and explicitly commit (/midcompact commit) or abort (/midcompact abort) later.",
|
|
205
|
+
);
|
|
206
|
+
if (!ok) {
|
|
207
|
+
ctx.ui.notify("Midcompact start cancelled.", "info");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
182
211
|
transaction = {
|
|
183
212
|
version: 1,
|
|
184
213
|
id: `tx-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`,
|
|
@@ -192,13 +221,19 @@ export default function (pi: ExtensionAPI) {
|
|
|
192
221
|
updateStatus(ctx, transaction, draft);
|
|
193
222
|
const awareness = formatTelemetry(draftTelemetry(transaction, draft));
|
|
194
223
|
ctx.ui.notify(`Midcompact started at anchor ${anchorEntryId}. ${compactUsage(transaction)}`, "info");
|
|
195
|
-
|
|
224
|
+
const promptLines = [
|
|
196
225
|
"A mid-compaction transaction is active on a frozen anchor snapshot.",
|
|
197
226
|
awareness,
|
|
198
227
|
"These numbers are context awareness, not a target or optimization constraint. Use them to judge the scale of proposed compression while preserving semantic value.",
|
|
228
|
+
];
|
|
229
|
+
if (customInstructions) {
|
|
230
|
+
promptLines.push(`User focus: ${customInstructions}`);
|
|
231
|
+
}
|
|
232
|
+
promptLines.push(
|
|
199
233
|
"Load the `midcompact` skill, use the `midcompact` tool to locate and draft compression ranges, and present the draft for user review.",
|
|
200
234
|
"The Agent cannot commit. The user can inspect `/midcompact review`, then explicitly run `/midcompact commit` when satisfied.",
|
|
201
|
-
|
|
235
|
+
);
|
|
236
|
+
pi.sendUserMessage(promptLines.join("\n"));
|
|
202
237
|
},
|
|
203
238
|
});
|
|
204
239
|
|
|
@@ -210,7 +245,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
210
245
|
async execute(_id: string, params: ParamsType, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: ExtensionContext) {
|
|
211
246
|
try {
|
|
212
247
|
if (params.action === "recall") return toolResult(handleRecall(params, ctx));
|
|
213
|
-
if (!transaction) return toolResult("No active midcompact transaction. Ask the user to run `/midcompact` first.");
|
|
248
|
+
if (!transaction) return toolResult("No active midcompact transaction. Ask the user to run `/midcompact start` first.");
|
|
214
249
|
const snapshot = buildAnchorSnapshot(ctx.sessionManager, transaction);
|
|
215
250
|
if (params.action === "locate") return toolResult(handleLocate(params, snapshot.atoms));
|
|
216
251
|
if (params.action === "plan") {
|