Description
sdk/dag-task-runner (and the copied skill at .cursor/skills/dag-task-runner) wraps run.stream() with withTimeout(..., streamIdleTimeoutMs). The default idle window is 5 minutes. If no stream event arrives in that window, the task is marked ERROR as a stalled model.
Headless @cursor/sdk local runs keep enableAgentRetries: true by default. When the transport stalls, the SDK retries internally and emits nothing on the public stream. The runner cannot tell model silence from that reconnect, so it cancels a live retry and the canvas shows a dead task.
The same guard exists in both copies of run_dag.ts:
https://github.com/cursor/cookbook/blob/main/sdk/dag-task-runner/src/run_dag.ts#L363-L375
const timeoutForNext = Math.min(deadline - Date.now(), streamIdleTimeoutMs);
if (timeoutForNext <= 0) {
throw new TimeoutError(`Task ${task.id} exceeded deadline of ${formatMs(taskTimeoutMs)}`);
}
const next = await withTimeout(
iterator.next(),
timeoutForNext,
streamWaitTimeoutMessage({
taskId: task.id,
timeoutMs: timeoutForNext,
streamIdleTimeoutMs,
}),
);
Expected
While the SDK is reconnecting, the idle clock should pause. Idle should fire only on real model silence.
Suggested fix
Subscribe to a reconnect/retry signal (onConnectionStateChange / RETRYING) and skip the idle timer while reconnecting. Until @cursor/sdk forwards that hook, wire the runner to the helper in sdk/reconnect-signal (or an equivalent) so transport retries are not logged as task failures.
Related
Description
sdk/dag-task-runner(and the copied skill at.cursor/skills/dag-task-runner) wrapsrun.stream()withwithTimeout(..., streamIdleTimeoutMs). The default idle window is 5 minutes. If no stream event arrives in that window, the task is markedERRORas a stalled model.Headless
@cursor/sdklocal runs keepenableAgentRetries: trueby default. When the transport stalls, the SDK retries internally and emits nothing on the public stream. The runner cannot tell model silence from that reconnect, so it cancels a live retry and the canvas shows a dead task.The same guard exists in both copies of
run_dag.ts:https://github.com/cursor/cookbook/blob/main/sdk/dag-task-runner/src/run_dag.ts#L363-L375
Expected
While the SDK is reconnecting, the idle clock should pause. Idle should fire only on real model silence.
Suggested fix
Subscribe to a reconnect/retry signal (
onConnectionStateChange/RETRYING) and skip the idle timer whilereconnecting. Until@cursor/sdkforwards that hook, wire the runner to the helper insdk/reconnect-signal(or an equivalent) so transport retries are not logged as task failures.Related