feat(coding-agent): added async compaction and in-place handoff
- Added compaction.asyncEnabled (Async Compaction, default on): when context enters the pre-threshold band [threshold - lead, threshold) with lead = clamp(threshold * 0.125, 8192, 32000), maintenance speculatively summarizes in the background off a branch snapshot (first configured LLM-backed method: remote, handoff, or soft) using a side session id isolated from the live turn. Crossing the threshold splices the armed result in instantly instead of blocking on a summarization round-trip. Armed results are invalidated by branch changes, reset boundaries, model switches that strand provider-native replay payloads, and context growth past keepRecentTokens (which re-speculates); extensions registering session_before_compact keep exact blocking semantics (speculation disabled). - Reworked handoff to commit in place: /handoff and the auto handoff method now write the generated document as a regular compaction entry on the current session (summary = document + <files> tag, cut from prepareCompaction) instead of starting a new session. SessionHandoff shrank to a document generator; session_before_switch/session_switch no longer fire with reason "handoff"; mid-turn maintenance no longer suppresses the handoff preference; overflow recovery can apply an armed handoff result. - Extracted the shared auto-compaction commit tail (#commitAutoCompactionResult / #commitCompactionEntry) used by the blocking production path, the armed speculative apply, manual compaction, and manual handoff. - Status line pulses the auto-compact icon while a speculation runs and holds it in accent once a result is armed. - Exported remotePreserveReusable from pi-agent-core/compaction for apply-time validation of speculative remote results.
This commit is contained in:
@@ -324,6 +324,9 @@ export class StatusLineComponent implements Component {
|
||||
#onBranchChange: (() => void) | null = null;
|
||||
#disposed = false;
|
||||
#autoCompactEnabled: boolean = true;
|
||||
/** Pulse timer for the running-speculation indicator; live only while speculation runs. */
|
||||
#speculationBlinkTimer: NodeJS.Timeout | undefined;
|
||||
#speculationBlinkOn = true;
|
||||
#hookStatuses: Map<string, string> = new Map();
|
||||
#subagentCount: number = 0;
|
||||
/**
|
||||
@@ -694,12 +697,38 @@ export class StatusLineComponent implements Component {
|
||||
this.#branchResolveActive = undefined;
|
||||
this.#resetJjRequests();
|
||||
this.#onBranchChange = null;
|
||||
this.#stopSpeculationBlink();
|
||||
this.#clearUsageStartTimer();
|
||||
this.#onCodexResetFireworks = undefined;
|
||||
this.#codexResetSnapshots.clear();
|
||||
this.#retireGitWatcher();
|
||||
}
|
||||
|
||||
/**
|
||||
* Drive the context segment's pulse while a background speculative
|
||||
* compaction runs: a slow toggle that invalidates and repaints through the
|
||||
* same host callback async git/PR resolves use. Stops (and resets phase)
|
||||
* the first render after speculation leaves the running state.
|
||||
*/
|
||||
#syncSpeculationBlink(state: "idle" | "running" | "armed"): void {
|
||||
if (state === "running" && !this.#disposed) {
|
||||
this.#speculationBlinkTimer ??= setInterval(() => {
|
||||
this.#speculationBlinkOn = !this.#speculationBlinkOn;
|
||||
this.invalidate();
|
||||
this.#onBranchChange?.();
|
||||
}, 600);
|
||||
return;
|
||||
}
|
||||
this.#stopSpeculationBlink();
|
||||
}
|
||||
|
||||
#stopSpeculationBlink(): void {
|
||||
if (!this.#speculationBlinkTimer) return;
|
||||
clearInterval(this.#speculationBlinkTimer);
|
||||
this.#speculationBlinkTimer = undefined;
|
||||
this.#speculationBlinkOn = true;
|
||||
}
|
||||
|
||||
#clearUsageStartTimer(): void {
|
||||
if (!this.#usageStartTimer) return;
|
||||
clearTimeout(this.#usageStartTimer);
|
||||
@@ -1599,6 +1628,8 @@ export class StatusLineComponent implements Component {
|
||||
this.#getGitStatus(activeRepoCache.effectiveGitCwd))
|
||||
: null;
|
||||
const gitPr = includePr ? this.#lookupPr(activeRepoCache.effectiveGitCwd) : null;
|
||||
const compactionSpeculation = this.session.compactionSpeculation ?? "idle";
|
||||
this.#syncSpeculationBlink(compactionSpeculation);
|
||||
return {
|
||||
session: this.session,
|
||||
focusedAgentId: this.#focusedAgentId,
|
||||
@@ -1621,6 +1652,8 @@ export class StatusLineComponent implements Component {
|
||||
contextTokens,
|
||||
contextWindow,
|
||||
autoCompactEnabled: this.#autoCompactEnabled,
|
||||
compactionSpeculation,
|
||||
speculationBlinkOn: this.#speculationBlinkOn,
|
||||
subagentCount: this.#subagentCount,
|
||||
activeMs: this.getActiveMs(),
|
||||
git: {
|
||||
|
||||
Reference in New Issue
Block a user