From 27cd59201dc60cf31d890637d8923d56ba5e1faf Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 30 May 2026 05:26:39 -0700 Subject: [PATCH] fix: avoid daemon router reconcile spread crash --- src/main/daemon/daemon-pty-router.test.ts | 26 +++++++++++++++++++++++ src/main/daemon/daemon-pty-router.ts | 11 ++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/main/daemon/daemon-pty-router.test.ts b/src/main/daemon/daemon-pty-router.test.ts index 725a14c38d5..4c4d0ac0dfd 100644 --- a/src/main/daemon/daemon-pty-router.test.ts +++ b/src/main/daemon/daemon-pty-router.test.ts @@ -158,6 +158,32 @@ describe('DaemonPtyRouter', () => { expect(current.write).toHaveBeenCalledWith('current-alive', 'new\n') }) + it('merges very large startup reconciliation results without dropping routes', async () => { + const currentAlive = Array.from({ length: 130_000 }, (_, index) => `current-alive-${index}`) + const legacyKilled = Array.from({ length: 130_000 }, (_, index) => `legacy-killed-${index}`) + const current = createAdapter('current', [], { + alive: currentAlive, + killed: [] + }) + const legacy = createAdapter('legacy', [], { + alive: ['legacy-alive'], + killed: legacyKilled + }) + const router = new DaemonPtyRouter({ current, legacy: [legacy] }) + + const result = await router.reconcileOnStartup(new Set(['wt'])) + router.write(currentAlive.at(-1)!, 'new\n') + router.write('legacy-alive', 'old\n') + + expect(result.alive).toHaveLength(currentAlive.length + 1) + expect(result.alive[0]).toBe(currentAlive[0]) + expect(result.alive.at(-1)).toBe('legacy-alive') + expect(result.killed).toHaveLength(legacyKilled.length) + expect(result.killed.at(-1)).toBe(legacyKilled.at(-1)) + expect(current.write).toHaveBeenCalledWith(currentAlive.at(-1), 'new\n') + expect(legacy.write).toHaveBeenCalledWith('legacy-alive', 'old\n') + }) + it('disposes current and legacy adapters', () => { const current = createAdapter('current') const legacy = createAdapter('legacy') diff --git a/src/main/daemon/daemon-pty-router.ts b/src/main/daemon/daemon-pty-router.ts index e1668457b29..a0ec4cd51b3 100644 --- a/src/main/daemon/daemon-pty-router.ts +++ b/src/main/daemon/daemon-pty-router.ts @@ -1,6 +1,13 @@ import type { DaemonPtyAdapter } from './daemon-pty-adapter' import type { IPtyProvider, PtySpawnOptions, PtySpawnResult } from '../providers/types' +function appendSessionIds(target: string[], source: readonly string[]): void { + // Why: daemon restore can report enough sessions to exceed V8's spread-argument cap. + for (const id of source) { + target.push(id) + } +} + export class DaemonPtyRouter implements IPtyProvider { private current: DaemonPtyAdapter private legacy: DaemonPtyAdapter[] @@ -175,8 +182,8 @@ export class DaemonPtyRouter implements IPtyProvider { const killed: string[] = [] for (const adapter of this.allAdapters()) { const result = await adapter.reconcileOnStartup(validWorktreeIds) - alive.push(...result.alive) - killed.push(...result.killed) + appendSessionIds(alive, result.alive) + appendSessionIds(killed, result.killed) for (const id of result.alive) { this.sessionAdapters.set(id, adapter) }