fix(web): SSE 重连 401/404 fail-fast,单帧解析容错(R-7)

TaskRunHttpError 携带 status;非法 JSON 帧跳过并保留 seq。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-18 03:44:08 +00:00
co-authored by Cursor
parent ce7d1b8ac5
commit 21049607f4
2 changed files with 23 additions and 2 deletions
+17 -1
View File
@@ -37,6 +37,19 @@ export function sleepMs(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export class TaskRunHttpError extends Error {
status: number;
constructor(status: number) {
super(`subscribe task run failed: HTTP ${status}`);
this.name = 'TaskRunHttpError';
this.status = status;
}
}
function isNonRetryableStatus(status: number): boolean {
return status === 401 || status === 404;
}
/** 单次 SSE 读取;意外 EOF 时返回 { terminal:false, lastSeq } 供上层重连。 */
export async function readTaskRunSSEOnce(
runId: string,
@@ -51,7 +64,7 @@ export async function readTaskRunSSEOnce(
signal,
});
if (!resp.ok || !resp.body) {
throw new Error(`subscribe task run failed: HTTP ${resp.status}`);
throw new TaskRunHttpError(resp.status);
}
const reader = resp.body.getReader();
const decoder = new TextDecoder();
@@ -113,6 +126,9 @@ export async function subscribeTaskRunEvents(
// 意外 EOF:未见到 done/error,按 after_seq 重连
} catch (err) {
if (signal?.aborted) return;
if (err instanceof TaskRunHttpError && isNonRetryableStatus(err.status)) {
throw err;
}
if (attempt >= maxReconnects) throw err;
}
if (attempt >= maxReconnects) {