Allow agent to run in background after browser refresh

This commit is contained in:
2026-06-15 12:47:32 -07:00
parent 041aef63e2
commit 962114d1d3

View File

@@ -905,13 +905,11 @@ export async function POST(request: Request) {
process.env.VERCEL_URL || process.env.VERCEL_URL ||
"https://vibnai.com"; "https://vibnai.com";
// Honor client-side abort (Stop button). When the user clicks Stop // We no longer honor client-side abort (Stop button / refresh) to cancel the AI.
// the browser's AbortController fires `request.signal.aborted` and // We use a dummy AbortController so that the backend can still be cancelled internally if needed,
// the fetch stream is closed; we use it as a polite checkpoint // but the browser refresh / disconnect will not stop the agent's work.
// between rounds and tool calls so we (a) don't keep paying Gemini const internalAbortController = new AbortController();
// for tokens the user no longer wants and (b) persist whatever the const clientSignal = internalAbortController.signal;
// assistant produced before the cancel.
const clientSignal = request.signal;
// Stream response // Stream response
const encoder = new TextEncoder(); const encoder = new TextEncoder();
@@ -1188,11 +1186,18 @@ export async function POST(request: Request) {
const flushTimeline = () => { const flushTimeline = () => {
if (!currentTimelineKind) return; if (!currentTimelineKind) return;
if (currentTimelineKind === "thought") { if (currentTimelineKind === "thought") {
assistantTimeline.push({ kind: "thought", text: currentTimelineText }); assistantTimeline.push({
kind: "thought",
text: currentTimelineText,
});
} else if (currentTimelineKind === "text") { } else if (currentTimelineKind === "text") {
assistantText += (assistantText ? "\n\n" : "") + currentTimelineText; assistantText +=
(assistantText ? "\n\n" : "") + currentTimelineText;
assistantTextSegments.push(currentTimelineText); assistantTextSegments.push(currentTimelineText);
assistantTimeline.push({ kind: "text", text: currentTimelineText }); assistantTimeline.push({
kind: "text",
text: currentTimelineText,
});
} }
currentTimelineKind = null; currentTimelineKind = null;
currentTimelineText = ""; currentTimelineText = "";
@@ -1947,9 +1952,11 @@ export async function POST(request: Request) {
} }
}, },
cancel() { cancel() {
// Browser disconnected (tab closed, navigated away). Clear the // Browser disconnected (tab closed, navigated away, refreshed).
// heartbeat so we stop writing to a closed stream. // We do NOT call internalAbortController.abort() here because we want
// The abort handler above already flipped the flag so the loop bails. // the agent to continue its work in the background!
// We just clear the heartbeat to stop writing to the closed stream.
clearInterval(heartbeat);
}, },
}); });