@tranhoangnguyen0310/pi-flow-external 2.7.0-external.0 → 2.9.0-external.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -2,21 +2,29 @@
2
2
 
3
3
  ## pi-flow external contract
4
4
 
5
- This fork changes the original pi-flow contract: `Agent` is not a generic Pi subagent launcher. It delegates only to external Claude Code, Codex CLI, Antigravity, Grok Build CLI, and Muse Code harnesses, plus named Pi harness configurations (below) — in-process, per-model configs registered by the user, not a spawned CLI.
6
-
7
- - Ordinary driver tools: `Agent`, read-only `external_help`, `external_runs`, and optional `workflow`. `pi_flow_role_create` is active only inside `/external role create`. `pi_flow_harness_create` is active only inside `/external harness create`.
8
- - User operations use `/external`. `/external profiles`, `/external profile create`, and `/pi-flow-profile create` are removed, including registrations, completions, and interview routing. They are not aliases or redirect handlers. Unknown-command help lists the current commands. `subagent_type` on `Agent` and `workflow` remains the exact-identity API.
9
- - The only execution-configuration file is `$PI_CODING_AGENT_DIR/pi-flow-external/settings.json` (settings v4): concurrency, timeout, permission and budget defaults, retention, `defaultHarness`, named `pi-*` harness entries (`model` / `thinking` / `preset`), and `disabledProfiles`. Reads do not write the file. Shared roles live in `pi-flow-external/roles/<role>.md` (one file, any harness; `description` only). Exact overrides live in `pi-flow-external/overrides/<harness>-<role>.md` and keep the execution profile schema as a complete replacement. `permission` and `capabilitySet` are obsolete metadata on both and are rejected, not used as authority. Both directories are created only when a file is saved. The extension does not write Pi's native `subagents/` directory. Settings mutations use one validated read-modify-write helper (private staged file, same-directory replacement, unrelated fields preserved, malformed or future-version files refused). Replacement prevents a torn write. There is no inter-process lock: concurrent writers from separate sessions are last-writer-wins and may drop one update. That limitation stays accepted. A trusted project may override `defaultHarness` via `.pi/pi-flow-external/settings.json` (that key only, read-only to the extension); explicit call harness > project default > global default > built-in default. `defaultHarness` may name a registered `pi-*` harness. The project file cannot inject roles or harnesses.
10
- - Every `Agent` call requires `description`, `prompt`, and either `role` with optional `harness` (`agy`, `claude`, `codex`, `grok`, `muse`, or a registered `pi-*` harness name) or the legacy exact `subagent_type`. The selectors cannot be combined. Without `harness`, use the effective default harness. A default that names a `pi-*` harness that is no longer registered fails with an actionable error and does not fall back to `agy`. `disabledProfiles` blocks both `role` selection and exact `subagent_type` selection. There is no harness substitution.
11
- - One catalog snapshot serves `Agent`, workflows, coordinator guidance, `external_help`, `/external roles`, and doctor. Precedence is exact override, then shared role, then built-in. An invalid higher-priority entry blocks that selection. Unrelated invalid entries are diagnostics and do not block other roles. Invalid settings JSON or an unsupported settings version blocks delegation; `/external settings` and `/external settings convert` stay available. Stable identities are `<harness>-<role>` plus exact legacy names. The next invocation reads settings, roles, and overrides from disk. A workflow freezes one snapshot for the whole run. A `maxConcurrentSubagents` change waits until no subagent is active and none are queued.
12
- - The six built-in roles (explorer, planner, implementer, reviewer, qa, worker) are in memory for all five CLI harnesses and every registered named Pi harness. Session start writes zero role files and zero seed markers. A shared file named `reviewer.md` replaces that built-in on every harness. Roles outside the six are authored once with `/external role create` (offline validation and write; a role is not an authenticated connection). `/external role override <role> <harness>` materializes one full override. `/external harness create` registers a named Pi harness with a real runtime smoke test and no role interview. A harness smoke test does not judge role quality. Deleted seeded identities survive conversion as `disabledProfiles` and are not recreated.
5
+ This fork changes the original pi-flow contract: `Agent` is not a generic Pi subagent launcher. It delegates only to external Claude Code, Codex CLI, Antigravity, Grok Build CLI, Muse Code, and OpenCode harnesses, plus named Pi harness configurations (below) — in-process, per-model configs registered by the user, not a spawned CLI.
6
+
7
+ - Ordinary driver tools: `Agent`, read-only `external_help`, `external_runs`, and optional `workflow`. `pi_flow_role_create` is active only inside `/external role create`. `pi_flow_harness_create` is active only inside `/external config harness create`.
8
+ - User operations use `/external`. Harness-centric configuration lives under `/external config`: `edit`, `convert`, `harnesses`, `harness create`, `enable <harness>`, `disable <harness>`, and `default <harness>`. Role authoring stays under `/external role`. Former `/external settings …`, `/external harnesses`, and `/external harness create` routes are removed, not aliases. `/external profiles`, `/external profile create`, and `/pi-flow-profile create` are removed, including registrations, completions, and interview routing. They are not aliases or redirect handlers. Unknown-command help lists the current commands. `subagent_type` on `Agent` and `workflow` remains the exact-identity API.
9
+ - The only execution-configuration file is `$PI_CODING_AGENT_DIR/pi-flow-external/settings.json` (settings v4): concurrency, timeout, permission and budget defaults, retention, `defaultHarness`, named `pi-*` harness entries (`model` / `thinking` / `preset`), `disabledProfiles`, and `disabledHarnesses`. Reads do not write the file. Shared roles live in `pi-flow-external/roles/<role>.md` (one file, any harness; `description` only). Exact overrides live in `pi-flow-external/overrides/<harness>-<role>.md` and keep the execution profile schema as a complete replacement. `permission` and `capabilitySet` are obsolete metadata on both and are rejected, not used as authority. Both directories are created only when a file is saved. The extension does not write Pi's native `subagents/` directory. Settings mutations use one validated read-modify-write helper (private staged file, same-directory replacement, unrelated fields preserved, malformed or future-version files refused). Replacement prevents a torn write. There is no inter-process lock: concurrent writers from separate sessions are last-writer-wins and may drop one update. That limitation stays accepted. A trusted project may override `defaultHarness` via `.pi/pi-flow-external/settings.json` (that key only, read-only to the extension); explicit call harness > project default > global default > built-in default. `defaultHarness` may name a registered `pi-*` harness. The project file cannot inject roles or harnesses.
10
+ - Every `Agent` call requires `description`, `prompt`, and either `role` with optional `harness` (`agy`, `claude`, `codex`, `grok`, `muse`, `opencode`, or a registered `pi-*` harness name) or the legacy exact `subagent_type`. The selectors cannot be combined. Without `harness`, use the effective default harness. A default that names a `pi-*` harness that is no longer registered fails with an actionable error and does not fall back to `agy`. `disabledProfiles` blocks individual identities; `disabledHarnesses` blocks whole CLI or named Pi harnesses for both `role` and exact `subagent_type` selection, including resumed calls and new workflow replay attempts. Disabled harnesses are hidden from executable discovery; config lists their state and doctor skips readiness probes. Definitions and overrides are preserved. Guided commands refuse disabling the effective/global default or selecting a disabled default. Manual inconsistencies fail selection. Active children and frozen workflows keep their snapshot. Unknown disabled names are preserved with diagnostics. There is no harness substitution.
11
+ - One catalog snapshot serves `Agent`, workflows, coordinator guidance, `external_help`, `/external roles`, and doctor. Precedence is exact override, then shared role, then built-in. An invalid higher-priority entry blocks that selection. Unrelated invalid entries are diagnostics and do not block other roles. Invalid settings JSON or an unsupported settings version blocks delegation; `/external config` and `/external config convert` stay available. Stable identities are `<harness>-<role>` plus exact legacy names. The next invocation reads settings, roles, and overrides from disk. A workflow freezes one snapshot for the whole run. A `maxConcurrentSubagents` change waits until no subagent is active and none are queued.
12
+ - The six built-in roles (explorer, planner, implementer, reviewer, qa, worker) are in memory for all six CLI harnesses and every registered named Pi harness. Session start writes zero role files and zero seed markers. A shared file named `reviewer.md` replaces that built-in on every harness. Roles outside the six are authored once with `/external role create` (offline validation and write; a role is not an authenticated connection). `/external role override <role> <harness>` materializes one full override. `/external config harness create` registers a named Pi harness with a real runtime smoke test and no role interview. A harness smoke test does not judge role quality. Deleted seeded identities survive conversion as `disabledProfiles` and are not recreated.
13
13
  - Native Pi subagent files stay native. This extension does not modify them. A `backend: pi` file under native `subagents/` is outside the external catalog. A Pi exact override is delegation-eligible only when its `harness` names an entry in the live settings `harnesses` map.
14
- - A registered `pi-*` harness is selected with the same `Agent` and `workflow` `role` plus optional `harness` path as `agy`, `claude`, `codex`, `grok`, and `muse`. Do not tell callers to route Pi-backed work to Pi's native subagent tool.
15
- - **Named Pi harness configuration:** a `pi-<label>` entry in settings v4 `harnesses`, pinning a `provider/model` id (resolved through Pi's own model registry), a thinking level (`off|minimal|low|medium|high|xhigh`, always persisted explicitly), and a resource preset (`minimal` or `skills`). New writes persist the preset. A legacy registration that omits it is `minimal`. The five CLI harnesses are built in and need no entries. It runs in-process via Pi's own SDK, not as a spawned CLI. `minimal` leaves skills unloaded (`noSkills: true`). `skills` loads installed skills through `DefaultResourceLoader` (`noSkills: false`); project-scope skills load only when the caller's project is trusted. Extensions, prompt templates, and themes stay unloaded in both presets. The catalog, the workflow's frozen descriptor, the replay fingerprint, and spawn all use that registration value. The tool surface stays the SDK builtins (`read`/`bash`/`edit`/`write`, plus `grep`/`find`/`ls` where a tier's allow-list adds them), curated but never claimed to be an OS sandbox: `danger` tier `bash` is exactly as exposed as on any external CLI. Retry is disabled per child (in-memory, call-scoped, never touching the user's real settings) to honor this extension's no-auto-retry contract, since the underlying SDK otherwise retries transient provider errors on its own. Pi children cannot resume (no persisted session) and have no enforced budget cap — both are deliberate v1 limitations, not oversights. Shared roles apply to every harness from one file. Backend-specific model, tools, budget, or instruction changes belong in one exact override; `tools` is enforceable only on Pi, and a Pi override's `model`/`thinking` must match the harness entry. Follow-up capability expansion (trusted extensions/MCP, resumable sessions, real budget controls) stays tracked in issue #43. `pi-web-access` was not wired: the pinned SDK docs do not verify that extension integration, and it is left for a separate investigation. Pre-v4 installs convert once through `/external settings convert`. `/external settings` points there when conversion is ready. Originals stay on disk. Custom profiles are copied as overrides with obsolete `permission` and `capabilitySet` removed. A legacy `subagents/pi-<role>.md` file with `harness: "pi-*"` becomes an ordinary cross-harness `roles/<role>.md`. Those copies install before v4 activation, and after activation old paths are ignored. `piCapabilitySets` is not copied. `/external [danger]purge-old-files` lists the legacy inventory, including customized copies, and deletes only the paths you select and then confirm. Ordinary delegation does not require it. Downgrade after purge needs the user's own backup. The literal `[danger]` is part of the purge spelling. There is no dual-write and no `/external migrate` command.
14
+ - A registered `pi-*` harness is selected with the same `Agent` and `workflow` `role` plus optional `harness` path as `agy`, `claude`, `codex`, `grok`, `muse`, and `opencode`. Do not tell callers to route Pi-backed work to Pi's native subagent tool.
15
+ - **Named Pi harness configuration:** a `pi-<label>` entry in settings v4 `harnesses`, pinning a `provider/model` id (resolved through Pi's own model registry), a thinking level (`off|minimal|low|medium|high|xhigh`, always persisted explicitly), and a resource preset (`minimal` or `skills`). New writes persist the preset. A legacy registration that omits it is `minimal`. The six CLI harnesses are built in and need no entries. It runs in-process via Pi's own SDK, not as a spawned CLI. `minimal` leaves skills unloaded (`noSkills: true`). `skills` loads installed skills through `DefaultResourceLoader` (`noSkills: false`); project-scope skills load only when the caller's project is trusted. Extensions, prompt templates, and themes stay unloaded in both presets. The catalog, the workflow's frozen descriptor, the replay fingerprint, and spawn all use that registration value. The tool surface stays the SDK builtins (`read`/`bash`/`edit`/`write`, plus `grep`/`find`/`ls` where a tier's allow-list adds them), curated but never claimed to be an OS sandbox: `danger` tier `bash` is exactly as exposed as on any external CLI. Retry is disabled per child (in-memory, call-scoped, never touching the user's real settings) to honor this extension's no-auto-retry contract, since the underlying SDK otherwise retries transient provider errors on its own. Pi children cannot resume (no persisted session) and have no enforced budget cap — both are deliberate v1 limitations, not oversights. Shared roles apply to every harness from one file. Backend-specific model, tools, budget, or instruction changes belong in one exact override; `tools` is enforceable only on Pi, and a Pi override's `model`/`thinking` must match the harness entry. Follow-up capability expansion (trusted extensions/MCP, resumable sessions, real budget controls) stays tracked in issue #43. `pi-web-access` was not wired: the pinned SDK docs do not verify that extension integration, and it is left for a separate investigation. Pre-v4 installs convert once through `/external config convert`. `/external config` points there when conversion is ready. Originals stay on disk. Custom profiles are copied as overrides with obsolete `permission` and `capabilitySet` removed. A legacy `subagents/pi-<role>.md` file with `harness: "pi-*"` becomes an ordinary cross-harness `roles/<role>.md`. Those copies install before v4 activation, and after activation old paths are ignored. `piCapabilitySets` is not copied. `/external [danger]purge-old-files` lists the legacy inventory, including customized copies, and deletes only the paths you select and then confirm. Ordinary delegation does not require it. Downgrade after purge needs the user's own backup. The literal `[danger]` is part of the purge spelling. There is no dual-write and no `/external migrate` command.
16
16
  - Permission is the call's explicit `permission`, otherwise settings `defaultPermission` (`danger` unless changed). A role describes intent and does not grant or limit authority. There is no profile floor and no role-name escalation. Each backend maps a tier it supports and rejects a restriction it cannot enforce. Disclosure and receipts show that resolved tier.
17
17
  - External CLI backends use their own tools and permission mechanisms. Codex tiers map to its `--sandbox` axis. Grok tiers also map to its `--sandbox` axis (`read-only`/`workspace`/`off`), always paired with `--permission-mode bypassPermissions`; bypass only skips the interactive prompt, and the kernel sandbox remains the enforced boundary at every tier. Grok's readonly network-blocking guarantee is Linux-only (a no-op on macOS), and sandbox startup can fail closed rather than silently downgrading on some macOS hosts (for example when `/var/run/docker.sock` resolves to a symlink). Claude falls back to `--permission-mode auto` when its effective UID is 0 because Claude refuses bypass mode under root. Claude `edit` uses `acceptEdits` and denies Bash headlessly; a role name does not raise that tier. Antigravity (`agy`) accepts only unsandboxed `--dangerously-skip-permissions`. A `readonly` or `edit` request is rejected rather than broadened. Run agy only in trusted repositories. Muse (`muse exec`) has approval and its own sandbox ON by default; every supported tier passes `--disable-approval` so headless runs never hang on an interactive prompt. `readonly` additionally passes `--disable-write --disable-shell`; `edit` leaves the sandbox enabled with only approval bypassed; `danger` uses `--yolo`, which disables approval and the sandbox and additionally trusts the workspace for this run (loads its skills/rules). Pi curated tool lists are not an OS sandbox.
18
18
  - Grok supports `resume` via its own `--resume <sessionId>` flag and reports native cost (`total_cost_usd`) rather than an estimate. It has no budget-enforcement mechanism, so `max_budget_usd` is recorded but unenforceable, and — like codex — it is never automatically retried (agy's one infra-failure retry exception does not apply to Grok).
19
19
  - Muse supports `resume` via its own `exec --session-id <uuid>` flag: verified against the real CLI (two independent `muse exec` processes sharing the same `--session-id` reported the same session, and the second recalled a fact only told to the first). Only the root run's own `run.terminal.completed`/`run.terminal.failed`/`run.output.delta` envelopes — identified by matching `payload.run_stream.id` against the id established from the process's own `runtime.command.accepted`/`session.run.linked` bootstrap pair — can finalize, fail, or contribute partial output to the result; a nested or foreign-run envelope with the same shape is ignored, and `sessionId` is captured once from that same bootstrap rather than overwritten by every subsequent envelope. It has never been observed to report token usage or cost on any run, so usage is reported as unknown (`costKnown: false`) rather than a fabricated zero or a locally estimated cost, and it has no budget-enforcement mechanism. It is never automatically retried by this extension; muse's own `meta` provider integration performs its own internal retries (observed up to 10 attempts, disclosed via activity narration such as "retrying meta model stream in 60000ms (attempt 3/10)") entirely inside the muse process, invisible to and unrelated to this extension's no-auto-retry contract. `muse exec` has no native system-prompt flag (unlike claude/codex/grok); the profile's `systemPrompt` is folded into the prompt file content instead, the same pattern agy uses. Nested-agent (sub-delegation) detection for Muse always reports false and never extends the nested-timeout deadline: every probe run saw only internal `reminder.agent.*` skill-reminder tasks (never a genuine delegation), and matching a speculative `task_kind` prefix would let ordinary internal task activity spuriously grant the one-time deadline extension, which is worse than never extending it. Revisit only once a real muse delegation event has actually been observed.
20
+ - OpenCode targets OpenCode 2 only (`opencode run --standalone --format json`, verified against `@opencode/cli` 2.0.16 source and live runs). No dual-version support. `--standalone` is mandatory: per-run env reaches only that private server, never the shared background service, and cancellation must never stop the user's service. v2 has no `--dir`; spawn sets `PWD` and cwd to the workspace. It receives stdin prompts and resumes via `--session` (verified across two processes). The JSON stream is progress only: v2 stops forwarding events after idle, ignores `session.execution.succeeded`, and exits 0 after interruptions and cancelled MCP forms. Success therefore needs zero exit, no error event, and no stderr `auto-rejecting` notice. It also needs a bounded `opencode session export --standalone <id>` (after close; before the run too on resume; honoring cancellation and timeout) proving all of the following:
21
+ - the reported session ID;
22
+ - a new user turn with exactly this prompt (the only one in a new session, or absent from the pre-run export on resume);
23
+ - after it, a `succeeded` session outcome and idle terminal;
24
+ - a last assistant message with `finish: stop`, `time.completed`, no error, and nonempty text;
25
+ - on restricted tiers, every turn assistant run by the injected agent.
26
+
27
+ The result is that export text, never streamed narration. Malformed, oversized, or unknown exports fail. Usage is this turn's assistant messages. Cost is unknown if one lacks usage or ran `subagent`. Budget is unenforceable. No extension retries; OpenCode retries up to 10 times internally. `danger` uses `--auto` (explicit native denies remain). Restricted tiers inject a random-name deny-by-default primary agent through `OPENCODE_CONFIG_CONTENT` and pass `--agent <name>` on every run, resumes included. A resumed session otherwise keeps its saved agent, and `default_agent` covers only new sessions (verified live: a saved `build` agent shell-wrote a marker under the injected config without `--agent`). An agent that fails to load resolves to deny-all. Restricted resume of a session with its own `permissions` is refused, because they apply after the agent's. Danger resume of a session last run by an injected agent is refused actionably. readonly allows read/grep/glob; edit adds edit/write/patch. This is not an OS sandbox; plugins/MCP still load. Existing inline config blocks restricted launch rather than being overwritten. A pinned thinking level requires a pinned model and becomes `#variant`; OpenCode 2 rejects an unknown variant. Structured schemas are rejected; inherited Pi thinking is not forwarded. Preserve historical five-backend seed cohorts: OpenCode was never seeded pre-v4.
20
28
  - Children receive no parent history by default. `Agent` and workflow `agent()` can opt into `context: {mode: "recent", turns: N}` (last N user turns, including the current one) or `{mode: "full"}` (available post-compaction conversation). Snapshots exclude system instructions, thinking, tool-result metadata, and pending calls; unsupported content/images and more than 1 MiB fail explicitly. Use the smallest sufficient snapshot plus a clear task, absolute paths, and read-only/edit intent. Shared context goes to the external harness and private local evidence; avoid unnecessary sensitive history. `resume` continues an existing child and cannot be combined with sharing. Workflow children select from one frozen parent snapshot, and replay fingerprints include the transferred context.
21
29
  - Backend-native nested agents may use a different workspace. Include explicit absolute paths and required context when asking an external backend to delegate further.
22
30
  - `Agent` and `workflow` block by default; `background: true` returns a stable handle after validation/registration. Background work is owned by the originating session, not by the launching tool call or a daemon. Blocking-call interruption cancels work; wait interruption only stops waiting. Orderly session shutdown requests cancellation with bounded cleanup. A crash or unconfirmed shutdown leaves unfinished evidence interrupted/uncertain; restart never adopts live work.
@@ -41,15 +49,15 @@ This fork changes the original pi-flow contract: `Agent` is not a generic Pi sub
41
49
  - Structured nested-agent activity may extend the wall-clock deadline once, by one fresh base timeout, capped at twice the original deadline.
42
50
  - Do not automatically retry failed or aborted external runs. Preserve the receipt and retry only when the user asks. Exception: the agy backend retries once on infrastructure-classified failures (auth, eligibility, network); the retry is disclosed in the receipt details (`retries`, `retryOf`) and never applies to agent-level failures, aborts, or timeouts.
43
51
  - `external_runs` is session/project scoped. `list` pages runs and workflow roots, each carrying a shared timing projection (`queuedAt`/`executionStartedAt`/`processStartedAt`/activity timestamps plus derived `queueDelayMs`/`elapsedMs`/`activityAgeMs`/`processDurationMs`); `activityAgeMs` and a running `elapsedMs` are computed only for a target independently confirmed live by the registry, never inferred from a durable record's status field alone (an unowned "running"-looking record may be an orphaned/crashed run, not a live one). `inspect` pages `summary`/`output`/`diagnostics`/`final` for one `runId`; `final` returns only the same canonical terminal result every other view already reads (no separate backend parser, no narration promoted to final) and is empty with `finalAvailable: false` until a verified successful terminal boundary exists — including for workflows, where an intentional JSON `null` result still counts as available. `inspect` also accepts a bounded batch `runIds` (up to 20, deduplicated, order preserved, summary-only, mutually exclusive with `runId`): every requested ID's ownership is validated before any page returns, entries share one consistent nested shape regardless of live/durable/agent/workflow origin (a workflow entry omits its unbounded children array in favor of `outputRef`/`diagnosticsRef`), reuses the existing per-inspect `limitBytes` cap with no second budget, and fails with an actionable error naming the run rather than ever silently dropping a target or serving a page over the caller's own byte budget. `wait` returns outcomes only for the selected one/any/all target set and never cancels pending work; while waiting it reports bounded live progress (watched targets, completed/pending counts, recent activity) through the tool's own update channel on a fixed heartbeat, stopped on settlement/error/interruption, that never mutates run state or affects cancellation; each settled outcome's `result` is spent from one shared byte budget (`limitBytes`, default 32768) across the whole response in the caller's requested order, never settlement race order — complete when it fits, `resultTruncated: true` plus the existing `outputRef`/`diagnosticsRef` when it does not, never a fixed-length teaser regardless of size; a target's evidence is never read from disk once that shared budget is already exhausted. `cancel` targets one stable ID and preserves its reason. An unresolvable `wf_...` ID fails as unknown/unavailable across `inspect`/`cancel`/`wait`/batch `inspect`, never falling through to the agent-only durable reader. No routine run event sends a parent message or notification.
44
- - `list`, `inspect`, and `wait` share one run projection (`src/core/run-projection.ts`: `projectLiveAgent`/`projectDurableAgent` for agents, `projectWorkflowRun` for workflows) instead of independent per-surface field extraction. Task identity includes the resolved profile and an explicit `harness` (registered pi-* config, or equal to backend for agy/claude/codex/grok/muse), persisted at the source (run-record metadata, live progress nodes, workflow child snapshots) by every backend — never reparsed or guessed from a profile/subagentType name.
52
+ - `list`, `inspect`, and `wait` share one run projection (`src/core/run-projection.ts`: `projectLiveAgent`/`projectDurableAgent` for agents, `projectWorkflowRun` for workflows) instead of independent per-surface field extraction. Task identity includes the resolved profile and an explicit `harness` (registered pi-* config, or equal to backend for agy/claude/codex/grok/muse/opencode), persisted at the source (run-record metadata, live progress nodes, workflow child snapshots) by every backend — never reparsed or guessed from a profile/subagentType name.
45
53
 
46
54
  ## Workflow contract
47
55
 
48
- `workflow` remains trusted JavaScript orchestration over the same external-only role roster, including all five CLI harnesses and registered named Pi harnesses. Its first statement declares `meta.apiVersion: 1`; missing/unsupported versions fail before child launch. Every workflow `agent()` child uses the same `role`/optional `harness` resolution as `Agent`, with legacy exact `subagent_type` available as an escape hatch. A workflow run freezes one catalog snapshot up front — built-in roles, shared roles, exact overrides, and the harness model, thinking, and resource preset — so a later edit to `settings.json`, `roles/`, or `overrides/` cannot desync what the replay fingerprint recorded from what that run executed. Replay reuses a prefix only when the effective descriptor is identical; changed instructions invalidate reuse. Successful calls return their value; failed, cancelled, and timed-out calls throw structured catchable child errors. Explicitly handled errors permit siblings to finish; escaping errors fail the workflow and drain active siblings.
56
+ `workflow` remains trusted JavaScript orchestration over the same external-only role roster, including all six CLI harnesses and registered named Pi harnesses. Its first statement declares `meta.apiVersion: 1`; missing/unsupported versions fail before child launch. Every workflow `agent()` child uses the same `role`/optional `harness` resolution as `Agent`, with legacy exact `subagent_type` available as an escape hatch. A workflow run freezes one catalog snapshot up front — built-in roles, shared roles, exact overrides, and the harness model, thinking, and resource preset — so a later edit to `settings.json`, `roles/`, or `overrides/` cannot desync what the replay fingerprint recorded from what that run executed. Replay reuses a prefix only when the effective descriptor is identical; changed instructions invalidate reuse. Successful calls return their value; failed, cancelled, and timed-out calls throw structured catchable child errors. Explicitly handled errors permit siblings to finish; escaping errors fail the workflow and drain active siblings.
49
57
 
50
58
  Replay is explicit through a persisted `scriptPath` plus `resumeFromRunId`. Reuse only the longest unchanged successful prefix; changed or unsuccessful calls and their suffix execute again. Never describe script recomposition as making child reruns free or side-effect-free. There is no live steering or automatic repaired-script retry.
51
59
 
52
- Use workflows for requested fan-out or multi-agent orchestration across agy, Claude, Codex, Grok, Muse, and registered named Pi harnesses.
60
+ Use workflows for requested fan-out or multi-agent orchestration across agy, Claude, Codex, Grok, Muse, OpenCode, and registered named Pi harnesses.
53
61
 
54
62
  ## Essential test mandate
55
63
 
@@ -57,11 +65,11 @@ Use workflows for requested fan-out or multi-agent orchestration across agy, Cla
57
65
  - Test public contracts, trust/security boundaries, process lifecycle, cancellation/timeouts, harness-registration rollback, the zero-generated-file catalog, one-time settings conversion, the purge inventory, workflow execution/resume, and receipt integrity.
58
66
  - Do not test cosmetic rendering variations, prompt prose fragments, trivial accessors, or a model's interpretation of instructions.
59
67
  - Do not repeat the same behavior at unit, integration, and E2E levels. A regression test must replace or extend overlapping coverage.
60
- - `npm test` must remain deterministic and offline. Real-provider E2E is opt-in and change-triggered. Its default lane never prompts a root LLM to pick the tool call — it builds an in-process Pi SDK session with a faux, never-streamed root model and calls the `Agent`/`workflow`/`external_runs` tool executors directly, so only the selected external backend's own child (a real spawned CLI process, or, for `pi`, a real in-process nested Pi child) is real: `npm run e2e -- --backend <claude|codex|agy|grok|muse> [--workflow|--interrupt]`, or `npm run e2e -- --backend pi --harness <registered-name> [--workflow|--interrupt]` for a named Pi harness the caller has already registered under `harnesses` in their own real `settings.json` with real credentials configured. A separate `--routing-smoke` lane spawns a real `pi` CLI process with a real root model and a plain-language instruction to exercise natural-language tool-call routing end to end; it requires real root Pi auth/config and is never the default. An auth-blocked lane is not passing evidence.
68
+ - `npm test` must remain deterministic and offline. Real-provider E2E is opt-in and change-triggered. Its default lane never prompts a root LLM to pick the tool call — it builds an in-process Pi SDK session with a faux, never-streamed root model and calls the `Agent`/`workflow`/`external_runs` tool executors directly, so only the selected external backend's own child (a real spawned CLI process, or, for `pi`, a real in-process nested Pi child) is real: `npm run e2e -- --backend <claude|codex|agy|grok|muse|opencode> [--workflow|--interrupt]`, or `npm run e2e -- --backend pi --harness <registered-name> [--workflow|--interrupt]` for a named Pi harness the caller has already registered under `harnesses` in their own real `settings.json` with real credentials configured. A separate `--routing-smoke` lane spawns a real `pi` CLI process with a real root model and a plain-language instruction to exercise natural-language tool-call routing end to end; it requires real root Pi auth/config and is never the default. An auth-blocked lane is not passing evidence.
61
69
 
62
70
  ## Verification and release
63
71
 
64
72
  - Run `npm run check`, `npm pack --dry-run --json`, and `npm audit --omit=dev --audit-level=high` before release.
65
73
  - Follow `docs/field-testing.md` for real-backend checks and `docs/releasing.md` for the automated release flow, versioning, trusted publishing, and registry verification.
66
- - Releases are automated: merging a PR that bumps the version (labelled `release:patch|minor|major|prerelease`, or `release:none` to skip) tags the commit, publishes to npm via OIDC trusted publishing, and creates the GitHub release. `release:none` PRs must not change shipped files without a bump; CI enforces the label/version/CHANGELOG contract. For this redesign, the product owner explicitly chose to remain on v2: target `2.6.0-external.0` with `release:minor`. Removing slash commands and moving to settings v4 still requires an explicit breaking-update notice: and put the upgrade notice (removed commands, one-time `/external settings convert`, originals kept, v4 ignores old paths, purge deletes only selected legacy copies, downgrade after purge needs the user's backup) in the CHANGELOG section that becomes the GitHub release notes. Do not hide that break in a patch note.
74
+ - Releases are automated: merging a PR that bumps the version (labelled `release:patch|minor|major|prerelease`, or `release:none` to skip) tags the commit, publishes to npm via OIDC trusted publishing, and creates the GitHub release. `release:none` PRs must not change shipped files without a bump; CI enforces the label/version/CHANGELOG contract. The v4 redesign shipped as `2.6.0-external.0` with `release:minor`. Subsequent command removals still require explicit old→new mappings in the CHANGELOG section that becomes the GitHub release notes. Existing v4 settings require no new conversion for harness toggles. Pre-v4 conversion preserves originals; purge deletes only selected legacy copies, and downgrade after purge needs the user's backup. Do not hide command breaks in a patch note.
67
75
  - Never republish an existing npm version or force-push release history.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,23 @@ All notable changes to pi-flow external are documented here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## [2.9.0-external.0] - 2026-09-25
8
+ ### Added
9
+
10
+ - OpenCode (`opencode`) as a sixth CLI backend, with JSONL progress, verified-final-step completion, session resume, native root-step usage, cancellation, and durable receipts. Targets OpenCode 2 only (verified against `@opencode/cli` 2.0.16); OpenCode 1.x is not supported. Every run uses `--standalone`, a private server the run owns, and never touches the shared background service. Success is verified from the persisted session export: a new user turn for this prompt, a `succeeded` idle outcome, and a clean final assistant message whose text is the result. It never relies on the zero exit code or streamed narration alone. Restricted tiers use native deny-by-default tool rules, not an OS sandbox, and select their agent with `--agent` on every run, including resumes. Unsafe resumes (a restricted resume of a session with its own permission rules, or a danger resume of a restricted session) are refused. A pinned thinking level is passed as the pinned model's `#variant`, which OpenCode validates. Structured schemas are rejected. Nested-subagent total cost is unknown, and budgets are unenforceable.
11
+ - Whole-harness toggles through `/external config enable|disable <harness>` and settings v4 `disabledHarnesses`. Disabling preserves definitions, hides executable availability, and rejects direct/exact/resumed selections and new workflow replay without fallback. Active work retains its snapshot. `/external config default <harness>` selects an enabled global default; doctor skips disabled readiness checks.
12
+
13
+ ### Changed — command migration
14
+
15
+ - Harness-centric configuration is consolidated under `/external config`. Replace `/external settings`, `settings edit`, and `settings convert` with `/external config`, `config edit`, and `config convert`; replace `/external harnesses` and `/external harness create` with `/external config harnesses` and `/external config harness create`. Old routes are removed, not aliases. `/external role …`, runs, workflows, and optional purge remain separate.
16
+ - Existing v4 installations need no new storage conversion. Pre-v4 conversion is still explicit, keeps originals, and is available through `/external config convert`. Documentation, command help, tool descriptions, and coordinator guidance use the new surface.
17
+
18
+ ## [2.8.0-external.0] - 2026-09-24
19
+
20
+ ### Added
21
+ - Expand `/external doctor` with CLI readiness, supported login-status checks, and secret-safe inherited authentication-setting warnings.
22
+ - Report project-scoped historical usage-limit evidence from retained summaries, including reset times and later successful runs. Capture new Claude structured rejections and Antigravity terminal quota errors without live quota probes.
23
+
7
24
  ## [2.7.0-external.0] - 2026-09-24
8
25
 
9
26
  ### Added
package/CONTEXT.md CHANGED
@@ -4,14 +4,14 @@ This package is a fork of pi-flow whose `Agent` and `workflow` tools are reserve
4
4
 
5
5
  ## Domain language
6
6
 
7
- - **Settings:** The only execution-configuration file, `$PI_CODING_AGENT_DIR/pi-flow-external/settings.json` version 4. It holds the default harness, concurrency, timeouts, permission and budget defaults, retention, named `pi-*` harness entries, and `disabledProfiles`. The five CLI harnesses (`agy`, `claude`, `codex`, `grok`, `muse`) are built in.
8
- - **Harness:** Where and how a role executes. One of the five CLI harnesses, or a registered `pi-<label>` entry in settings. Discovery and readiness are separate: a listed harness may still be uninstalled or unauthenticated.
7
+ - **Settings:** The only execution-configuration file, `$PI_CODING_AGENT_DIR/pi-flow-external/settings.json` version 4. It holds the default harness, concurrency, timeouts, permission and budget defaults, retention, named `pi-*` harness entries, `disabledProfiles`, and `disabledHarnesses`. The six CLI harnesses (`agy`, `claude`, `codex`, `grok`, `muse`, `opencode`) are built in.
8
+ - **Harness:** Where and how a role executes. One of the six CLI harnesses, or a registered `pi-<label>` entry in settings. Discovery and readiness are separate: a listed harness may still be uninstalled or unauthenticated.
9
9
  - **Role:** The work instructions and description. A role does not grant authority. Six built-ins (explorer, planner, implementer, reviewer, qa, worker) exist in memory for every CLI harness and every registered named Pi harness. A user-authored shared role is one file, `pi-flow-external/roles/<role>.md`, with `description` only, and it applies to every harness. `roles/reviewer.md` replaces that built-in everywhere.
10
10
  - **Exact override:** `pi-flow-external/overrides/<harness>-<role>.md`, a complete replacement of one execution identity, using the existing profile schema. `tools` is enforced only on a named Pi harness. Nonstandard legacy names stay selectable through exact `subagent_type`.
11
11
  - **Resolved profile:** The internal execution object produced by binding a role definition to a harness. It is not a user-facing command and it is not a file under Pi's native `subagents/` directory. Precedence is exact override, then shared role, then built-in.
12
12
  - **Native Pi subagent:** A subagent file owned by Pi's native subagent system. This extension does not load or modify those files. They are not entries in the external catalog.
13
- - **Agent call:** A direct external delegation selected by `role` and optional `harness` (`agy`, `claude`, `codex`, `grok`, `muse`, or a registered named Pi harness), or by legacy exact `subagent_type`.
14
- - **Named Pi harness configuration:** A `pi-<label>` entry in settings v4 `harnesses`, pinning a `provider/model` id and a thinking level, run in-process via Pi's own SDK rather than as a spawned CLI. `harness` selects *which* configuration; `backend` (always the literal `"pi"` for these) selects the execution mechanism. The six built-in roles are available on it immediately from the same canonical source as the five CLI harnesses, with zero generated files.
13
+ - **Agent call:** A direct external delegation selected by `role` and optional `harness` (`agy`, `claude`, `codex`, `grok`, `muse`, `opencode`, or a registered named Pi harness), or by legacy exact `subagent_type`.
14
+ - **Named Pi harness configuration:** A `pi-<label>` entry in settings v4 `harnesses`, pinning a `provider/model` id and a thinking level, run in-process via Pi's own SDK rather than as a spawned CLI. `harness` selects *which* configuration; `backend` (always the literal `"pi"` for these) selects the execution mechanism. The six built-in roles are available on it immediately from the same canonical source as the six CLI harnesses, with zero generated files.
15
15
  - **Parent context:** An opt-in frozen text snapshot: `none` (default), `recent` with last N user turns including the current turn, or `full` available post-compaction conversation. It is background for a new external conversation, not a native session clone, system-prompt inheritance, or guaranteed cache reuse. Sharing excludes thinking and pending calls and cannot be combined with child `resume`. Workflow children share one invocation-time snapshot; prior child outputs remain explicit inputs.
16
16
  - **Workflow call:** Trusted JavaScript orchestration beginning with `meta.apiVersion: 1` that may fan out several explicit external `agent()` calls. Each child returns a value or throws a catchable `ChildRunError`; escaping child errors fail the workflow and drain siblings.
17
17
  - **Background run:** A validated, registered `Agent` or `workflow` invocation that returns a stable handle while work remains owned by the originating session. It is not a daemon or cross-session job.
@@ -27,21 +27,21 @@ This package is a fork of pi-flow whose `Agent` and `workflow` tools are reserve
27
27
 
28
28
  ## Selection
29
29
 
30
- Claude Code, Codex CLI, Antigravity, Grok Build CLI, Muse Code, and registered named Pi harnesses are selected through this extension's `Agent` or `workflow`. A role is the work. A harness is the execution environment. Pi's native subagent files stay outside the catalog and are not modified.
30
+ Claude Code, Codex CLI, Antigravity, Grok Build CLI, Muse Code, OpenCode, and registered named Pi harnesses are selected through this extension's `Agent` or `workflow`. A role is the work. A harness is the execution environment. Pi's native subagent files stay outside the catalog and are not modified.
31
31
 
32
- The ordinary driver sees `Agent`, optional `workflow`, and read-only `external_help`. `pi_flow_role_create` is activated only by `/external role create`. `pi_flow_harness_create` is activated only by `/external harness create`. External children start in the requested working directory, but backend-native nested helpers may create or use a separate workspace; prompts that request further nesting should include explicit absolute paths and all required context. Named Pi harness children are the one exception to "backend-native nested helpers may use a different workspace": a pi child runs in-process with no extensions, prompt templates, or themes loaded. The registration preset chooses skills: `minimal` leaves them unloaded, and `skills` loads installed skills, including project skills only when the project is trusted. Extensions stay unloaded, so the child still cannot launch a further nested agent through an extension.
32
+ The ordinary driver sees `Agent`, optional `workflow`, and read-only `external_help`. `pi_flow_role_create` is activated only by `/external role create`. `pi_flow_harness_create` is activated only by `/external config harness create`. External children start in the requested working directory, but backend-native nested helpers may create or use a separate workspace; prompts that request further nesting should include explicit absolute paths and all required context. Named Pi harness children are the one exception to "backend-native nested helpers may use a different workspace": a pi child runs in-process with no extensions, prompt templates, or themes loaded. The registration preset chooses skills: `minimal` leaves them unloaded, and `skills` loads installed skills, including project skills only when the project is trusted. Extensions stay unloaded, so the child still cannot launch a further nested agent through an extension.
33
33
 
34
34
  ## User control surface
35
35
 
36
- User operations are namespaced under `/external`: overview, doctor, settings, `settings edit`, `settings convert` (the one-time pre-v4 preview and apply; `settings` points there), harnesses, `harness create`, roles, `role create`, `role inspect`, `role override`, the literal `[danger]purge-old-files` maintenance command, workflows, interactive run navigation, durable run summary/pruning, and help. The next invocation reads the live configuration. A concurrency-cap change waits until active and queued work drains. A workflow already running keeps its frozen snapshot. `/external profiles`, `/external profile create`, and `/pi-flow-profile create` are removed and are not aliases. `/external runs` uses the same registry/readers as `external_runs`, including every cursor-paged workflow child and output/diagnostic page. Settings v4 is the only execution-configuration file, including named Pi harness registrations. Shared roles and exact overrides are the extension-owned markdown. A trusted project may override only `defaultHarness`, which may itself name a registered `pi-*` harness. After version 4 is active, old `subagents/` profiles, seed markers, and `harnesses.json` are ignored. Purge is optional, destructive, and separate from conversion.
36
+ User operations are namespaced under `/external`: overview, doctor, `config` (harness-centric settings and defaults), `config edit`, `config convert` (the one-time pre-v4 preview and apply), `config harnesses`, `config harness create`, `config enable`, `config disable`, `config default`, roles, `role create`, `role inspect`, `role override`, the literal `[danger]purge-old-files` maintenance command, workflows, interactive run navigation, durable run summary/pruning, and help. The next invocation reads the live configuration. A concurrency-cap change waits until active and queued work drains. A workflow already running keeps its frozen snapshot. `/external profiles`, `/external profile create`, and `/pi-flow-profile create` are removed and are not aliases. `/external runs` uses the same registry/readers as `external_runs`, including every cursor-paged workflow child and output/diagnostic page. Settings v4 is the only execution-configuration file, including named Pi harness registrations. Shared roles and exact overrides are the extension-owned markdown. A trusted project may override only `defaultHarness`, which may itself name a registered `pi-*` harness. After version 4 is active, old `subagents/` profiles, seed markers, and `harnesses.json` are ignored. Purge is optional, destructive, and separate from conversion.
37
37
 
38
38
  > Next agent: full implementation map is `docs/ARCHITECTURE_SNAPSHOT.md` (configuration contract 2026-09-23; execution-adapter notes last verified 2026-09-21). Keep this file and `AGENTS.md` as the persistent breadcrumbs. Files under `docs/plans/` are dated records. They are not rewritten, and they are not a second runtime contract. Where a plan disagrees with `AGENTS.md`, this file, `README.md`, or the architecture snapshot, those current docs win.
39
39
 
40
40
  ## Design stance
41
41
 
42
- Guardrails exist to keep lanes from bleeding into each other (a reviewer that edits, a worker that fixes), not to constrain how a model works. Role bodies stay short: define the job, state the boundary, then get out of the way and trust the model's judgment on approach, depth, and method. The generic `worker` role covers non-coding tasks with no method constraints at all. The shipped roster is five code-oriented roles (explorer, planner, implementer, reviewer, qa) plus `worker`, in memory for `agy`, `claude`, `codex`, `grok`, `muse`, and every registered named Pi harness. Other roles are user-created once through `/external role create`. Authority is the call's `permission`, or `defaultPermission` when the call omits it. The default remains `danger`. A role file cannot raise or lower that tier.
42
+ Guardrails exist to keep lanes from bleeding into each other (a reviewer that edits, a worker that fixes), not to constrain how a model works. Role bodies stay short: define the job, state the boundary, then get out of the way and trust the model's judgment on approach, depth, and method. The generic `worker` role covers non-coding tasks with no method constraints at all. The shipped roster is five code-oriented roles (explorer, planner, implementer, reviewer, qa) plus `worker`, in memory for `agy`, `claude`, `codex`, `grok`, `muse`, `opencode`, and every registered named Pi harness. Other roles are user-created once through `/external role create`. Authority is the call's `permission`, or `defaultPermission` when the call omits it. The default remains `danger`. A role file cannot raise or lower that tier.
43
43
 
44
- Role resolution is mechanical, not model-routed. The identity is `<harness>-<role>` (`agy-reviewer` is role `reviewer` on harness `agy`). Precedence is exact override, then shared role, then built-in. The selected role and harness must resolve to that exact identity; unavailable roles report their supported harnesses and do not fall back. Identities listed in `disabledProfiles` are blocked for both `role` and exact `subagent_type`. Nonstandard names remain available only through legacy exact `subagent_type`. The resolved profile stays authoritative for its instructions and model. Permission is not stored on the role.
44
+ Role resolution is mechanical, not model-routed. The identity is `<harness>-<role>` (`agy-reviewer` is role `reviewer` on harness `agy`). Precedence is exact override, then shared role, then built-in. The selected role and harness must resolve to that exact identity; unavailable roles report their supported harnesses and do not fall back. Harnesses listed in `disabledHarnesses` are absent from executable discovery and blocked for both `role` and exact `subagent_type`; configuration and overrides remain intact. `disabledProfiles` independently blocks individual identities. Toggles affect new invocations, not active children or frozen workflows. No disabled default silently falls back to another harness. Nonstandard names remain available only through legacy exact `subagent_type`. The resolved profile stays authoritative for its instructions and model. Permission is not stored on the role.
45
45
 
46
46
  The always-visible parent guidance is a compact role catalog and a short operating guide. `external_help` topic `usage` is the worked playbook. Topics `roles`, `permissions`, and `workflow` are the references, returned only when requested. Catalog availability reflects the resolved roster, not CLI installation or authentication.
47
47
 
@@ -55,6 +55,8 @@ Applied to the Grok Build CLI, permission tiers map onto its own kernel-enforced
55
55
 
56
56
  Applied to Muse Code, approval and its own sandbox are ON by default, so every tier passes `--disable-approval` (a headless run must never hang on an interactive prompt). `readonly` additionally strips non-shell writes and shell execution (`--disable-write --disable-shell`); `edit` leaves the sandbox enabled with only approval bypassed, so shell/write stay available within it. A role name does not change that mapping. `danger` uses `--yolo`, which disables approval and the sandbox *and additionally trusts the workspace for this run* (loads its skills/rules) — a materially broader grant than an unsandboxed run alone, so its disclosure names that extra trust rather than collapsing it into the same terse label every other danger tier uses. `resume` was verified against the real CLI (`exec --session-id`, not the separate interactive-only `resume` subcommand): two independent processes sharing one session id shared context, confirmed by the second recalling a fact only told to the first. Usage/cost has never been observed on any run, so it is reported unknown rather than a fabricated zero or estimate. Root-run ownership is checked explicitly: only envelopes whose `payload.run_stream.id` matches the id established from that same process's own `runtime.command.accepted`/`session.run.linked` bootstrap pair can finalize, fail, or contribute partial output to the result, so a nested or foreign-run terminal event can never masquerade as the root's own answer, and `sessionId` is captured once from that bootstrap rather than from every subsequent envelope. Nested-agent (sub-delegation) detection always reports false — every probe run only ever produced internal `reminder.agent.*` skill-reminder tasks, never an actual delegation, so there is no confirmed event shape to key off, and a speculative match would risk spuriously extending the nested-timeout deadline on ordinary internal task activity.
57
57
 
58
+ OpenCode (OpenCode 2 only) runs `run --standalone --format json`, a private per-run server, with stdin prompts and `--session` resume. The stream is progress only. Success requires zero exit, no error event, and a bounded `session export` proving this invocation's new user turn ended in a `succeeded` idle outcome, with a clean `stop` final assistant message that supplies the result. Restricted runs pass their injected agent with `--agent`, resumes included, and the export must show that agent. Restricted tiers use a deny-by-default injected agent, not an OS sandbox; plugins and MCP servers still load. A pinned thinking level is a model variant and needs a pinned model. Structured schemas are rejected. Usage is this turn's exported assistant messages. Native subagent-child usage is not included, so total cost is unknown after delegation. See README for the complete limitations.
59
+
58
60
  ## Known inelegance
59
61
 
60
62
  <!-- ponytail: shared roles removed the role x harness file product; keep backend-specific prompts in exact overrides. Do not grow a second inheritance language. -->
package/README.md CHANGED
@@ -7,6 +7,7 @@ External agent delegation for [pi](https://github.com/earendil-works/pi). A **ro
7
7
  - [Codex CLI](https://github.com/openai/codex) (`codex`)
8
8
  - [Grok Build CLI](https://github.com/xai-org/grok-build) (`grok`)
9
9
  - Muse Code (`muse`)
10
+ - [OpenCode](https://opencode.ai/docs/cli/) (`opencode`)
10
11
  - Named Pi harnesses (`pi-<label>`) — in-process, per-model configs you register yourself, not a spawned CLI
11
12
 
12
13
  Six built-in roles are available in memory on every one of those harnesses. A fresh installation writes no profile files.
@@ -18,7 +19,7 @@ The ordinary driver has four tools (`workflow` can be disabled):
18
19
  - `external_help` returns the usage playbook, role details, permission behavior, or workflow guidance on demand.
19
20
  - `external_runs` lists, inspects, waits for, and cancels session-owned runs.
20
21
 
21
- `Agent` and `workflow` accept a role with an optional harness — `agy`, `claude`, `codex`, `grok`, `muse`, or a registered `pi-*` name. A registered Pi harness uses that same selection. Legacy exact `subagent_type` remains available and cannot be combined with `role` or `harness`. `pi_flow_role_create` is active only during `/external role create`. `pi_flow_harness_create` is active only during `/external harness create`. Worked calls are `external_help` topic `usage`.
22
+ `Agent` and `workflow` accept a role with an optional harness — `agy`, `claude`, `codex`, `grok`, `muse`, `opencode`, or a registered `pi-*` name. A registered Pi harness uses that same selection. Legacy exact `subagent_type` remains available and cannot be combined with `role` or `harness`. `pi_flow_role_create` is active only during `/external role create`. `pi_flow_harness_create` is active only during `/external config harness create`. Worked calls are `external_help` topic `usage`.
22
23
 
23
24
  ## Install
24
25
 
@@ -54,9 +55,10 @@ codex --version
54
55
  agy --version
55
56
  grok --version
56
57
  muse --version
58
+ opencode --version
57
59
  ```
58
60
 
59
- Pi's coordinator model and the external CLIs authenticate independently. A working Claude, Codex, Antigravity, Grok, or Muse login does not authenticate the root Pi model. The Grok Build CLI installs and authenticates entirely separately from Pi: install with `curl -fsSL https://x.ai/cli/install.sh | bash`, then authenticate with `grok login` or an `XAI_API_KEY` environment variable. This integration is verified against Grok Build CLI `1.0.40`. Muse Code likewise installs and authenticates separately from Pi; this integration is verified against Muse Code `1.3.0` against its `meta` provider.
61
+ Pi's coordinator model and the external CLIs authenticate independently. A working Claude, Codex, Antigravity, Grok, Muse, or OpenCode login does not authenticate the root Pi model. The Grok Build CLI installs and authenticates entirely separately from Pi: install with `curl -fsSL https://x.ai/cli/install.sh | bash`, then authenticate with `grok login` or an `XAI_API_KEY` environment variable. This integration is verified against Grok Build CLI `1.0.40`. Muse Code likewise installs and authenticates separately from Pi; this integration is verified against Muse Code `1.3.0` against its `meta` provider.
60
62
 
61
63
  External agents use the effective permission tier and each harness's native mechanism:
62
64
 
@@ -65,6 +67,7 @@ External agents use the effective permission tier and each harness's native mech
65
67
  - Antigravity: only `--dangerously-skip-permissions`. `readonly` and `edit` are rejected.
66
68
  - Grok: `--sandbox read-only`, `workspace`, or `off`, always alongside `--permission-mode bypassPermissions` — bypass only skips the interactive approval prompt; the kernel sandbox remains the enforced boundary. `readonly`'s network-blocking guarantee is Linux-only (a no-op on macOS), and sandbox startup can fail closed on some macOS hosts (for example when `/var/run/docker.sock` resolves to a symlink) rather than silently running unsandboxed.
67
69
  - Muse: every tier passes `--disable-approval` (approval and Muse's own sandbox are ON by default, and headless runs must not hang on an interactive prompt); `readonly` additionally passes `--disable-write --disable-shell`; `edit` leaves the sandbox enabled with only approval bypassed; `danger` uses `--yolo`, which disables approval and the sandbox and additionally trusts the workspace for this run (loads its skills/rules) — a broader grant than an unsandboxed run alone.
70
+ - OpenCode: `danger` uses `--auto`; restricted tiers inject deny-by-default native tool rules. These are not an OS sandbox; plugins and MCP still load. See the OpenCode section for restrictions.
68
71
  - Named Pi harnesses: a curated tool list (`read`/`grep`/`find`/`ls` at `readonly`; those plus `edit`/`write` at `edit`), or the SDK's own default active tools (`read`/`bash`/`edit`/`write`) at `danger`. That list is not an OS sandbox. The registration preset is `minimal` (skills stay unloaded; the default when the field is absent) or `skills` (installed skills load; project skills load only when the project is trusted). Extensions, prompt templates, and themes stay unloaded.
69
72
 
70
73
  The effective tier is the call's `permission`, or settings `defaultPermission` when the call omits it. The default is `danger`. A role does not change the tier. Claude refuses bypass mode when its effective UID is `0`; danger then uses `--permission-mode auto`. Claude `edit` denies Bash headlessly. Pass `danger` on the call when the task needs a shell. Run external agents only in repositories you trust and state whether each task is read-only or may edit files.
@@ -73,19 +76,21 @@ The TUI labels a direct run with its effective access, including `unsandboxed ex
73
76
 
74
77
  ## Breaking upgrade
75
78
 
76
- Settings are version 4. This v2 minor release includes breaking configuration and command changes; the product owner chose `2.6.0-external.0` (`release:minor`) rather than a v3 bump. The changelog and GitHub release notes carry this notice. Earlier notes under `docs/plans/` stay historical; this section is the current contract.
79
+ Settings remain version 4; existing v4 installations need no new conversion. Harness management now lives under `/external config`: replace `/external settings …` with `/external config …`, `/external harnesses` with `/external config harnesses`, and `/external harness create` with `/external config harness create`. These old command routes are removed, not aliases. `/external role …`, runs, and workflows are unchanged.
80
+
81
+ The earlier v4 storage redesign shipped in `2.6.0-external.0`. Its pre-v4 conversion remains available as described below. Earlier notes under `docs/plans/` stay historical; this section is the current contract.
77
82
 
78
83
  `/external profiles`, `/external profile create`, and `/pi-flow-profile create` are removed. They are not aliases, redirects, or hidden handlers. An unknown `/external` command lists the commands below. `Agent` and `workflow` still accept exact `subagent_type`. That API is unchanged.
79
84
 
80
85
  | Removed command | Replacement |
81
86
  |---|---|
82
87
  | `/external profiles` | `/external roles` |
83
- | `/external profile create` | `/external role create` for a shared role; `/external harness create` for a named Pi harness; `/external role override <role> <harness>` for one exact execution file |
88
+ | `/external profile create` | `/external role create` for a shared role; `/external config harness create` for a named Pi harness; `/external role override <role> <harness>` for one exact execution file |
84
89
  | `/pi-flow-profile create` | The same three commands. This spelling is removed. |
85
90
 
86
91
  ### One-time conversion
87
92
 
88
- `/external settings` shows the effective values and, when a pre-v4 installation is present, points at `/external settings convert`. That command previews the conversion and applies it after confirmation. There is no `/external migrate` command. A pre-v4 installation — settings older than version 4, a `pi-flow-external/harnesses.json` file, or a historical seed marker — gets an actionable setup error on delegation until conversion finishes. `/external settings` stays available while delegation is blocked.
93
+ `/external config` shows the effective values and, when a pre-v4 installation is present, points at `/external config convert`. That command previews the conversion and applies it after confirmation. There is no `/external migrate` command. A pre-v4 installation — settings older than version 4, a `pi-flow-external/harnesses.json` file, or a historical seed marker — gets an actionable setup error on delegation until conversion finishes. `/external config` stays available while delegation is blocked.
89
94
 
90
95
  Conversion runs once:
91
96
 
@@ -97,7 +102,7 @@ Conversion runs once:
97
102
 
98
103
  Once version 4 is active, delegation reads only `settings.json`, `roles/`, and `overrides/`. Old `subagents/` profiles, seed markers, and `harnesses.json` are ignored. This release does not write both layouts, and it does not keep the old layout as a fallback resolver.
99
104
 
100
- A shared role is one file for every harness. `roles/reviewer.md` replaces the built-in reviewer on `agy`, `claude`, `codex`, `grok`, `muse`, and every named Pi harness:
105
+ A shared role is one file for every harness. `roles/reviewer.md` replaces the built-in reviewer on `agy`, `claude`, `codex`, `grok`, `muse`, `opencode`, and every named Pi harness:
101
106
 
102
107
  ```md
103
108
  ---
@@ -144,7 +149,7 @@ Downgrade after a purge needs your own backup of the deleted files. Originals re
144
149
 
145
150
  ## Quick start
146
151
 
147
- The six built-in roles — explorer, planner, implementer, reviewer, qa, and worker — are already available on `agy`, `claude`, `codex`, `grok`, `muse`, and any named Pi harness you register. Nothing has to be authored first.
152
+ The six built-in roles — explorer, planner, implementer, reviewer, qa, and worker — are already available on `agy`, `claude`, `codex`, `grok`, `muse`, `opencode`, and any enabled named Pi harness you register. Nothing has to be authored first.
148
153
 
149
154
  Check the setup:
150
155
 
@@ -152,7 +157,7 @@ Check the setup:
152
157
  /external
153
158
  /external doctor
154
159
  /external roles
155
- /external harnesses
160
+ /external config
156
161
  ```
157
162
 
158
163
  Then delegate by role. The built-in default harness is `agy`:
@@ -163,7 +168,23 @@ Use the Agent tool with role "explorer" and harness "claude" to map this reposit
163
168
 
164
169
  `/external doctor` checks the catalog separately from CLI readiness. A configured harness can still be uninstalled or unauthenticated.
165
170
 
166
- Author one additional role for every harness with `/external role create`. That interview only validates and writes the role. Register a named Pi harness with `/external harness create`. The interview asks for a resource preset, `minimal` or `skills`, then smoke-tests the Pi runtime with that preset before saving. A harness smoke test does not judge role quality.
171
+ Author one additional role for every harness with `/external role create`. That interview only validates and writes the role. Register a named Pi harness with `/external config harness create`. The interview asks for a resource preset, `minimal` or `skills`, then smoke-tests the Pi runtime with that preset before saving. A harness smoke test does not judge role quality.
172
+
173
+ ### Choose which harnesses are available
174
+
175
+ Use `/external config` for harness-centric configuration. Roles remain under `/external role …`.
176
+
177
+ ```text
178
+ /external config harnesses
179
+ /external config default claude
180
+ /external config disable muse
181
+ /external config enable muse
182
+ /external config edit
183
+ ```
184
+
185
+ All harnesses start enabled. Disabling a harness preserves its configuration and overrides, removes it from executable discovery, and blocks both role-based and exact-identity calls. It never switches a call to another harness. Choose another default before disabling the current default. Existing children and already-running workflows keep their invocation-time configuration.
186
+
187
+ `disabledHarnesses` controls whole harnesses, including individual named `pi-*` registrations; `disabledProfiles` still controls individual execution identities. Re-enabling a harness does not clear those per-identity exclusions. `/external doctor` reports disabled harnesses without probing their readiness.
167
188
 
168
189
  ## Commands
169
190
 
@@ -173,11 +194,14 @@ All user commands use the `/external` namespace:
173
194
  |---|---|
174
195
  | `/external` | Overview: default harness and its source, roles, harnesses, settings path, and actionable problems |
175
196
  | `/external doctor` | Validate the catalog, then report CLI and named-Pi readiness separately |
176
- | `/external settings` | Effective values, harness source, and the canonical JSON path. Points at conversion when a pre-v4 installation is present |
177
- | `/external settings edit` | Edit and validate canonical settings in the standard editor |
178
- | `/external settings convert` | Preview and apply the one-time version 4 conversion |
179
- | `/external harnesses` | List the five CLI harnesses and named Pi harnesses. Readiness is separate from registration |
180
- | `/external harness create` | Register one named Pi harness. No role interview |
197
+ | `/external config` | Harness-centric configuration overview and actions; effective values and canonical JSON path |
198
+ | `/external config edit` | Edit and validate canonical settings in the standard editor |
199
+ | `/external config convert` | Preview and apply the one-time version 4 conversion |
200
+ | `/external config harnesses` | List CLI and named Pi harnesses, including disabled entries. Readiness is separate from registration |
201
+ | `/external config harness create` | Register one named Pi harness. No role interview |
202
+ | `/external config enable <harness>` | Enable a CLI or named Pi harness without changing its definitions |
203
+ | `/external config disable <harness>` | Disable a harness for new calls; preserve configuration |
204
+ | `/external config default <harness>` | Choose an enabled global default; trusted project overrides still take precedence |
181
205
  | `/external roles` | Six built-ins plus user roles, with restrictions and overrides. The list is not the role × harness product |
182
206
  | `/external role create` | Author one reusable role |
183
207
  | `/external role inspect <role> [harness]` | Effective instructions, source, model, and the backend's real authority for the default permission |
@@ -189,7 +213,9 @@ All user commands use the `/external` namespace:
189
213
  | `/external runs --prune` | Prune eligible completed run records |
190
214
  | `/external help` | Show the command reference |
191
215
 
192
- `/external doctor` reports CLI availability separately from provider authentication, and separately from whether the catalog itself is valid.
216
+ `/external doctor` reports CLI availability separately from provider authentication and catalog validity. Claude and Codex use their non-interactive login-status commands; other CLIs report login as unverified. Credential presence does not prove a request will succeed. Known inherited authentication/routing environment variables are listed by name only, never value. Native status commands may access credential storage or perform network activity; no model prompts or quota requests are sent, and no credentials are changed.
217
+
218
+ Doctor also reads retained run summaries for this project and shows the latest recorded usage-limit observation per harness, its source run, any reset time, and whether a later run succeeded. These are historical observations, not current account status. New failed Claude runs can preserve structured rate-limit rejections; Antigravity runs can preserve terminal individual-quota errors. Other backends and older records may have no evidence. Missing, malformed, or oversized summaries are skipped and counted; backend event logs and model answers are never searched. Remaining allowance stays unavailable.
193
219
 
194
220
  A typical overview:
195
221
 
@@ -197,7 +223,7 @@ A typical overview:
197
223
  External agents
198
224
  Default: pi-deepseek (global)
199
225
  Roles: 6 built-in · 1 custom · 1 harness override
200
- Harnesses: 5 CLI · 1 named Pi
226
+ Harnesses: 6 CLI · 1 named Pi
201
227
  Settings: ~/.pi/agent/pi-flow-external/settings.json
202
228
  ```
203
229
 
@@ -222,11 +248,11 @@ Normally `$PI_CODING_AGENT_DIR` is `~/.pi/agent`. Markdown holds authored instru
222
248
  At each direct call the extension loads one catalog snapshot. A workflow freezes that same snapshot for the whole run. Resolution order:
223
249
 
224
250
  1. Harness: explicit call, then the trusted project default, then the global default, then the built-in default (`agy`).
225
- 2. Unknown harnesses and identities listed in `disabledProfiles` are rejected. The call does not switch to another harness.
251
+ 2. Unknown harnesses, harnesses listed in `disabledHarnesses`, and identities listed in `disabledProfiles` are rejected. The call does not switch to another harness.
226
252
  3. Role definition: exact override, then a shared role, then the built-in role.
227
253
  4. That definition is bound to the chosen harness and executed through the existing runners.
228
254
 
229
- An invalid higher-priority override blocks that selection. An unrelated invalid file is reported and does not take a different role offline. Invalid settings JSON, or a settings version this release does not support, blocks delegation. `/external settings`, `/external settings convert`, and `/external role inspect` remain available. The next invocation reads settings, roles, and overrides from disk. A workflow that has already started keeps the snapshot it froze at start. A change to `maxConcurrentSubagents` waits until no subagent is active and none are queued.
255
+ An invalid higher-priority override blocks that selection. An unrelated invalid file is reported and does not take a different role offline. Invalid settings JSON, or a settings version this release does not support, blocks delegation. `/external config`, `/external config convert`, and `/external role inspect` remain available. The next invocation reads settings, roles, and overrides from disk. A workflow that has already started keeps the snapshot it froze at start. A change to `maxConcurrentSubagents` waits until no subagent is active and none are queued.
230
256
 
231
257
  Stable identities stay `<harness>-<role>`. Receipts and workflow descriptors use that identity, or the exact legacy `subagent_type` name when that was the selector. Permission is the call, or `defaultPermission`. It is not a role field.
232
258
 
@@ -286,13 +312,13 @@ A named Pi override adds `backend: pi` and `harness: <registered pi-* name>`. CL
286
312
 
287
313
  ### Built-in roles
288
314
 
289
- explorer, planner, implementer, reviewer, qa, and worker ship in memory for `agy`, `claude`, `codex`, `grok`, and `muse`, and for every registered named Pi harness. Session start writes no default files and no seed markers. Built-in CLI roles leave `model` and `thinking` unpinned, so they track that CLI's own model and the current Pi thinking level. A named Pi harness's six built-ins use that harness's registered model and thinking. Adding a harness writes no role files. Seven authored roles are seven files under `roles/`, whatever the harness count is.
315
+ explorer, planner, implementer, reviewer, qa, and worker ship in memory for `agy`, `claude`, `codex`, `grok`, `muse`, and `opencode`, and for every registered named Pi harness. Session start writes no default files and no seed markers. Built-in CLI roles leave `model` and `thinking` unpinned, so they track that CLI's own model and, where supported, the current Pi thinking level. OpenCode does not forward inherited thinking. A named Pi harness's six built-ins use that harness's registered model and thinking. Adding a harness writes no role files. Seven authored roles are seven files under `roles/`, whatever the harness count is.
290
316
 
291
317
  Disable an execution identity by listing its exact name in `disabledProfiles`, for example `codex-explorer`. That blocks both `role` selection and exact `subagent_type` selection. Deletions captured during conversion are stored the same way. The extension does not recreate a disabled identity.
292
318
 
293
319
  ### Named Pi harness configurations
294
320
 
295
- A named Pi harness runs in-process through Pi's own SDK, for any model Pi can already resolve (built-in, self-hosted, or a custom-registered provider). Register it with `/external harness create`: a `pi-<label>` name, a `provider/model` id, a thinking level (`off`, `minimal`, `low`, `medium`, `high`, or `xhigh`, always stored explicitly), and a resource preset (`minimal` or `skills`). `minimal` is the default and leaves skills unloaded. `skills` loads installed skills, and project skills only when the project is trusted. The smoke test uses the selected preset. Registration is then saved into the `harnesses` object of `settings.json`. A legacy entry that omits `preset` is `minimal`. The five CLI harnesses are built in and do not need entries there.
321
+ A named Pi harness runs in-process through Pi's own SDK, for any model Pi can already resolve (built-in, self-hosted, or a custom-registered provider). Register it with `/external config harness create`: a `pi-<label>` name, a `provider/model` id, a thinking level (`off`, `minimal`, `low`, `medium`, `high`, or `xhigh`, always stored explicitly), and a resource preset (`minimal` or `skills`). `minimal` is the default and leaves skills unloaded. `skills` loads installed skills, and project skills only when the project is trusted. The smoke test uses the selected preset. Registration is then saved into the `harnesses` object of `settings.json`. A legacy entry that omits `preset` is `minimal`. The six CLI harnesses are built in and do not need entries there.
296
322
 
297
323
  ```json
298
324
  {
@@ -330,6 +356,29 @@ Settings writes use a private staged file and a same-directory replacement, and
330
356
 
331
357
  **v1 scope, by design:** a pi child's tools are the SDK builtins (`read`/`bash`/`edit`/`write`, plus `grep`/`find`/`ls` at `readonly`/`edit`). The registration preset selects `minimal` (skills stay unloaded) or `skills` (installed skills load; project skills require a trusted project). Extensions, prompt templates, and themes stay unloaded. The tool list is not an OS sandbox, and `bash` at `danger` is host access. Retry is disabled per pi child (in-memory, never touching your real Pi settings). Pi children cannot resume a prior conversation and have no enforced budget cap. Trusted extensions, MCP, resumable sessions, and budget controls remain [issue #43](https://github.com/tranhoangnguyen03/pi-flow-external/issues/43).
332
358
 
359
+ ### OpenCode
360
+
361
+ Install and authenticate OpenCode separately (`opencode auth login`); use `opencode models` for available `provider/model` IDs. The adapter targets **OpenCode 2** (`@opencode/cli`, verified against **2.0.16**) only; OpenCode 1.x is not supported. Every run is `opencode run --standalone --format json`, which starts a private server that the run owns. It never uses or stops your shared background service (`opencode service`). The prompt goes over stdin. OpenCode 2 has no `--dir`; the run takes its project from `PWD` and the working directory, which the adapter sets to the Pi workspace. An omitted model uses OpenCode's configured default. For an exact override:
362
+
363
+ ```yaml
364
+ backend: opencode
365
+ model: anthropic/claude-sonnet-4-5
366
+ thinking: high
367
+ ```
368
+
369
+ Choose an ID available in your own installation. `thinking` is optional and is passed as the model variant (`--model provider/model#high`), so it needs a pinned `model` and must name a variant that model offers. OpenCode 2 rejects an unknown variant before the prompt runs. Inherited Pi thinking is never forwarded. Structured workflow `schema` is unsupported and rejected; plain text results remain available.
370
+
371
+ `danger` uses `--auto`; explicit OpenCode deny rules still apply. `readonly` and `edit` inject a private, deny-by-default agent through `OPENCODE_CONFIG_CONTENT` and select it with `--agent` on every run, resumes included. A resumed session otherwise keeps the agent it was saved with; OpenCode's `default_agent` applies only to new sessions. The agent allows read/grep/glob, plus edit/write/patch at `edit`. Shell, subagent delegation, web, and every other tool are hidden, and edits outside the workspace are denied. That config reaches only the private standalone server. These are **native tool permission rules, not an OS sandbox**; plugins and MCP servers still load. Restricted tiers refuse an existing `OPENCODE_CONFIG_CONTENT` rather than overwrite it. A restricted resume of a session that carries its own permission rules is refused, because OpenCode applies those after the agent's. A `danger` resume of a session last run by one of these per-run agents is refused too, because that agent no longer exists and the session would run with no tools. Resume it at `readonly` or `edit`, or start a new run.
372
+
373
+ The streamed JSON is progress only. OpenCode 2 stops forwarding events once the session is idle, and it exits 0 even after an interruption or a cancelled MCP form. So after a zero exit, the adapter reads the persisted session with `opencode session export --standalone <id>`. The export's output is bounded, and it honors the run's cancellation and timeout. A resume also exports the session once before running. Success requires all of the following:
374
+ - the session ID the run reported;
375
+ - a new user turn carrying exactly this prompt: the only one in a new session, or one absent from the pre-run export on a resume;
376
+ - after that turn, a session outcome and a terminal idle outcome of `succeeded`;
377
+ - a last assistant message that completed with `stop` and no error, with nonempty text;
378
+ - on restricted tiers, every assistant message of the turn run by the injected agent.
379
+
380
+ The result is that last assistant message's text, never streamed narration, which can include late or stale text. A malformed, oversized, or unknown export fails the run. Usage is this turn's assistant messages only. Cost is unknown if one lacks usage or ran a native `subagent`, whose child usage is not included. No budget cap is enforced. OpenCode retries transient provider errors internally (up to 10 retries); this extension does not retry it. Cancelling a run ends the CLI; its private server exits when the CLI's lease pipe closes.
381
+
333
382
  ## Agent usage
334
383
 
335
384
  A direct tool call requires `description`, `prompt`, and either `role` or legacy exact-profile `subagent_type`. With `role`, `harness` is optional and defaults to the global `defaultHarness` setting:
@@ -502,8 +551,8 @@ Every child selects from one parent snapshot and effective settings frozen at wo
502
551
  Every `Agent` call and workflow `agent()` child also accepts these optional run parameters (`context` is covered under agent usage above):
503
552
 
504
553
  - `permission`: `readonly` | `edit` | `danger`. Omit it to use settings `defaultPermission` (`danger` unless changed). A role or override `permission` field is obsolete and is rejected. Tiers map onto native harness mechanisms where the backend can enforce them. Antigravity accepts only `danger` (`--dangerously-skip-permissions`) and rejects `readonly` and `edit`. Claude `readonly`/`edit` auto-deny shell commands headlessly; denials are surfaced in the receipt. A named Pi harness restricts tool names. That is not an OS sandbox.
505
- - `max_budget_usd`: a spending cap. Claude Code enforces it mid-run with its native `--max-budget-usd` flag. Codex estimates cost from a price map and agy does not report cost at all; Grok reports its own native cost (`total_cost_usd`) but exposes no enforcement flag; Muse has never been observed to report cost at all. For codex, agy, grok, and muse alike, the cap is recorded and marked `budget unenforceable` instead of pretended.
506
- - `resume`: a prior run id. Continues the same backend conversation (Claude `--resume`, Codex `exec resume`, agy `--conversation`, Grok `--resume`, Muse `exec --session-id`) instead of starting from scratch. The prior run must use the same backend. Claude sessions persist in Claude Code's own local storage (this extension no longer passes `--no-session-persistence`) so recorded session ids stay resumable; remove old conversations from Claude Code itself if that matters to you. Muse's `--session-id` resume was verified directly: two independent `muse exec` processes sharing the same `--session-id` reported the same session, and the second recalled a fact only told to the first. `resume` cannot be combined with `context` sharing — continue an existing child, or start a new one with a snapshot.
554
+ - `max_budget_usd`: a spending cap. Claude Code enforces it mid-run with its native `--max-budget-usd` flag. Codex estimates cost from a price map and agy does not report cost at all; Grok reports its own native cost (`total_cost_usd`) but exposes no enforcement flag; Muse has never been observed to report cost at all. OpenCode reports root-step cost but cannot account for nested subagent sessions. For codex, agy, grok, muse, and opencode alike, the cap is recorded and marked `budget unenforceable` instead of pretended.
555
+ - `resume`: a prior run id. Continues the same backend conversation (Claude `--resume`, Codex `exec resume`, agy `--conversation`, Grok `--resume`, Muse `exec --session-id`, OpenCode `run --session`) instead of starting from scratch. The prior run must use the same backend. Claude sessions persist in Claude Code's own local storage (this extension no longer passes `--no-session-persistence`) so recorded session ids stay resumable; remove old conversations from Claude Code itself if that matters to you. Muse's `--session-id` resume was verified directly: two independent `muse exec` processes sharing the same `--session-id` reported the same session, and the second recalled a fact only told to the first. `resume` cannot be combined with `context` sharing — continue an existing child, or start a new one with a snapshot.
507
556
 
508
557
  Resolution order for the tier is call `permission`, then settings `defaultPermission`. Budget order is call `max_budget_usd`, then an exact override's `max_budget_usd`, then the settings default. Conversion of a pre-v4 install drops `permission` and `capabilitySet` from copied overrides, turns `harness: "pi-*"` templates into ordinary roles, and does not copy `piCapabilitySets`.
509
558
 
@@ -529,11 +578,11 @@ Normally this resolves to `~/.pi/agent/pi-flow-external/settings.json`. A missin
529
578
  }
530
579
  ```
531
580
 
532
- Field names and units are unchanged. Add `harnesses` only for named `pi-*` registrations; the five CLI harnesses need no entries. Add `disabledProfiles` to list exact execution identities to exclude, such as `"codex-explorer"`. Empty objects and lists may be omitted. A file that also registers a harness is shown under Named Pi harness configurations. `maxRunRecords` prunes the oldest completed run records at session start and via `/external runs --prune`; records still running or interrupted are never pruned, and `0` keeps everything.
581
+ Field names and units are unchanged. Add `harnesses` only for named `pi-*` registrations; the six CLI harnesses need no entries. Add `disabledHarnesses` to exclude entire CLI or named Pi harnesses. Add `disabledProfiles` to list exact execution identities to exclude, such as `"codex-explorer"`. Empty objects and lists may be omitted. A file that also registers a harness is shown under Named Pi harness configurations. `maxRunRecords` prunes the oldest completed run records at session start and via `/external runs --prune`; records still running or interrupted are never pruned, and `0` keeps everything.
533
582
 
534
- `/external settings` shows the effective values, the harness source, and the canonical path. When conversion is ready, it tells you to run `/external settings convert`. `/external settings edit` opens that JSON in the standard editor and validates it before saving. The next invocation reads the saved file. A workflow already running keeps the snapshot it froze at start. A new `maxConcurrentSubagents` value waits until active and queued work has drained.
583
+ `/external config` shows the effective values, the harness source, and the canonical path. When conversion is ready, use `/external config convert`. `/external config edit` opens that JSON in the standard editor and validates it before saving. The next invocation reads the saved file. A workflow already running keeps the snapshot it froze at start. A new `maxConcurrentSubagents` value waits until active and queued work has drained.
535
584
 
536
- Pre-v4 files are not applied as live settings. Convert them once with `/external settings convert`, as described in Breaking upgrade. After that, version 4 ignores the old files.
585
+ Pre-v4 files are not applied as live settings. Convert them once with `/external config convert`, as described in Breaking upgrade. After that, version 4 ignores the old files.
537
586
 
538
587
  ### Project default-harness override
539
588
 
@@ -547,7 +596,7 @@ A trusted project can override the global `defaultHarness` without editing the g
547
596
  { "defaultHarness": "claude" }
548
597
  ```
549
598
 
550
- The override applies only when Pi marks the project trusted, supports `defaultHarness` only, and is never created or written by the extension. The project file cannot inject roles or harness registrations. Precedence: an explicit `harness` in the call wins, then the trusted project default, then the global setting, then the built-in default. `/external settings` reports the effective harness and its source. An untrusted or invalid project file is ignored with a warning. A role that does not resolve on the effective harness fails with an actionable error and does not switch harnesses. The next invocation reads the project file. Startup flags override extension factory options, which override this file, which override built-in defaults.
599
+ The override applies only when Pi marks the project trusted, supports `defaultHarness` only, and is never created or written by the extension. The project file cannot inject roles or harness registrations. Precedence: an explicit `harness` in the call wins, then the trusted project default, then the global setting, then the built-in default. `/external config` reports the effective harness and its source. An untrusted or invalid project file is ignored with a warning. A role that does not resolve on the effective harness fails with an actionable error and does not switch harnesses. The next invocation reads the project file. Startup flags override extension factory options, which override this file, which override built-in defaults.
551
600
 
552
601
  Equivalent startup flags:
553
602
 
@@ -585,9 +634,10 @@ A failed backend can still have complete diagnostic evidence. Treat `incompleteR
585
634
 
586
635
  ## Troubleshooting
587
636
 
588
- - **Delegation says configuration must be converted:** run `/external settings` for the pointer, then `/external settings convert` to preview and apply. Delegation stays blocked until version 4 is active. Old files are kept and are not a live fallback.
637
+ - **Delegation says configuration must be converted:** run `/external config` for the pointer, then `/external config convert` to preview and apply. Delegation stays blocked until version 4 is active. Old files are kept and are not a live fallback.
589
638
  - **Role unavailable on the selected harness:** choose one of the harnesses listed in the error, author the shared role with `/external role create`, or materialize that one binding with `/external role override <role> <harness>`. The extension does not substitute another harness.
590
- - **Disabled identity:** remove that exact name from `disabledProfiles` in `/external settings edit` when you intend to use it again. Conversion records deleted seeded identities there on purpose.
639
+ - **Disabled harness:** use `/external config enable <harness>` to restore its availability. If a default points at a disabled harness, enable it or choose an enabled default; no fallback is performed.
640
+ - **Disabled identity:** remove that exact name from `disabledProfiles` in `/external config edit` when you intend to use it again. Conversion records deleted seeded identities there on purpose.
591
641
  - **CLI available but authentication fails:** authenticate that CLI directly; Pi and every external backend keep separate credentials.
592
642
  - **Claude rejects `--dangerously-skip-permissions` under root:** reload the current extension version; root runs use Claude's `auto` permission mode.
593
643
  - **Nested agent cannot find the repository:** include the repository's absolute path and required context in the prompt.
@@ -614,11 +664,13 @@ npm run e2e -- --backend codex
614
664
  npm run e2e -- --backend agy
615
665
  npm run e2e -- --backend grok
616
666
  npm run e2e -- --backend muse
667
+ npm run e2e -- --backend opencode --model <provider/model>
617
668
  npm run e2e -- --backend claude --workflow
618
669
  npm run e2e -- --backend codex --workflow
619
670
  npm run e2e -- --backend agy --workflow
620
671
  npm run e2e -- --backend grok --workflow
621
672
  npm run e2e -- --backend muse --workflow
673
+ npm run e2e -- --backend opencode --model <provider/model> --workflow
622
674
  npm run e2e -- --backend pi --harness pi-deepseek
623
675
  npm run e2e -- --backend pi --harness pi-deepseek --workflow
624
676
  ```