pi-midcompact 0.5.2 → 0.5.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/package.json +2 -2
- package/skills/midcompact/SKILL.md +7 -7
- package/skills/midcompact/references/tool-interface.md +7 -7
- package/src/SPEC.md +93 -0
- package/src/index.ts +87 -62
- package/src/projection.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-midcompact",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
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-start.test.mjs test/runtime-agent.test.mjs test/runtime-user.test.mjs test/runtime-transaction.test.mjs test/runtime-lock.test.mjs",
|
|
35
|
+
"test": "rm -rf .test-dist && tsc -p tsconfig.test.json && node test/install-mocks.mjs && node --test test/schema-contract.test.mjs 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
|
},
|
|
@@ -12,8 +12,8 @@ This skill handles two independent tasks: planning compression and recalling com
|
|
|
12
12
|
| Signal | Immediate duty | First action |
|
|
13
13
|
|--------|----------------|--------------|
|
|
14
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
|
|
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
|
|
15
|
+
| Runtime prompt says `FINAL STATE: AGENT DIRECT` | Start from the new empty draft | `request={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 | `request={action:"plan", op:"show"}` |
|
|
17
17
|
| The user or a projected summary needs detail from a committed block | Retrieve that history only | Follow **Recall workflow** |
|
|
18
18
|
|
|
19
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.
|
|
@@ -34,7 +34,7 @@ Apply one invariant:
|
|
|
34
34
|
|
|
35
35
|
#### 1. Read the entry state and user intent
|
|
36
36
|
|
|
37
|
-
For a fresh Agent-direct transaction, begin with `action
|
|
37
|
+
For a fresh Agent-direct transaction, begin with `request={action:"inspect"}`. For a handed-off DraftPlan, begin with `request={action:"plan", op:"show"}`; inspect the anchor only if the requested work needs broader context.
|
|
38
38
|
|
|
39
39
|
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.
|
|
40
40
|
|
|
@@ -71,7 +71,7 @@ When treatments involve a meaningful tradeoff, present concise alternatives and
|
|
|
71
71
|
|
|
72
72
|
#### 4. Resolve boundaries and build the DraftPlan
|
|
73
73
|
|
|
74
|
-
After the intended treatment is clear, use `action
|
|
74
|
+
After the intended treatment is clear, use `request={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.
|
|
75
75
|
|
|
76
76
|
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.
|
|
77
77
|
|
|
@@ -107,7 +107,7 @@ Length follows the information that must survive, not a target ratio. Final test
|
|
|
107
107
|
|
|
108
108
|
#### 6. Verify and hand off
|
|
109
109
|
|
|
110
|
-
Call `action
|
|
110
|
+
Call `request={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.
|
|
111
111
|
|
|
112
112
|
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.
|
|
113
113
|
|
|
@@ -119,11 +119,11 @@ Recall works with or without an active planning transaction. It reads committed
|
|
|
119
119
|
|
|
120
120
|
#### 1. Find the block
|
|
121
121
|
|
|
122
|
-
If its id is unknown, call `action
|
|
122
|
+
If its id is unknown, call `request={action:"recall", pattern:"..."}` to search active topics and summaries. A projected summary also states its block id and exact recall call.
|
|
123
123
|
|
|
124
124
|
#### 2. Retrieve the detail
|
|
125
125
|
|
|
126
|
-
Call `action
|
|
126
|
+
Call `request={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.
|
|
127
127
|
|
|
128
128
|
## Tool conventions
|
|
129
129
|
|
|
@@ -4,11 +4,11 @@ Read this reference when exact call requirements, limits, rejection behavior, re
|
|
|
4
4
|
|
|
5
5
|
## Parameter grouping
|
|
6
6
|
|
|
7
|
-
The
|
|
7
|
+
The parameters are one object with a single `request` field; `request` is a discriminated union where each branch binds one `action` value to exactly that action's fields and is closed (`additionalProperties: false`). A call that mixes actions (for example `locate` fields on `plan`) is rejected by the schema itself; do not repair it by dropping fields, re-issue the call with only the selected action's parameters. Shared field names (`ref`, `pattern`, `limit`, `detail`) are defined independently per action with the meaning documented in that section.
|
|
8
8
|
|
|
9
9
|
## Inspect
|
|
10
10
|
|
|
11
|
-
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.
|
|
11
|
+
Without `spans`, `request.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.
|
|
12
12
|
|
|
13
13
|
- `page_size`: default 20 groups, maximum 50.
|
|
14
14
|
- `cursor`: opaque value returned by the previous page.
|
|
@@ -19,17 +19,17 @@ Stop paging after the candidate regions are covered.
|
|
|
19
19
|
To compare explicit candidates without mutating the DraftPlan, pass one or more possibly overlapping spans:
|
|
20
20
|
|
|
21
21
|
```text
|
|
22
|
-
midcompact(action
|
|
22
|
+
midcompact(request={action:"inspect", spans=[
|
|
23
23
|
{"start":"a0006","end":"a0014"},
|
|
24
24
|
{"start":"a0006","end":"a0020"}
|
|
25
|
-
])
|
|
25
|
+
]})
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
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`.
|
|
29
29
|
|
|
30
30
|
## Locate
|
|
31
31
|
|
|
32
|
-
`action="locate"` returns atoms from the frozen anchor. Supply either:
|
|
32
|
+
`request.action="locate"` returns atoms from the frozen anchor. Supply either:
|
|
33
33
|
|
|
34
34
|
- `ref`: one direct atom lookup; or
|
|
35
35
|
- at least one real filter: `pattern`, `tool_name`, or `source` other than `any`.
|
|
@@ -42,7 +42,7 @@ A `g...` inventory ref is not a locate ref; use the group's `a...` start/end lan
|
|
|
42
42
|
|
|
43
43
|
## Plan
|
|
44
44
|
|
|
45
|
-
`action="plan"` uses `op="show"` by default. Agent and user mutate the same DraftPlan.
|
|
45
|
+
`request.action="plan"` uses `op="show"` by default. Agent and user mutate the same DraftPlan.
|
|
46
46
|
|
|
47
47
|
| op | Required fields |
|
|
48
48
|
|----|-----------------|
|
|
@@ -78,7 +78,7 @@ Committed blocks appear as protected atoms in later transaction snapshots and ca
|
|
|
78
78
|
|
|
79
79
|
## Recall
|
|
80
80
|
|
|
81
|
-
`action="recall"` works independently of a planning transaction and reads committed blocks active on the current branch.
|
|
81
|
+
`request.action="recall"` works independently of a planning transaction and reads committed blocks active on the current branch.
|
|
82
82
|
|
|
83
83
|
- Without `ref`, `pattern` searches block topics and summaries; `limit` defaults to 8 and has a maximum of 20.
|
|
84
84
|
- With `ref="c0001"`, the tool renders that block's stored messages.
|
package/src/SPEC.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# SPEC — midcompact extension module (`src/`)
|
|
2
|
+
|
|
3
|
+
Maintenance contract for `src/`. The model-facing usage contract lives in
|
|
4
|
+
`skills/midcompact/` (SKILL.md + references/tool-interface.md) and is
|
|
5
|
+
authoritative for observable tool/command behavior; this spec records the
|
|
6
|
+
durable invariants a future maintainer must preserve, which are not reliably
|
|
7
|
+
inferable from code at a glance. Entry point: `src/index.ts` (factory in
|
|
8
|
+
package.json → `pi.extensions`).
|
|
9
|
+
|
|
10
|
+
## Persistence
|
|
11
|
+
|
|
12
|
+
- Exactly three custom entry types are ever persisted on the session branch:
|
|
13
|
+
`midcompact-transaction`, `midcompact-draft`, `midcompact-state`
|
|
14
|
+
(`state.ts`). All are `version: 1`, shape-checked before use.
|
|
15
|
+
- Restore is *latest entry wins* over the branch. A draft restores only when
|
|
16
|
+
a transaction entry precedes it with a matching `transactionId`; the two
|
|
17
|
+
form one unit. State entries are independent of transactions.
|
|
18
|
+
- Commit appends a new state entry — never mutates old ones. Abort writes
|
|
19
|
+
nothing.
|
|
20
|
+
- Compatibility is a standing constraint: old transactions without
|
|
21
|
+
`startMode` default to `"agent"`; old ranges lacking factual char/image
|
|
22
|
+
fields are coerced (`coerceDraftRange`). Legacy `approxTokens` fields are
|
|
23
|
+
deprecated: never authoritative for decisions or UI.
|
|
24
|
+
- The planning lock is deliberately not persisted: reload clears the owner.
|
|
25
|
+
|
|
26
|
+
## Transactions
|
|
27
|
+
|
|
28
|
+
- Start freezes the current leaf as anchor, appends TXN + DRAFT, and routes
|
|
29
|
+
Agent-direct or User-manual. All planning happens on the child branch; the
|
|
30
|
+
discussion never enters the working context.
|
|
31
|
+
- Commit and abort both navigate back to the anchor (`navigateTree`); commit
|
|
32
|
+
then appends state, abort appends nothing. Both refuse to run while the
|
|
33
|
+
Agent holds the planning lock.
|
|
34
|
+
- `anchorUsage` is informational only — Pi-reported awareness, never an
|
|
35
|
+
optimization target (same rule as `projectedTokens` / estimates).
|
|
36
|
+
- Atom refs are transaction-local: re-run inspect/locate in a later
|
|
37
|
+
transaction; group refs (`g...`) are never locate refs.
|
|
38
|
+
|
|
39
|
+
## Projection
|
|
40
|
+
|
|
41
|
+
- While blocks are active, the context hook replaces committed blocks with
|
|
42
|
+
their summary wrapper. The original history is restored only by recall;
|
|
43
|
+
recall never re-projects. Committed blocks appear as protected atoms in
|
|
44
|
+
later snapshots (no double compression).
|
|
45
|
+
|
|
46
|
+
## Atoms and ranges
|
|
47
|
+
|
|
48
|
+
- The atom is the smallest selectable unit; a tool call plus its matching
|
|
49
|
+
results is one indivisible `tool_exchange` atom.
|
|
50
|
+
- Protected (never compressible): incomplete/orphaned tool protocol,
|
|
51
|
+
existing compressed blocks, unsupported message kinds, entries lacking
|
|
52
|
+
the persistent anchor entry.
|
|
53
|
+
- Draft ranges never overlap and never contain protected atoms; boundaries
|
|
54
|
+
cannot be updated in place (remove + re-add). Empty `summary` = pending;
|
|
55
|
+
commit rejects pending, reversed, overlapping, or protected-crossing
|
|
56
|
+
ranges. `KEEP` is expressed by leaving atoms outside all ranges.
|
|
57
|
+
|
|
58
|
+
## Concurrency
|
|
59
|
+
|
|
60
|
+
- One runtime mutex over DraftPlan edits: owner ∈ {agent, ui}. Agent turns
|
|
61
|
+
hold it for their whole lifetime (`agent_start` → `agent_settled`); UIs
|
|
62
|
+
hold it per session via the `midcompactPlanningLock` API object (exposed
|
|
63
|
+
for UI and tests).
|
|
64
|
+
- Blocked operations notify and return — there is no queue.
|
|
65
|
+
|
|
66
|
+
## External contracts (reference, don't duplicate)
|
|
67
|
+
|
|
68
|
+
- Tool: one `midcompact` tool whose parameters are `{ request: <union> }` —
|
|
69
|
+
a root `type: "object"` wrapping a discriminated union on `action`
|
|
70
|
+
(inspect/locate/plan/recall); each branch is closed
|
|
71
|
+
(`additionalProperties: false`), so cross-action parameters are
|
|
72
|
+
schema-rejected. The `request` wrapper exists because some providers
|
|
73
|
+
(e.g. DeepSeek) reject a root-level `anyOf` before the model sees the
|
|
74
|
+
schema.
|
|
75
|
+
Details: `skills/midcompact/references/tool-interface.md`.
|
|
76
|
+
- Commands: `midcompact:start|abort|commit|review|review-webui|select|select-webui|status`;
|
|
77
|
+
no composite `/midcompact`; native naming convention `name:sub` (Pi's
|
|
78
|
+
`skill:<name>`).
|
|
79
|
+
- The tool never starts a transaction and never commits; both are command-
|
|
80
|
+
or user-gated. Recall is the only action valid without a transaction.
|
|
81
|
+
|
|
82
|
+
## Change rules
|
|
83
|
+
|
|
84
|
+
- Adding a tool action → new request branch + handler type + tool-interface
|
|
85
|
+
section + SKILL.md routing; adding parameters to an action → its branch
|
|
86
|
+
only (the union stays nested under `request`).
|
|
87
|
+
- Changing persistence shapes → keep `version: 1` readable (coerce) or add
|
|
88
|
+
a migration; restore predicates (`state.ts`) are the compatibility gate.
|
|
89
|
+
- Renaming commands → update SKILL.md/README/tests together; the stale-name
|
|
90
|
+
failure mode is a doc-arbitrated contract violation.
|
|
91
|
+
- Test seams: `setOpenReviewWebBrowser` and the mocks in `test/` must stay
|
|
92
|
+
behavior-faithful to the real extension API; suites drive commands via
|
|
93
|
+
`pi.commands.get(...)` and the tool via plain param objects.
|
package/src/index.ts
CHANGED
|
@@ -67,55 +67,79 @@ const TOOL_DESCRIPTION =
|
|
|
67
67
|
const STATUS_KEY = "midcompact";
|
|
68
68
|
const START_PROMPT_PREFIX = "A mid-compaction transaction is active on a frozen anchor snapshot.";
|
|
69
69
|
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
70
|
+
// Canonical request model: one branch per action, and each branch owns exactly
|
|
71
|
+
// its own fields (additionalProperties: false). The discriminant is a
|
|
72
|
+
// single-value StringEnum instead of Type.Literal so it serializes as
|
|
73
|
+
// string+enum, which restricted JSON-Schema subsets (e.g. DeepSeek) accept
|
|
74
|
+
// more readily than const.
|
|
75
|
+
//
|
|
76
|
+
// The union sits under a root `request` property instead of being the
|
|
77
|
+
// parameters root: some providers validate that a tool's parameters root is
|
|
78
|
+
// `type: "object"` and reject a root-level anyOf before the model ever sees
|
|
79
|
+
// the schema (observed on DeepSeek). Field descriptions stay in sync with
|
|
80
|
+
// skills/midcompact/references/tool-interface.md.
|
|
81
|
+
const InspectRequest = Type.Object(
|
|
82
|
+
{
|
|
83
|
+
action: StringEnum(["inspect"] as const, { description: "Inventory the frozen anchor, or measure explicit candidate spans." }),
|
|
84
|
+
spans: Type.Optional(Type.Array(Type.Object({ start: Type.String(), end: Type.String() }), { description: "Candidate spans to measure, as {start,end} atom refs." })),
|
|
85
|
+
page_size: Type.Optional(Type.Number({ description: "Inventory groups per page (default 20, max 50)." })),
|
|
86
|
+
cursor: Type.Optional(Type.String({ description: "Pagination cursor from the previous page." })),
|
|
87
|
+
},
|
|
88
|
+
{ additionalProperties: false },
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const LocateRequest = Type.Object(
|
|
92
|
+
{
|
|
93
|
+
action: StringEnum(["locate"] as const, { description: "Locate atoms in the frozen anchor by ref or filters." }),
|
|
94
|
+
ref: Type.Optional(Type.String({ description: "One direct atom ref; mutually exclusive with search filters." })),
|
|
95
|
+
pattern: Type.Optional(Type.String({ description: "Content filter over anchor atoms." })),
|
|
96
|
+
source: Type.Optional(StringEnum(["any", "user", "assistant", "tool_call", "tool_result"] as const, { description: "Filter by entry source." })),
|
|
97
|
+
tool_name: Type.Optional(Type.String({ description: "Filter by originating tool name." })),
|
|
98
|
+
direction: Type.Optional(StringEnum(["oldest", "newest"] as const, { description: "Match ordering, oldest (default) or newest." })),
|
|
99
|
+
limit: Type.Optional(Type.Number({ description: "1-3 results for filtered searches." })),
|
|
100
|
+
detail: Type.Optional(StringEnum(["brief", "full"] as const, { description: "brief (default) or full atom output." })),
|
|
101
|
+
},
|
|
102
|
+
{ additionalProperties: false },
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const PlanRequest = Type.Object(
|
|
106
|
+
{
|
|
107
|
+
action: StringEnum(["plan"] as const, { description: "Show or mutate the shared DraftPlan." }),
|
|
108
|
+
op: Type.Optional(StringEnum(["show", "add", "update", "remove"] as const, { description: "show (default) / add / update / remove." })),
|
|
109
|
+
start: Type.Optional(Type.String({ description: "add: range start atom ref." })),
|
|
110
|
+
end: Type.Optional(Type.String({ description: "add: range end atom ref." })),
|
|
111
|
+
draft_id: Type.Optional(Type.String({ description: "show/update/remove: target draft range id." })),
|
|
112
|
+
topic: Type.Optional(Type.String({ description: "add/update: range topic." })),
|
|
113
|
+
summary: Type.Optional(Type.String({ description: "add/update: range summary (omitted or empty = pending range)." })),
|
|
114
|
+
detail: Type.Optional(StringEnum(["brief", "full"] as const, { description: "show: brief (default) or full range output." })),
|
|
115
|
+
},
|
|
116
|
+
{ additionalProperties: false },
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const RecallRequest = Type.Object(
|
|
120
|
+
{
|
|
121
|
+
action: StringEnum(["recall"] as const, { description: "Read committed compression blocks; works without a transaction." }),
|
|
122
|
+
ref: Type.Optional(Type.String({ description: "One committed block id, e.g. c0001; renders its messages." })),
|
|
123
|
+
pattern: Type.Optional(Type.String({ description: "Filter block topics and summaries." })),
|
|
124
|
+
limit: Type.Optional(Type.Number({ description: "Blocks to list (default 8, max 20)." })),
|
|
125
|
+
detail: Type.Optional(StringEnum(["brief", "full"] as const, { description: "full raises the rendering cap on truncated blocks." })),
|
|
126
|
+
},
|
|
127
|
+
{ additionalProperties: false },
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const Params = Type.Object(
|
|
131
|
+
{ request: Type.Union([InspectRequest, LocateRequest, PlanRequest, RecallRequest]) },
|
|
132
|
+
{
|
|
133
|
+
additionalProperties: false,
|
|
134
|
+
description: "`request.action` selects exactly one request shape; fields of the other actions are not valid.",
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
type ToolParams = Static<typeof Params>;
|
|
139
|
+
type InspectRequestType = Static<typeof InspectRequest>;
|
|
140
|
+
type LocateRequestType = Static<typeof LocateRequest>;
|
|
141
|
+
type PlanRequestType = Static<typeof PlanRequest>;
|
|
142
|
+
type RecallRequestType = Static<typeof RecallRequest>;
|
|
119
143
|
|
|
120
144
|
type RuntimeSnapshot = { atoms: Atom[]; anchorState?: CompressionState };
|
|
121
145
|
|
|
@@ -171,7 +195,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
171
195
|
content: [
|
|
172
196
|
"An active midcompact transaction exists with a persisted DraftPlan.",
|
|
173
197
|
`Draft revision ${currentDraft.revision}; ${currentDraft.ranges.length} existing range(s), which may have been created by the user.`,
|
|
174
|
-
"If the current user request asks to continue midcompact, read the `midcompact` skill first, then call midcompact(action
|
|
198
|
+
"If the current user request asks to continue midcompact, read the `midcompact` skill first, then call midcompact(request={action:\"plan\", op:\"show\"}) before any other midcompact action. Treat the existing plan as the current shared draft. Infer from the user's request whether to preserve, refine, or extend it; ask only if materially ambiguous.",
|
|
175
199
|
].join("\n"),
|
|
176
200
|
display: false,
|
|
177
201
|
},
|
|
@@ -573,9 +597,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
573
597
|
label: "Midcompact",
|
|
574
598
|
description: TOOL_DESCRIPTION,
|
|
575
599
|
parameters: Params,
|
|
576
|
-
async execute(_id: string, params:
|
|
600
|
+
async execute(_id: string, params: ToolParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: ExtensionContext) {
|
|
577
601
|
try {
|
|
578
|
-
|
|
602
|
+
const request = params.request;
|
|
603
|
+
if (request.action === "recall") return toolResult(handleRecall(request, ctx));
|
|
579
604
|
const restored = restoreTransaction(ctx.sessionManager.getBranch() as SessionEntry[]);
|
|
580
605
|
const currentTx = withCompatDefaults(restored.transaction ?? transaction);
|
|
581
606
|
if (!currentTx) return toolResult("No active midcompact transaction. Ask the user to run `/midcompact:start` first.");
|
|
@@ -586,14 +611,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
586
611
|
}
|
|
587
612
|
const snapshot = buildAnchorSnapshot(ctx.sessionManager, currentTx);
|
|
588
613
|
|
|
589
|
-
if (
|
|
590
|
-
if (
|
|
591
|
-
if (
|
|
592
|
-
const result = handlePlan(
|
|
614
|
+
if (request.action === "inspect") return toolResult(handleInspect(request, snapshot.atoms, currentTx));
|
|
615
|
+
if (request.action === "locate") return toolResult(handleLocate(request, snapshot.atoms));
|
|
616
|
+
if (request.action === "plan") {
|
|
617
|
+
const result = handlePlan(request, draft!, snapshot.atoms);
|
|
593
618
|
if (result.op === "show") {
|
|
594
619
|
return toolResult(formatDraft(draft!, draftTelemetry(transaction, draft), {
|
|
595
|
-
detail:
|
|
596
|
-
draftId:
|
|
620
|
+
detail: request.detail,
|
|
621
|
+
draftId: request.draft_id,
|
|
597
622
|
atoms: snapshot.atoms,
|
|
598
623
|
}));
|
|
599
624
|
}
|
|
@@ -609,7 +634,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
609
634
|
},
|
|
610
635
|
});
|
|
611
636
|
|
|
612
|
-
function handleInspect(params:
|
|
637
|
+
function handleInspect(params: InspectRequestType, atoms: Atom[], tx: TransactionState): string {
|
|
613
638
|
if (params.spans) {
|
|
614
639
|
if (params.page_size !== undefined || params.cursor !== undefined) {
|
|
615
640
|
throw new Error("inspect spans cannot be combined with inventory pagination.");
|
|
@@ -620,7 +645,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
620
645
|
return formatInventory(page);
|
|
621
646
|
}
|
|
622
647
|
|
|
623
|
-
function handleRecall(params:
|
|
648
|
+
function handleRecall(params: RecallRequestType, ctx: ExtensionContext): string {
|
|
624
649
|
const sm = ctx.sessionManager;
|
|
625
650
|
const branchState = restoreCompressionState(sm.getBranch() as SessionEntry[]) ?? activeState;
|
|
626
651
|
if (!branchState?.blocks.length) return "No compressed blocks are active on this branch.";
|
|
@@ -656,7 +681,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
656
681
|
|
|
657
682
|
// ---- Pure handlers ----
|
|
658
683
|
|
|
659
|
-
function handleLocate(params:
|
|
684
|
+
function handleLocate(params: LocateRequestType, atoms: Atom[]): string {
|
|
660
685
|
const hasFilter = Boolean(params.pattern || params.tool_name || (params.source && params.source !== "any"));
|
|
661
686
|
if (params.ref && hasFilter) {
|
|
662
687
|
throw new Error("locate accepts either one direct ref or search filters, not both.");
|
|
@@ -688,7 +713,7 @@ type PlanHandleResult =
|
|
|
688
713
|
| { op: "show"; draft: DraftPlan }
|
|
689
714
|
| { op: "add" | "update" | "remove"; draft: DraftPlan; changedId: string };
|
|
690
715
|
|
|
691
|
-
function handlePlan(params:
|
|
716
|
+
function handlePlan(params: PlanRequestType, current: DraftPlan, atoms: Atom[]): PlanHandleResult {
|
|
692
717
|
const op = params.op ?? "show";
|
|
693
718
|
if (op === "show") return { op, draft: current };
|
|
694
719
|
if (op === "remove") {
|
package/src/projection.ts
CHANGED
|
@@ -17,7 +17,7 @@ export function summaryMessage(block: CompressionBlock, timestamp = Date.now()):
|
|
|
17
17
|
block.summary,
|
|
18
18
|
"",
|
|
19
19
|
`Original block: ${block.id}`,
|
|
20
|
-
`Use midcompact(action
|
|
20
|
+
`Use midcompact(request={action:\"recall\", ref:\"${block.id}\"}) if exact details are needed.`,
|
|
21
21
|
].join("\n"),
|
|
22
22
|
display: true,
|
|
23
23
|
details: {
|