(directory, sessionID, job, client)
| 1100 | } |
| 1101 | if (delay <= 0) await maybeRunDueJobs(directory, client, sessionID) |
| 1102 | else await scheduleDueWork(directory, client, sessionID) |
| 1103 | }) |
| 1104 | .catch((error) => appendLoopLog(directory, "watchdog-error", { sessionID, error: sdkErrorMessage(error) }).catch(() => {})) |
| 1105 | }, Math.max(1_000, BUSY_RETRY_MS)) |
| 1106 | // Keep this interval referenced. In current OpenCode TUI builds, plugin hooks |
| 1107 | // are event-driven; a referenced watchdog helps scheduled loops wake up even |
| 1108 | // when no manual /loop-status command is typed. |
| 1109 | watchdogTimers.set(sessionID, timer) |
| 1110 | } |
| 1111 | |
| 1112 | function stopWatchdog(sessionID) { |
| 1113 | const timer = watchdogTimers.get(sessionID) |
| 1114 | if (timer) clearInterval(timer) |
| 1115 | watchdogTimers.delete(sessionID) |
| 1116 | } |
| 1117 | |
| 1118 | async function scheduleDueWork(directory, client, sessionID, minDelayMs = 0) { |
| 1119 | const previous = dueTimers.get(sessionID) |
| 1120 | if (previous) clearTimeout(previous) |
| 1121 | |
| 1122 | const state = await readState(directory, sessionID) |
| 1123 | const delay = nextDueDelay(state) |
| 1124 | if (!Number.isFinite(delay)) { |
| 1125 | dueTimers.delete(sessionID) |
| 1126 | return |
| 1127 | } |
| 1128 | |
| 1129 | const wait = Math.min(Math.max(delay, minDelayMs, MIN_DUE_TIMER_MS), MAX_DUE_TIMER_MS) |
| 1130 | const timer = setTimeout(() => { |
| 1131 | dueTimers.delete(sessionID) |
| 1132 | Promise.resolve() |
| 1133 | .then(async () => { |
| 1134 | if (!await sessionIsIdle(client, sessionID, directory)) { |
| 1135 | await scheduleDueWork(directory, client, sessionID, BUSY_RETRY_MS) |
| 1136 | return |
| 1137 | } |
| 1138 | await finalizeActiveRun(directory, client, sessionID) |
| 1139 | if (!await sessionIsIdle(client, sessionID, directory)) { |
no test coverage detected