pi-goosedump 0.12.21 → 0.12.22

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.
Files changed (3) hide show
  1. package/README.md +14 -13
  2. package/dist/index.ts +43 -43
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -92,19 +92,20 @@ Optional settings live under `goosedump` in:
92
92
  }
93
93
  ```
94
94
 
95
- `turnsBeforeCompact` is an integer from 0 to 255. `0` disables scheduled
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
95
+ `turnsBeforeCompact` is an integer from 0 to 255. `0` disables the turn-based
96
+ trigger. Pi's `compaction.enabled` setting controls all automatic compaction, including
97
+ the turn-based trigger. Each completed model response counts as one turn, including
98
+ responses that call tools. The countdown is restored when a session resumes and
99
+ appears in the footer with a status dot (green when >25% turns remain, yellow when
100
+ ≤25%, red when ≤12.5%) followed by a hexadecimal countdown. `● GD:0A` means ten
101
+ turns remain, `● GD:00` is ready to compact, and `● GD:--` means the turn trigger is
102
+ disabled. The footer adds `queued` while compaction is waiting to start and
103
+ `compacting` while the summary is being built. When the counter hits zero mid
104
+ tool-loop, compaction starts immediately after the current tool batch is persisted
105
+ and the task is resumed automatically. When the counter hits zero on a terminal
106
+ turn, compaction starts after the agent has settled. Pi remains the scheduler for
107
+ its context threshold. Whichever enabled limit is reached first compacts once, and
108
+ every successful compaction resets the countdown. A failed or cancelled compaction
108
109
  leaves the counter armed at zero and retries on the next tool turn or settlement.
109
110
 
110
111
  `summaryMaxTokens` is an integer from 512 to 65536 (default 4096). The effective
package/dist/index.ts CHANGED
@@ -607,6 +607,16 @@ function piKeepRecentTokens(cwd, includeProject) {
607
607
  }
608
608
  return DEFAULT_COMPACTION_SETTINGS.keepRecentTokens;
609
609
  }
610
+ function piCompactionEnabled(cwd, includeProject) {
611
+ const paths = includeProject ? [projectSettingsPath(cwd), globalSettingsPath()] : [globalSettingsPath()];
612
+ for (const path of paths) {
613
+ const compaction = readJsonObject(path).compaction;
614
+ if (isRecord(compaction) && typeof compaction.enabled === "boolean") {
615
+ return compaction.enabled;
616
+ }
617
+ }
618
+ return DEFAULT_COMPACTION_SETTINGS.enabled;
619
+ }
610
620
  function piReserveTokens(cwd, includeProject) {
611
621
  const paths = includeProject ? [projectSettingsPath(cwd), globalSettingsPath()] : [globalSettingsPath()];
612
622
  for (const path of paths) {
@@ -845,13 +855,11 @@ async function runSettingsEditor(ctx) {
845
855
  const specs = [
846
856
  {
847
857
  key: "turnsBeforeCompact",
848
- description: "LLM turns before compaction; 0 disables.",
849
858
  min: TURNS_BEFORE_COMPACT_MIN,
850
859
  max: TURNS_BEFORE_COMPACT_MAX
851
860
  },
852
861
  {
853
862
  key: "summaryMaxTokens",
854
- description: "Maximum estimated tokens in each summary.",
855
863
  min: SUMMARY_MAX_TOKENS_MIN,
856
864
  max: SUMMARY_MAX_TOKENS_MAX
857
865
  }
@@ -964,11 +972,6 @@ async function runSettingsEditor(ctx) {
964
972
  const cursor = selected ? accent("›") : " ";
965
973
  content.push(`${cursor} ${renderedValue} ${text(spec.key)}`);
966
974
  }
967
- const selectedSpec = specs[selectedSetting];
968
- if (selectedSpec) {
969
- const inherited = scope === "project" && projectSettings[selectedSpec.key] === undefined;
970
- content.push("", dim(inherited ? `${selectedSpec.description} Using the Global value.` : selectedSpec.description));
971
- }
972
975
  const effective = resolveGoosedumpSettings(location, projectTrusted);
973
976
  content.push("", dim(`Effective: turns=${formatCompactSetting(effective.turnsBeforeCompact)} · summary=${effectiveSummaryMaxTokens(location, projectTrusted)} tokens`));
974
977
  }
@@ -1403,6 +1406,7 @@ function createGoosedumpIntegration(options = {}) {
1403
1406
  const nativeExecutablePath = options.nativeExecutablePath;
1404
1407
  let goosedumpAvailable = false;
1405
1408
  let compactSettings = DEFAULT_GOOSEDUMP_SETTINGS;
1409
+ let piAutoCompactionEnabled = DEFAULT_COMPACTION_SETTINGS.enabled;
1406
1410
  let turnsRemaining = 0;
1407
1411
  let counterTriggered = false;
1408
1412
  let compactionState = { phase: "idle" };
@@ -1448,15 +1452,27 @@ function createGoosedumpIntegration(options = {}) {
1448
1452
  await rememberRunning;
1449
1453
  }
1450
1454
  function syncCompactCounters(ctx) {
1451
- compactSettings = resolveGoosedumpSettings(ctx.cwd, projectSettingsTrusted(ctx));
1455
+ const projectTrusted = projectSettingsTrusted(ctx);
1456
+ compactSettings = resolveGoosedumpSettings(ctx.cwd, projectTrusted);
1457
+ piAutoCompactionEnabled = piCompactionEnabled(ctx.cwd, projectTrusted);
1452
1458
  const completedTurns = completedTurnsSinceCompaction(ctx.sessionManager.getBranch());
1453
1459
  turnsRemaining = Math.max(0, compactSettings.turnsBeforeCompact - completedTurns);
1454
1460
  counterTriggered = compactSettings.turnsBeforeCompact > 0 && completedTurns >= compactSettings.turnsBeforeCompact;
1455
1461
  }
1462
+ function turnCompactionEnabled() {
1463
+ return shouldOverrideDefaultCompaction && piAutoCompactionEnabled && compactSettings.turnsBeforeCompact > 0;
1464
+ }
1465
+ function turnRequestIsEligible(request) {
1466
+ return request.origin !== "turn" || turnCompactionEnabled() && counterTriggered;
1467
+ }
1468
+ function requeueRequest(request, eligibleSettlement) {
1469
+ const queued = { ...request, eligibleSettlement };
1470
+ return requestIsCurrent(queued) && turnRequestIsEligible(queued) ? { phase: "queued", request: queued } : { phase: "idle" };
1471
+ }
1456
1472
  function updateStatus(ctx) {
1457
1473
  if (!goosedumpAvailable)
1458
1474
  return;
1459
- const active = shouldOverrideDefaultCompaction && compactSettings.turnsBeforeCompact > 0;
1475
+ const active = turnCompactionEnabled();
1460
1476
  let dotColor = "success";
1461
1477
  if (active) {
1462
1478
  const remainingRatio = turnsRemaining / compactSettings.turnsBeforeCompact;
@@ -1493,7 +1509,7 @@ function createGoosedumpIntegration(options = {}) {
1493
1509
  }, { triggerTurn: true });
1494
1510
  }
1495
1511
  function tryMidRunCounterCompaction(ctx) {
1496
- if (!goosedumpAvailable || !shouldOverrideDefaultCompaction)
1512
+ if (!goosedumpAvailable || !turnCompactionEnabled())
1497
1513
  return false;
1498
1514
  if (!counterTriggered)
1499
1515
  return false;
@@ -1503,14 +1519,10 @@ function createGoosedumpIntegration(options = {}) {
1503
1519
  return false;
1504
1520
  if (compactionState.phase === "idle") {
1505
1521
  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
- });
1522
+ } else if (compactionState.phase === "queued" && compactionState.request.origin === "turn") {
1523
+ setCompactionState(ctx, requeueRequest(compactionState.request, Math.min(compactionState.request.eligibleSettlement, settlementGeneration)));
1524
+ } else {
1525
+ return false;
1514
1526
  }
1515
1527
  return startQueuedCompaction(ctx, { resumeAfter: true, allowActiveRun: true });
1516
1528
  }
@@ -1538,20 +1550,14 @@ function createGoosedumpIntegration(options = {}) {
1538
1550
  signal?.addEventListener("abort", () => {
1539
1551
  if (!runningOperationIsCurrent(operation, generation, sessionFile))
1540
1552
  return;
1541
- setCompactionState(ctx, reason !== "manual" && request && requestIsCurrent(request) ? {
1542
- phase: "queued",
1543
- request: {
1544
- ...request,
1545
- eligibleSettlement: Math.max(request.eligibleSettlement, settlementGeneration + 2)
1546
- }
1547
- } : { phase: "idle" });
1553
+ setCompactionState(ctx, reason !== "manual" && request ? requeueRequest(request, Math.max(request.eligibleSettlement, settlementGeneration + 2)) : { phase: "idle" });
1548
1554
  }, { once: true });
1549
1555
  }
1550
1556
  function requestHasCompactableHistory(ctx, keep) {
1551
1557
  return keep === null ? hasCompactableHistory(ctx) : keepBoundaryId(ctx.sessionManager.getBranch(), keep) !== undefined;
1552
1558
  }
1553
1559
  function queueTurnCompaction(ctx, eligibleSettlement) {
1554
- if (!counterTriggered || compactionState.phase !== "idle")
1560
+ if (!turnCompactionEnabled() || !counterTriggered || compactionState.phase !== "idle")
1555
1561
  return;
1556
1562
  if (!hasCompactableHistory(ctx))
1557
1563
  return;
@@ -1559,6 +1565,7 @@ function createGoosedumpIntegration(options = {}) {
1559
1565
  phase: "queued",
1560
1566
  request: {
1561
1567
  keep: null,
1568
+ origin: "turn",
1562
1569
  sessionGeneration,
1563
1570
  sessionFile: activeSessionFile,
1564
1571
  eligibleSettlement
@@ -1569,7 +1576,9 @@ function createGoosedumpIntegration(options = {}) {
1569
1576
  if (compactionState.phase !== "queued")
1570
1577
  return false;
1571
1578
  const request = compactionState.request;
1572
- if (!requestIsCurrent(request)) {
1579
+ if (request.origin === "turn")
1580
+ syncCompactCounters(ctx);
1581
+ if (!requestIsCurrent(request) || !turnRequestIsEligible(request)) {
1573
1582
  setCompactionState(ctx, { phase: "idle" });
1574
1583
  return false;
1575
1584
  }
@@ -1636,6 +1645,7 @@ function createGoosedumpIntegration(options = {}) {
1636
1645
  phase: "queued",
1637
1646
  request: {
1638
1647
  keep,
1648
+ origin: "manual",
1639
1649
  sessionGeneration,
1640
1650
  sessionFile: activeSessionFile,
1641
1651
  eligibleSettlement: settlementGeneration + 1
@@ -2000,13 +2010,10 @@ ${lines.join(`
2000
2010
  updateStatus(ctx);
2001
2011
  if (compactionState.phase === "running" && compactionState.owner === "pi") {
2002
2012
  const request = compactionState.request;
2003
- setCompactionState(ctx, request && requestIsCurrent(request) ? {
2004
- phase: "queued",
2005
- request: { ...request, eligibleSettlement: settlementGeneration + 1 }
2006
- } : { phase: "idle" });
2013
+ setCompactionState(ctx, request ? requeueRequest(request, settlementGeneration + 1) : { phase: "idle" });
2007
2014
  return;
2008
2015
  }
2009
- if (compactionState.phase === "queued" && !requestIsCurrent(compactionState.request)) {
2016
+ if (compactionState.phase === "queued" && (!requestIsCurrent(compactionState.request) || !turnRequestIsEligible(compactionState.request))) {
2010
2017
  setCompactionState(ctx, { phase: "idle" });
2011
2018
  }
2012
2019
  if (compactionState.phase === "idle") {
@@ -2051,10 +2058,9 @@ ${lines.join(`
2051
2058
  if (compactionState.phase !== "running" || compactionState.owner !== "pi")
2052
2059
  return;
2053
2060
  const request = compactionState.request;
2054
- setCompactionState(ctx, request && requestIsCurrent(request) ? {
2055
- phase: "queued",
2056
- request: { ...request, eligibleSettlement: settlementGeneration + 1 }
2057
- } : { phase: "idle" });
2061
+ if (request?.origin === "turn")
2062
+ syncCompactCounters(ctx);
2063
+ setCompactionState(ctx, request ? requeueRequest(request, settlementGeneration + 1) : { phase: "idle" });
2058
2064
  });
2059
2065
  pi.on("turn_end", async (event, ctx) => {
2060
2066
  if (isCompletedAssistant(event.message))
@@ -2071,13 +2077,7 @@ ${lines.join(`
2071
2077
  const signal = event.signal;
2072
2078
  if (signal?.aborted) {
2073
2079
  if (compactionState.phase === "queued" && requestIsCurrent(compactionState.request)) {
2074
- setCompactionState(ctx, event.reason === "manual" ? { phase: "idle" } : {
2075
- phase: "queued",
2076
- request: {
2077
- ...compactionState.request,
2078
- eligibleSettlement: Math.max(compactionState.request.eligibleSettlement, settlementGeneration + 2)
2079
- }
2080
- });
2080
+ setCompactionState(ctx, event.reason === "manual" ? { phase: "idle" } : requeueRequest(compactionState.request, Math.max(compactionState.request.eligibleSettlement, settlementGeneration + 2)));
2081
2081
  }
2082
2082
  return { cancel: true };
2083
2083
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goosedump",
3
- "version": "0.12.21",
3
+ "version": "0.12.22",
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.21",
41
+ "@jarkkojs/goosedump": "^0.12.22",
42
42
  "@sinclair/typebox": "^0.34.49"
43
43
  },
44
44
  "devDependencies": {