fix(gateway): harden chat abort semantics

This commit is contained in:
Peter Steinberger
2026-01-10 17:23:16 +01:00
parent 84d64f9395
commit a1533a17f7
12 changed files with 456 additions and 111 deletions

View File

@@ -110,6 +110,10 @@ import {
type ResolvedGatewayAuth,
resolveGatewayAuth,
} from "./auth.js";
import {
abortChatRunById,
type ChatAbortControllerEntry,
} from "./chat-abort.js";
import {
type GatewayReloadPlan,
type ProviderKind,
@@ -685,10 +689,7 @@ export async function startGatewayServer(
}
return sessionKey;
};
const chatAbortControllers = new Map<
string,
{ controller: AbortController; sessionId: string; sessionKey: string }
>();
const chatAbortControllers = new Map<string, ChatAbortControllerEntry>();
setCommandLaneConcurrency("cron", cfgAtStart.cron?.maxConcurrentRuns ?? 1);
setCommandLaneConcurrency(
"main",
@@ -967,6 +968,7 @@ export async function startGatewayServer(
addChatRun,
removeChatRun,
chatAbortControllers,
chatAbortedRuns: chatRunState.abortedRuns,
chatRunBuffers,
chatDeltaSentAt,
dedupe,
@@ -1192,6 +1194,31 @@ export async function startGatewayServer(
dedupe.delete(entries[i][0]);
}
}
for (const [runId, entry] of chatAbortControllers) {
if (now <= entry.expiresAtMs) continue;
abortChatRunById(
{
chatAbortControllers,
chatRunBuffers,
chatDeltaSentAt,
chatAbortedRuns: chatRunState.abortedRuns,
removeChatRun,
agentRunSeq,
broadcast,
bridgeSendToSession,
},
{ runId, sessionKey: entry.sessionKey, stopReason: "timeout" },
);
}
const ABORTED_RUN_TTL_MS = 60 * 60_000;
for (const [runId, abortedAt] of chatRunState.abortedRuns) {
if (now - abortedAt <= ABORTED_RUN_TTL_MS) continue;
chatRunState.abortedRuns.delete(runId);
chatRunBuffers.delete(runId);
chatDeltaSentAt.delete(runId);
}
}, 60_000);
const agentUnsub = onAgentEvent(
@@ -1647,6 +1674,7 @@ export async function startGatewayServer(
hasConnectedMobileNode,
agentRunSeq,
chatAbortControllers,
chatAbortedRuns: chatRunState.abortedRuns,
chatRunBuffers,
chatDeltaSentAt,
addChatRun,