From f1b866e8545fe87f961f25ed37a2890d10222605 Mon Sep 17 00:00:00 2001 From: spiceoogway Date: Thu, 29 Jan 2026 23:39:31 -0500 Subject: [PATCH] Fix #4355: Make session write lock timeout configurable - Increase default timeout from 10s to 60s to prevent premature termination when multiple subagents compete for session file locks - Add sessionWriteLockTimeoutMs config option to agent defaults - Update call sites to use config value when available - Maintain backwards compatibility for explicit timeoutMs callers This fixes the issue where subagents would terminate prematurely with 'terminated' error when session write lock timeout (10s) was exceeded during concurrent subagent execution. --- pnpm-lock.yaml | 53 ++++++++++---------- src/agents/pi-embedded-runner/compact.ts | 1 + src/agents/pi-embedded-runner/run/attempt.ts | 1 + src/agents/session-write-lock.ts | 4 +- src/config/zod-schema.agent-defaults.ts | 1 + 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7d3c6776..5992513ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -172,6 +172,13 @@ importers: zod: specifier: ^4.3.6 version: 4.3.6 + optionalDependencies: + '@napi-rs/canvas': + specifier: ^0.1.88 + version: 0.1.88 + node-llama-cpp: + specifier: 3.15.0 + version: 3.15.0(typescript@5.9.3) devDependencies: '@grammyjs/types': specifier: ^3.23.0 @@ -254,13 +261,6 @@ importers: wireit: specifier: ^0.14.12 version: 0.14.12 - optionalDependencies: - '@napi-rs/canvas': - specifier: ^0.1.88 - version: 0.1.88 - node-llama-cpp: - specifier: 3.15.0 - version: 3.15.0(typescript@5.9.3) extensions/bluebubbles: {} @@ -314,17 +314,17 @@ importers: specifier: ^10.5.0 version: 10.5.0 devDependencies: - moltbot: + openclaw: specifier: workspace:* - version: link:../../packages/moltbot + version: link:../.. extensions/imessage: {} extensions/line: devDependencies: - moltbot: + openclaw: specifier: workspace:* - version: link:../../packages/moltbot + version: link:../.. extensions/llm-task: {} @@ -348,17 +348,17 @@ importers: specifier: ^4.3.6 version: 4.3.6 devDependencies: - moltbot: + openclaw: specifier: workspace:* - version: link:../../packages/moltbot + version: link:../.. extensions/mattermost: {} extensions/memory-core: devDependencies: - moltbot: + openclaw: specifier: workspace:* - version: link:../../packages/moltbot + version: link:../.. extensions/memory-lancedb: dependencies: @@ -386,9 +386,9 @@ importers: express: specifier: ^5.2.1 version: 5.2.1 - moltbot: + openclaw: specifier: workspace:* - version: link:../../packages/moltbot + version: link:../.. proper-lockfile: specifier: ^4.1.2 version: 4.1.2 @@ -397,12 +397,12 @@ importers: extensions/nostr: dependencies: - moltbot: - specifier: workspace:* - version: link:../../packages/moltbot nostr-tools: specifier: ^2.20.0 version: 2.20.0(typescript@5.9.3) + openclaw: + specifier: workspace:* + version: link:../.. zod: specifier: ^4.3.6 version: 4.3.6 @@ -439,9 +439,9 @@ importers: specifier: ^4.3.5 version: 4.3.6 devDependencies: - moltbot: + openclaw: specifier: workspace:* - version: link:../../packages/moltbot + version: link:../.. extensions/voice-call: dependencies: @@ -459,9 +459,9 @@ importers: extensions/zalo: dependencies: - moltbot: + openclaw: specifier: workspace:* - version: link:../../packages/moltbot + version: link:../.. undici: specifier: 7.19.0 version: 7.19.0 @@ -471,9 +471,9 @@ importers: '@sinclair/typebox': specifier: 0.34.47 version: 0.34.47 - moltbot: + openclaw: specifier: workspace:* - version: link:../../packages/moltbot + version: link:../.. packages/clawdbot: dependencies: @@ -1328,7 +1328,6 @@ packages: '@lancedb/lancedb@0.23.0': resolution: {integrity: sha512-aYrIoEG24AC+wILCL57Ius/Y4yU+xFHDPKLvmjzzN4byAjzeIGF0TC86S5RBt4Ji+dxS7yIWV5Q/gE5/fybIFQ==} engines: {node: '>= 18'} - cpu: [x64, arm64] os: [darwin, linux, win32] peerDependencies: apache-arrow: '>=15.0.0 <=18.1.0' diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index 2dc4c5325..cf1191d2c 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -351,6 +351,7 @@ export async function compactEmbeddedPiSessionDirect( const sessionLock = await acquireSessionWriteLock({ sessionFile: params.sessionFile, + timeoutMs: params.config?.agents?.defaults?.sessionWriteLockTimeoutMs, }); try { await prewarmSessionFile(params.sessionFile); diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index e83c3ae4a..dae26bb36 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -386,6 +386,7 @@ export async function runEmbeddedAttempt( const sessionLock = await acquireSessionWriteLock({ sessionFile: params.sessionFile, + timeoutMs: params.config?.agents?.defaults?.sessionWriteLockTimeoutMs, }); let sessionManager: ReturnType | undefined; diff --git a/src/agents/session-write-lock.ts b/src/agents/session-write-lock.ts index 82a2428da..24c88f5da 100644 --- a/src/agents/session-write-lock.ts +++ b/src/agents/session-write-lock.ts @@ -107,7 +107,9 @@ export async function acquireSessionWriteLock(params: { release: () => Promise; }> { registerCleanupHandlers(); - const timeoutMs = params.timeoutMs ?? 10_000; + // Default timeout increased from 10s to 60s to prevent premature termination + // when multiple subagents compete for session file locks (see issue #4355) + const timeoutMs = params.timeoutMs ?? 60_000; const staleMs = params.staleMs ?? 30 * 60 * 1000; const sessionFile = path.resolve(params.sessionFile); const sessionDir = path.dirname(sessionFile); diff --git a/src/config/zod-schema.agent-defaults.ts b/src/config/zod-schema.agent-defaults.ts index a849078ed..906719132 100644 --- a/src/config/zod-schema.agent-defaults.ts +++ b/src/config/zod-schema.agent-defaults.ts @@ -123,6 +123,7 @@ export const AgentDefaultsSchema = z blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(), humanDelay: HumanDelaySchema.optional(), timeoutSeconds: z.number().int().positive().optional(), + sessionWriteLockTimeoutMs: z.number().int().positive().optional(), mediaMaxMb: z.number().positive().optional(), typingIntervalSeconds: z.number().int().positive().optional(), typingMode: z