| 221 | // retry its in-flight job once; a job that keeps crashing workers fails |
| 222 | // gracefully so it can't loop the pool forever. |
| 223 | private onWorkerGone(w: PoolWorker): void { |
| 224 | if (!this.workers.has(w)) return; // already handled (error+exit both fire) |
| 225 | this.workers.delete(w); |
| 226 | this.pendingWorkers.delete(w); |
| 227 | this.idle = this.idle.filter((x) => x !== w); |
| 228 | this.totalCrashes++; |
| 229 | const job = this.inflight.get(w); |
| 230 | this.inflight.delete(w); |
| 231 | try { void w.terminate(); } catch { /* already gone */ } |
| 232 | if (this.healthy) this.spawnOne(); // keep capacity |
| 233 | if (job) { |
| 234 | if (job.retries < this.maxRetries && this.healthy) { |
| 235 | job.retries++; |
| 236 | this.queue.unshift(job); // head of line — retry promptly |
| 237 | } else { |
| 238 | this.settle(job, { isError: true, content: [{ type: 'text', text: 'codegraph worker crashed; please retry the call.' }] }); |
| 239 | } |
| 240 | } |
| 241 | this.drain(); |
| 242 | } |
| 243 | |
| 244 | private drain(): void { |
| 245 | // Grow toward maxSize while queued work outstrips workers that are idle OR |