From 53d373257ef2b04ad0bb376661fa49f1b3067d41 Mon Sep 17 00:00:00 2001 From: ThinkOffApp Date: Mon, 6 Jul 2026 20:07:07 +0300 Subject: [PATCH] openclaw-ssh plugin: retry through session-compaction failures Long transcripts (20+ events) deterministically kill runs: OpenClaw session compaction trips every ~10 messages, one call exits 1 with empty output, and the runner had no retry, so every Stage-6 scenario died mid-transcript (reproduced: 12-message burst fails at msg 11; the same session then answers correctly about pre-failure messages, so context survives the hiccup). callAgent now retries twice with 5s/15s backoff. Verified live: the phantom-deadlock full transcript completes through multiple compaction walls where it previously always failed. Co-Authored-By: Claude Opus 4.8 --- src/plugins/openclaw-ssh-plugin.mjs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/plugins/openclaw-ssh-plugin.mjs b/src/plugins/openclaw-ssh-plugin.mjs index ed0ce0e..6339334 100644 --- a/src/plugins/openclaw-ssh-plugin.mjs +++ b/src/plugins/openclaw-ssh-plugin.mjs @@ -120,7 +120,24 @@ export function createOpenClawSshPlugin(options) { `openclaw agent --agent ${shellEscape(agentId)} --json --session-id ${shellEscape(sessionId)} --message ${escapedMessage} --timeout ${timeoutSeconds} --thinking ${thinking} 2>/dev/null` ].join(' && '); - return execSsh(sshHost, cmd, timeoutSeconds * 1000 + 15000); + // Long transcripts (20+ events) trip OpenClaw session compaction every + // ~10 messages: one call exits 1 with empty output, then the session + // recovers WITH its context intact (verified: post-failure recall of + // pre-failure messages works). Retry with backoff rides through it; + // without this, every long scenario dies mid-transcript. + const delays = [5000, 15000]; + let lastErr; + for (let attempt = 0; attempt <= delays.length; attempt++) { + try { + return await execSsh(sshHost, cmd, timeoutSeconds * 1000 + 15000); + } catch (e) { + lastErr = e; + if (attempt < delays.length) { + await new Promise((r) => setTimeout(r, delays[attempt])); + } + } + } + throw lastErr; } async function ensureBootstrapped() {