pi-goosedump 0.12.43 → 0.12.44

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 (2) hide show
  1. package/dist/index.ts +63 -9
  2. package/package.json +2 -2
package/dist/index.ts CHANGED
@@ -826,6 +826,8 @@ class CompactionRuntime {
826
826
  activeSessionFile = null;
827
827
  sessionActive = false;
828
828
  activePi = null;
829
+ expectedTurnBoundaryAbort = false;
830
+ suppressedAbortMessages = new WeakSet;
829
831
  constructor(deps) {
830
832
  this.overrideDefaultCompaction = deps.overrideDefaultCompaction;
831
833
  this.nativeExecutablePath = deps.nativeExecutablePath;
@@ -884,6 +886,7 @@ class CompactionRuntime {
884
886
  this.activeSessionFile = ctx.sessionManager.getSessionFile?.() ?? null;
885
887
  this.sessionActive = true;
886
888
  this.compactionState = { phase: "idle" };
889
+ this.expectedTurnBoundaryAbort = false;
887
890
  this.syncCompactCounters(ctx);
888
891
  this.updateStatus(ctx);
889
892
  this.queueTurnCompaction(ctx, 1);
@@ -895,6 +898,7 @@ class CompactionRuntime {
895
898
  this.activeSessionFile = null;
896
899
  this.sessionActive = false;
897
900
  this.compactionState = { phase: "idle" };
901
+ this.expectedTurnBoundaryAbort = false;
898
902
  }
899
903
  onAgentStart(ctx) {
900
904
  if (this.compactionState.phase !== "running" || this.compactionState.owner !== "pi")
@@ -904,7 +908,19 @@ class CompactionRuntime {
904
908
  this.syncCompactCounters(ctx);
905
909
  this.setCompactionState(ctx, request ? this.requeueRequest(request, this.settlementGeneration + 1) : { phase: "idle" });
906
910
  }
911
+ onMessageEnd(event, ctx) {
912
+ if (!this.expectedTurnBoundaryAbort || !this.contextIsCurrent(ctx) || event.message.role !== "assistant" || event.message.stopReason !== "aborted") {
913
+ return;
914
+ }
915
+ this.expectedTurnBoundaryAbort = false;
916
+ this.suppressedAbortMessages.add(event.message);
917
+ return {
918
+ message: { ...event.message, stopReason: "stop", errorMessage: undefined }
919
+ };
920
+ }
907
921
  onTurnEnd(event, ctx) {
922
+ if (this.suppressedAbortMessages.delete(event.message))
923
+ return;
908
924
  if (!isCompletedAssistant(event.message))
909
925
  return;
910
926
  const resumeAfter = event.toolResults.length > 0 && !ctx.hasPendingMessages();
@@ -917,6 +933,7 @@ class CompactionRuntime {
917
933
  this.interruptToolLoopForQueuedCompaction(ctx, resumeAfter);
918
934
  }
919
935
  onAgentSettled(ctx) {
936
+ this.expectedTurnBoundaryAbort = false;
920
937
  this.settlementGeneration += 1;
921
938
  this.syncCompactCounters(ctx);
922
939
  this.updateStatus(ctx);
@@ -1094,7 +1111,7 @@ class CompactionRuntime {
1094
1111
  const target = compactionEntry.details.target;
1095
1112
  if (typeof target !== "string")
1096
1113
  return;
1097
- this.enqueueRemember(target, (message) => {
1114
+ this.enqueueRemember(target, ctx, (message) => {
1098
1115
  if (ctx.hasUI) {
1099
1116
  ctx.ui.notify(`goosedump memory learning skipped: ${message}`, "warning");
1100
1117
  }
@@ -1147,6 +1164,7 @@ class CompactionRuntime {
1147
1164
  phase: "queued",
1148
1165
  request: { ...request, resumeAfter: true }
1149
1166
  };
1167
+ this.expectedTurnBoundaryAbort = true;
1150
1168
  ctx.abort();
1151
1169
  }
1152
1170
  queueThresholdCompaction(ctx, eligibleSettlement, resumeAfter) {
@@ -1258,6 +1276,38 @@ class CompactionRuntime {
1258
1276
  }
1259
1277
  }
1260
1278
 
1279
+ // src/memory-activity.ts
1280
+ import { truncateToWidth } from "@earendil-works/pi-tui";
1281
+ var MEMORY_ACTIVITY_WIDGET_KEY = "goosedump-memory-activity";
1282
+ var ACTIVITY_FRAMES = ["▰▱▱▱", "▱▰▱▱", "▱▱▰▱", "▱▱▱▰", "▱▱▰▱", "▱▰▱▱"];
1283
+ function showMemoryActivity(ctx) {
1284
+ if (!ctx.hasUI)
1285
+ return;
1286
+ ctx.ui.setWidget(MEMORY_ACTIVITY_WIDGET_KEY, (tui, theme) => {
1287
+ let frame = 0;
1288
+ const timer = setInterval(() => {
1289
+ frame = (frame + 1) % ACTIVITY_FRAMES.length;
1290
+ tui.requestRender();
1291
+ }, 140);
1292
+ timer.unref();
1293
+ return {
1294
+ render(width) {
1295
+ const bar = theme.fg("accent", ACTIVITY_FRAMES[frame] ?? ACTIVITY_FRAMES[0]);
1296
+ const label = theme.fg("muted", "long-term memory");
1297
+ return [truncateToWidth(`${bar} ${label}`, width, "")];
1298
+ },
1299
+ invalidate() {},
1300
+ dispose() {
1301
+ clearInterval(timer);
1302
+ }
1303
+ };
1304
+ }, { placement: "belowEditor" });
1305
+ }
1306
+ function hideMemoryActivity(ctx) {
1307
+ if (ctx.hasUI)
1308
+ ctx.ui.setWidget(MEMORY_ACTIVITY_WIDGET_KEY, undefined);
1309
+ }
1310
+
1261
1311
  // src/policy.ts
1262
1312
  var GOOSE_TOOL_NAMES = [
1263
1313
  "goose_remember",
@@ -1502,7 +1552,7 @@ function registerGooseTools(pi, deps) {
1502
1552
  const resolved = resolveHistoryTarget(ctx, "goose_remember", params);
1503
1553
  if (!resolved.ok)
1504
1554
  return resolved.result;
1505
- enqueueRemember(resolved.target, (message) => {
1555
+ enqueueRemember(resolved.target, ctx, (message) => {
1506
1556
  if (ctx.hasUI) {
1507
1557
  ctx.ui.notify(`goosedump memory learning failed: ${message}`, "warning");
1508
1558
  }
@@ -1671,7 +1721,7 @@ import {
1671
1721
  Key,
1672
1722
  decodeKittyPrintable,
1673
1723
  matchesKey,
1674
- truncateToWidth,
1724
+ truncateToWidth as truncateToWidth2,
1675
1725
  visibleWidth,
1676
1726
  wrapTextWithAnsi
1677
1727
  } from "@earendil-works/pi-tui";
@@ -1745,7 +1795,7 @@ class NumberInputSubmenu {
1745
1795
  }
1746
1796
  }
1747
1797
  function makeRow(content, innerW, border) {
1748
- const line = truncateToWidth(content, innerW);
1798
+ const line = truncateToWidth2(content, innerW);
1749
1799
  const pad = Math.max(0, innerW - visibleWidth(line));
1750
1800
  return `${border} ${line}${" ".repeat(pad)} ${border}`;
1751
1801
  }
@@ -1873,7 +1923,7 @@ async function runSettingsEditor(ctx) {
1873
1923
  const { accent, bold, dim, muted, text } = colors;
1874
1924
  const innerW = overlayInnerWidth(width);
1875
1925
  const pad = (value, cellWidth) => {
1876
- const clipped = truncateToWidth(value, cellWidth);
1926
+ const clipped = truncateToWidth2(value, cellWidth);
1877
1927
  return `${clipped}${" ".repeat(Math.max(0, cellWidth - visibleWidth(clipped)))}`;
1878
1928
  };
1879
1929
  const tabLabel = (label, active) => active ? accent(bold(`[${label}]`)) : muted(label);
@@ -1884,7 +1934,7 @@ async function runSettingsEditor(ctx) {
1884
1934
  } else if (tab === "help") {
1885
1935
  const shortcutWidth = Math.max(visibleWidth("Shortcut"), ...helpRows.map(([shortcut]) => visibleWidth(shortcut)));
1886
1936
  const descriptionWidth = Math.max(1, innerW - shortcutWidth - 4);
1887
- content.push(dim(` ${pad("Shortcut", shortcutWidth)} Description`), ...helpRows.map(([shortcut, description]) => ` ${accent(pad(shortcut, shortcutWidth))} ${text(truncateToWidth(description, descriptionWidth))}`));
1937
+ content.push(dim(` ${pad("Shortcut", shortcutWidth)} Description`), ...helpRows.map(([shortcut, description]) => ` ${accent(pad(shortcut, shortcutWidth))} ${text(truncateToWidth2(description, descriptionWidth))}`));
1888
1938
  } else {
1889
1939
  for (const [index, spec] of specs.entries()) {
1890
1940
  const selected = index === selectedSetting;
@@ -2015,7 +2065,7 @@ async function runSearchPanel(ctx, query, executablePath) {
2015
2065
  }
2016
2066
  return {
2017
2067
  render(width) {
2018
- const header = overlayHeader(colors, width, `Goosedump search: ${truncateToWidth(query, 44)}`);
2068
+ const header = overlayHeader(colors, width, `Goosedump search: ${truncateToWidth2(query, 44)}`);
2019
2069
  if (error) {
2020
2070
  return [header, ...overlayError(colors, width, error), overlayFooter(colors, width)];
2021
2071
  }
@@ -2375,12 +2425,12 @@ function createGoosedumpIntegration(options = {}) {
2375
2425
  const nativeExecutablePath = options.nativeExecutablePath ?? resolveNativeGoosedumpBinary();
2376
2426
  const rememberQueue = [];
2377
2427
  let rememberRunning = null;
2378
- function enqueueRemember(target, reportError) {
2428
+ function enqueueRemember(target, ctx, reportError) {
2379
2429
  for (let i = rememberQueue.length - 1;i >= 0; i -= 1) {
2380
2430
  if (rememberQueue[i]?.target === target)
2381
2431
  rememberQueue.splice(i, 1);
2382
2432
  }
2383
- rememberQueue.push({ target, reportError });
2433
+ rememberQueue.push({ target, ctx, reportError });
2384
2434
  ensureRememberWorker();
2385
2435
  }
2386
2436
  function ensureRememberWorker() {
@@ -2391,11 +2441,14 @@ function createGoosedumpIntegration(options = {}) {
2391
2441
  const job = rememberQueue.shift();
2392
2442
  if (!job)
2393
2443
  continue;
2444
+ showMemoryActivity(job.ctx);
2394
2445
  try {
2395
2446
  await goosedumpMemoryRemember(job.target, undefined, nativeExecutablePath);
2396
2447
  } catch (err) {
2397
2448
  const message = err instanceof Error ? err.message : String(err);
2398
2449
  job.reportError(message);
2450
+ } finally {
2451
+ hideMemoryActivity(job.ctx);
2399
2452
  }
2400
2453
  }
2401
2454
  })().finally(() => {
@@ -2444,6 +2497,7 @@ ${renderToolRoutingPolicy()}`
2444
2497
  pi.on("agent_start", async (_event, ctx) => {
2445
2498
  compaction.onAgentStart(ctx);
2446
2499
  });
2500
+ pi.on("message_end", (event, ctx) => compaction.onMessageEnd(event, ctx));
2447
2501
  pi.on("turn_end", async (event, ctx) => {
2448
2502
  compaction.onTurnEnd(event, ctx);
2449
2503
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goosedump",
3
- "version": "0.12.43",
3
+ "version": "0.12.44",
4
4
  "description": "Pi extension for goosedump session search, durable memory, session management, 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.43",
41
+ "@jarkkojs/goosedump": "^0.12.44",
42
42
  "@sinclair/typebox": "^0.34.49"
43
43
  },
44
44
  "devDependencies": {