@henryqw/pi-auto-compact 0.2.0 → 0.2.2

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 CHANGED
@@ -41,10 +41,10 @@ Threshold must be at least 25% and below 100%; lower values are not meaningful.
41
41
  ## Behavior
42
42
 
43
43
  - Refuses activation with an error when Pi's effective `compaction.enabled` setting is not `false`; competing automatic compactors can start duplicate summaries.
44
- - Checks `turn_start`, tool-call `turn_end`, `context`, and resumed/forked `session_start`.
44
+ - Checks `turn_start`, tool-call `turn_end`, `agent_end`, `context`, and resumed/forked `session_start`.
45
45
  - Uses Pi's default `ctx.compact()` summary and session persistence.
46
46
  - Keeps newest 15% as temporary emergency context while compaction runs.
47
- - Sends a follow-up message after compaction so task execution continues.
47
+ - Sends a follow-up message after mid-task compaction so task execution continues; final-answer compaction stays idle.
48
48
  - Compacts above configured `autoCompactThreshold` percentage (50% by default).
49
49
 
50
50
  `ctx.compact()` aborts current low-level run. Extension hides that empty internal abort message, then starts new run with current task resume message. Other aborts and provider errors remain visible.
@@ -13,13 +13,14 @@ import type {
13
13
  type AgentMessage = Parameters<typeof estimateTokens>[0];
14
14
 
15
15
  /**
16
- * Proactive compaction runs at three points:
16
+ * Proactive compaction runs at four points:
17
17
  * - turn_start: catch sessions already over threshold before next request.
18
18
  * - turn_end: catch growth caused by tool results before next LLM turn.
19
+ * - agent_end: catch growth from the final provider turn.
19
20
  * - context: last-resort guard with a temporary keep-recent context.
20
21
  *
21
- * Pi's ctx.compact() aborts active low-level run internally. Its completion
22
- * callback sends follow-up user message, which resumes task after summary.
22
+ * Pi's ctx.compact() aborts active low-level run internally. Mid-task
23
+ * compaction sends a follow-up user message to resume work after summary.
23
24
  */
24
25
  const DEFAULT_COMPACT_THRESHOLD_PERCENT = 50;
25
26
  const MIN_COMPACT_THRESHOLD_PERCENT = 25;
@@ -119,16 +120,17 @@ function hasToolCall(message: AgentMessage): boolean {
119
120
  export default function (pi: ExtensionAPI) {
120
121
  let active = false;
121
122
  let autoCompactThreshold = DEFAULT_COMPACT_THRESHOLD_PERCENT;
122
- // Prevent turn_start, turn_end, and context from starting duplicate summaries.
123
+ // Prevent lifecycle hooks from starting duplicate summaries.
123
124
  let compactionPending = false;
124
125
  let compactionAbortExpected = false;
125
126
 
126
- const runCompaction = (ctx: ExtensionContext) => {
127
+ const runCompaction = (ctx: ExtensionContext, resumeTask = true) => {
127
128
  compactionAbortExpected = Boolean(ctx.signal && !ctx.signal.aborted);
128
129
  ctx.compact({
129
130
  onComplete: () => {
130
131
  compactionPending = false;
131
132
  compactionAbortExpected = false;
133
+ if (!resumeTask) return;
132
134
  // Pi may flush queued input during compaction_end. Wait one macrotask
133
135
  // before checking idle, otherwise follow-up can race that flush.
134
136
  setImmediate(() => {
@@ -142,14 +144,14 @@ export default function (pi: ExtensionAPI) {
142
144
  });
143
145
  };
144
146
 
145
- const compactIfNeeded = (ctx: ExtensionContext) => {
147
+ const compactIfNeeded = (ctx: ExtensionContext, resumeTask = true) => {
146
148
  if (!active || compactionPending) return;
147
149
 
148
150
  const usage = ctx.getContextUsage();
149
151
  if (usage?.percent == null || usage.percent <= autoCompactThreshold) return;
150
152
 
151
153
  compactionPending = true;
152
- runCompaction(ctx);
154
+ runCompaction(ctx, resumeTask);
153
155
  };
154
156
 
155
157
  // Hide only empty abort produced when ctx.compact() cancels active run.
@@ -176,12 +178,14 @@ export default function (pi: ExtensionAPI) {
176
178
  // Pre-turn catches resumed/queued work before provider request starts.
177
179
  pi.on("turn_start", (_event, ctx) => compactIfNeeded(ctx));
178
180
 
179
- // Only tool-call turns need mid-run compaction. Final answers should not
180
- // receive an unsolicited continuation message.
181
+ // Only tool-call turns need mid-run compaction.
181
182
  pi.on("turn_end", (event, ctx) => {
182
183
  if (hasToolCall(event.message)) compactIfNeeded(ctx);
183
184
  });
184
185
 
186
+ // Catch threshold crossings caused by the final provider turn.
187
+ pi.on("agent_end", (_event, ctx) => compactIfNeeded(ctx, false));
188
+
185
189
  // Runs before every provider request. Temporary truncation protects request
186
190
  // size while asynchronous default compaction summarizes persisted history.
187
191
  pi.on("context", (event, ctx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-auto-compact",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Proactively compact Pi context at a configurable threshold and resume the current task.",
5
5
  "keywords": [
6
6
  "pi-package",