Self-healing OpenCode sessions · retries · model rotation · permission autopilot · quality loops
Install once. Prompt a big task. Go to sleep. Come back to completed, verified work.
- Resilience — heals transport failures, 5xx, 429, quota, truncated output, stalls, crashes, and restarts automatically
- Model rotation — switches to best healthy model when quota/rate-limit/failures persist (opt-in, never silent)
- Permission autopilot — safe-mode answers edits/web-fetches; rejects dangerous patterns (
rm -rf, disk format, registry, pipe-to-shell) automatically; answers workspace-external directory grants - Task drive — unfinished todos/checklists continue; stop-asking questions get answered; wrap-up proposes concrete follow-ups
- Quality loop — when the model declares done it reviews its own work for correctness/perf/security/robustness and implements safe improvements; when work is complete it pivots to optimization/UX/performance instead of stopping
- Self-monitoring — live title tag (
[auto-resume: 🟢 armed / 🔁 recovering / ⏸️ stopped / 🚫 paused]); sayauto-resume off/on - Self-updating — checks GitHub daily; applies new releases on restart (acknowledged via OS notification)
mkdir -p ~/.config/opencode/plugins
curl -fsSL https://raw.githubusercontent.com/neohiro/auto-resume/main/auto-resume.js \
-o ~/.config/opencode/plugins/auto-resume.jsWindows (PowerShell):
New-Item -ItemType Directory -Force "$env:USERPROFILE\.config\opencode\plugins" | Out-Null
Invoke-WebRequest "https://raw.githubusercontent.com/neohiro/auto-resume/main/auto-resume.js" `
-OutFile "$env:USERPROFILE\.config\opencode\plugins\auto-resume.js"Restart OpenCode. The plugin loads automatically — no extra dependencies.
- No double prompts — session-level error-synthesis lock (
sessionErrorSynthLock) + recent-error guard (lastErrorSynthAt) + keyed dedupe (retryErrKey) prevent duplicate resume injections when bothsession.step.failedandsession.retry.scheduledfire. - No missed failures — retry events carrying
payload.errorsynthesize recovery once ifstep.failednever arrives; boundedretryErrorSent(max 256) prevents leaks. - Encrypted sidecars — set
OPENCODE_RESUME_ENCRYPTION_KEY; all persistent markers (.stopped.json,.off.json,.paused.json,.active.json,.health.json) use AES-256-GCM. No plaintext session IDs on disk. - Bounded state everywhere —
messageCache(512),statusBySession(64),dirAskCounts(1000),sweepState(64),retryErrorSent(256),lastSeenBySession(256). No unbounded growth. - Defensive event adapter — normalizes all V2 vocabulary (
session.step.*,session.text.*,session.retry.scheduled,session.permissions, legacy names) into V1 engine shapes; drop noisy families (skill.*,plugin.*, etc.) before processing. - Quality-first directives —
AUTONOMY_DIRECTIVErequires build/test/lint verification; improvement cycles pivot to optimization/UX when scoped work completes; wrap-up demands verified proof + follow-up proposals. - Plugin registry manifest —
plugin-manifest.jsondeclaresid,version,registry,update_url,min_opencode_version, tags. Ready for marketplace integration.
| Variable | Default | Purpose |
|---|---|---|
OPENCODE_RESUME_ENABLED |
true |
Master switch |
OPENCODE_RESUME_MAX_CHAIN |
8 (balanced aggression) |
Recovery attempts per task |
OPENCODE_RESUME_AUTONOMY |
true |
Enable task driver (subs 3+4) |
OPENCODE_AUTOPILOT_PERMISSIONS |
true |
Auto-answer safe permission prompts |
OPENCODE_AUTOPILOT_PROCEED |
true |
Answer agent's own questions |
OPENCODE_AUTOPILOT_IMPROVE |
true |
Self-improvement pass after done |
OPENCODE_RESUME_ROTATION_ENABLED |
false |
Master switch for model rotation (never silent) |
OPENCODE_RESUME_ENCRYPTION_KEY |
— | AES-256-GCM encryption for sidecars |
OPENCODE_RESUME_AUTO_UPDATE |
true |
Daily self-update check |
OPENCODE_RESUME_BASE_DELAY_MS |
30 |
Base retry delay |
OPENCODE_RESUME_MAX_DELAY_MS |
500 |
Max retry delay cap |
OPENCODE_RESUME_NUDGE_DELAY_MS |
30 |
Nudge delay (continue / budget) |
OPENCODE_RESUME_RATE_LIMIT_BASE_MS |
40 |
Rate-limit base delay |
OPENCODE_RESUME_THINK_STALL_MS |
30_000 |
Think-stall detection |
OPENCODE_RESUME_REARM_MS |
30_000 |
Re-arm after clean turn |
OPENCODE_RESUME_WATCHDOG_MS |
60_000 |
Watchdog tick |
OPENCODE_RESUME_RUNNING_TOOL_FACTOR |
2 |
Tool-activity multiplier |
OPENCODE_RESUME_RETRY_TAKEOVER_MS |
1500 |
Takeover retry delay |
OPENCODE_RESUME_RETRY_FUTURE_CAP_MS |
600_000 |
Retry future cap |
OPENCODE_RESUME_REANIMATE |
true |
Restart session revival |
OPENCODE_RESUME_REANIMATE_WINDOW_MS |
86_400_000 |
Revival window |
OPENCODE_RESUME_BREAKER_THRESHOLD |
0 |
Circuit-breaker disable (0 = off) |
OPENCODE_RESUME_BREAKER_WINDOW_MS |
300_000 |
Breaker observation window |
OPENCODE_RESUME_BREAKER_COOLDOWN_MS |
60_000 |
Breaker cooldown |
OPENCODE_RESUME_COMPACT_ON_OVERFLOW |
true |
Compact history on overflow |
OPENCODE_RESUME_NOTICE_THROTTLE_MS |
30_000 |
Notice throttle |
OPENCODE_RESUME_TOAST_THROTTLE_MS |
30_000 |
Toast throttle |
OPENCODE_RESUME_SWITCH_ON_QUOTA |
true |
Rotation on quota errors |
OPENCODE_RESUME_SWITCH_ON_RATELIMIT |
true |
Rotation on rate-limit |
OPENCODE_RESUME_SWITCH_ON_FAILURES |
true |
Rotation on persistent failures |
OPENCODE_RESUME_DISABLE_ROTATION |
false |
Hard-disable rotation |
OPENCODE_RESUME_FAVORITE_RETURN |
true |
Favorite-model stability gate |
OPENCODE_RESUME_FAVORITE_CHECK_AFTER_MS |
300_000 |
Favorite-check interval |
OPENCODE_RESUME_FAVORITE_MIN_TURNS |
3 |
Min turns before favorite check |
OPENCODE_RESUME_RL_SWITCH_AFTER |
3 |
Rate-limit switch after N failures |
OPENCODE_RESUME_ROTATE_AFTER_FAILURES |
2 |
Rotation after failure count |
OPENCODE_RESUME_MAX_ROTATIONS |
3 |
Max rotations per session |
OPENCODE_RESUME_MODEL_COOLDOWN_MS |
300_000 |
Cooldown between same-model use |
OPENCODE_RESUME_STOPSTORE |
env path | Stop-store path |
OPENCODE_RESUME_OFFSTORE |
env path | Off-store path |
OPENCODE_RESUME_AGGRESSION |
balanced |
Aggression preset |
OPENCODE_RESUME_MAX_STALL_TAKEOVERS |
3 |
Max stall takeovers |
OPENCODE_RESUME_DENYLIST_MODELS |
— | Denylisted model IDs |
OPENCODE_RESUME_BUDGET_PROMPTS |
false |
Enable budget/quota prompts |
OPENCODE_RESUME_FAST_RETRY |
true |
Fast-retry mode |
OPENCODE_RESUME_FAST_RETRY_BASE_MS |
30 |
Fast retry base |
OPENCODE_RESUME_FAST_RETRY_GROWTH |
2 |
Fast retry growth factor |
OPENCODE_RESUME_FAST_RETRY_MAX_MS |
500 |
Fast retry max |
OPENCODE_RESUME_FAST_RETRY_MAX_CHAIN |
8 |
Fast retry max chain |
OPENCODE_RESUME_MAX_DIR_ASK_COUNTS |
3 |
Max direct-ask counts |
OPENCODE_RESUME_PAUSESTORE |
env path | Pause-store path |
OPENCODE_RESUME_ACTIVESTORE |
env path | Active-store path |
OPENCODE_RESUME_HEALTHSTORE |
env path | Provider-health store |
OPENCODE_RESUME_FALLBACK_MODELS |
false |
Fallback model list |
OPENCODE_RESUME_OUTPUT_LENGTH_MAX |
3 |
Max output-length hits before giving up |
OPENCODE_RESUME_STALL_TIMEOUT_MS |
30_000 |
Stall timeout |
OPENCODE_RESUME_NUDGE_DELAY_MS |
30 |
Nudge delay (continue / budget) |
Full list in file header (lines 128–201).
- Safe mode defaults — dangerous commands rejected; external directory grants auto-answered only after denylist scan (
SENSITIVE_DIR_REScovers Windows/Linux/macOS paths). - ReDoS protection — extra deny regexes are screened against catastrophic backtracking (
DANGEROUS_REGEX_PATTERN) before use. - Credential redaction — error messages holding tokens/keys are redacted before writing to disk or console.
- Encrypted persistence — sidecars can be encrypted with
OPENCODE_RESUME_ENCRYPTION_KEY. If not set, files stay local next to the plugin.
| Claim | Evidence |
|---|---|
| Recovers from interrupted turns | handleError schedules resume; reanimate revives on startup; session.compacted resumes after compaction |
| Answers permission prompts | permission.updated / permission.asked → respondToPermission; native V2 ctx.permission.hook("evaluate") also registered |
| Rotates models | rotateAwayFrom picks from catalog with tier ranking (TIER_BONUS / TIER_PENALTY); only when rotationEnabled=true |
| Resumes after restart | startupActiveSnapshot scanned; reanimate() revives recent session with kind derived from last assistant message |
| Prevents double prompts | sessionErrorSynthLock + lastErrorSynthAt 5s + retryErrKey dedup (v2.1.3) |
| Encrypts sidecars | encryptPayload / decryptPayload using AES-256-GCM (v2.1.5) |
| Registry manifest exists | plugin-manifest.json at repo root (v2.1.5) |
- 2.1.7 — double-prompt race fixed (synchronous
dispatchClaimsclaim, byte-identical text dedupe, bounded deferred retry);scheduledKindstimer-key leak fixed;ReferenceErrorin user-turn handler fixed; regression suitetests/doubleprompt.mjs; bounded-concurrency reanimate prefetch (8 workers, 15 s deadline) - 2.1.5 — encrypted sidecars (AES-256-GCM); plugin registry manifest (
plugin-manifest.json) - 2.1.4 — improvement cycles pivot to optimization/UX when work complete;
proposedemands concrete payoff - 2.1.3 — session-level error-synthesis lock stops double prompts;
sessionErrorSynthLock - 2.1.2 — package version sync for plugin list visibility
- 2.1.1 — fast retry schedule + bounded retry budget; duplicate incident dedupe improved
- 2.1.0 – rapid transient retry when rotation off
- 2.0.4 – credential redaction in error prompts
- 2.0.3 – redaction of provider error text
- 2.0.2 – honor provider-stated retry window
- 2.0.1 – rotation master control for auth/rotation
- 2.0.0 – complete OpenCode 2 migration
350+ assertions across 18 suites
MIT — see LICENSE. For bugs or feature requests: open issue at neohiro/auto-resume.
Made to exceed expectations — not just meet them.