Phase 6: add compaction coexistence hint — one-time notify when both compactors run.

Shows ui.notify once when pi-auto-compact is detected while Pi compaction.enabled stays true.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-17 11:47:27 +07:00
parent 2e6d36a855
commit bf862656ae
3 changed files with 112 additions and 1 deletions
+56 -1
View File
@@ -1,4 +1,5 @@
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
import { SettingsManager, getAgentDir } from "@earendil-works/pi-coding-agent";
import {
effectiveIntegration,
type ReinjectDeliveryMode,
@@ -33,3 +34,57 @@ export const PI_AUTO_COMPACT_FOLLOW_UP_PREFIXES = [
"Emergency auto-compact ran.",
"Auto-compact ran on session resume.",
] as const;
const PI_DEFAULT_COMPACTION_ENABLED = true;
function readCompactionEnabled(settings: object): boolean | undefined {
const compaction = (settings as Record<string, unknown>).compaction;
if (!compaction || typeof compaction !== "object" || Array.isArray(compaction)) {
return undefined;
}
const enabled = (compaction as Record<string, unknown>).enabled;
return typeof enabled === "boolean" ? enabled : undefined;
}
/** Merged Pi compaction.enabled with project-over-global semantics (SPEC §16.7). */
export function resolvePiDefaultCompactionEnabled(
globalSettings: object,
projectSettings: object,
): boolean {
return (
readCompactionEnabled(projectSettings) ??
readCompactionEnabled(globalSettings) ??
PI_DEFAULT_COMPACTION_ENABLED
);
}
export function isPiDefaultCompactionEnabled(ctx: ExtensionContext): boolean {
const manager = SettingsManager.create(ctx.cwd, getAgentDir(), {
projectTrusted: ctx.isProjectTrusted(),
});
return resolvePiDefaultCompactionEnabled(
manager.getGlobalSettings(),
manager.getProjectSettings(),
);
}
/** One-time ui.notify when Pi default compaction and pi-auto-compact are both active (SPEC §16.7). */
export function maybeNotifyCompactionCoexistenceHint(
ctx: ExtensionContext,
runtime: RuntimeFlags,
piDefaultCompactionEnabled = isPiDefaultCompactionEnabled(ctx),
): void {
if (runtime.compactionCoexistenceHintShown || !runtime.autoCompactDetected) {
return;
}
if (!piDefaultCompactionEnabled) {
return;
}
if (ctx.hasUI) {
ctx.ui.notify(
"Pi built-in auto-compaction and pi-auto-compact are both enabled. Consider setting compaction.enabled to false in settings.json.",
"info",
);
}
runtime.compactionCoexistenceHintShown = true;
}
+3
View File
@@ -31,6 +31,8 @@ export interface ExtensionState {
export interface RuntimeFlags {
autoCompactDetected: boolean;
autoCompactIntegration: AutoCompactIntegration;
/** One-time hint when Pi default compaction coexists with pi-auto-compact (SPEC §16.7). */
compactionCoexistenceHintShown: boolean;
}
export const STATE_ENTRY_TYPE = "skill-reinject:state";
@@ -84,6 +86,7 @@ export function createRuntimeFlags(): RuntimeFlags {
return {
autoCompactDetected: false,
autoCompactIntegration: "auto",
compactionCoexistenceHintShown: false,
};
}
+53
View File
@@ -2,8 +2,10 @@ import { describe, expect, it } from "vitest";
import {
detectAndCachePiAutoCompact,
detectPiAutoCompact,
maybeNotifyCompactionCoexistenceHint,
PI_AUTO_COMPACT_FOLLOW_UP_PREFIXES,
resolveDeliveryMode,
resolvePiDefaultCompactionEnabled,
} from "../src/auto-compact";
import { createDefaultSettings } from "../src/settings";
import { createRuntimeFlags } from "../src/state";
@@ -53,3 +55,54 @@ describe("PI_AUTO_COMPACT_FOLLOW_UP_PREFIXES", () => {
]);
});
});
describe("resolvePiDefaultCompactionEnabled", () => {
it("defaults to enabled when compaction settings are absent", () => {
expect(resolvePiDefaultCompactionEnabled({}, {})).toBe(true);
});
it("lets project override global compaction.enabled", () => {
expect(
resolvePiDefaultCompactionEnabled(
{ compaction: { enabled: true } },
{ compaction: { enabled: false } },
),
).toBe(false);
});
});
describe("maybeNotifyCompactionCoexistenceHint", () => {
it("notifies once when pi-auto-compact and Pi compaction are both active", () => {
const runtime = createRuntimeFlags();
runtime.autoCompactDetected = true;
const notifications: string[] = [];
const ctx = {
hasUI: true,
ui: {
notify: (message: string) => {
notifications.push(message);
},
},
};
maybeNotifyCompactionCoexistenceHint(ctx as never, runtime, true);
maybeNotifyCompactionCoexistenceHint(ctx as never, runtime, true);
expect(notifications).toHaveLength(1);
expect(runtime.compactionCoexistenceHintShown).toBe(true);
});
it("skips notify when pi-auto-compact is not detected", () => {
const runtime = createRuntimeFlags();
const notifications: string[] = [];
const ctx = {
hasUI: true,
ui: { notify: (message: string) => notifications.push(message) },
};
maybeNotifyCompactionCoexistenceHint(ctx as never, runtime, true);
expect(notifications).toHaveLength(0);
expect(runtime.compactionCoexistenceHintShown).toBe(false);
});
});