pi-goosedump 0.9.2 → 0.9.4
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/index.ts +206 -77
- package/package.json +2 -2
package/index.ts
CHANGED
|
@@ -10,7 +10,12 @@ import type {
|
|
|
10
10
|
SessionEntry,
|
|
11
11
|
} from '@earendil-works/pi-coding-agent';
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_COMPACTION_SETTINGS,
|
|
15
|
+
defineTool,
|
|
16
|
+
findCutPoint,
|
|
17
|
+
getSettingsListTheme,
|
|
18
|
+
} from '@earendil-works/pi-coding-agent';
|
|
14
19
|
|
|
15
20
|
import { execFile, execFileSync } from 'node:child_process';
|
|
16
21
|
import {
|
|
@@ -80,6 +85,9 @@ const DEFAULT_GOOSEDUMP_SETTINGS: GoosedumpSettings = {
|
|
|
80
85
|
turnsBeforeCompact: 0,
|
|
81
86
|
};
|
|
82
87
|
|
|
88
|
+
const TURNS_BEFORE_COMPACT_MIN = 0;
|
|
89
|
+
const TURNS_BEFORE_COMPACT_MAX = 255;
|
|
90
|
+
|
|
83
91
|
function piAgentDir(): string {
|
|
84
92
|
return process.env.PI_CODING_AGENT_DIR ?? join(homedir(), '.pi', 'agent');
|
|
85
93
|
}
|
|
@@ -113,7 +121,12 @@ function readGoosedumpSettingsFrom(path: string): Partial<GoosedumpSettings> {
|
|
|
113
121
|
|
|
114
122
|
const settings: Partial<GoosedumpSettings> = {};
|
|
115
123
|
const value = raw.turnsBeforeCompact;
|
|
116
|
-
if (
|
|
124
|
+
if (
|
|
125
|
+
typeof value === 'number' &&
|
|
126
|
+
Number.isSafeInteger(value) &&
|
|
127
|
+
value >= TURNS_BEFORE_COMPACT_MIN &&
|
|
128
|
+
value <= TURNS_BEFORE_COMPACT_MAX
|
|
129
|
+
) {
|
|
117
130
|
settings.turnsBeforeCompact = value;
|
|
118
131
|
}
|
|
119
132
|
return settings;
|
|
@@ -248,6 +261,13 @@ function missingSessionId(tool: string, provider = 'pi'): AgentToolResult<void>
|
|
|
248
261
|
return textResult(`session is required for ${tool} when no current session is available`);
|
|
249
262
|
}
|
|
250
263
|
|
|
264
|
+
const GOOSEDUMP_UNAVAILABLE_MESSAGE =
|
|
265
|
+
'goosedump is not available. Install with: npm install @jarkkojs/goosedump';
|
|
266
|
+
|
|
267
|
+
function goosedumpUnavailableResult(): AgentToolResult<void> {
|
|
268
|
+
return textResult(GOOSEDUMP_UNAVAILABLE_MESSAGE);
|
|
269
|
+
}
|
|
270
|
+
|
|
251
271
|
function runGoosedump(args: string[]): string {
|
|
252
272
|
try {
|
|
253
273
|
return execFileSync('node', [resolveGoosedumpBinary(), ...args], {
|
|
@@ -434,6 +454,56 @@ function isMessageEntry(entry: SessionEntry): entry is SessionEntry & {
|
|
|
434
454
|
return entry.type === 'message';
|
|
435
455
|
}
|
|
436
456
|
|
|
457
|
+
// Entry types that sessionEntryToContextMessages turns into context messages;
|
|
458
|
+
// only these count toward "is there anything to summarize".
|
|
459
|
+
const SUMMARIZABLE_ENTRY_TYPES = new Set(['message', 'custom_message', 'branch_summary']);
|
|
460
|
+
|
|
461
|
+
// Pi's compaction keepRecentTokens setting, project scope over global, matching
|
|
462
|
+
// SettingsManager's merge order.
|
|
463
|
+
function piKeepRecentTokens(cwd: string): number {
|
|
464
|
+
for (const path of [projectSettingsPath(cwd), globalSettingsPath()]) {
|
|
465
|
+
const compaction = readJsonObject(path).compaction;
|
|
466
|
+
if (isRecord(compaction) && typeof compaction.keepRecentTokens === 'number') {
|
|
467
|
+
return compaction.keepRecentTokens;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return DEFAULT_COMPACTION_SETTINGS.keepRecentTokens;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Predict whether ctx.compact() would throw "Nothing to compact" or "Already
|
|
474
|
+
// compacted": mirrors prepareCompaction's guards over the active lineage so
|
|
475
|
+
// plugin-triggered compaction can skip the call instead of surfacing pi's
|
|
476
|
+
// error toast (and needlessly aborting the agent).
|
|
477
|
+
function hasCompactableHistory(ctx: ExtensionContext): boolean {
|
|
478
|
+
const branch = ctx.sessionManager.getBranch();
|
|
479
|
+
if (branch.length === 0) return false;
|
|
480
|
+
if (branch[branch.length - 1].type === 'compaction') return false;
|
|
481
|
+
|
|
482
|
+
let boundaryStart = 0;
|
|
483
|
+
for (let i = branch.length - 1; i >= 0; i -= 1) {
|
|
484
|
+
const entry = branch[i];
|
|
485
|
+
if (isCompactionEntry(entry)) {
|
|
486
|
+
const keptIndex = branch.findIndex((e) => e.id === entry.firstKeptEntryId);
|
|
487
|
+
boundaryStart = keptIndex >= 0 ? keptIndex : i + 1;
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const cut = findCutPoint(branch, boundaryStart, branch.length, piKeepRecentTokens(ctx.cwd));
|
|
493
|
+
if (!branch[cut.firstKeptEntryIndex]?.id) return false;
|
|
494
|
+
|
|
495
|
+
const historyEnd = cut.isSplitTurn ? cut.turnStartIndex : cut.firstKeptEntryIndex;
|
|
496
|
+
for (let i = boundaryStart; i < historyEnd; i += 1) {
|
|
497
|
+
if (SUMMARIZABLE_ENTRY_TYPES.has(branch[i].type)) return true;
|
|
498
|
+
}
|
|
499
|
+
if (cut.isSplitTurn) {
|
|
500
|
+
for (let i = cut.turnStartIndex; i < cut.firstKeptEntryIndex; i += 1) {
|
|
501
|
+
if (SUMMARIZABLE_ENTRY_TYPES.has(branch[i].type)) return true;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
|
|
437
507
|
// Resolution of the `keep:N` boundary: scan the active lineage from the newest
|
|
438
508
|
// entry backward, count user turns (those whose message role is `user`), and
|
|
439
509
|
// return the id of the Nth-from-last user entry — the earliest entry to keep.
|
|
@@ -552,29 +622,49 @@ function isControlChar(char: string): boolean {
|
|
|
552
622
|
class NumberInputSubmenu implements Component {
|
|
553
623
|
private readonly input = new Input();
|
|
554
624
|
private readonly hint: (text: string) => string;
|
|
555
|
-
private readonly onSubmitValue: (value:
|
|
625
|
+
private readonly onSubmitValue: (value: number) => void;
|
|
556
626
|
private readonly onCancelEdit: () => void;
|
|
627
|
+
private readonly onInvalidValue: (message: string) => void;
|
|
557
628
|
private readonly title: string;
|
|
629
|
+
private readonly currentValue: number;
|
|
630
|
+
private readonly min: number;
|
|
631
|
+
private readonly max: number;
|
|
558
632
|
|
|
559
633
|
constructor(
|
|
560
634
|
title: string,
|
|
561
|
-
|
|
562
|
-
onSubmit: (value:
|
|
635
|
+
currentValue: number,
|
|
636
|
+
onSubmit: (value: number) => void,
|
|
563
637
|
onCancel: () => void,
|
|
638
|
+
options?: { min?: number; max?: number; onInvalid?: (message: string) => void },
|
|
564
639
|
) {
|
|
565
640
|
this.title = title;
|
|
641
|
+
this.currentValue = currentValue;
|
|
566
642
|
this.onSubmitValue = onSubmit;
|
|
567
643
|
this.onCancelEdit = onCancel;
|
|
568
|
-
this.
|
|
644
|
+
this.onInvalidValue = options?.onInvalid ?? (() => {});
|
|
645
|
+
this.min = options?.min ?? 0;
|
|
646
|
+
this.max = options?.max ?? 255;
|
|
569
647
|
this.hint = getSettingsListTheme().hint;
|
|
570
648
|
}
|
|
571
649
|
|
|
572
650
|
handleInput(data: string): void {
|
|
573
651
|
const kb = getKeybindings();
|
|
574
652
|
if (kb.matches(data, 'tui.select.confirm') || data === '\n') {
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
|
|
653
|
+
const value = this.input.getValue();
|
|
654
|
+
if (value === '') {
|
|
655
|
+
this.onCancelEdit();
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
if (!/^\d+$/.test(value)) {
|
|
659
|
+
this.onInvalidValue(`Enter a whole number between ${this.min} and ${this.max}`);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
const parsed = Number(value);
|
|
663
|
+
if (!Number.isSafeInteger(parsed) || parsed < this.min || parsed > this.max) {
|
|
664
|
+
this.onInvalidValue(`Enter a whole number between ${this.min} and ${this.max}`);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
this.onSubmitValue(parsed);
|
|
578
668
|
return;
|
|
579
669
|
}
|
|
580
670
|
if (kb.matches(data, 'tui.select.cancel')) {
|
|
@@ -593,7 +683,7 @@ class NumberInputSubmenu implements Component {
|
|
|
593
683
|
|
|
594
684
|
render(width: number): string[] {
|
|
595
685
|
return [
|
|
596
|
-
this.hint(` ${this.title}`),
|
|
686
|
+
this.hint(` ${this.title} · current ${this.currentValue} · ${this.min}-${this.max}`),
|
|
597
687
|
'',
|
|
598
688
|
...this.input.render(width),
|
|
599
689
|
'',
|
|
@@ -637,9 +727,14 @@ async function runSettingsEditor(ctx: ExtensionContext): Promise<void> {
|
|
|
637
727
|
submenu: (currentValue, subDone) =>
|
|
638
728
|
new NumberInputSubmenu(
|
|
639
729
|
settingLabel(scope, 'turnsBeforeCompact'),
|
|
640
|
-
currentValue,
|
|
641
|
-
(value) => subDone(value),
|
|
730
|
+
Number(currentValue),
|
|
731
|
+
(value) => subDone(String(value)),
|
|
642
732
|
() => subDone(),
|
|
733
|
+
{
|
|
734
|
+
min: TURNS_BEFORE_COMPACT_MIN,
|
|
735
|
+
max: TURNS_BEFORE_COMPACT_MAX,
|
|
736
|
+
onInvalid: (message) => ctx.ui.notify(message, 'error'),
|
|
737
|
+
},
|
|
643
738
|
),
|
|
644
739
|
}));
|
|
645
740
|
}
|
|
@@ -652,7 +747,12 @@ async function runSettingsEditor(ctx: ExtensionContext): Promise<void> {
|
|
|
652
747
|
(id, newValue) => {
|
|
653
748
|
const [scope, key] = id.split(':') as [SettingsScope, GoosedumpSettingKey];
|
|
654
749
|
const parsed = Number(newValue);
|
|
655
|
-
if (
|
|
750
|
+
if (
|
|
751
|
+
!Number.isSafeInteger(parsed) ||
|
|
752
|
+
parsed < TURNS_BEFORE_COMPACT_MIN ||
|
|
753
|
+
parsed > TURNS_BEFORE_COMPACT_MAX
|
|
754
|
+
)
|
|
755
|
+
return;
|
|
656
756
|
writeScopedGoosedumpSetting(location, scope, key, parsed);
|
|
657
757
|
settingsList.updateValue(id, newValue);
|
|
658
758
|
ctx.ui.notify(`${settingLabel(scope, key)} = ${formatCompactSetting(parsed)}`, 'info');
|
|
@@ -976,29 +1076,6 @@ export function createGoosedumpIntegration(
|
|
|
976
1076
|
if (turnsRemaining <= 0) counterTriggered = true;
|
|
977
1077
|
}
|
|
978
1078
|
updateStatus(ctx);
|
|
979
|
-
|
|
980
|
-
if (!counterTriggered || compactionQueued) return;
|
|
981
|
-
// ctx.compact() uses manual compaction semantics and aborts the current run.
|
|
982
|
-
// Wait until no queued work remains before triggering plugin-driven compaction.
|
|
983
|
-
if (ctx.hasPendingMessages()) return;
|
|
984
|
-
|
|
985
|
-
resetCompactCounters(ctx.cwd);
|
|
986
|
-
updateStatus(ctx);
|
|
987
|
-
compactionQueued = true;
|
|
988
|
-
ctx.compact({
|
|
989
|
-
onComplete: () => {
|
|
990
|
-
compactionQueued = false;
|
|
991
|
-
resetCompactCounters(ctx.cwd);
|
|
992
|
-
updateStatus(ctx);
|
|
993
|
-
},
|
|
994
|
-
onError: (error) => {
|
|
995
|
-
compactionQueued = false;
|
|
996
|
-
resetCompactCounters(ctx.cwd);
|
|
997
|
-
updateStatus(ctx);
|
|
998
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
999
|
-
if (ctx.hasUI) ctx.ui.notify(`goosedump compact failed: ${message}`, 'error');
|
|
1000
|
-
},
|
|
1001
|
-
});
|
|
1002
1079
|
}
|
|
1003
1080
|
|
|
1004
1081
|
function checkGoosedump(ctx?: ExtensionContext): boolean {
|
|
@@ -1020,18 +1097,28 @@ export function createGoosedumpIntegration(
|
|
|
1020
1097
|
// command and the goose_compact tool). Stashes a turn-based keep override and
|
|
1021
1098
|
// fires Pi's compaction flow; the session_before_compact hook reads pendingKeep
|
|
1022
1099
|
// and resets it on use, completion, or error.
|
|
1023
|
-
function requestCompaction(
|
|
1100
|
+
function requestCompaction(
|
|
1101
|
+
ctx: ExtensionContext,
|
|
1102
|
+
keep: number | null,
|
|
1103
|
+
): 'started' | 'busy' | 'nothing-to-compact' {
|
|
1104
|
+
if (compactionQueued) return 'busy';
|
|
1105
|
+
if (!hasCompactableHistory(ctx)) return 'nothing-to-compact';
|
|
1106
|
+
|
|
1107
|
+
compactionQueued = true;
|
|
1024
1108
|
pendingKeep = keep;
|
|
1025
1109
|
ctx.compact({
|
|
1026
1110
|
onComplete: () => {
|
|
1111
|
+
compactionQueued = false;
|
|
1027
1112
|
pendingKeep = null;
|
|
1028
1113
|
},
|
|
1029
1114
|
onError: (error) => {
|
|
1115
|
+
compactionQueued = false;
|
|
1030
1116
|
pendingKeep = null;
|
|
1031
1117
|
const message = error instanceof Error ? error.message : String(error);
|
|
1032
1118
|
if (ctx.hasUI) ctx.ui.notify(`goosedump compact failed: ${message}`, 'error');
|
|
1033
1119
|
},
|
|
1034
1120
|
});
|
|
1121
|
+
return 'started';
|
|
1035
1122
|
}
|
|
1036
1123
|
|
|
1037
1124
|
function register(pi: ExtensionAPI): void {
|
|
@@ -1091,11 +1178,7 @@ export function createGoosedumpIntegration(
|
|
|
1091
1178
|
),
|
|
1092
1179
|
}),
|
|
1093
1180
|
async execute(_id, params, _signal, _onUpdate, ctx): Promise<AgentToolResult<void>> {
|
|
1094
|
-
if (!goosedumpAvailable)
|
|
1095
|
-
return textResult(
|
|
1096
|
-
'goosedump is not available. Install with: npm install @jarkkojs/goosedump',
|
|
1097
|
-
);
|
|
1098
|
-
}
|
|
1181
|
+
if (!goosedumpAvailable) return goosedumpUnavailableResult();
|
|
1099
1182
|
|
|
1100
1183
|
try {
|
|
1101
1184
|
const provider = params.provider ?? 'pi';
|
|
@@ -1172,11 +1255,7 @@ export function createGoosedumpIntegration(
|
|
|
1172
1255
|
),
|
|
1173
1256
|
}),
|
|
1174
1257
|
async execute(_id, params, _signal, _onUpdate, ctx): Promise<AgentToolResult<void>> {
|
|
1175
|
-
if (!goosedumpAvailable)
|
|
1176
|
-
return textResult(
|
|
1177
|
-
'goosedump is not available. Install with: npm install @jarkkojs/goosedump',
|
|
1178
|
-
);
|
|
1179
|
-
}
|
|
1258
|
+
if (!goosedumpAvailable) return goosedumpUnavailableResult();
|
|
1180
1259
|
|
|
1181
1260
|
try {
|
|
1182
1261
|
const provider = params.provider ?? 'pi';
|
|
@@ -1247,11 +1326,7 @@ export function createGoosedumpIntegration(
|
|
|
1247
1326
|
),
|
|
1248
1327
|
}),
|
|
1249
1328
|
async execute(_id, params, _signal, _onUpdate, ctx): Promise<AgentToolResult<void>> {
|
|
1250
|
-
if (!goosedumpAvailable)
|
|
1251
|
-
return textResult(
|
|
1252
|
-
'goosedump is not available. Install with: npm install @jarkkojs/goosedump',
|
|
1253
|
-
);
|
|
1254
|
-
}
|
|
1329
|
+
if (!goosedumpAvailable) return goosedumpUnavailableResult();
|
|
1255
1330
|
|
|
1256
1331
|
try {
|
|
1257
1332
|
const provider = params.provider ?? 'pi';
|
|
@@ -1298,14 +1373,14 @@ export function createGoosedumpIntegration(
|
|
|
1298
1373
|
),
|
|
1299
1374
|
}),
|
|
1300
1375
|
async execute(_id, params, _signal, _onUpdate, ctx): Promise<AgentToolResult<void>> {
|
|
1301
|
-
if (!goosedumpAvailable)
|
|
1302
|
-
return textResult(
|
|
1303
|
-
'goosedump is not available. Install with: npm install @jarkkojs/goosedump',
|
|
1304
|
-
);
|
|
1305
|
-
}
|
|
1376
|
+
if (!goosedumpAvailable) return goosedumpUnavailableResult();
|
|
1306
1377
|
|
|
1307
1378
|
const keep = params.keep ?? null;
|
|
1308
|
-
requestCompaction(ctx, keep);
|
|
1379
|
+
const result = requestCompaction(ctx, keep);
|
|
1380
|
+
if (result === 'busy') return textResult('Compaction already in progress.');
|
|
1381
|
+
if (result === 'nothing-to-compact') {
|
|
1382
|
+
return textResult('Nothing to compact (session too small).');
|
|
1383
|
+
}
|
|
1309
1384
|
return textResult(
|
|
1310
1385
|
keep !== null
|
|
1311
1386
|
? `Compacting with goosedump; keeping the last ${keep} user turn${keep === 1 ? '' : 's'}.`
|
|
@@ -1316,14 +1391,62 @@ export function createGoosedumpIntegration(
|
|
|
1316
1391
|
);
|
|
1317
1392
|
}
|
|
1318
1393
|
|
|
1394
|
+
// Fire counter-driven compaction. ctx.compact() uses manual compaction
|
|
1395
|
+
// semantics and aborts the current run, so a mid-run trigger (resumeAfter)
|
|
1396
|
+
// re-triggers the agent with a synthetic continue message once the
|
|
1397
|
+
// compaction lands. If the session is still too small to compact, the
|
|
1398
|
+
// counter stays armed and retries at a later turn_end/agent_end.
|
|
1399
|
+
function maybeAutoCompact(ctx: ExtensionContext, resumeAfter: boolean): void {
|
|
1400
|
+
if (!goosedumpAvailable || !shouldOverrideDefaultCompaction) return;
|
|
1401
|
+
if (!counterTriggered || compactionQueued) return;
|
|
1402
|
+
if (ctx.hasPendingMessages()) return;
|
|
1403
|
+
if (!hasCompactableHistory(ctx)) return;
|
|
1404
|
+
|
|
1405
|
+
resetCompactCounters(ctx.cwd);
|
|
1406
|
+
updateStatus(ctx);
|
|
1407
|
+
compactionQueued = true;
|
|
1408
|
+
ctx.compact({
|
|
1409
|
+
onComplete: () => {
|
|
1410
|
+
compactionQueued = false;
|
|
1411
|
+
resetCompactCounters(ctx.cwd);
|
|
1412
|
+
updateStatus(ctx);
|
|
1413
|
+
if (resumeAfter && !ctx.hasPendingMessages()) {
|
|
1414
|
+
pi.sendMessage(
|
|
1415
|
+
{
|
|
1416
|
+
customType: 'goosedump-autocompact-resume',
|
|
1417
|
+
content:
|
|
1418
|
+
'The session was compacted to reduce context size. Continue the task you were working on from where you left off.',
|
|
1419
|
+
display: false,
|
|
1420
|
+
},
|
|
1421
|
+
{ triggerTurn: true },
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
},
|
|
1425
|
+
onError: (error) => {
|
|
1426
|
+
compactionQueued = false;
|
|
1427
|
+
resetCompactCounters(ctx.cwd);
|
|
1428
|
+
updateStatus(ctx);
|
|
1429
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1430
|
+
if (ctx.hasUI) ctx.ui.notify(`goosedump compact failed: ${message}`, 'error');
|
|
1431
|
+
},
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1319
1435
|
pi.on('session_start', async (_event, ctx) => {
|
|
1320
1436
|
goosedumpAvailable = checkGoosedump(ctx);
|
|
1321
1437
|
resetCompactCounters(ctx.cwd);
|
|
1322
1438
|
updateStatus(ctx);
|
|
1323
1439
|
});
|
|
1324
1440
|
|
|
1325
|
-
pi.on('turn_end', async (
|
|
1441
|
+
pi.on('turn_end', async (event, ctx) => {
|
|
1326
1442
|
noteCompletedTurn(ctx);
|
|
1443
|
+
// A turn with tool results means the agent loop would keep going; compact
|
|
1444
|
+
// between turns and resume so the run's progress is not lost.
|
|
1445
|
+
if (event.toolResults.length > 0) maybeAutoCompact(ctx, true);
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
pi.on('agent_end', async (_event, ctx) => {
|
|
1449
|
+
maybeAutoCompact(ctx, false);
|
|
1327
1450
|
});
|
|
1328
1451
|
|
|
1329
1452
|
pi.on('session_before_compact', async (event, ctx) => {
|
|
@@ -1346,11 +1469,9 @@ export function createGoosedumpIntegration(
|
|
|
1346
1469
|
keep,
|
|
1347
1470
|
);
|
|
1348
1471
|
if (compaction && 'cancel' in compaction) {
|
|
1349
|
-
pendingKeep = null;
|
|
1350
1472
|
if (ctx.hasUI) ctx.ui.notify(compaction.reason, 'info');
|
|
1351
1473
|
return { cancel: true };
|
|
1352
1474
|
}
|
|
1353
|
-
pendingKeep = null;
|
|
1354
1475
|
return compaction ? { compaction } : undefined;
|
|
1355
1476
|
} catch (err) {
|
|
1356
1477
|
if (ctx.hasUI) {
|
|
@@ -1361,15 +1482,26 @@ export function createGoosedumpIntegration(
|
|
|
1361
1482
|
);
|
|
1362
1483
|
}
|
|
1363
1484
|
return undefined;
|
|
1485
|
+
} finally {
|
|
1486
|
+
pendingKeep = null;
|
|
1364
1487
|
}
|
|
1365
1488
|
});
|
|
1366
1489
|
|
|
1367
1490
|
pi.on('session_compact', async (event, ctx) => {
|
|
1368
|
-
if (!goosedumpAvailable || !
|
|
1491
|
+
if (!goosedumpAvailable || !shouldOverrideDefaultCompaction) return;
|
|
1369
1492
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1493
|
+
resetCompactCounters(ctx.cwd);
|
|
1494
|
+
updateStatus(ctx);
|
|
1372
1495
|
|
|
1496
|
+
const compactionEntry = event.compactionEntry;
|
|
1497
|
+
if (
|
|
1498
|
+
!event.fromExtension ||
|
|
1499
|
+
!compactionEntry ||
|
|
1500
|
+
!isRecord(compactionEntry.details) ||
|
|
1501
|
+
compactionEntry.details.source !== 'goosedump'
|
|
1502
|
+
) {
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1373
1505
|
const tokensBefore = compactionEntry.tokensBefore;
|
|
1374
1506
|
const summaryLen = compactionEntry.summary.length;
|
|
1375
1507
|
const tokenEstimate = Math.ceil(summaryLen / 4);
|
|
@@ -1390,15 +1522,13 @@ export function createGoosedumpIntegration(
|
|
|
1390
1522
|
description: 'Edit goosedump compaction settings',
|
|
1391
1523
|
handler: async (_args, ctx) => {
|
|
1392
1524
|
if (!goosedumpAvailable) {
|
|
1393
|
-
ctx.ui.notify(
|
|
1394
|
-
'goosedump is not available. Install with: npm install @jarkkojs/goosedump',
|
|
1395
|
-
'error',
|
|
1396
|
-
);
|
|
1525
|
+
ctx.ui.notify(GOOSEDUMP_UNAVAILABLE_MESSAGE, 'error');
|
|
1397
1526
|
return;
|
|
1398
1527
|
}
|
|
1399
1528
|
|
|
1400
1529
|
try {
|
|
1401
1530
|
await runSettingsEditor(ctx);
|
|
1531
|
+
reloadCompactSettings(ctx.cwd);
|
|
1402
1532
|
updateStatus(ctx);
|
|
1403
1533
|
} catch (err) {
|
|
1404
1534
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1411,10 +1541,7 @@ export function createGoosedumpIntegration(
|
|
|
1411
1541
|
description: 'Compact now with goosedump; optionally keep the last N user turns',
|
|
1412
1542
|
handler: async (args, ctx) => {
|
|
1413
1543
|
if (!goosedumpAvailable) {
|
|
1414
|
-
ctx.ui.notify(
|
|
1415
|
-
'goosedump is not available. Install with: npm install @jarkkojs/goosedump',
|
|
1416
|
-
'error',
|
|
1417
|
-
);
|
|
1544
|
+
ctx.ui.notify(GOOSEDUMP_UNAVAILABLE_MESSAGE, 'error');
|
|
1418
1545
|
return;
|
|
1419
1546
|
}
|
|
1420
1547
|
|
|
@@ -1431,7 +1558,12 @@ export function createGoosedumpIntegration(
|
|
|
1431
1558
|
}
|
|
1432
1559
|
}
|
|
1433
1560
|
|
|
1434
|
-
requestCompaction(ctx, keep);
|
|
1561
|
+
const result = requestCompaction(ctx, keep);
|
|
1562
|
+
if (result === 'busy') {
|
|
1563
|
+
ctx.ui.notify('Compaction already in progress', 'info');
|
|
1564
|
+
} else if (result === 'nothing-to-compact') {
|
|
1565
|
+
ctx.ui.notify('Nothing to compact (session too small)', 'info');
|
|
1566
|
+
}
|
|
1435
1567
|
},
|
|
1436
1568
|
});
|
|
1437
1569
|
|
|
@@ -1439,10 +1571,7 @@ export function createGoosedumpIntegration(
|
|
|
1439
1571
|
description: 'Search the current session history',
|
|
1440
1572
|
handler: async (args, ctx) => {
|
|
1441
1573
|
if (!goosedumpAvailable) {
|
|
1442
|
-
ctx.ui.notify(
|
|
1443
|
-
'goosedump is not available. Install with: npm install @jarkkojs/goosedump',
|
|
1444
|
-
'error',
|
|
1445
|
-
);
|
|
1574
|
+
ctx.ui.notify(GOOSEDUMP_UNAVAILABLE_MESSAGE, 'error');
|
|
1446
1575
|
return;
|
|
1447
1576
|
}
|
|
1448
1577
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goosedump",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Pi extension for goosedump-backed session-history search, entry expansion, and compaction",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compaction",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@earendil-works/pi-tui": "^0.78.0",
|
|
34
|
-
"@jarkkojs/goosedump": "^0.9.
|
|
34
|
+
"@jarkkojs/goosedump": "^0.9.4",
|
|
35
35
|
"@sinclair/typebox": "^0.34.49"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|