Skip to content

Commit 17b3165

Browse files
committed
fix: skip timeout compaction when run was aborted by user (5.9 fork)
When the user presses the stop button in the Control UI, the model request times out. Previously, if context token usage exceeded 65%, the timeout compaction mechanism would fire even though the run was intentionally aborted — causing unnecessary compaction at an inappropriate time. Add !externalAbort check to the timeout compaction condition so that user-initiated stops skip compaction entirely. Unlike the original PR #80746 which used !aborted (incorrectly skipping internal timeout compaction too), this fix uses !externalAbort which is only set for user-initiated stops via the abort signal handler. Also updates the existing test to explicitly set externalAbort: false for the internal timeout case, and adds a new test for the user abort case. CHANGELOG: Added to Unreleased section.
1 parent 9d5fe1d commit 17b3165

3 files changed

Lines changed: 29 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
Docs: https://docs.openclaw.ai
44

5+
## Unreleased
6+
7+
### Fixes
8+
9+
- Agents/compaction: skip timeout-triggered compaction when the run was aborted by the user (Stop button). Previously, user-initiated aborts would still fire timeout compaction if context usage exceeded 65%, causing unnecessary compaction at an inappropriate time. Uses `externalAbort` (set only for user-initiated stops) rather than `aborted` (also set for internal timeouts) to correctly distinguish user aborts from provider timeouts.
10+
511
## 2026.5.9
612

713
### Changes

src/agents/pi-embedded-runner/run.timeout-triggered-compaction.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ describe("timeout-triggered compaction", () => {
308308
makeAttemptResult({
309309
timedOut: true,
310310
aborted: true,
311+
externalAbort: false,
311312
lastAssistant: {
312313
usage: { input: 180000 },
313314
} as never,
@@ -329,6 +330,26 @@ describe("timeout-triggered compaction", () => {
329330
expect(result.meta.error).toBeUndefined();
330331
});
331332

333+
it("does not attempt compaction when user aborted (externalAbort=true)", async () => {
334+
mockedRunEmbeddedAttempt.mockResolvedValueOnce(
335+
makeAttemptResult({
336+
timedOut: true,
337+
aborted: true,
338+
externalAbort: true,
339+
lastAssistant: {
340+
usage: { input: 180000 },
341+
} as never,
342+
}),
343+
);
344+
345+
const result = await runEmbeddedPiAgent(overflowBaseRunParams);
346+
347+
// User-initiated abort should skip timeout compaction entirely
348+
expect(mockedCompactDirect).not.toHaveBeenCalled();
349+
expect(result.payloads?.[0]?.isError).toBe(true);
350+
expect(result.payloads?.[0]?.text).toContain("timed out");
351+
});
352+
332353
it("does not attempt compaction when timedOutDuringCompaction is true", async () => {
333354
mockedRunEmbeddedAttempt.mockResolvedValueOnce(
334355
makeAttemptResult({

src/agents/pi-embedded-runner/run.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,7 +1447,7 @@ export async function runEmbeddedPiAgent(
14471447
// ── Timeout-triggered compaction ──────────────────────────────────
14481448
// When the LLM times out with high context usage, compact before
14491449
// retrying to break the death spiral of repeated timeouts.
1450-
if (timedOut && !timedOutDuringCompaction && !timedOutDuringToolExecution) {
1450+
if (timedOut && !timedOutDuringCompaction && !timedOutDuringToolExecution && !externalAbort) {
14511451
// Only consider prompt-side tokens here. API totals include output
14521452
// tokens, which can make a long generation look like high context
14531453
// pressure even when the prompt itself was small.
@@ -2373,7 +2373,7 @@ export async function runEmbeddedPiAgent(
23732373
toolAudioAsVoice: attempt.toolAudioAsVoice,
23742374
});
23752375
const timedOutDuringPrompt =
2376-
timedOut && !timedOutDuringCompaction && !timedOutDuringToolExecution;
2376+
timedOut && !timedOutDuringCompaction && !timedOutDuringToolExecution && !externalAbort;
23772377
const hasPartialAssistantTextAfterPromptTimeout =
23782378
timedOutDuringPrompt &&
23792379
(attempt.assistantTexts ?? []).some((text) => text.trim().length > 0) &&

0 commit comments

Comments
 (0)