Progress lived only in the open SSE stream, and interrupted history was shown as completed so the card could not be continued. Co-authored-by: Cursor <cursoragent@cursor.com>
41 lines
1.3 KiB
TypeScript
41 lines
1.3 KiB
TypeScript
/**
|
|
* 长任务断线自动重试:用显式 reconnectRunId 跨 attempt 保留,
|
|
* 禁止依赖 setState updater / 已被清空的 stream.runId。
|
|
*/
|
|
|
|
export function resolveReconnectRunId(input: {
|
|
attemptIndex: number;
|
|
optsReconnectRunId?: string | null;
|
|
}): string | undefined {
|
|
const fromOpts = input.optsReconnectRunId?.trim();
|
|
if (fromOpts) return fromOpts;
|
|
// attempt 0 且无显式 id → 允许新 POST;重试必须靠 opts 传入
|
|
return undefined;
|
|
}
|
|
|
|
export function shouldSubscribeExistingRun(status: string | null | undefined): boolean {
|
|
return status === 'running' || status === 'pending';
|
|
}
|
|
|
|
export function isRemoteTaskTerminal(status: string | null | undefined): boolean {
|
|
return (
|
|
status === 'completed' ||
|
|
status === 'pending_author_confirmation' ||
|
|
status === 'quality_failed' ||
|
|
status === 'blocked_pending_governance' ||
|
|
status === 'interrupted'
|
|
);
|
|
}
|
|
|
|
/** 自动重试调度时把已知 runId 写入下一轮 opts,避免 abort 后丢失。 */
|
|
export function nextAttemptOpts<T extends { reconnectRunId?: string }>(
|
|
opts: T | undefined,
|
|
retainedRunId: string | undefined,
|
|
): T & { reconnectRunId?: string } {
|
|
const base = { ...(opts || ({} as T)) };
|
|
if (retainedRunId) {
|
|
return { ...base, reconnectRunId: retainedRunId };
|
|
}
|
|
return base;
|
|
}
|