Skip to content

Auto-reload after an update: Pi reloads itself when idle; OpenCode 2 installer updates the running plugin - #2356

Draft
backnotprop wants to merge 2 commits into
mainfrom
feat/auto-reload-after-update
Draft

backnotprop wants to merge 2 commits into
mainfrom
feat/auto-reload-after-update

Conversation

@backnotprop

@backnotprop backnotprop commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

After a Plannotator update, the running agent loads the new version by itself where that is safe. The design comes from the auto-reload spike; this PR ports it by hand onto current main.

What users see

Pi (0.84.2 or newer): after pi update --extensions or the install script, each open Pi session reloads itself once nothing can be lost. Pi shows its own "Reloading…" box for a moment, then one line in place of its "Reloaded …" status:
Plannotator updated to 0.29.1. Pi reloaded while idle.
The old "Plannotator was updated … Type /reload" note is not shown while Pi will reload by itself.

OpenCode 2: when its service is running with @plannotator/opencode or @plannotator/opencode@latest, the install script runs opencode2 plugin update <that spec>. The service loads the new plugin without stopping anything. The script prints one of:

  • OpenCode 2: the running service now uses Plannotator X. Nothing was restarted.
  • OpenCode 2: the running service already uses Plannotator X.
  • OpenCode 2: the Plannotator plugin is pinned to X; left as it is.
  • OpenCode 2: the running service still uses Plannotator Y (<reason>). To load X, run: opencode2 models && opencode2 plugin update <spec>

When the service is not running, the script does what it did before, and the next start loads the newest plugin.

Oh My Pi: only the note, once per version (… Type /restart …). Oh My Pi never restarts by itself.

Guards

Pi. A reload happens only when all of these hold. The extension checks them every 2 s, and checks them again inside the command just before it reloads:

  • the same new version has been on disk for 10 s;
  • no run, no queued message, no compaction;
  • no prompt that was submitted but has not started its run (30 s grace);
  • no dialog from any extension;
  • no open Plannotator review, plan review or Ask question;
  • nothing typed for 15 s, and an empty editor.

Other rules:

  • Pi gets one try per version. If the reload loads nothing new (a broken update, or Pi refused), the person gets the /reload note, and Pi does not try again.
  • The extension reads Pi's version from the SDK. An older Pi gets the note only, because there sendUserMessage would send the command text to the model.

Installer. The update step never fails the install. It only asks the service and writes nothing itself:

  • every opencode2 call has a time limit (90 s for the update, 15 s for the others) and stdin from /dev/null, and its exit code is checked;
  • the result is read back from opencode2 plugin list, because a broken package or a registry that is down also exits 0;
  • the step is skipped for a pinned install (--version, with the same rule as the Claude Code plugin step) and for --skip-opencode;
  • the OpenCode 2 cache is kept while the service has the plugin. Without the cache, the service answers "No plugin updates available".

How to turn it off

  • Pi: set PLANNOTATOR_AUTO_RELOAD=0, or add { "autoReload": false } to ~/.plannotator/config.json. The env var wins. When it is off, the session shows the /reload note once per version. It is on by default (owner's call); the default is one constant, AUTO_RELOAD_DEFAULT.
  • Installer: use --skip-opencode, or pin the plugin in opencode.json.

Dropped, and why

  • An OpenCode 2 Inbox action that reloads "when every session is idle". opencode2 reload cancels pending permissions and question forms. The idle check also races the reload. plugin update does the same job without cancelling anything, so it needs no idle gate. This action was never built, so nothing is removed.
  • Automatic /restart in Oh My Pi. Extensions cannot call it, and it kills a running turn.
  • Pre-filling /reload or /restart in the editor. Pi does not need it. In Oh My Pi, what the person types next would be appended to it.

Tests

  • apps/pi-extension/auto-reload.test.ts: every guard, the command's re-check, one try per version, the version gate, the notes and the setting, all against a fake Pi.
  • scripts/install.test.ts, "install.sh updates a running OpenCode 2 service's plugin": a fake opencode2 in a sandbox HOME. It covers success, a late load, a first "No plugins found", current, pinned, other specs, stopped, no row, registry down, a broken package, a non-zero exit, hangs, a pinned versus an auto-update run, the opt-out, no opencode2 and no mktemp.
  • Real-host runs in sandboxes: temp HOME, XDG, config and data dirs; a local scripted model; a local npm registry; no credentials. Each run used the npm pack of this branch. Results:
    • Pi 0.84.4, 5 rounds of each situation:
      • Idle: reloaded 10–12 s after the update.
      • A turn running: no reload and no warning during the turn; it reloaded after the turn settled.
      • Another extension's dialog open: no reload while it was open; the answer was kept.
      • Prompts sent around the reload time: every one was answered.
      • Text in the editor: no reload; it reloaded 15–17 s after the editor was cleared.
      • A Plannotator review open: no reload; the feedback was delivered, and the reload came after.
      • A broken update: one reload, Pi's "Failed to load extension", and no second try.
    • Pi with auto-reload off, 2 rounds: the /reload note was shown once and nothing reloaded.
    • Oh My Pi 18.12.6, 3 rounds: the /restart note was shown once, 9–11 s after the update, and Oh My Pi never restarted.
    • OpenCode 2.0.22, 8 cases:
      • A held permission, a question form and a running turn: the service updated to the new plugin, and all three were kept and finished.
      • Current version: "already uses".
      • Pinned: the plugin stayed on its version.
      • Broken package: "still uses" (19 s).
      • Registry down: "still uses" (89 s).
      • Service stopped: no line, and the next start loaded the new plugin.
      • install.sh exited 0 every time.
    • Found by these runs and fixed: Pi writes its "Reloaded …" status after the extensions' session_start, and that status replaced our line a moment later. The new instance now shows the line at its first poll, about 2 s after the reload. Rerun twice for idle and an open review: the line was still on the last screen 6 s later.

…installer updates the running plugin

Pi (0.84.2+): when the Plannotator extension on disk is newer than the one
loaded, Pi reloads itself through the extension's own /plannotator-reload
command once nothing can be lost (stable version 10 s, no run, queued
message, compaction, pending prompt, extension dialog, open review / plan
review / Ask, nothing typed 15 s, empty editor; re-checked in the command;
one try per version). Off, older Pi and Oh My Pi: the /reload (/restart)
note once per version. autoReload / PLANNOTATOR_AUTO_RELOAD, default on.

install.sh: when the OpenCode 2 service is running with an unpinned
@plannotator/opencode spec, run opencode2 plugin update <spec> (bounded,
stdin /dev/null, exit code checked, version read back), keep the OC2 cache
while the service has the plugin; any failure prints the command and
changes nothing else.

Ported from the auto-reload spike (spike/auto-reload, never pushed).
@backnotprop backnotprop added the ci:full Run the heavy CI tier on this PR (added by the merge script) label Oct 29, 2026
…ry per version per process

Pi writes its own "Reloaded …" status after the extensions' session_start, and
a status line written right after another one replaces it, so the line sent from
session_start was gone a moment later (real host, Pi 0.84.4). The new instance
now shows it at its first poll. The tried versions live in globalThis, so a
reload that loads the old version again cannot loop, and that case gets the
/reload note from the new instance (the old context is stale after the reload).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:full Run the heavy CI tier on this PR (added by the merge script)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant