pi-goosedump 0.12.18 → 0.12.20
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 +13 -11
- package/dist/index.ts +61 -69
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -93,17 +93,19 @@ Optional settings live under `goosedump` in:
|
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
`turnsBeforeCompact` is an integer from 0 to 255. `0` disables scheduled
|
|
96
|
-
compaction. Each
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
the
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
compaction. Each completed model response counts as one turn, including responses
|
|
97
|
+
that call tools. The countdown is restored when a session resumes and appears in
|
|
98
|
+
the footer with a status dot (green when >25% turns remain, yellow when ≤25%, red
|
|
99
|
+
when ≤12.5%) followed by a hexadecimal countdown. `● GD:0A` means ten turns
|
|
100
|
+
remain, `● GD:00` is ready to compact, and `● GD:--` is disabled. The footer
|
|
101
|
+
adds `queued` while compaction is waiting to start and `compacting` while the
|
|
102
|
+
summary is being built. When the counter hits zero mid tool-loop, compaction
|
|
103
|
+
starts immediately after the current tool batch is persisted and the task is
|
|
104
|
+
resumed automatically. When the counter hits zero on a terminal turn, compaction
|
|
105
|
+
starts after the agent has settled. Pi remains the sole scheduler for its normal
|
|
106
|
+
context threshold. Whichever limit is reached first compacts once, and every
|
|
107
|
+
successful compaction resets the countdown. A failed or cancelled compaction
|
|
108
|
+
leaves the counter armed at zero and retries on the next tool turn or settlement.
|
|
107
109
|
|
|
108
110
|
`summaryMaxTokens` is an integer from 512 to 65536 (default 4096). The effective
|
|
109
111
|
summary budget is the lower of this value and 80% of Pi's `compaction.reserveTokens`,
|
package/dist/index.ts
CHANGED
|
@@ -590,26 +590,11 @@ function currentSessionTarget(ctx) {
|
|
|
590
590
|
function isCompactionEntry(entry) {
|
|
591
591
|
return entry.type === "compaction";
|
|
592
592
|
}
|
|
593
|
-
var COMPLETED_AGENT_RUN_ENTRY = "goosedump-completed-agent-run";
|
|
594
593
|
function isMessageEntry(entry) {
|
|
595
594
|
return entry.type === "message";
|
|
596
595
|
}
|
|
597
|
-
function
|
|
598
|
-
|
|
599
|
-
return false;
|
|
600
|
-
if (message.stopReason !== "stop" && message.stopReason !== "length")
|
|
601
|
-
return false;
|
|
602
|
-
return !(Array.isArray(message.content) && message.content.some((part) => isRecord(part) && part.type === "toolCall"));
|
|
603
|
-
}
|
|
604
|
-
function completedAgentRunData(entry) {
|
|
605
|
-
if (entry.type !== "custom" || entry.customType !== COMPLETED_AGENT_RUN_ENTRY)
|
|
606
|
-
return;
|
|
607
|
-
if (!isRecord(entry.data) || entry.data.version !== 1)
|
|
608
|
-
return;
|
|
609
|
-
const startedAfterEntryId = entry.data.startedAfterEntryId;
|
|
610
|
-
if (startedAfterEntryId !== null && typeof startedAfterEntryId !== "string")
|
|
611
|
-
return;
|
|
612
|
-
return { version: 1, startedAfterEntryId };
|
|
596
|
+
function isCompletedAssistant(message) {
|
|
597
|
+
return message.role === "assistant" && message.stopReason !== "error" && message.stopReason !== "aborted";
|
|
613
598
|
}
|
|
614
599
|
var SUMMARIZABLE_ENTRY_TYPES = new Set(["message", "custom_message", "branch_summary"]);
|
|
615
600
|
function piKeepRecentTokens(cwd, includeProject) {
|
|
@@ -681,7 +666,7 @@ function activeHistoryStartIndex(branchEntries) {
|
|
|
681
666
|
const compaction = latestCompaction(branchEntries);
|
|
682
667
|
return compaction ? compactionBoundaryStartIndex(branchEntries, compaction) : 0;
|
|
683
668
|
}
|
|
684
|
-
function
|
|
669
|
+
function completedTurnsSinceCompaction(branchEntries) {
|
|
685
670
|
let start = 0;
|
|
686
671
|
for (let i = branchEntries.length - 1;i >= 0; i -= 1) {
|
|
687
672
|
if (isCompactionEntry(branchEntries[i])) {
|
|
@@ -689,27 +674,10 @@ function completedRunsSinceCompaction(branchEntries) {
|
|
|
689
674
|
break;
|
|
690
675
|
}
|
|
691
676
|
}
|
|
692
|
-
const firstMarkerIndex = branchEntries.findIndex((entry, index) => index >= start && completedAgentRunData(entry) !== undefined);
|
|
693
|
-
if (firstMarkerIndex < 0) {
|
|
694
|
-
let completed2 = 0;
|
|
695
|
-
for (let i = start;i < branchEntries.length; i += 1) {
|
|
696
|
-
const entry = branchEntries[i];
|
|
697
|
-
if (isMessageEntry(entry) && isTerminalAssistant(entry.message))
|
|
698
|
-
completed2 += 1;
|
|
699
|
-
}
|
|
700
|
-
return completed2;
|
|
701
|
-
}
|
|
702
|
-
const firstMarker = completedAgentRunData(branchEntries[firstMarkerIndex]);
|
|
703
|
-
const boundaryIndex = firstMarker?.startedAfterEntryId ? branchEntries.findIndex((entry) => entry.id === firstMarker.startedAfterEntryId) : start - 1;
|
|
704
|
-
const legacyEnd = boundaryIndex >= start ? boundaryIndex + 1 : start;
|
|
705
677
|
let completed = 0;
|
|
706
|
-
for (let i = start;i <
|
|
678
|
+
for (let i = start;i < branchEntries.length; i += 1) {
|
|
707
679
|
const entry = branchEntries[i];
|
|
708
|
-
if (isMessageEntry(entry) &&
|
|
709
|
-
completed += 1;
|
|
710
|
-
}
|
|
711
|
-
for (let i = firstMarkerIndex;i < branchEntries.length; i += 1) {
|
|
712
|
-
if (completedAgentRunData(branchEntries[i]) !== undefined)
|
|
680
|
+
if (isMessageEntry(entry) && isCompletedAssistant(entry.message))
|
|
713
681
|
completed += 1;
|
|
714
682
|
}
|
|
715
683
|
return completed;
|
|
@@ -877,7 +845,7 @@ async function runSettingsEditor(ctx) {
|
|
|
877
845
|
const specs = [
|
|
878
846
|
{
|
|
879
847
|
key: "turnsBeforeCompact",
|
|
880
|
-
description: "
|
|
848
|
+
description: "LLM turns before compaction; 0 disables.",
|
|
881
849
|
min: TURNS_BEFORE_COMPACT_MIN,
|
|
882
850
|
max: TURNS_BEFORE_COMPACT_MAX
|
|
883
851
|
},
|
|
@@ -1443,8 +1411,7 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1443
1411
|
let operationGeneration = 0;
|
|
1444
1412
|
let activeSessionFile = null;
|
|
1445
1413
|
let sessionActive = false;
|
|
1446
|
-
let
|
|
1447
|
-
let agentRunStartEntryId = null;
|
|
1414
|
+
let activePi = null;
|
|
1448
1415
|
const rememberQueue = [];
|
|
1449
1416
|
let rememberRunning = null;
|
|
1450
1417
|
function enqueueRemember(target, reportError) {
|
|
@@ -1482,9 +1449,9 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1482
1449
|
}
|
|
1483
1450
|
function syncCompactCounters(ctx) {
|
|
1484
1451
|
compactSettings = resolveGoosedumpSettings(ctx.cwd, projectSettingsTrusted(ctx));
|
|
1485
|
-
const
|
|
1486
|
-
turnsRemaining = Math.max(0, compactSettings.turnsBeforeCompact -
|
|
1487
|
-
counterTriggered = compactSettings.turnsBeforeCompact > 0 &&
|
|
1452
|
+
const completedTurns = completedTurnsSinceCompaction(ctx.sessionManager.getBranch());
|
|
1453
|
+
turnsRemaining = Math.max(0, compactSettings.turnsBeforeCompact - completedTurns);
|
|
1454
|
+
counterTriggered = compactSettings.turnsBeforeCompact > 0 && completedTurns >= compactSettings.turnsBeforeCompact;
|
|
1488
1455
|
}
|
|
1489
1456
|
function updateStatus(ctx) {
|
|
1490
1457
|
if (!goosedumpAvailable)
|
|
@@ -1509,13 +1476,44 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1509
1476
|
compactionState = state;
|
|
1510
1477
|
updateStatus(ctx);
|
|
1511
1478
|
}
|
|
1512
|
-
function
|
|
1479
|
+
function noteCompletedTurn(ctx) {
|
|
1513
1480
|
if (!goosedumpAvailable || !shouldOverrideDefaultCompaction)
|
|
1514
1481
|
return;
|
|
1515
1482
|
syncCompactCounters(ctx);
|
|
1516
1483
|
updateStatus(ctx);
|
|
1517
1484
|
queueTurnCompaction(ctx, settlementGeneration + 1);
|
|
1518
1485
|
}
|
|
1486
|
+
function resumeAfterSuccessfulCompaction(ctx, resume) {
|
|
1487
|
+
if (!resume || !activePi || ctx.hasPendingMessages())
|
|
1488
|
+
return;
|
|
1489
|
+
activePi.sendMessage({
|
|
1490
|
+
customType: "goosedump-autocompact-resume",
|
|
1491
|
+
content: "The session was compacted to reduce context size. Continue the task you were working on from where you left off.",
|
|
1492
|
+
display: false
|
|
1493
|
+
}, { triggerTurn: true });
|
|
1494
|
+
}
|
|
1495
|
+
function tryMidRunCounterCompaction(ctx) {
|
|
1496
|
+
if (!goosedumpAvailable || !shouldOverrideDefaultCompaction)
|
|
1497
|
+
return false;
|
|
1498
|
+
if (!counterTriggered)
|
|
1499
|
+
return false;
|
|
1500
|
+
if (ctx.hasPendingMessages())
|
|
1501
|
+
return false;
|
|
1502
|
+
if (!hasCompactableHistory(ctx))
|
|
1503
|
+
return false;
|
|
1504
|
+
if (compactionState.phase === "idle") {
|
|
1505
|
+
queueTurnCompaction(ctx, settlementGeneration);
|
|
1506
|
+
} else if (compactionState.phase === "queued") {
|
|
1507
|
+
setCompactionState(ctx, {
|
|
1508
|
+
phase: "queued",
|
|
1509
|
+
request: {
|
|
1510
|
+
...compactionState.request,
|
|
1511
|
+
eligibleSettlement: Math.min(compactionState.request.eligibleSettlement, settlementGeneration)
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
return startQueuedCompaction(ctx, { resumeAfter: true, allowActiveRun: true });
|
|
1516
|
+
}
|
|
1519
1517
|
function checkGoosedump(ctx) {
|
|
1520
1518
|
try {
|
|
1521
1519
|
execFileSync(process.execPath, [resolveGoosedumpBinary(), "--version"], { stdio: "ignore" });
|
|
@@ -1567,7 +1565,7 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1567
1565
|
}
|
|
1568
1566
|
});
|
|
1569
1567
|
}
|
|
1570
|
-
function startQueuedCompaction(ctx) {
|
|
1568
|
+
function startQueuedCompaction(ctx, options2 = {}) {
|
|
1571
1569
|
if (compactionState.phase !== "queued")
|
|
1572
1570
|
return false;
|
|
1573
1571
|
const request = compactionState.request;
|
|
@@ -1575,7 +1573,9 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1575
1573
|
setCompactionState(ctx, { phase: "idle" });
|
|
1576
1574
|
return false;
|
|
1577
1575
|
}
|
|
1578
|
-
if (
|
|
1576
|
+
if (ctx.hasPendingMessages())
|
|
1577
|
+
return false;
|
|
1578
|
+
if (!options2.allowActiveRun && !ctx.isIdle())
|
|
1579
1579
|
return false;
|
|
1580
1580
|
if (!requestHasCompactableHistory(ctx, request.keep)) {
|
|
1581
1581
|
setCompactionState(ctx, { phase: "idle" });
|
|
@@ -1584,6 +1584,7 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1584
1584
|
const operation = ++operationGeneration;
|
|
1585
1585
|
const generation = request.sessionGeneration;
|
|
1586
1586
|
const sessionFile = request.sessionFile;
|
|
1587
|
+
const resumeAfter = options2.resumeAfter === true;
|
|
1587
1588
|
setCompactionState(ctx, {
|
|
1588
1589
|
phase: "running",
|
|
1589
1590
|
request,
|
|
@@ -1598,11 +1599,16 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1598
1599
|
if (!runningOperationIsCurrent(operation, generation, sessionFile))
|
|
1599
1600
|
return;
|
|
1600
1601
|
setCompactionState(ctx, { phase: "idle" });
|
|
1602
|
+
syncCompactCounters(ctx);
|
|
1603
|
+
updateStatus(ctx);
|
|
1604
|
+
resumeAfterSuccessfulCompaction(ctx, resumeAfter);
|
|
1601
1605
|
},
|
|
1602
1606
|
onError: (error) => {
|
|
1603
1607
|
if (!runningOperationIsCurrent(operation, generation, sessionFile))
|
|
1604
1608
|
return;
|
|
1605
1609
|
setCompactionState(ctx, { phase: "idle" });
|
|
1610
|
+
syncCompactCounters(ctx);
|
|
1611
|
+
updateStatus(ctx);
|
|
1606
1612
|
const message = error instanceof Error ? error.message : String(error);
|
|
1607
1613
|
if (ctx.hasUI)
|
|
1608
1614
|
ctx.ui.notify(`goosedump compaction failed: ${message}`, "error");
|
|
@@ -1638,6 +1644,7 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1638
1644
|
return startQueuedCompaction(ctx) ? "started" : "queued";
|
|
1639
1645
|
}
|
|
1640
1646
|
function register(pi) {
|
|
1647
|
+
activePi = pi;
|
|
1641
1648
|
if (shouldRegisterTool) {
|
|
1642
1649
|
pi.registerTool(defineTool({
|
|
1643
1650
|
name: "goose_remember",
|
|
@@ -1989,6 +1996,8 @@ ${lines.join(`
|
|
|
1989
1996
|
if (!goosedumpAvailable || !shouldOverrideDefaultCompaction)
|
|
1990
1997
|
return;
|
|
1991
1998
|
settlementGeneration += 1;
|
|
1999
|
+
syncCompactCounters(ctx);
|
|
2000
|
+
updateStatus(ctx);
|
|
1992
2001
|
if (compactionState.phase === "running" && compactionState.owner === "pi") {
|
|
1993
2002
|
const request = compactionState.request;
|
|
1994
2003
|
setCompactionState(ctx, request && requestIsCurrent(request) ? {
|
|
@@ -2012,8 +2021,6 @@ ${lines.join(`
|
|
|
2012
2021
|
settlementGeneration = 0;
|
|
2013
2022
|
activeSessionFile = ctx.sessionManager.getSessionFile?.() ?? null;
|
|
2014
2023
|
sessionActive = true;
|
|
2015
|
-
agentRunObserved = false;
|
|
2016
|
-
agentRunStartEntryId = null;
|
|
2017
2024
|
compactionState = { phase: "idle" };
|
|
2018
2025
|
goosedumpAvailable = checkGoosedump(ctx);
|
|
2019
2026
|
notifyInvalidSettings(ctx);
|
|
@@ -2027,8 +2034,6 @@ ${lines.join(`
|
|
|
2027
2034
|
settlementGeneration = 0;
|
|
2028
2035
|
activeSessionFile = null;
|
|
2029
2036
|
sessionActive = false;
|
|
2030
|
-
agentRunObserved = false;
|
|
2031
|
-
agentRunStartEntryId = null;
|
|
2032
2037
|
compactionState = { phase: "idle" };
|
|
2033
2038
|
});
|
|
2034
2039
|
pi.on("session_tree", async (_event, ctx) => {
|
|
@@ -2036,8 +2041,6 @@ ${lines.join(`
|
|
|
2036
2041
|
settlementGeneration = 0;
|
|
2037
2042
|
activeSessionFile = ctx.sessionManager.getSessionFile?.() ?? null;
|
|
2038
2043
|
sessionActive = true;
|
|
2039
|
-
agentRunObserved = false;
|
|
2040
|
-
agentRunStartEntryId = null;
|
|
2041
2044
|
compactionState = { phase: "idle" };
|
|
2042
2045
|
syncCompactCounters(ctx);
|
|
2043
2046
|
updateStatus(ctx);
|
|
@@ -2045,10 +2048,6 @@ ${lines.join(`
|
|
|
2045
2048
|
deferQueuedCompaction(ctx);
|
|
2046
2049
|
});
|
|
2047
2050
|
pi.on("agent_start", async (_event, ctx) => {
|
|
2048
|
-
if (!agentRunObserved) {
|
|
2049
|
-
agentRunObserved = true;
|
|
2050
|
-
agentRunStartEntryId = ctx.sessionManager.getBranch().at(-1)?.id ?? null;
|
|
2051
|
-
}
|
|
2052
2051
|
if (compactionState.phase !== "running" || compactionState.owner !== "pi")
|
|
2053
2052
|
return;
|
|
2054
2053
|
const request = compactionState.request;
|
|
@@ -2057,20 +2056,13 @@ ${lines.join(`
|
|
|
2057
2056
|
request: { ...request, eligibleSettlement: settlementGeneration + 1 }
|
|
2058
2057
|
} : { phase: "idle" });
|
|
2059
2058
|
});
|
|
2059
|
+
pi.on("turn_end", async (event, ctx) => {
|
|
2060
|
+
if (isCompletedAssistant(event.message))
|
|
2061
|
+
noteCompletedTurn(ctx);
|
|
2062
|
+
if (event.toolResults.length > 0)
|
|
2063
|
+
tryMidRunCounterCompaction(ctx);
|
|
2064
|
+
});
|
|
2060
2065
|
pi.on("agent_settled", async (_event, ctx) => {
|
|
2061
|
-
if (agentRunObserved) {
|
|
2062
|
-
const countRun = shouldOverrideDefaultCompaction && compactSettings.turnsBeforeCompact > 0;
|
|
2063
|
-
if (countRun) {
|
|
2064
|
-
pi.appendEntry(COMPLETED_AGENT_RUN_ENTRY, {
|
|
2065
|
-
version: 1,
|
|
2066
|
-
startedAfterEntryId: agentRunStartEntryId
|
|
2067
|
-
});
|
|
2068
|
-
}
|
|
2069
|
-
agentRunObserved = false;
|
|
2070
|
-
agentRunStartEntryId = null;
|
|
2071
|
-
if (countRun)
|
|
2072
|
-
noteCompletedRun(ctx);
|
|
2073
|
-
}
|
|
2074
2066
|
handleAgentSettled(ctx);
|
|
2075
2067
|
});
|
|
2076
2068
|
pi.on("session_before_compact", async (event, ctx) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goosedump",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.20",
|
|
4
4
|
"description": "Pi extension for goosedump session search, durable memory, and compaction",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compaction",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@earendil-works/pi-tui": "^0.82.1",
|
|
41
|
-
"@jarkkojs/goosedump": "^0.12.
|
|
41
|
+
"@jarkkojs/goosedump": "^0.12.20",
|
|
42
42
|
"@sinclair/typebox": "^0.34.49"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|