Files
openwork/pr/reload-engine.md
2026-01-16 15:01:13 -08:00

6.9 KiB
Raw Permalink Blame History

PRD — Reload Engine After Plugin / Skill Changes (OpenWork)

Summary

OpenWork lets users install skills (OpenPackage into .opencode/skill) and configure plugins (via opencode.json). Today those changes are visible on disk immediately, but the running OpenCode engine may not pick them up until the current instance is disposed.

This PRD adds a calm, premium UX affordance: a contextual “Reload engine” button that appears only when OpenWork detects changes that require an engine reload to take effect.

Problem

Users can:

  • add a plugin via the Plugins tab (writes opencode.json)
  • install/import skills via the Skills tab (writes .opencode/skill/*)

…but the agent runtime is often “stale” until the engine reloads its instance:

  • npm plugins are loaded/installed at instance startup
  • agent skills can be cached by the instance (skill discovery/state)

So the user experiences:

  • “I added it, but it doesnt work”
  • no clear feedback about what to do next

Clarifying Terms

“Skill added to opencode.json

OpenCodes config file (opencode.json) does not declare skills. It declares configuration such as providers/models and plugins via the plugin key.

In OpenWork UX, users often conflate “skills” (OpenPackage installs) with “plugins” (OpenCode plugins). This feature addresses both:

  • Plugin change: opencode.json changed (project/global)
  • Skill change: .opencode/skill/** changed

In both cases, the fix is the same: dispose the current OpenCode instance so the next request recreates it and reloads config/plugins/skills.

Goals

  • Make it obvious when a reload is required.
  • Provide a one-click, safe “Reload engine” action.
  • Avoid false positives (dont nag constantly).
  • Dont break running sessions; keep user in control.
  • Work across OpenWorks modes:
    • Host mode (engine started by OpenWork)
    • Client mode (connected to remote engine)
    • Engine source PATH vs Sidecar (Host mode detail)

Non-goals

  • A full plugin installer UI (dependency management, version pins, etc.).
  • Hot-reloading plugins/skills without instance disposal.
  • Restarting the entire server process unless disposal fails (fallback only).

Current Behavior (Baseline)

  • Plugins tab mutates opencode.json via Tauri commands (read_opencode_config / write_opencode_config).
  • Skills tab installs packages via opkg_install and reads .opencode/skill/*/SKILL.md via the server file API.
  • OpenWork does not currently tell the engine that config/skill files changed.

Proposed UX

Where the reload affordance lives

A lightweight banner component shown in “engine-scoped” surfaces:

  • Skills tab
  • Plugins tab
  • Settings (optional; if we want a central place)

Banner copy:

  • Title: “Reload required”
  • Body (reason-dependent):
    • Plugin: “OpenCode loads plugins on startup. Reload to apply opencode.json changes.”
    • Skill: “OpenCode caches skill state. Reload to make newly installed skills available.”

Buttons:

  • Primary: Reload engine
  • Secondary: Dismiss (hides banner until next change)

Interaction flows

Flow A — Add plugin

  1. User clicks “Add” on a plugin.
  2. OpenWork writes updated opencode.json.
  3. OpenWork shows banner: “Reload required to apply plugin changes.”
  4. User clicks “Reload engine”.
  5. OpenWork reloads the instance and refreshes:
    • providers/models
    • plugins list
    • skills list

Flow B — Install skill (OpenPackage)

  1. User installs an OpenPackage source.
  2. OpenWork refreshes the file list and shows the installed skill(s).
  3. OpenWork shows banner: “Reload required to make skills available to the engine.”
  4. User reloads.

Flow C — Active run

If any session is currently running (status running / retry):

  • Banner still appears, but reload is disabled by default.
  • Copy: “A run is in progress. Reloading may interrupt tool execution.”
  • Actions:
    • Primary disabled: “Reload engine”
    • Secondary: “Reload anyway” (optional confirm modal)

MVP choice: disable reload while any session is running.

Flow D — Client mode safety

When in Client mode, OpenWork is connected to a server it does not own.

  • Banner still appears (because config/skills changed in the workspace), but the reload button is gated:
    • If the user is connected to a remote host, disposing the instance may affect other clients.

MVP choice:

  • Show “Reload engine” only in Host mode.
  • In Client mode, show informational text: “Changes will apply after the host reloads.”

Detection (Reliable, Minimal)

“Reload needed” state

We track a reloadRequired flag with metadata:

  • reasons: plugin-config-changed | skills-changed
  • detectedAt: timestamp
  • scope: project/global (for plugin changes)

How we set it (MVP)

Set reloadRequired = true whenever OpenWork itself performs a write that we know requires reload:

  • after successful writeOpencodeConfig(...)
  • after successful opkgInstall(...)
  • after successful importSkill(...)

This is reliable for the in-app workflow and avoids expensive filesystem polling.

Optional: detect out-of-band edits (follow-up)

To catch users editing opencode.json in their editor:

  • compute a cheap “desired fingerprint”:
    • opencode.json content hash (project/global)
    • .opencode/skill directory names + SKILL.md mtime/hash
  • store the fingerprint at:
    • engine start
    • after reload
  • compare periodically (e.g. when entering Skills/Plugins tab)

If mismatch, set reloadRequired.

Reload Mechanism (Correct mapping to OpenCode)

OpenCode server docs expose:

  • POST /instance/dispose (dispose current instance)

This is the correct “reload engine” primitive for this UX:

  • sessions persist (stored in DB)
  • the next request recreates the instance and reloads config/plugins/skills

Proposed implementation

Preferred path (all modes):

  1. Call client.instance.dispose() (SDK wrapper for POST /instance/dispose).
  2. Poll client.global.health() until healthy.
  3. Refresh UI state:
    • client.config.providers()
    • refreshPlugins()
    • refreshSkills()

Fallback path (Host mode only):

  • If disposal fails, perform a hard restart:
    • engineStop() then engineStart() and reconnect.

Acceptance Criteria

  • After adding a plugin via OpenWork, user sees a “Reload required” banner.
  • After installing/importing a skill via OpenWork, user sees the banner.
  • Clicking “Reload engine” triggers instance disposal and then clears the banner.
  • Reload is not offered while a session is actively running (MVP).
  • In Client mode, the UI does not dispose remote instances (MVP).

Open Questions

  • Should we show plugin install progress after reload using installation.updated SSE events?
  • Should reload be auto-triggered when idle (with a toast), instead of manual?
  • Do we want to support Client mode reload behind a confirmation gate?