# Upstream plugin rewrite: migration and compatibility

## 0.2.0 compatibility assessment

The current checkout uses published PI WEB **1.202610.9** (verified through npm registry metadata and installed public declarations) and Pi coding agent **1.4.0**. Server API v5 plus `catalog@1` provide machine-scoped management and registered-project/workspace discovery, including empty workspaces. The browser also uses API v5, with a registered-project/workspace selector for new drafts, including empty targets; saved definitions keep their original target. Node.js 22.19+ is required for runtime SQLite. These are the minimum supported host versions; older releases' compatibility statements do not apply.

Chat management now calls `createCompanionBackend(pi.events, "automations")` during each tool invocation. The host handles request/reply correlation, deadlines, cancellation, runtime replacement and backend revision revocation. No browser binding, management event channels or heartbeat remain. The paired browser sends all mutations through the same workspace-scoped `manage` handler with the same revision and exact cancellation-identity checks. Its separate scoped `snapshot` operation remains read-only for polling, costs and editor defaults; old individual browser mutation operations are removed. The creation selector uses the browser's machine-captured catalog and supplies both exact IDs; discovery is not mutation authority. Current-workspace defaults are derived from a unique catalog cwd match only, with ambiguity requiring explicit IDs. Discovery is never authority: workspaces are resolved again before reading definitions or changing state. Chat machine attribution is the backend hostname and daemon-relative `local` identity, not caller-supplied browser labels.

### Host runner decision

**Retain the existing execution runner for this release.** The published `PiWebHostPiSessionsV1.run()` on PI WEB 1.202610.9 supports strict model/thinking admission, a published session ID, and a completion promise with completed/failed/cancelled status. Its public `PiWebHostPiSessionRun` has **no run-specific cancellation method or token**, no partial usage/cost stream or completion totals, and no preparation-before-prompt hook for installing Automations' attributed companion task. Revocation only cancels unpublished startup; published conversations belong to the user. Starting with `run()` then sending a generic session abort would lose attribution and risk cancelling later user work. Waiting only for its completion would also lose the partial usage accounting required here.

Therefore Automations keeps `sessions.create()` followed by its existing correlated prepare/run/cancel protocol and companion observation of assistant messages and `agent_settled`. Only **management transport** is replaced. The execution protocol is deliberately not removed: it provides task-attributed cooperative cancellation, interference detection and partial usage while fencing unconfirmed work. This decision is grounded in the npm-published public declarations, not inferred private host internals or main-branch features.

### Packaging and verification

Both the server and companion entries now bundle only the required public host helpers and MIT attribution. The separately emitted chat-tool module is bundled as well so no emitted JavaScript requires a separately installed host peer. The production-only tarball validator checks server API v5 and exact catalog/session/workspace capabilities, instantiates the companion, verifies deferred registration and fail-closed non-hosted calls, and opens runtime SQLite with no native addon or host peers installed. These are isolated tests, not hosted end-to-end or remote federation verification.

## Operating contract

This is a **cooperative scheduler** adopting upstream's user-owned session contract, not a drop-in replacement for the fork's exclusive background-session service. Version `0.2.0` targets `@jmfederico/pi-web@^1.202610.9` and `@earendil-works/pi-coding-agent@^1.4.0`. Historical host baselines are documented only in their respective [release notes](releases/0.1.2.md).

Automations uses the public upstream contract for durable definitions, schedules, run history, a machine-scoped workspace UI and agent execution. User-owned sessions, cooperative cancellation and partial usage accounting are accepted operating constraints, not blockers awaiting additional host APIs. Scheduling/persistence remain plugin-owned. Agent control belongs in the bundled Pi extension, not in private PI WEB routes or subprocesses.

| Original need | Public upstream representation | Difference / gap |
| --- | --- | --- |
| Persistent database | Server `dataDirectory` + SQLite | Directory relocation needs an operator-controlled copy; no automatic migration |
| Workspace validation | Exact `workspaces@1` + `pi-sessions@1` capabilities | IDs are re-resolved at every run; paths from saved definitions are not execution authority |
| UI/chat transport | Browser scoped backend and companion `createCompanionBackend` | Shared `manage` operations; read-only browser snapshot polling; no browser binding or private HTTP routes |
| Fresh session | `pi-sessions.create` | Published session belongs to the user; no exclusive lease or mutation fencing |
| Model/thinking selection | Companion `modelRegistry`, `setModel`, thinking APIs | No catalog before creating a session. Manual fixed IDs; invalid/unauthenticated policies fail rather than falling back |
| Prompt/completion | Session-local correlated messages, Pi `sendUserMessage` / `agent_settled` | `emit` is not acknowledgement. `agent_end` is not final settlement. Loss is not success |
| Cancellation | Correlated companion request + `ctx.abort()` | Cooperative only; never a force-stop guarantee, process kill, or daemon stop |
| Runtime shutdown | `lifetimeSignal`, then `dispose` | Event connections are revoked; do not assume disposal can cancel published work |
| Usage | Observed assistant `message_end` usage | Partial assistant-message totals, not complete root-session accounting |

No host source edits, private imports/routes, Pi CLI subprocess execution, or compatibility shims are used. Source runtime PI WEB imports use only public `server-plugin-api` capability helpers and the `companion` backend helper. The build bundles these helpers and their upstream parsers into the relevant entries; emitted JavaScript has no runtime host-package imports. Pi APIs are a type-only extension boundary.

## Development bootstrap

Use Node.js 22.19+ and install the registry dependencies; no host source build is required:

```sh
npm ci
npm run verify
npm run pack:dry
```

CI runs the same checks against the released host package on Windows and Linux. Version `0.2.0` replaces `better-sqlite3` with Node's built-in `node:sqlite`; Croner is the only direct runtime dependency. Node 22.19+ supplies synchronous SQLite without a separately compiled native addon. Node may print an experimental SQLite warning. Existing schema-version-1 SQLite files remain in place: no export/import or table rewrite is required. Foreign keys, WAL for file-backed stores, a five-second busy timeout, and atomic read/check/write transactions are retained; transactions acquire the write lock before checking revisions or overlap. A synthetic database created by 0.1.2 with better-sqlite3 13.0.3 is tested for retained definitions, history, usage and unknown-work blocking. Back up existing state while the owning daemon is safely stopped before any operator-managed upgrade; this change does not copy or modify live databases. PI WEB `^1.202610.9` and Pi coding agent `^1.4.0` are compatibility peers supplied by the running hosts, not dependencies that must be installed next to the plugin. Both are development dependencies for local typechecking; the build also uses the public PI WEB capability helpers. Pi-managed installs disable automatic peer installation, so the emitted server bundles those helpers instead of resolving a second host package at runtime. The package ships browser and server entries, the companion, and these docs. Import, real-pack and clean production-only tarball-install validators run in `npm run verify`. The install check uses `--legacy-peer-deps`, matching Pi-managed npm installation, asserts that neither host peer is installed in the temporary fixture, and verifies server/companion imports plus exact capability requirements and parser rejection of invalid values. It is not an end-to-end scheduler run in a hosted Pi profile. See the [0.2.0 release notes](releases/0.2.0.md) for current compatibility and the [0.1.0 release notes](releases/0.1.0.md) for the initial npm release scope and operating limitations.

## Published 0.1.0 packaging failure

The published initial release imports the host capability helpers at runtime. In a Pi-managed installation this can throw `Cannot find package '@jmfederico/pi-web'` from `dist/server-plugin.js`, even when a compatible host is already running. The original clean-install validator allowed npm to auto-install peers and therefore did not reproduce the managed layout. The `0.1.1` fix bundles the public helpers, retains their validation semantics and MIT notice, and forbids host runtime imports in distribution validation. Reboots do not fix the initial artifact. The fix is retained in `0.2.0`; use this release with its compatible hosts or a verified local source build. See the [patch release notes](releases/0.1.1.md). No live installation or daemon restart is performed by these build checks.

## Install and activate

Install `@compn3rd/pi-web-automations` from npm using the target machine's **Settings → Pi packages**; use `@compn3rd/pi-web-automations@0.2.0` to pin this release. For source development, install the built directory as a local package instead. The package declares `pi.extensions: ["dist/companion.js"]` as well as paired `piWeb.plugins` entries. Ensure the Pi package/extension is enabled and trusted. Merely linking the PI WEB plugin does not install the companion into Pi sessions.

When upgrading the host from the Pi 0.x baseline, update PI WEB and Pi together on the gateway and targets. Restart the updated web/API process and wait for readiness before restarting its session daemon; if web startup fails, leave the daemon untouched. Restarting the daemon interrupts active sessions and terminals. Inspect active work and arrange this outside hosted sessions that must survive.

Enable `automations` in **Settings → PI WEB plugins**. Restart the target session daemon when safe, then reload the browser. Rebuild source edits with `npm run build`; server or companion changes require appropriate daemon/session reloads. Fresh automation sessions must see the companion. Do not modify plugin configuration or restart the daemon from a hosted session that must survive.

The plugin is `machineSpecific: true`. Each target needs compatible host and package installations, its own credentials, and its own state. Gateway/UI reloads do not stop schedules. Mixed gateway/target host revisions are unsupported. No definitions fail over or copy automatically.

## Existing state: staged migration only

The previous fork stored `automations.sqlite` under `$PI_WEB_DATA_DIR/plugin-state/automations`. The rewrite supplies a different `dataDirectory` (currently `$PI_WEB_DATA_DIR/plugin-data/automations`); the implementation uses only that supplied directory. It does **not** search or copy the old path.

Before an operator switches hosts:

1. Inspect all active sessions and schedules. Take a SQLite online backup or stop the old daemon before backing up; copying a live database file without its WAL is unsafe.
2. Retain the original backup. Stage a separate database copy for the new host, with schedules disabled. Do not have two plugin instances scheduling from either the same state or copied enabled schedules.
3. Verify the target host's actual `dataDirectory` and only place the staged database there during the planned cutover. Preserve permissions. Do not replace live state while sessiond is running.
4. Install exactly one owner of plugin id `automations`; inspect definitions/history and run a manual test before enabling schedules individually.

Existing schema, revision records and configured fixed policies are retained. Existing `root_session` usage remains readable; new companion usage has scope `assistant_messages` and quality `partial`. No new successful test is inferred from old or interrupted work. Starting/running/cancelling records recover as `unknown`, and **all unknown records (including inherited ones) block their definition**.

State backup/staging and real-host smoke are operator/parent responsibilities, not actions performed by this port. Tests use temporary directories or in-memory databases.

## Daily operation

Save a disabled draft, manually test it successfully, then enable that exact revision. Changes to definition content pause it and require another successful test. Optimistic revision checks protect concurrent browser edits.

Triggers: manual, one future occurrence, interval (minimum one minute), and six-field cron with an explicit IANA time zone. Two different definitions can run concurrently. An active definition does not overlap itself; colliding scheduled occurrences are recorded as skipped. Additional distinct jobs queue. There is no workspace-wide lock across definitions or user sessions.

Choose the machine default or enter a fixed provider and model ID. Thinking levels are provisional known Pi values; the companion rejects unsupported values after Pi clamps them. The panel never resets an existing fixed policy silently when editing a model. Catalog absence is explicit, and no hidden catalog-discovery sessions are created.

Each run creates a visible session and records its ID immediately, before connecting/preparing. Connection/model/handshake failures preserve that ID for inspection in Sessions. Session IDs are retained for diagnostics; the panel links to sessions using the host's public navigation query parameters, not private routes. Empty failed setup conversations are not deleted by this plugin.

## Completion, cancellation and ambiguity

The backend subscribes before emitting a correlated request. Preparation must reply within five seconds; otherwise the run reports a missing/unavailable companion. Model and thinking validation occur in the fresh session before prompting. Current settings are rechecked at submission, and model/thinking changes after preparation or competing user messages are treated as interference. Pi 1.2 can also start a run from a custom message with no user `prompt`; these starts end automation attribution as `unknown`, without cancelling or counting unrelated work. These checks are detection, **not atomic host-side fencing**. Other extensions can transform input, send messages or mutate settings; not every arbitrary extension interaction can be attributed reliably.

Completion waits for Pi `agent_settled`, including automatic retries and compaction/queued continuations. Provider error/abort without successful final response fails the run. A successful send, disconnected browser, `agent_end`, connection loss or expired deadline never proves success.

**Cancel** records intent and asks only the matching active companion task to abort. Before prompt admission it prevents submission locally. After submission the companion must confirm settlement. It refuses unsafe cancellation when other queued/user work interferes, and ignores delayed cancellation once its task has ended; it must not abort later user work. Maximum configured run duration is 24 hours (default 60 minutes, minimum 1 minute), starting after session preparation. Cancellation gets a 15-second grace period.

If settlement is not confirmed, the run becomes `unknown`, the definition is paused, its test marker is cleared, and **manual starts, scheduled starts and re-enabling remain blocked even after definition edits or restart**. No runtime is force-stopped and `forceStopped` remains false. Connection closure detaches observation only. Plugin lifetime shutdown stops ingress, polling and deadlines immediately, records unconfirmed active work conservatively and closes connections; it does not try to cancel over revoked dependencies. Queued work without an admitted attempt remains queued for the next start.

There is intentionally no acknowledgement/unblock button in this release. Inspect the recorded conversation and its actual work in Sessions; independently stop/wait for uncertain work. Only after that inspection, create a separately named replacement definition and test it. You may delete a blocked definition once it has no queued, starting, running or cancelling runs. Delete archives the definition and retains its run/attempt history, including unknown outcomes and session IDs; it does not stop or confirm any uncertain session work. Never create a replacement just to bypass a live unknown run. A future explicit inspection/acknowledgement workflow would be a separate product change.

A daemon/process crash can occur between host publication and SQLite recording; no public atomic transaction covers those systems. Inspect Sessions even when an interrupted record lacks an ID. During ordinary disposal the store is retained until bounded pending host calls return so late-created identities can still be recorded; this is not a durable host execution lease.

## Usage, privacy and safety

New usage sums assistant-message token and estimated cost fields observed during the companion task (including failed attempts preceding retries). It excludes compaction-only/provider work not represented by those assistant messages, tool/subagent usage and external services. It is explicitly labeled **partial, assistant messages**, not full root-session or billing usage. Old imported totals retain their original scope/quality.

Plugin code and Pi tools run with the daemon user's permissions. Sessions isolate conversation context, not files/processes/network access. Review prompts and credentials. Cancellation cannot undo filesystem writes, network calls or other completed effects. Prompts, history and SQLite backups can contain sensitive data.

## Validation boundaries

For `0.2.0`, local verification against published PI WEB `1.202610.9` and Pi `1.4.0` covers Node 24.20.0 and minimum Node 22.19.0, including 125 tests, distribution checks and a clean production-only install without host peers. The old-driver fixture verifies schema-v1 compatibility. Current evidence and remaining live-host checks are recorded in the [0.2.0 release notes](releases/0.2.0.md). The isolated hosted evaluation below is historical, not a new end-to-end test of this host pair.

### Historical isolated evaluation

Unit tests cover storage/scheduler invariants, peer scoping, browser/controller behavior, synchronous handshake ordering, missing companion, policy rejection, settlement vs `agent_end`, cancellation, interference, connection loss, lifetime shutdown and unknown-run blocking. Distribution validators check public import boundaries, companion metadata and packed files.

The earlier Windows / Node 24.19.0 verification against the prerelease upstream source passed `npm run verify` (71 tests, typecheck, lint, build, public-import and real-package validation). The upstream full verification rerun passed 4,085 tests with 67 skipped; its initial run hit a five-second Vite-test timeout that passed both a focused rerun and the full rerun. Runtime-only npm audit reported no known vulnerabilities; the development dependency tree still reports four advisories.

An isolated real daemon/web/Pi SDK and compiled package, using a deterministic loopback model endpoint, passed these scenarios:

- Real Edge browser: package loading, create/edit/run, fixed model/thinking controls, history polling, partial usage, enable/pause, stale concurrent-tab edits, reload persistence, and closing all pages during a run.
- Public paired transport: stale host revision, invalid workspace, unsupported contract, caller-supplied scope and unknown operation rejected; valid work still succeeds afterward.
- Background execution: manual, one-time and recurring interval schedules; actual 60-second timeout; skipped overlapping occurrences; subsequent interval execution after completion; scheduled work starting and finishing with the web process offline; active work surviving web restart.
- Session lifecycle: cooperative cancellation, external user abort, same-daemon session closure producing unknown, subsequent user conversation, harmless late cancellation, daemon-loss recovery and persistent unknown-run blocking.
- Failure handling: unavailable model before prompt submission, missing companion, provider failure and preserved session identity.

Two defects found during evaluation were fixed: identical consumed user follow-ups must end automation attribution, and reopening the browser editor must display the saved fixed thinking level. The latter needs native selected-option markup because the select's value binding precedes dynamic option insertion. Unit coverage is supplemented by real-browser assertions; happy-dom alone does not reproduce native option-selection behavior reliably.

No real credentials or production prompts were used; all owned test processes and fake-model listeners were stopped. Existing local workspace IDs were separately checked through the read-only catalogue. These checks accept user-owned sessions and cooperative cancellation; they do not establish exclusive ownership or forced termination.

Not verified by the isolated evaluation: the user's full provider/extension profile, paid provider requests, remote federation, graceful POSIX disposal, or unresponsive-tool cancellation on a real host. A subsequent operator-controlled cutover installed this plugin on the running PI WEB host; this document's isolated evaluation results remain historical evidence, not proof of those untested scenarios or permission to publish the package.

### Provenance

Extracted from `CompN3rd/pi-web`, `feat/plugin-automations`, source commit `32962445305c5ec52d88bb19fc71357277e13e63`, `pi-web-plugins/automations`; standalone migration branch starts at `d3dc105`. Public design reference: [upstream PR #160 discussion](https://github.com/jmfederico/pi-web/pull/160#issuecomment-5735470984), [plugin guide](https://github.com/jmfederico/pi-web/blob/60a29acbfc710908e73b0df9857cb63b53b672d3/docs/plugins.md), Workspace Reviews and Captain's Log companion examples in that source revision.
