@monotykamary/pi-vcc 0.5.0 → 0.6.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 +6 -0
- package/docs/landmarks.md +337 -0
- package/npm-shrinkwrap.json +3270 -1806
- package/package.json +4 -4
- package/src/core/invisible-continue.ts +4 -0
- package/src/tools/recall.ts +50 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monotykamary/pi-vcc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Algorithmic conversation compactor for pi - transcript-preserving structured summaries, no LLM calls",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"keywords": [
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"url": "git+https://github.com/monotykamary/pi-vcc.git"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"@earendil-works/pi-ai": "
|
|
19
|
-
"@earendil-works/pi-agent-core": "
|
|
20
|
-
"@earendil-works/pi-coding-agent": "
|
|
18
|
+
"@earendil-works/pi-ai": "^0.79.8",
|
|
19
|
+
"@earendil-works/pi-agent-core": "^0.79.8",
|
|
20
|
+
"@earendil-works/pi-coding-agent": "^0.79.8",
|
|
21
21
|
"typebox": ">=1.1.24 <2.0.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
@@ -40,6 +40,10 @@ Agent.prototype.subscribe = function (this: Agent, ...args: any[]) {
|
|
|
40
40
|
// "Cannot continue from message role: assistant" and the agent loop
|
|
41
41
|
// exits — leaving mid-task work unfinished.
|
|
42
42
|
//
|
|
43
|
+
// Note (pi 0.79+): Agent.continue() now drains queued steering/follow-up
|
|
44
|
+
// messages before throwing, so this throw path only fires when there are
|
|
45
|
+
// genuinely no queued messages — the prompt([]) fallback is still correct.
|
|
46
|
+
//
|
|
43
47
|
// The fix: when continue() would throw because the last message is an
|
|
44
48
|
// assistant, fall back to prompt([]) instead. prompt([]) doesn't check
|
|
45
49
|
// the last message role — it starts a fresh agent loop with the current
|
package/src/tools/recall.ts
CHANGED
|
@@ -2,10 +2,12 @@ import { Type } from "typebox";
|
|
|
2
2
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { readFileSync } from "fs";
|
|
4
4
|
import { loadAllMessages } from "../core/load-messages";
|
|
5
|
-
import { searchEntries } from "../core/search-entries";
|
|
5
|
+
import { searchEntries, type SearchHit } from "../core/search-entries";
|
|
6
6
|
import { formatRecallOutput } from "../core/format-recall";
|
|
7
7
|
import { getActiveLineageEntryIds } from "../core/lineage";
|
|
8
8
|
import { normalizeRecallScope } from "../core/recall-scope";
|
|
9
|
+
import { renderMessage } from "../core/render-entries";
|
|
10
|
+
import type { Message } from "@earendil-works/pi-ai";
|
|
9
11
|
import type { PiVccCompactionDetails } from "../details";
|
|
10
12
|
|
|
11
13
|
const DEFAULT_RECENT = 25;
|
|
@@ -87,13 +89,14 @@ export const registerRecallTool = (pi: ExtensionAPI) => {
|
|
|
87
89
|
promptSnippet:
|
|
88
90
|
"vcc_recall: Search history; default scope is active lineage. " +
|
|
89
91
|
"Use scope:'all' for off-lineage branches. " +
|
|
90
|
-
"Use scope:'compaction:N' or scope:'compaction:latest' for targeted search within a compaction segment."
|
|
92
|
+
"Use scope:'compaction:N' or scope:'compaction:latest' for targeted search within a compaction segment. " +
|
|
93
|
+
"expand:[indices] returns full content for those entries, composable with query to expand matched results.",
|
|
91
94
|
parameters: Type.Object({
|
|
92
95
|
query: Type.Optional(
|
|
93
96
|
Type.String({ description: "Search terms or regex pattern (e.g. 'hook|inject', 'fail.*build'). Multi-word = OR ranked by relevance." }),
|
|
94
97
|
),
|
|
95
98
|
expand: Type.Optional(
|
|
96
|
-
Type.Array(Type.Number(), { description: "Entry indices to return full untruncated content for" }),
|
|
99
|
+
Type.Array(Type.Number(), { description: "Entry indices to return full untruncated content for. Works alone (any index in scope) or alongside query (expands matching entries on the current page)." }),
|
|
97
100
|
),
|
|
98
101
|
page: Type.Optional(
|
|
99
102
|
Type.Number({ description: "Page number (1-based) for paginated search results. Default: 1." }),
|
|
@@ -177,15 +180,53 @@ export const registerRecallTool = (pi: ExtensionAPI) => {
|
|
|
177
180
|
};
|
|
178
181
|
}
|
|
179
182
|
const start = (page - 1) * PAGE_SIZE;
|
|
180
|
-
const pageResults = allResults.slice(start, start + PAGE_SIZE);
|
|
183
|
+
const pageResults = allResults.slice(start, start + PAGE_SIZE) as SearchHit[];
|
|
181
184
|
const header = totalPages > 1
|
|
182
185
|
? `Page ${page}/${totalPages} (${allResults.length} total matches${searchScopeLabel})`
|
|
183
186
|
: `${allResults.length} matches${searchScopeLabel}`;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
|
|
188
|
+
// Compose: when expand indices accompany a query, swap the truncated
|
|
189
|
+
// snippet for full untruncated content on any paged result whose
|
|
190
|
+
// index is in expandSet. rawMessages (loaded above with full=false)
|
|
191
|
+
// are parallel to `msgs`, so we re-render only the requested
|
|
192
|
+
// indices at full=true instead of re-reading the session file.
|
|
193
|
+
const expanded: number[] = [];
|
|
194
|
+
if (hasExpand) {
|
|
195
|
+
const msgByIndex = new Map<number, Message>();
|
|
196
|
+
for (let i = 0; i < msgs.length; i++) {
|
|
197
|
+
msgByIndex.set(msgs[i].index, rawMessages[i]);
|
|
198
|
+
}
|
|
199
|
+
for (const r of pageResults) {
|
|
200
|
+
if (!expandSet.has(r.index)) continue;
|
|
201
|
+
const raw = msgByIndex.get(r.index);
|
|
202
|
+
if (!raw) continue;
|
|
203
|
+
const full = renderMessage(raw, r.index, true);
|
|
204
|
+
// formatEntry prefers `snippet` for matched entries; set both
|
|
205
|
+
// so the full content renders regardless of match state.
|
|
206
|
+
r.snippet = full.summary;
|
|
207
|
+
r.summary = full.summary;
|
|
208
|
+
expanded.push(r.index);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const footerParts: string[] = [];
|
|
213
|
+
if (page < totalPages && page < MAX_PAGES) {
|
|
214
|
+
footerParts.push(`--- Use page:${page + 1} for more results ---`);
|
|
215
|
+
} else if (totalPages > MAX_PAGES) {
|
|
216
|
+
footerParts.push(`--- Results truncated at ${MAX_PAGES} pages. Use a more specific query or scope to narrow results. ---`);
|
|
217
|
+
}
|
|
218
|
+
if (hasExpand) {
|
|
219
|
+
const notExpanded = [...expandSet].filter((i) => !expanded.includes(i));
|
|
220
|
+
const noun = expanded.length === 1 ? "entry" : "entries";
|
|
221
|
+
if (expanded.length > 0 && notExpanded.length === 0) {
|
|
222
|
+
footerParts.push(`--- expanded ${expanded.length} ${noun} to full content ---`);
|
|
223
|
+
} else if (expanded.length > 0) {
|
|
224
|
+
footerParts.push(`--- expanded ${expanded.length} ${noun} to full content; not on this page: ${notExpanded.join(", ")} ---`);
|
|
225
|
+
} else if (notExpanded.length > 0) {
|
|
226
|
+
footerParts.push(`--- no expand indices on this page: ${notExpanded.join(", ")} ---`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const footer = footerParts.length ? "\n" + footerParts.join("\n") : "";
|
|
189
230
|
const output = formatRecallOutput(pageResults, params.query, header) + footer;
|
|
190
231
|
return {
|
|
191
232
|
content: [{ type: "text", text: output }],
|