pi-midcompact 0.4.0 → 0.5.1

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 CHANGED
@@ -55,7 +55,7 @@ The raw session JSONL still contains:
55
55
 
56
56
  ### A reviewed draft can reclaim meaningful context
57
57
 
58
- The browser review captured below selects **42 of 73 atoms** in **2 ranges**: approximately **31.0k 488 tokens**, for an estimated **30.6k-token reduction**. The other 31 atoms remain unselected and visible as raw context. Click either image to open it at full resolution.
58
+ The earlier browser and TUI captures below illustrate a draft with **2 ranges** covering **42 of 73 atoms**, while the other 31 atoms remain verbatim. The current UI reports Pi-provided anchor usage separately from factual content chars and image counts; it does not derive projected token savings from local character estimates. Click either image to open it at full resolution.
59
59
 
60
60
  <p align="center">
61
61
  <a href="./figures/review-webui.png">
@@ -127,7 +127,7 @@ Run:
127
127
  /midcompact start
128
128
  ```
129
129
 
130
- 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:
130
+ Pi opens a three-way chooser before creating transaction state: **Agent direct**, **User manual**, or **Drop**. Agent direct is the first and default-highlighted option, matching the previous fast path. The chooser is the standard `select` dialog, identical in interactive and RPC mode; RPC carries it as an extension UI `select` message with a bounded timeout, so an unresponsive client cancels instead of blocking. print/JSON modes have no dialog and default to **Agent direct**. Agent direct starts the existing inventory-first Agent workflow. User manual sends the same transaction guidance with a final “acknowledge only” instruction; after the Agent replies briefly, the Selection workbench opens (browser-based outside interactive mode). It does not start planning or mutate the DraftPlan until the user hands off later. Save the initial DraftPlan, close the UI, then tell the Agent to continue when you are ready. You can include an initial focus in the same command:
131
131
 
132
132
  ```text
133
133
  /midcompact start Compress the early repository exploration, but keep user requirements verbatim.
@@ -161,7 +161,14 @@ For RPC, print, or other no-TUI modes, use the editable local browser interface
161
161
  /midcompact review-webui
162
162
  ```
163
163
 
164
- You can edit a selected summary or topic, remove a range, and switch between compression ranges in either review surface. To change range boundaries or leave an important hole uncompressed, tell the Agent what to keep and ask it to revise the plan, then review it again.
164
+ Use Selection to create or change ranges and KEEP holes:
165
+
166
+ ```text
167
+ /midcompact select
168
+ /midcompact select-webui
169
+ ```
170
+
171
+ Use the TUI or local browser Review surface to edit summaries/topics and reject ranges. Review deliberately does not create or resize ranges; reopen Selection for boundary changes. To continue with Agent after a user-created plan, send a normal message asking it to continue the current midcompact draft; the Agent is prompted to read the existing plan first.
165
172
 
166
173
  ### 4. Commit the reviewed compression
167
174
 
@@ -204,9 +211,11 @@ Enter/Esc/q close
204
211
 
205
212
  | Command | Result |
206
213
  | --- | --- |
207
- | `/midcompact start [instructions]` | Confirms and starts a transaction at the current session-tree leaf, optionally with an initial compression focus. |
208
- | `/midcompact review` | Opens the draft review timeline in the native TUI; in non-TUI modes, points to `review-webui`. |
209
- | `/midcompact review-webui` | Starts an editable local browser review page; works without a TUI. |
214
+ | `/midcompact start [instructions]` | Opens Agent direct / User manual / Drop, then starts a transaction at the current session-tree leaf. |
215
+ | `/midcompact select` | Opens the native TUI Selection workbench for range and KEEP editing. |
216
+ | `/midcompact select-webui` | Opens the local browser Selection workbench. |
217
+ | `/midcompact review` | Opens summary/topic review in the native TUI. |
218
+ | `/midcompact review-webui` | Opens summary/topic review in a local browser. |
210
219
  | `/midcompact commit` | Commits the reviewed draft. Human only. |
211
220
  | `/midcompact abort` | Abandons the transaction and returns to the anchor. |
212
221
  | `/midcompact status` | Displays the current draft, or the committed compression state on this branch. |
@@ -224,4 +233,4 @@ The extension shows planning status in Pi's footer only while a transaction is a
224
233
  - **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.
225
234
  - **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.
226
235
  - **Very long sessions are not stress-tested.** Large review snapshots and repeated block accumulation may eventually require consolidation.
227
- - **Browser review is local.** `review-webui` binds to loopback, opens an editable review page, and keeps the draft mutations in the same branch-local transaction as the native TUI.
236
+ - **Browser workbenches are local.** `select-webui` and `review-webui` bind to loopback and mutate the same branch-local DraftPlan as the native TUI surfaces.
package/README.zh-CN.md CHANGED
@@ -55,7 +55,7 @@ Pi 内置的 `/compact` 可理解为**前缀压缩**(prefix compaction):
55
55
 
56
56
  ### 实际压缩效果
57
57
 
58
- 下方浏览器审查界面中的草案包含 **2 个区段**,覆盖 **73 个对话原子(atom,最小可压缩单元)中的 42 个**:约从 **31.0k token 降至 488 token**,预计减少 **30.6k token**。其余 31 个原子未被选中,仍以原文留在上下文中。点击图片可查看原图。
58
+ 下面的早期浏览器和 TUI 截图展示了一份包含 **2 个区段**、覆盖 **73 atom 42 个**的草案,其余 31 atom 保留原文。当前 UI 会把 Pi 提供的锚点 usage 与扩展统计的 content chars、图片数量分开显示,不再根据本地字符估算推导预计 token 节省量。点击图片可查看原图。
59
59
 
60
60
  <p align="center">
61
61
  <a href="./figures/review-webui.png">
@@ -127,7 +127,7 @@ pi install git:github.com/frostime/pi-midcompact
127
127
  /midcompact start
128
128
  ```
129
129
 
130
- Pi 会要求确认,随后在锚点之后创建临时事务,并提示 Agent 开始规划。此时尚未改动任何会话内容。也可以在命令中直接写明初始范围:
130
+ Pi 会在创建事务状态前提供三个选项:**Drop**、**Agent direct** 和 **User manual**。Agent direct 进入现有的 inventory-first Agent 流程;User manual 会发送同样的事务说明,但末尾要求 Agent 只确认知悉。Agent 简短回复后,才打开 Selection 工作台;在用户交接前不会开始规划或修改 DraftPlan。用户保存初始 DraftPlan 并关闭界面后,可在准备好时再让 Agent 继续。也可以在命令中直接写明初始重点:
131
131
 
132
132
  ```text
133
133
  /midcompact start 压缩前期仓库探索过程,但保留用户需求原文。
@@ -161,7 +161,14 @@ Agent 会在冻结的会话快照中定位相关内容,提出一个或多个
161
161
  /midcompact review-webui
162
162
  ```
163
163
 
164
- 两种审查界面都可以修改所选区段的摘要或主题、移除区段、切换不同区段。若需调整边界,或保留一个重要的未压缩部分,请告诉 Agent 保留什么,并要求它修订方案后再次审查。
164
+ 需要创建或调整区段与 `KEEP` 保留洞时,使用 Selection:
165
+
166
+ ```text
167
+ /midcompact select
168
+ /midcompact select-webui
169
+ ```
170
+
171
+ TUI 与本地浏览器 Review 界面用于编辑摘要/主题和否决区段。Review 不创建或调整区段边界;边界变化应重新打开 Selection。用户先创建计划后,只需发送普通消息要求 Agent 继续当前 midcompact draft,Agent 会先读取已有计划。
165
172
 
166
173
  ### 4. 提交已审查的压缩
167
174
 
@@ -204,9 +211,11 @@ Enter/Esc/q 关闭
204
211
 
205
212
  | 命令 | 作用 |
206
213
  | --- | --- |
207
- | `/midcompact start [instructions]` | 确认后在当前会话树叶节点启动事务;可选地提供初始压缩重点。 |
208
- | `/midcompact review` | 在原生 TUI 中打开草案审查;非 TUI 模式下会提示使用 `review-webui`。 |
209
- | `/midcompact review-webui` | 启动可编辑的本地浏览器审查页面;无需 TUI。 |
214
+ | `/midcompact start [instructions]` | 显示 Drop / Agent direct / User manual,并在当前会话树叶节点启动事务。 |
215
+ | `/midcompact select` | 在原生 TUI 中打开 Selection 工作台,编辑区段和 `KEEP`。 |
216
+ | `/midcompact select-webui` | 在本地浏览器中打开 Selection 工作台。 |
217
+ | `/midcompact review` | 在原生 TUI 中审查摘要和主题。 |
218
+ | `/midcompact review-webui` | 在本地浏览器中审查摘要和主题。 |
210
219
  | `/midcompact commit` | 提交已审查的草案;只能由用户执行。 |
211
220
  | `/midcompact abort` | 放弃事务并回到锚点。 |
212
221
  | `/midcompact status` | 显示当前草案,或本分支已提交的压缩状态。 |
@@ -224,4 +233,4 @@ Enter/Esc/q 关闭
224
233
  - **与 Pi 原生 `/compact` 的组合仍需更多真实会话验证。** 在完成充分验证前,不应在关键工作中依赖两者混用。
225
234
  - **Provider 与扩展互操作性仍需更多真实会话验证。** 非常规消息形态、第三方上下文转换顺序,以及长时间运行的精确消息指纹尚未得到广泛验证。
226
235
  - **超长会话尚未完成压力测试。** 审查快照很大、压缩块反复累积时,最终可能需要进一步整合。
227
- - **浏览器审查仅在本机开放。** `review-webui` 绑定到 loopback 地址,提供可编辑的审查页面;其草案修改与原生 TUI 使用同一条分支事务。
236
+ - **浏览器工作台仅在本机开放。** `select-webui` 与 `review-webui` 绑定到 loopback,并与原生 TUI 操作同一份分支内 DraftPlan。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-midcompact",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Branch-aware mid-context compression for the Pi coding agent",
5
5
  "author": "frostime",
6
6
  "repository": {
@@ -32,7 +32,7 @@
32
32
  ],
33
33
  "scripts": {
34
34
  "typecheck": "tsc -p tsconfig.json --noEmit",
35
- "test": "rm -rf .test-dist && tsc -p tsconfig.test.json && node test/install-mocks.mjs && node --test test/core.test.mjs test/runtime.test.mjs",
35
+ "test": "rm -rf .test-dist && tsc -p tsconfig.test.json && node test/install-mocks.mjs && node --test test/core.test.mjs test/runtime-start.test.mjs test/runtime-agent.test.mjs test/runtime-user.test.mjs test/runtime-transaction.test.mjs test/runtime-lock.test.mjs",
36
36
  "typecheck:contract": "tsc -p tsconfig.test.json --noEmit",
37
37
  "pack:check": "npm pack --dry-run"
38
38
  },
@@ -1,117 +1,132 @@
1
1
  ---
2
2
  name: midcompact
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.
3
+ description: Use when a midcompact runtime prompt starts or hands off a compression-planning transaction, or when current work needs details from an active committed midcompact block. Guides user-aligned candidate discovery, effort-scaled inspect/locate/plan work, replacement-summary writing, and recall.
4
4
  ---
5
5
 
6
6
  # Midcompact
7
7
 
8
- Use this skill when a `/midcompact start` transaction is active, or when information must be retrieved from a previously compressed block.
8
+ ## Route the activation
9
9
 
10
- ## What compression does
10
+ This skill handles two independent tasks: planning compression and recalling committed content. Choose the route before calling the tool.
11
11
 
12
- Mid-context compaction is **selective replacement** inside one linear conversation, not a restart:
12
+ | Signal | Immediate duty | First action |
13
+ |--------|----------------|--------------|
14
+ | Runtime prompt says `FINAL STATE: USER MANUAL` | Let the user create the initial DraftPlan | Reply exactly `OK`; call no midcompact tool |
15
+ | Runtime prompt says `FINAL STATE: AGENT DIRECT` | Start from the new empty draft | `action="inspect"` |
16
+ | A handoff reports a persisted DraftPlan and the user asks to continue | Read the shared selection and determine what help the user wants | `action="plan", op="show"` |
17
+ | The user or a projected summary needs detail from a committed block | Retrieve that history only | Follow **Recall workflow** |
13
18
 
14
- ```
15
- A → B → C → ... → NOW
16
- └─ selected slices become summaries; everything else stays verbatim
17
- ```
19
+ The state-specific runtime prompt is authoritative. Recall does not enter planning or mutate the DraftPlan. During the User-manual acknowledgement turn, the no-tool instruction overrides every other route.
20
+
21
+ ## Plan compression
22
+
23
+ Selected ranges become summaries in future model context; content outside them stays verbatim. Originals remain stored, but recall is a recovery path, not a substitute for a sufficient summary. Agent and user edit one DraftPlan; the user retains final control through review and `/midcompact commit`.
24
+
25
+ The start mode controls how the first draft is created. It does not determine whether a user selection is final, how much initiative the Agent should take, or how deeply the history should be investigated.
26
+
27
+ Apply one invariant:
28
+
29
+ > Every fact that can still affect future work must remain with sufficient fidelity, either outside the selected ranges or in their replacement summaries.
30
+
31
+ ### Planning workflow
18
32
 
19
- Three mechanical facts shape how you work:
33
+ #### 1. Read the entry state and user intent
20
34
 
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.
35
+ For a fresh Agent-direct transaction, begin with `action="inspect"`. For a handed-off DraftPlan, begin with `action="plan", op="show"`; inspect the anchor only if the requested work needs broader context.
24
36
 
25
- ## Who decides what
37
+ Establish the user's desired scope, fidelity, and planning effort from their words, current selection, `User focus: ...`, and surrounding interaction. Treat answers collected through question, questionnaire, or similar tools as user-originated input even when represented as tool results.
26
38
 
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 |
39
+ A manual selection records what the user selected, not whether they mean "only this" or "help me refine it." Follow a clear intent directly. If different interpretations would materially change the work, ask briefly whether to limit work to the selection or inspect and suggest changes. Do not force clarification when the user has already expressed a preference or delegated the judgment.
32
40
 
33
- You cannot commit. `/midcompact commit` is the user's gate. Your output is a proposal.
41
+ Match effort to the requested fidelity. A quick or approximate request calls for bounded planning; a precision-sensitive request may justify deeper work after alignment.
34
42
 
35
- ## What may be compressed
43
+ #### 2. Form a bounded semantic view
36
44
 
37
- One conservation law decides every case:
45
+ `inspect` returns factual structure with bounded user landmarks, grouped by user message. Use it for structure and volume, not to invent semantics. Ground candidates in visible conversation or a few targeted `locate` calls, and stop paging after the potentially relevant region. When exact candidate spans are known and their relative volume could change the choice, use read-only `inspect` spans to compare them without mutating the DraftPlan.
38
46
 
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.
47
+ Segment by completed work phase, not message count. User-originated input and concluding Agent responses are useful landmarks for intent and outcome, but are not automatically KEEP. Intermediate tool exchanges may also contain decisions or evidence absent from the final response.
40
48
 
41
- Not age. Not token count. Not whether it is a tool call or prose. Two consequences follow:
49
+ | Often able to yield to a summary | Often load-bearing |
50
+ |-----------------------------------|--------------------|
51
+ | Repetitive or superseded exploration | Current intent and active constraints |
52
+ | Tool output absorbed into a conclusion | Exact errors, evidence, or wording whose form matters |
53
+ | A completed subtask's intermediate process | Decisions and rationale still governing the work |
54
+ | A rejected attempt's mechanics | Its still-relevant failure reason |
42
55
 
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.
56
+ Use little or no `locate` during this reconnaissance. If a phase cannot yet be described faithfully, omit it from the proposal or mark it as needing confirmation rather than exploring the anchor broadly.
45
57
 
46
- Illustrative cases, derived from the law:
58
+ #### 3. Present semantic options and align
47
59
 
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.
60
+ Before deep `locate` work or substantial DraftPlan mutation, establish the user's compression preference through explicit instruction, a reliable implication, or concise clarification.
52
61
 
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.
62
+ Describe each proposal in recognizable conversation terms:
54
63
 
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.
64
+ > From `<semantic start>` to `<semantic end>`, `<replace the whole phase / keep the endpoints and compress the work between>`; preserve `<load-bearing information>` in the summary.
56
65
 
57
- ## Phase 1 read, segment, propose, align
66
+ Use short recognizable excerpts when available and clear paraphrases otherwise. State whether the endpoint messages remain verbatim, what intermediate work disappears, and what the summary carries. Do not identify a user-facing range primarily by atom refs, DraftPlan ids, or arbitrary item numbers.
58
67
 
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.
68
+ When treatments involve a meaningful tradeoff, present concise alternatives and recommend one. A clear quick request may need only one proportionate proposal. Add factual content or image measurements only when they help the choice, and do not convert them into unsupported token-savings claims.
60
69
 
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.
70
+ #### 4. Resolve boundaries and build the DraftPlan
65
71
 
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.
72
+ After the intended treatment is clear, use `action="locate"` for targeted content and boundary checks. An atom is the smallest selectable unit; a tool call and its matching results form one indivisible `tool_exchange` atom. Keep source text outside a range when exact wording or provenance matters and a summary cannot preserve it equivalently.
67
73
 
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.
74
+ Choose boundaries from the information that must survive, not from a fixed category. A range may replace a whole semantic phase, including its initiating and concluding messages. It may instead retain a load-bearing user instruction and concluding Agent response while replacing only the execution between them. It may split around important material to leave KEEP holes. These are reasoning patterns, not rules tied to start mode, message age, or one prescribed kind of work.
69
75
 
70
- ## Phase 2 locate, draft, review
76
+ Build or refine the shared DraftPlan with `plan add`, `update`, `remove`, and `show`. A handed-off selection may be preserved or revised as the user's intent permits. Use separate ranges for non-contiguous phases; fill pending summaries before commit. If deeper inspection would materially change the agreed treatment, surface the change instead of silently applying it.
71
77
 
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. In non-interactive modes (no TUI, RPC, print), `/midcompact review` only warns and points to `/midcompact review-webui`, which starts a local web page that works without a TUI. Apply changes with `op="update"`, `op="remove"`, or new ranges.
76
- 5. Ask the user to run `/midcompact commit` when satisfied.
78
+ Read `references/tool-interface.md` before retrying a rejected operation or when exact parameters, protected-atom causes, or measurements matter.
77
79
 
78
- ## Tool interface
80
+ #### 5. Write replacement summaries
81
+
82
+ A replacement summary is successor context for a future Agent, not a transcript or a prompt to perform compression. State the resulting knowledge and working state directly.
83
+
84
+ Preserve, when applicable:
85
+
86
+ - user intent and active constraints;
87
+ - decisions, conclusions, and necessary rationale;
88
+ - relevant files, symbols, interfaces, commands, or configuration;
89
+ - completed changes and validation results;
90
+ - rejected approaches only when their failure reason still matters;
91
+ - unresolved issues and any established next step or continuation state.
92
+
93
+ Remove repetitive exploration, superseded hypotheses, raw output captured by a conclusion, and chronology with no remaining consequence. Do not turn uncertainty into fact, invent a next action, or use references that only make sense inside the removed text. If retained endpoint messages already carry part of the intended context, do not duplicate them mechanically; use the summary to preserve what would otherwise be lost.
94
+
95
+ Organize by future utility rather than original chronology. When useful, use this compact frame without forcing empty fields:
96
+
97
+ ```text
98
+ Goal and constraints:
99
+ Established state and decisions:
100
+ Artifacts and validation:
101
+ Open issues and established next state:
102
+ ```
79
103
 
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.
104
+ Length follows the information that must survive, not a target ratio. Final test: could a fresh Agent continue correctly from this summary plus retained context, without repeating work or violating a prior decision?
81
105
 
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`.
106
+ #### 6. Verify and hand off
83
107
 
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`).
108
+ Call `action="plan", op="show"`. Check that the intended semantic phases are covered, KEEP holes remain outside ranges, every range has a summary, and the summaries conserve the future working state.
85
109
 
86
- `action="plan"` `op` defaults to `show`.
110
+ Describe the completed proposal with the same recognizable landmarks used during alignment. Direct the user to `/midcompact select` or `/midcompact select-webui` for boundaries and KEEP holes, and to `/midcompact review` or `/midcompact review-webui` for summary inspection or rejection. Use browser variants when the TUI is unavailable or preferred. Ask the user to run `/midcompact commit` when ready; never commit for them.
87
111
 
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` |
112
+ ## Recall compressed content
94
113
 
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.
114
+ Recall works with or without an active planning transaction. It reads committed blocks active on the current branch without changing projection or DraftPlan.
96
115
 
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. |
116
+ ### Recall workflow
103
117
 
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.
118
+ #### 1. Find the block
105
119
 
106
- ## Repeated compression
120
+ If its id is unknown, call `action="recall", pattern="..."` to search active topics and summaries. A projected summary also states its block id and exact recall call.
107
121
 
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.
122
+ #### 2. Retrieve the detail
109
123
 
110
- ## Recall
124
+ Call `action="recall", ref="c0001"`. If the readable, structure-flattened result ends with a truncation marker, retry with `detail="full"`. Retrieve only what the current task needs; do not start or change a plan merely to recall history.
111
125
 
112
- `action="recall"` works whether or not a transaction is active, does not change the projection, and reads blocks active on the current branch.
126
+ ## Tool conventions
113
127
 
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.
128
+ - `g0001` labels an inventory group; use its `a...` span as internal landmarks.
129
+ - `a0001` is a transaction-local atom ref; `d1` is a DraftPlan range id; `c0001` is an active committed-block id.
130
+ - Protected atoms cannot enter a range. Split around them.
116
131
 
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.
132
+ These refs are planning handles, not the primary way to explain compression to the user. Read `references/tool-interface.md` for exact defaults and limits, rejected operations, protected-atom causes, measurements, repeated compression, or recall truncation.
@@ -0,0 +1,83 @@
1
+ # Midcompact Tool Interface
2
+
3
+ Read this reference when exact call requirements, limits, rejection behavior, repeated compression, or recall truncation matters. The main workflow remains in `../SKILL.md`.
4
+
5
+ ## Inspect
6
+
7
+ Without `spans`, `action="inspect"` inventories the frozen anchor. It returns factual structure and bounded user landmarks, not full message bodies, assistant/tool previews, summaries, or image base64.
8
+
9
+ - `page_size`: default 20 groups, maximum 50.
10
+ - `cursor`: opaque value returned by the previous page.
11
+ - Results include group refs, atom spans, content chars, image facts, protected/compressible counts, and Pi-reported anchor usage.
12
+
13
+ Stop paging after the candidate regions are covered.
14
+
15
+ To compare explicit candidates without mutating the DraftPlan, pass one or more possibly overlapping spans:
16
+
17
+ ```text
18
+ midcompact(action="inspect", spans=[
19
+ {"start":"a0006","end":"a0014"},
20
+ {"start":"a0006","end":"a0020"}
21
+ ])
22
+ ```
23
+
24
+ Span inspection reports bounded endpoint landmarks, atom/message and role counts, tool exchanges and calls, factual content share, images, and protected/compressible counts. It has a 12,000-character total output budget and reports how many requested spans fit. It does not report per-span tokens: Pi supplies usage for the whole anchor, not token attribution by range. Do not combine `spans` with `page_size` or `cursor`.
25
+
26
+ ## Locate
27
+
28
+ `action="locate"` returns atoms from the frozen anchor. Supply either:
29
+
30
+ - `ref`: one direct atom lookup; or
31
+ - at least one real filter: `pattern`, `tool_name`, or `source` other than `any`.
32
+
33
+ With no lookup or filter it returns no matches rather than an error. Filtered searches return at most three brief candidates; when more match, the result reports the total and asks for a more specific pattern or additional filter. `direction` is `oldest` by default or `newest`; `limit` may request one to three results.
34
+
35
+ Brief direct lookups preserve both ends of an atom landmark. Pattern searches show a bounded excerpt around the match rather than the atom prefix. `detail="full"` is allowed only with one direct `ref` and preserves both ends with an explicit middle-omission marker when the 12,000-character atom limit is exceeded.
36
+
37
+ A `g...` inventory ref is not a locate ref; use the group's `a...` start/end landmarks.
38
+
39
+ ## Plan
40
+
41
+ `action="plan"` uses `op="show"` by default. Agent and user mutate the same DraftPlan.
42
+
43
+ | op | Required fields |
44
+ |----|-----------------|
45
+ | `show` | none; optional `draft_id` for one range |
46
+ | `add` | `start`, `end`; optional `summary`, `topic` |
47
+ | `update` | `draft_id` and at least one of `summary`, `topic` |
48
+ | `remove` | `draft_id` |
49
+
50
+ Default show lists each range with bounded `from`, `to`, and `summary` landmarks plus factual metrics. Use `op="show", detail="full", draft_id="d1"` for one stored summary and endpoint previews under a 40,000-character total budget; full detail without `draft_id` is rejected. Show is read-only and does not persist a duplicate DraftPlan entry.
51
+
52
+ Add/update return the changed range in brief form; remove returns its id and compact draft totals. Mutation responses omit the Pi-awareness header and unchanged ranges. Use explicit show when complete awareness is needed.
53
+
54
+ An omitted or empty `summary` creates a pending range. Review can open it, but commit requires at least one range and a non-empty summary for every range. Changing boundaries requires removing the old range and adding the replacement.
55
+
56
+ ### Add rejection conditions
57
+
58
+ | Condition | Response |
59
+ |-----------|----------|
60
+ | The range crosses a protected atom | Split the range around that atom |
61
+ | The range overlaps an existing draft range | Remove or replace the existing range first |
62
+ | `start` occurs after `end` | Correct the positional order |
63
+ | An atom ref is unknown | Re-run inspect/locate against the current transaction snapshot |
64
+
65
+ An atom is protected when its tool protocol is incomplete or orphaned, it represents an existing compressed block, its message kind is unsupported, or it lacks the persistent session entry needed to anchor compression.
66
+
67
+ ## Telemetry
68
+
69
+ Explicit `plan show` includes Pi-reported anchor usage when available and factual draft measurements: original/replacement content chars, image count, and decoded payload bytes. Mutation results omit awareness and report only compact totals plus the changed range where one remains. Local character or image measurements are not converted into token savings or projected-token percentages. Use measurements to compare the proposal with user-directed depth, not as an optimization target.
70
+
71
+ ## Repeated compression
72
+
73
+ Committed blocks appear as protected atoms in later transaction snapshots and cannot be compressed again. A later transaction may compress newly accumulated raw history around those blocks. Re-run inspect/locate because atom refs are transaction-local.
74
+
75
+ ## Recall
76
+
77
+ `action="recall"` works independently of a planning transaction and reads committed blocks active on the current branch.
78
+
79
+ - Without `ref`, `pattern` searches block topics and summaries; `limit` defaults to 8 and has a maximum of 20.
80
+ - With `ref="c0001"`, the tool renders that block's stored messages.
81
+ - `detail="full"` raises the rendering cap when the normal result is truncated.
82
+
83
+ Recall has no paging. The truncation marker is `[truncated; refine the recall request or inspect the source session for more]`. If `detail="full"` still truncates before the needed detail, report that recall cannot expose the omitted content; do not infer it.
package/src/atoms.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Atom, LocateQuery, MessageLike, MessageRef, SessionEntryLike } from "./types.js";
2
- import { approxTokens, mapEntryIds, messageKey, renderMessage, toolCalls, truncate } from "./messages.js";
2
+ import { aggregateMetrics, measureMessage } from "./content-metrics.js";
3
+ import { approxTokens, excerptAround, mapEntryIds, messageKey, renderMessage, toolCalls, truncateMiddle } from "./messages.js";
3
4
 
4
5
  export function buildAtoms(messages: MessageLike[], branch: readonly SessionEntryLike[]): Atom[] {
5
6
  const entryIds = mapEntryIds(messages, branch);
@@ -70,6 +71,11 @@ export function buildAtoms(messages: MessageLike[], branch: readonly SessionEntr
70
71
  return atoms;
71
72
  }
72
73
 
74
+ /** A protected atom cannot be part of any compressible range. */
75
+ export function isProtectedAtom(atom: Atom): boolean {
76
+ return !atom.compressible || !atom.protocolClosed || atom.kind === "compressed";
77
+ }
78
+
73
79
  function hasEntry(ref: MessageRef): boolean {
74
80
  return typeof ref.entryId === "string" && ref.entryId.length > 0;
75
81
  }
@@ -103,8 +109,9 @@ function makeAtom(
103
109
  messages,
104
110
  entryIds: messages.flatMap((ref) => (ref.entryId ? [ref.entryId] : [])),
105
111
  messageKeys: messages.map((ref) => ref.key),
106
- preview: truncate(fullText, 700),
112
+ preview: truncateMiddle(fullText, 700),
107
113
  fullText,
114
+ metrics: aggregateMetrics(messages.map((ref) => measureMessage(ref.message))),
108
115
  approxTokens: approxTokens(fullText),
109
116
  compressible,
110
117
  protocolClosed,
@@ -114,10 +121,17 @@ function makeAtom(
114
121
  };
115
122
  }
116
123
 
117
- export function locateAtoms(atoms: Atom[], query: LocateQuery): Atom[] {
124
+ export const MAX_LOCATE_MATCHES = 3;
125
+
126
+ export interface LocatedAtoms {
127
+ atoms: Atom[];
128
+ totalMatches: number;
129
+ }
130
+
131
+ export function locateAtomMatches(atoms: Atom[], query: LocateQuery): LocatedAtoms {
118
132
  if (query.ref) {
119
133
  const atom = atoms.find((candidate) => candidate.ref === query.ref);
120
- return atom ? [atom] : [];
134
+ return { atoms: atom ? [atom] : [], totalMatches: atom ? 1 : 0 };
121
135
  }
122
136
  const pattern = query.pattern?.toLocaleLowerCase();
123
137
  const toolName = query.toolName?.toLocaleLowerCase();
@@ -129,7 +143,13 @@ export function locateAtoms(atoms: Atom[], query: LocateQuery): Atom[] {
129
143
  return Boolean(pattern || toolName || source !== "any");
130
144
  });
131
145
  if ((query.direction ?? "oldest") === "newest") matches = matches.reverse();
132
- return matches.slice(0, Math.max(1, Math.min(query.limit ?? 5, 20)));
146
+ const totalMatches = matches.length;
147
+ const limit = Math.max(1, Math.min(query.limit ?? MAX_LOCATE_MATCHES, MAX_LOCATE_MATCHES));
148
+ return { atoms: matches.slice(0, limit), totalMatches };
149
+ }
150
+
151
+ export function locateAtoms(atoms: Atom[], query: LocateQuery): Atom[] {
152
+ return locateAtomMatches(atoms, query).atoms;
133
153
  }
134
154
 
135
155
  function matchesSource(atom: Atom, source: NonNullable<LocateQuery["source"]>): boolean {
@@ -141,9 +161,13 @@ function matchesSource(atom: Atom, source: NonNullable<LocateQuery["source"]>):
141
161
  return false;
142
162
  }
143
163
 
144
- export function formatLocatedAtom(atom: Atom, detail: "brief" | "full" = "brief"): string {
164
+ export function formatLocatedAtom(atom: Atom, detail: "brief" | "full" = "brief", pattern?: string): string {
145
165
  const flags = [atom.kind, atom.compressible ? "compressible" : "protected", atom.protocolClosed ? "closed" : "open"].join(", ");
146
- const text = detail === "full" ? truncate(atom.fullText, 12_000) : atom.preview;
166
+ const text = detail === "full"
167
+ ? atom.fullText.length <= 12_000 ? atom.fullText : truncateMiddle(atom.fullText, 12_000)
168
+ : pattern
169
+ ? excerptAround(atom.fullText, pattern, 700)
170
+ : atom.preview;
147
171
  return [
148
172
  `${atom.ref} | position ${atom.index + 1} | ${flags}`,
149
173
  atom.toolNames.length ? `tools: ${atom.toolNames.join(", ")}` : "",