From 381adfd925a80006b915d4b877b9e12df1e558bc Mon Sep 17 00:00:00 2001 From: Alan Wizemann Date: Fri, 1 May 2026 15:40:33 +0200 Subject: [PATCH] =?UTF-8?q?fix(acp):=20bump=20control-message=20timeout=20?= =?UTF-8?q?30s=E2=86=9260s=20for=20db-contended=20hosts=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Field-reported (#61): under realistic concurrency where the Hermes gateway is also running, state.db lock contention (Discord sync / skill registration / cron scheduling all holding write locks) stalls ACP's `initialize` / `session/new` / `session/load` past the previous 30s watchdog, surfacing as "Starting…" indefinitely or an opaque timeout error. SQLite contention on a healthy host clears in seconds, so 60s gives the lock-resolution path room to breathe while still surfacing genuinely broken transports promptly. `session/prompt` remains untimed (it streams events and can run for minutes). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../ScarfCore/Sources/ScarfCore/ACP/ACPClient.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scarf/Packages/ScarfCore/Sources/ScarfCore/ACP/ACPClient.swift b/scarf/Packages/ScarfCore/Sources/ScarfCore/ACP/ACPClient.swift index ba146d0..e865b86 100644 --- a/scarf/Packages/ScarfCore/Sources/ScarfCore/ACP/ACPClient.swift +++ b/scarf/Packages/ScarfCore/Sources/ScarfCore/ACP/ACPClient.swift @@ -362,10 +362,17 @@ public actor ACPClient { #endif // session/prompt streams events and can run for minutes — no hard - // timeout. Control messages get a 30s watchdog. + // timeout. Control messages get a 60s watchdog. Older versions + // capped at 30s, which the field reported (#61) was tripping + // under realistic gateway+ACP concurrency: the gateway holds + // state.db locks for Discord sync / skill registration / cron + // scheduling, and ACP's `initialize` / `session/new` / + // `session/load` stall waiting for the lock. SQLite contention + // on a healthy host clears in seconds; 60s gives that headroom + // while still surfacing genuinely broken transports promptly. let timeoutTask: Task? = if method != "session/prompt" { Task { [weak self] in - try await Task.sleep(nanoseconds: 30 * 1_000_000_000) + try await Task.sleep(nanoseconds: 60 * 1_000_000_000) await self?.timeoutRequest(id: requestId, method: method) } } else {