fix(queue): harden drain/abort/timeout race handling

- reject new lane enqueues once gateway drain begins
- always reset lane draining state and isolate onWait callback failures
- persist per-session abort cutoff and skip stale queued messages
- avoid false 600s agentTurn timeout in isolated cron jobs

Fixes #27407
Fixes #27332
Fixes #27427

Co-authored-by: Kevin Shenghui <shenghuikevin@github.com>
Co-authored-by: zjmy <zhangjunmengyang@gmail.com>
Co-authored-by: suko <miha.sukic@gmail.com>
This commit is contained in:
Peter Steinberger
2026-02-26 13:43:30 +01:00
parent 1aef45bc06
commit c397a02c9a
13 changed files with 551 additions and 42 deletions

View File

@@ -19,6 +19,7 @@ import { normalizeUsageDisplay, resolveResponseUsageMode } from "../thinking.js"
import {
formatAbortReplyText,
isAbortTrigger,
resolveAbortCutoffFromContext,
resolveSessionEntryForKey,
setAbortMemory,
stopSubagentsForRequester,
@@ -99,6 +100,7 @@ async function applyAbortTarget(params: {
sessionStore?: Record<string, SessionEntry>;
storePath?: string;
abortKey?: string;
abortCutoff?: { messageSid?: string; timestamp?: number };
}) {
const { abortTarget } = params;
if (abortTarget.sessionId) {
@@ -106,11 +108,19 @@ async function applyAbortTarget(params: {
}
if (abortTarget.entry && params.sessionStore && abortTarget.key) {
abortTarget.entry.abortedLastRun = true;
abortTarget.entry.abortCutoffMessageSid = params.abortCutoff?.messageSid;
abortTarget.entry.abortCutoffTimestamp = params.abortCutoff?.timestamp;
abortTarget.entry.updatedAt = Date.now();
params.sessionStore[abortTarget.key] = abortTarget.entry;
if (params.storePath) {
await updateSessionStore(params.storePath, (store) => {
store[abortTarget.key] = abortTarget.entry;
store[abortTarget.key] = {
...abortTarget.entry,
abortedLastRun: true,
abortCutoffMessageSid: params.abortCutoff?.messageSid,
abortCutoffTimestamp: params.abortCutoff?.timestamp,
updatedAt: Date.now(),
};
});
}
} else if (params.abortKey) {
@@ -503,6 +513,12 @@ export const handleStopCommand: CommandHandler = async (params, allowTextCommand
sessionStore: params.sessionStore,
storePath: params.storePath,
abortKey: params.command.abortKey,
abortCutoff:
params.sessionKey?.trim() &&
abortTarget.key?.trim() &&
params.sessionKey.trim() === abortTarget.key.trim()
? resolveAbortCutoffFromContext(params.ctx)
: undefined,
});
// Trigger internal hook for stop command
@@ -545,6 +561,12 @@ export const handleAbortTrigger: CommandHandler = async (params, allowTextComman
sessionStore: params.sessionStore,
storePath: params.storePath,
abortKey: params.command.abortKey,
abortCutoff:
params.sessionKey?.trim() &&
abortTarget.key?.trim() &&
params.sessionKey.trim() === abortTarget.key.trim()
? resolveAbortCutoffFromContext(params.ctx)
: undefined,
});
return { shouldContinue: false, reply: { text: "⚙️ Agent was aborted." } };
};