Merge pull request #3663 from Tibsfox/fix/queued-user-message-skip-pause
fix(gsd): pause auto-mode on queued-user-message tool skip instead of retrying
This commit is contained in:
commit
f2c87d71ee
4 changed files with 48 additions and 6 deletions
|
|
@ -589,11 +589,14 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|||
"error",
|
||||
);
|
||||
} else if (!triggerArtifactVerified) {
|
||||
// #2883: If the artifact is missing because the tool invocation itself
|
||||
// failed (malformed/truncated JSON arguments), retrying will produce the
|
||||
// same failure. Pause auto-mode instead of entering a stuck retry loop.
|
||||
// #2883/#3595: If the artifact is missing because the tool invocation
|
||||
// failed (malformed JSON) or was skipped (queued user message), retrying
|
||||
// will produce the same failure. Pause auto-mode instead of looping.
|
||||
if (s.lastToolInvocationError) {
|
||||
const errMsg = `Tool invocation failed for ${s.currentUnit.type}: ${s.lastToolInvocationError}. Structured argument generation failed — pausing auto-mode.`;
|
||||
const isUserSkip = /queued user message/i.test(s.lastToolInvocationError);
|
||||
const errMsg = isUserSkip
|
||||
? `Tool skipped for ${s.currentUnit.type}: ${s.lastToolInvocationError}. Queued user message interrupted the turn — pausing auto-mode.`
|
||||
: `Tool invocation failed for ${s.currentUnit.type}: ${s.lastToolInvocationError}. Structured argument generation failed — pausing auto-mode.`;
|
||||
debugLog("postUnit", { phase: "tool-invocation-error-pause", unitType: s.currentUnit.type, unitId: s.currentUnit.id, error: s.lastToolInvocationError });
|
||||
ctx.ui.notify(errMsg, "error");
|
||||
s.lastToolInvocationError = null;
|
||||
|
|
|
|||
|
|
@ -102,3 +102,13 @@ export function isToolInvocationError(errorMsg: string): boolean {
|
|||
if (!errorMsg) return false;
|
||||
return TOOL_INVOCATION_ERROR_RE.test(errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the error message indicates the tool was skipped because
|
||||
* a queued user message interrupted the turn (#3595). Retrying will produce
|
||||
* the same skip, so the unit should be paused rather than retried.
|
||||
*/
|
||||
export function isQueuedUserMessageSkip(errorMsg: string): boolean {
|
||||
if (!errorMsg) return false;
|
||||
return /^Skipped due to queued user message\.?$/i.test(errorMsg.trim());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ import {
|
|||
hasInteractiveToolInFlight,
|
||||
clearInFlightTools,
|
||||
isToolInvocationError,
|
||||
isQueuedUserMessageSkip,
|
||||
} from "./auto-tool-tracking.js";
|
||||
import { closeoutUnit } from "./auto-unit-closeout.js";
|
||||
import { recoverTimedOutUnit } from "./auto-timeout-recovery.js";
|
||||
|
|
@ -397,7 +398,7 @@ export function markToolEnd(toolCallId: string): void {
|
|||
*/
|
||||
export function recordToolInvocationError(toolName: string, errorMsg: string): void {
|
||||
if (!s.active) return;
|
||||
if (isToolInvocationError(errorMsg)) {
|
||||
if (isToolInvocationError(errorMsg) || isQueuedUserMessageSkip(errorMsg)) {
|
||||
s.lastToolInvocationError = `${toolName}: ${errorMsg}`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ describe("#2883: tool invocation error tracking on AutoSession", () => {
|
|||
|
||||
// ─── isToolInvocationError classifier ────────────────────────────────────
|
||||
|
||||
import { isToolInvocationError } from "../auto-tool-tracking.ts";
|
||||
import { isToolInvocationError, isQueuedUserMessageSkip } from "../auto-tool-tracking.ts";
|
||||
|
||||
describe("#2883: isToolInvocationError classification", () => {
|
||||
test("detects JSON validation failure pattern", () => {
|
||||
|
|
@ -101,3 +101,31 @@ describe("#2883: isToolInvocationError classification", () => {
|
|||
assert.equal(isToolInvocationError("ECONNRESET"), false);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── isQueuedUserMessageSkip classifier (#3595) ─────────────────────────
|
||||
|
||||
describe("#3595: isQueuedUserMessageSkip classification", () => {
|
||||
test("detects exact skip message with period", () => {
|
||||
assert.equal(isQueuedUserMessageSkip("Skipped due to queued user message."), true);
|
||||
});
|
||||
|
||||
test("detects skip message without period", () => {
|
||||
assert.equal(isQueuedUserMessageSkip("Skipped due to queued user message"), true);
|
||||
});
|
||||
|
||||
test("detects skip message with surrounding whitespace", () => {
|
||||
assert.equal(isQueuedUserMessageSkip(" Skipped due to queued user message. "), true);
|
||||
});
|
||||
|
||||
test("returns false for normal tool errors", () => {
|
||||
assert.equal(isQueuedUserMessageSkip("Slice S01 is already complete"), false);
|
||||
});
|
||||
|
||||
test("returns false for empty string", () => {
|
||||
assert.equal(isQueuedUserMessageSkip(""), false);
|
||||
});
|
||||
|
||||
test("returns false for partial match (substring)", () => {
|
||||
assert.equal(isQueuedUserMessageSkip("Error: Skipped due to queued user message. Retry later."), false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue