/** * 长任务断线自动重试:用显式 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( opts: T | undefined, retainedRunId: string | undefined, ): T & { reconnectRunId?: string } { const base = { ...(opts || ({} as T)) }; if (retainedRunId) { return { ...base, reconnectRunId: retainedRunId }; } return base; }