Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/plugins/openclaw-ssh-plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate retries to the compaction error

This catches and retries every execSsh rejection, not just the documented exit-1/empty-stdout compaction hiccup. In cases where SSH times out or JSON parsing fails after openclaw agent has already accepted the --message, the retry sends the same OMATS event again with the same sessionId; the runner contract delivers each event once, so a later successful retry can score a transcript with duplicated room events instead of failing fast. Please retry only the exact empty-output compaction failure and rethrow other errors.

Useful? React with 👍 / 👎.

lastErr = e;
if (attempt < delays.length) {
await new Promise((r) => setTimeout(r, delays[attempt]));
}
}
}
throw lastErr;
}

async function ensureBootstrapped() {
Expand Down