@tranhoangnguyen0310/pi-flow-external 2.6.0-external.0 → 2.7.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
@@ -11,8 +11,8 @@ This fork changes the original pi-flow contract: `Agent` is not a generic Pi sub
11
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
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.
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.
14
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.
15
- - Use Pi's native subagent system for Pi-backed scout/reviewer/planner/worker/oracle work.
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).
@@ -26,6 +26,7 @@ This fork changes the original pi-flow contract: `Agent` is not a generic Pi sub
26
26
  - Treat each `description` as a concise user-facing task label. Role and override descriptions are also user-visible as the declared reason for selection. The resolved execution object is still an internal profile; receipts keep the stable `<harness>-<role>` identity or the exact `subagent_type` name.
27
27
  - `unsandboxed external CLI` and `external host access` disclose the real execution boundary for CLI backends; a pi harness delegation discloses `Pi SDK child · host access · curated tools` instead — never call an in-process pi child an "external CLI". Never present a read-only prompt as permission enforcement. Antigravity accepts only danger and rejects narrower tiers. On pi, the curated tool table bounds which tool *names* exist and is not an OS sandbox; `bash` at `danger` stays host access.
28
28
  - Keep direct intent visible during execution. Workflow access belongs once at the workflow level, not on every child row.
29
+ - Parent guidance stays split. The always-on coordinator prompt is a short operating guide. `external_help` topic `usage` is the worked playbook. README is the user model. This file holds contributor invariants. Do not copy the playbook into the coordinator prompt, and do not rank harnesses by intelligence.
29
30
  - Parent-context sharing is a disclosure, not a silent optimization: the intent card names the mode before launch, and receipts name the mode and shared/requested turns. Shared conversation content leaves for the external harness and lands in local evidence, so never describe sharing as internal or free.
30
31
  - Keep default progress bounded and human-readable. Expanded terminal output shows bounded canonical output and a full-ID `/external runs` route; record paths, backend-event counts, workflow IDs, and journal paths remain advanced evidence.
31
32
  - `/external runs` must use the same registry/readers as `external_runs`. Every hidden workflow child and truncated output/diagnostic page needs a cursor-backed navigation path; never add a silent hard cap.
@@ -48,7 +49,7 @@ This fork changes the original pi-flow contract: `Agent` is not a generic Pi sub
48
49
 
49
50
  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.
50
51
 
51
- Use workflows for requested fan-out or multi-agent orchestration across agy, Claude, Codex, Grok, Muse, and named Pi harnesses. Do not route native Pi subagents through `workflow`.
52
+ Use workflows for requested fan-out or multi-agent orchestration across agy, Claude, Codex, Grok, Muse, and registered named Pi harnesses.
52
53
 
53
54
  ## Essential test mandate
54
55
 
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.7.0-external.0] - 2026-09-24
8
+
9
+ ### Added
10
+
11
+ - `external_help` topic `usage`: a worked delegation playbook for one `Agent` call, an independent review, background supervision through `external_runs`, a parallel workflow that catches a child error, and `resume` where the harness stored a session. Ordinary examples omit `permission` and keep no-edit intent in the prompt; one example confines a tier the harness can enforce. Prefer the configured default harness unless the user or a capability needs another. The always-on coordinator prompt stays a short operating guide and points there.
12
+
13
+ ### Changed
14
+
15
+ - Active guidance selects a registered `pi-*` harness through `Agent` or `workflow`, the same way as the CLI harnesses. It no longer tells the parent to route Pi-backed work to Pi's native subagent tool.
16
+ - Coordinator guidance states that Antigravity rejects `readonly` and `edit`. Release notes record the product owner's choice to ship the settings v4 break as `2.6.0-external.0` with `release:minor`, with the breaking-upgrade notice still required.
17
+
18
+ ### Fixed
19
+
20
+ - Workflow help supervision uses `runIds` for inspect, wait, and cancel. One id reads any inspect view, including `final`; several ids batch summary only.
21
+
22
+ ## [2.6.0-external.0] - 2026-09-23
23
+
7
24
  ### Fixed
8
25
 
9
26
  - Antigravity's unsupported-tier error tells the caller to pass `permission: "danger"` or change `defaultPermission`. Omitting the call tier keeps the global default.
@@ -11,8 +28,6 @@ All notable changes to pi-flow external are documented here.
11
28
  - Conversion reports a malformed short `pi-<role>.md` wildcard template and leaves it in place. Parsed native profiles stay excluded.
12
29
  - An unresolved delegation intent shows the tier as unresolved instead of borrowing another harness's enforcement label.
13
30
 
14
- ## [2.6.0-external.0] - 2026-09-23
15
-
16
31
  ### Breaking upgrade
17
32
 
18
33
  - Removed `/external profiles`, `/external profile create`, and `/pi-flow-profile create` without aliases. Use `/external roles`, `/external role create`, or `/external harness create` instead. Agent/workflow `role`, `harness`, and exact `subagent_type` APIs remain supported.
package/CONTEXT.md CHANGED
@@ -9,7 +9,7 @@ This package is a fork of pi-flow whose `Agent` and `workflow` tools are reserve
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
- - **Native Pi subagent:** A subagent exposed by Pi's native subagent system. This fork intentionally does not route it through `Agent` and does not modify those files.
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
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
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.
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.
@@ -25,18 +25,17 @@ This package is a fork of pi-flow whose `Agent` and `workflow` tools are reserve
25
25
  - **Incomplete record:** Local evidence is missing, malformed, or internally inconsistent; it must not be reported as trustworthy merely because the backend status says `done`.
26
26
  - **Nested activity:** A recognized backend-native subagent event. Its first observation may extend the deadline once by one fresh base timeout, capped at twice the original deadline.
27
27
 
28
- ## Routing rule
28
+ ## Selection
29
29
 
30
- - Native Pi work -> native subagent tool.
31
- - Claude Code / Codex CLI / Antigravity / Grok Build CLI / Muse Code work, and named Pi harness work -> this extension's `Agent` or `workflow`.
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.
32
31
 
33
- The split is global and intentional to avoid tool ambiguity across projects. 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 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.
34
33
 
35
34
  ## User control surface
36
35
 
37
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.
38
37
 
39
- > 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. Historical files under `docs/plans/` are not rewritten.
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.
40
39
 
41
40
  ## Design stance
42
41
 
@@ -44,7 +43,7 @@ Guardrails exist to keep lanes from bleeding into each other (a reviewer that ed
44
43
 
45
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.
46
45
 
47
- The always-visible parent guidance is limited to a compact role catalog and essential routing/safety facts. Catalog availability reflects the resolved roster, not CLI installation or authentication. `external_help` supplies descriptions, permission details, workflow APIs/examples, and trust-aware saved-workflow discovery only when requested.
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.
48
47
 
49
48
  Workflow replay is explicit and successful-prefix-only. A persisted script resumed with `resumeFromRunId` reuses the longest unchanged prefix whose fingerprints and outcomes are successful; the first changed or unsuccessful call and its suffix run again. Script recomposition is cheap, but child execution can cost money or repeat side effects. There is no automatic repaired-script replay, completion-order winner selection, or live steering.
50
49
 
package/README.md CHANGED
@@ -15,10 +15,10 @@ The ordinary driver has four tools (`workflow` can be disabled):
15
15
 
16
16
  - `Agent` resolves and runs one external role.
17
17
  - `workflow` orchestrates multiple external roles with trusted JavaScript.
18
- - `external_help` returns role details, permission behavior, or workflow guidance on demand.
18
+ - `external_help` returns the usage playbook, role details, permission behavior, or workflow guidance on demand.
19
19
  - `external_runs` lists, inspects, waits for, and cancels session-owned runs.
20
20
 
21
- `Agent` and `workflow` accept a role with an optional harness — `agy`, `claude`, `codex`, `grok`, `muse`, or a registered `pi-*` name. Legacy exact `subagent_type` remains available and cannot be combined with `role` or `harness`. Use pi's native subagent system for Pi-backed agents. `pi_flow_role_create` is active only during `/external role create`. `pi_flow_harness_create` is active only during `/external harness create`.
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
22
 
23
23
  ## Install
24
24
 
@@ -324,7 +324,7 @@ Agent({
324
324
  });
325
325
  ```
326
326
 
327
- A custom role is the one shared file from `/external role create`. Use `/external role override reviewer pi-deepseek` when only that harness needs a different body, permission, tools, or budget. The override's `model` and `thinking` come from the harness entry. A different pin is rejected.
327
+ A custom role is the one shared file from `/external role create`. Use `/external role override reviewer pi-deepseek` when only that harness needs a different body, tools, or budget. Permission stays on the call or in `defaultPermission`; an override cannot set it. The override's `model` and `thinking` come from the harness entry. A different pin is rejected.
328
328
 
329
329
  Settings writes use a private staged file and a same-directory replacement, and they preserve unrelated fields. The extension refuses to overwrite a malformed or newer-version file. Replacement avoids a torn write. Concurrent sessions can still overwrite each other's update: there is no inter-process lock. A harness smoke test finishes before that short commit. The writer then rereads and checks for a duplicate or a conflict.
330
330
 
@@ -345,7 +345,7 @@ Agent({
345
345
 
346
346
  Resolution always targets the exact `<harness>-<role>` identity and does not switch harness. If a role is unavailable, the error lists the harnesses that provide it. Existing calls may instead use `subagent_type` as an exact-identity escape hatch; do not combine it with `role` or `harness`. Nonstandard override names are exact-only.
347
347
 
348
- The parent prompt always includes one compact catalog of role names, restricted harness availability, exact-only names, and the default harness. It does not repeat role descriptions or the workflow manual. Call `external_help` with topic `roles` for descriptions, `permissions` for harness caveats, or `workflow` for syntax, examples, and saved workflow discovery. The optional `harness` filter applies to `roles` and `permissions`. A listed role is a catalog entry. It does not mean the CLI is installed or authenticated.
348
+ The parent prompt always includes one compact catalog of role names, restricted harness availability, exact-only names, the default harness, and a short operating guide. It does not repeat role descriptions, the usage playbook, or the workflow manual. Call `external_help` with topic `usage` for worked examples, `roles` for descriptions, `permissions` for harness caveats, or `workflow` for syntax and saved workflow discovery. The optional `harness` filter applies to `roles` and `permissions`. A listed role is a catalog entry. It does not mean the CLI is installed or authenticated.
349
349
 
350
350
  External agents start fresh in the requested working directory unless `resume` continues a previous child. By default, they receive only the task briefing and the resolved role instructions. The parent can explicitly share conversation context:
351
351
 
@@ -136,7 +136,7 @@ Run only when batch inspection, the `final` view, timing projection, or `/extern
136
136
 
137
137
  1. Launch two independent `Agent({ ..., background: true })` calls back to back (do not wait on either), then do one unrelated piece of parent work (e.g. read a file) before checking on them — this exercises the actually-recommended usage shape (separate launches, other work in between), not a tight launch-then-immediately-inspect loop.
138
138
  2. Call `external_runs({ action: "inspect", runIds: [<first>, <second>] })` once and confirm both come back as one bounded batch of `summary` projections (queued/running/terminal, `outputRef`/`diagnosticsRef` per entry) without waiting for either to finish.
139
- 3. After both children are confirmed terminal (`external_runs wait` or a later `inspect`), call `external_runs({ action: "inspect", runId: <runId>, view: "final" })` for a completed run and confirm it returns only the verified canonical answer; for a run cancelled mid-flight (or inspected before it settles), confirm the tool response reports `finalAvailable: false` with a deliberately empty, bounded projection (`items: []`, `integrity: "incomplete"`) rather than a fabricated success — this empty shape is correct at the tool layer, not a bug. The human-readable explanation ("No verified final answer is available yet for `<runId>`.") is a separate, UI-only notice that `/external runs` shows when Final is selected on an unsettled run; the tool response itself stays narration-free by design.
139
+ 3. After both children are confirmed terminal (`external_runs wait` or a later `inspect`), call `external_runs({ action: "inspect", runId: <runId>, view: "final" })` for a completed run and confirm it returns only the verified canonical answer. Also call `external_runs({ action: "inspect", runIds: [<that settled id>], view: "final" })` and confirm the one-element `runIds` list returns the same verified answer. For a run cancelled mid-flight (or inspected before it settles), confirm the tool response reports `finalAvailable: false` with a deliberately empty, bounded projection (`items: []`, `integrity: "incomplete"`) rather than a fabricated success — this empty shape is correct at the tool layer, not a bug. The human-readable explanation ("No verified final answer is available yet for `<runId>`.") is a separate, UI-only notice that `/external runs` shows when Final is selected on an unsettled run; the tool response itself stays narration-free by design.
140
140
  4. Run `/external runs`, open the list, and manually select `Refresh`: confirm it re-reads the first page and resets any list cursor without navigating into a run or waiting/polling. Select a still-running row and confirm its queue/elapsed timing and final-availability marker are present and update only when you refresh again (never on a timer).
141
141
 
142
142
  Local fixture tests already cover batch pagination/cursor/byte-limit edge cases, the `final` view's success/unavailable/legitimate-null shapes, and `Refresh`'s cursor-reset behavior deterministically; this check only proves the real backend/registry timing (queued→running, activity age, terminal settlement) matches what those fixtures assume.
@@ -152,10 +152,13 @@ Run when settings ownership, the role catalog, slash commands, upgrade, or purge
152
152
  1. Fresh directory: start Pi against this checkout. `/external` shows the default harness, six built-in roles, five CLI harnesses (`agy`, `claude`, `codex`, `grok`, `muse`), and the settings path. The directory gains no extension role files under `subagents/` and no `.pi-flow-defaults-seeded-v1`, `-v2`, or `-v3` markers. Reading settings does not create `settings.json`.
153
153
  2. `/reload`, then start another session. Still no generated role files. `/external role create` writes one file under `pi-flow-external/roles/` and no per-harness copies.
154
154
  3. `/external harness create` for one named Pi harness writes that entry into `settings.json` only. `/external roles` and a following `Agent` or `workflow` call on that harness use the same model binding. No role file appears for the six built-ins.
155
- 4. Upgrade: place a pre-v4 `settings.json`, a `pi-flow-external/harnesses.json` registry, and one customized external profile in the disposable directory. `/external settings` points at `/external settings convert`. Run convert, confirm the preview, and check that originals remain. A following delegation reads the version 4 catalog. Old paths are ignored after activation. Confirm a deleted seeded identity from a historical cohort is present in `disabledProfiles` and is not recreated. An auth-blocked backend is not a passing receipt.
155
+ 4. Upgrade: place a pre-v4 `settings.json`, a `pi-flow-external/harnesses.json` registry, one customized external profile, and `subagents/pi-reviewer.md` with `harness: "pi-*"` plus a `piCapabilitySets` entry in the disposable directory. `/external settings` points at `/external settings convert`. Run convert, confirm the preview, and check that originals remain. Conversion writes `roles/reviewer.md` for every harness and does not copy `piCapabilitySets`. A following delegation reads the version 4 catalog. Old paths are ignored after activation. Confirm a deleted seeded identity from a historical cohort is present in `disabledProfiles` and is not recreated. An auth-blocked backend is not a passing receipt.
156
156
  5. Frozen workflow: start a workflow, edit the role file or settings while it runs, and confirm that run keeps the snapshot from its start. The next invocation outside that workflow sees the edit. Lower `maxConcurrentSubagents` while work is active or queued and confirm the cap stays until that work drains.
157
157
  6. Optional purge: `/external [danger]purge-old-files` lists candidates, including customized legacy copies. Select individual paths, then confirm. Skipping the purge still leaves delegation working. Confirm current `settings.json`, `roles/`, `overrides/`, project settings, native profiles, and `runs/` stay. Repeat the command and confirm it reports nothing further to delete.
158
158
  7. Because catalog and command guidance changed, also run `--routing-smoke` for a backend you can authenticate. Unknown `/external profile create` must list the current commands and must not start the old interview.
159
+ 8. Presets: register two named Pi harnesses, one with `"preset": "minimal"` and one with `"preset": "skills"`, plus one entry that omits `preset`. Confirm the omitted entry is treated as `minimal`. Confirm neither preset loads extensions, prompt templates, or themes. `skills` loads installed skills, and project skills only when the project is trusted. `minimal` leaves skills unloaded.
160
+ 9. Permission default and call override: set `defaultPermission` to `readonly`. A Claude or Codex call that omits `permission` records readonly. The next call with `permission: "danger"` records danger. A role or override file that still contains `permission` is rejected and does not change the tier.
161
+ 10. Antigravity rejection: `permission: "readonly"` and `permission: "edit"` on `agy` fail before launch. The receipt is a rejection. It is not an unsandboxed run described as advisory.
159
162
 
160
163
  ## Release minimum
161
164
 
@@ -170,3 +173,4 @@ Before a runtime release:
170
173
  7. Run the nested timeout check only if nested detection or timeout behavior changed.
171
174
  8. Run the background-run observability check only if batch inspection, the `final` view, timing projection, or `/external runs` browsing changed.
172
175
  9. Remove temporary runner evidence. Ordinary use does not require `/external [danger]purge-old-files`.
176
+ 10. When presets, permission defaults, Antigravity tier rejection, wildcard conversion, or `runIds` selection changed, run the matching configuration-surface and background-run checks. Coordinator-guidance changes also require the routing smoke.
package/docs/releasing.md CHANGED
@@ -92,19 +92,20 @@ surface check when settings, roles, upgrade, or purge changed, and the nested
92
92
  timeout scenario when nested detection or timeout behavior changed. An
93
93
  auth-blocked lane is not a pass.
94
94
 
95
- A release that removes `/external profiles`, `/external profile create`, and
96
- `/pi-flow-profile create`, or that moves execution settings to version 4, is
97
- breaking. Label it `release:major` (core major, `external.0`). The CHANGELOG
98
- section, which becomes the GitHub release notes, must include the README
99
- upgrade notice: the old-to-new command mapping, one-time conversion through
95
+ Removing `/external profiles`, `/external profile create`, and
96
+ `/pi-flow-profile create`, or moving execution settings to version 4, is a
97
+ breaking configuration change. The product owner explicitly chose to ship
98
+ that break as `2.6.0-external.0` with `release:minor`, staying on v2. Do not
99
+ relabel it `release:major`. The minor label does not make the break optional
100
+ to disclose, and it does not belong in a patch note. The CHANGELOG section,
101
+ which becomes the GitHub release notes, must include the README upgrade
102
+ notice: the old-to-new command mapping, one-time conversion through
100
103
  `/external settings convert` (`/external settings` points there), originals
101
104
  kept until an explicit purge, version 4 ignores old paths,
102
105
  `/external [danger]purge-old-files` deletes only the legacy paths you select
103
106
  (customized copies included), and downgrade after purge needs the user's own
104
- backup. Do not
105
- hide that break in a patch or minor note. Do not claim side-by-side old and
106
- new layouts. The version bump and CHANGELOG text are owned by the release PR;
107
- this file only states the contract.
107
+ backup. Do not claim side-by-side old and new layouts. The version bump and
108
+ CHANGELOG text are owned by the release PR; this file only states the contract.
108
109
 
109
110
  ## After the release
110
111
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tranhoangnguyen0310/pi-flow-external",
3
- "version": "2.6.0-external.0",
3
+ "version": "2.7.0-external.0",
4
4
  "description": "External Claude Code, Codex CLI, Antigravity, Grok Build CLI, and Muse Code delegation for pi.",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -13,16 +13,17 @@ import {
13
13
  EXTERNAL_HELP_PROMPT_SNIPPET,
14
14
  formatExternalRoleHelp,
15
15
  formatSavedWorkflows,
16
+ formatUsagePlaybook,
16
17
  } from "./prompts.ts";
17
18
  import { listSavedWorkflows } from "./workflow/registry.ts";
18
19
  import { EXTERNAL_HARNESSES, type ExternalHarness } from "./types.ts";
19
20
 
20
21
  const externalHelpParameters = Type.Object({
21
- topic: StringEnum(["roles", "permissions", "workflow"] as const, {
22
- description: "Help topic: role descriptions/configured profile availability, harness permissions, or workflow syntax and saved workflows.",
22
+ topic: StringEnum(["usage", "roles", "permissions", "workflow"] as const, {
23
+ description: "Help topic: usage playbook, role descriptions/configured profile availability, harness permissions, or workflow syntax and saved workflows.",
23
24
  }),
24
25
  harness: Type.Optional(Type.String({
25
- description: "Optional harness filter for roles or permissions: agy, claude, codex, grok, muse, or a registered pi-* harness. Workflows orchestrate across harnesses.",
26
+ description: "Optional harness filter for roles or permissions: agy, claude, codex, grok, muse, or a registered pi-* harness. usage and workflow describe every harness.",
26
27
  })),
27
28
  });
28
29
 
@@ -95,7 +96,7 @@ Provide exactly one workflow source: name, scriptPath, or script. Every script s
95
96
 
96
97
  APIs: agent(prompt, { description, label, role, harness, subagent_type, permission, max_budget_usd, resume, context, schema, phase }); parallel(thunks); pipeline(items, ...stages); phase(title); log(message). Globals: args and cwd. Agent options other than the profile selector are optional. "description" is the common task-name option shared with the direct Agent tool; "label" is a compatible alias — set only one, or both to the same value. Choose role/harness or legacy exact subagent_type, never both. Use unique descriptions/labels and clear task prompts. An agent() succeeds with its value or throws ChildRunError { runId, outcome, message, outputRef, diagnosticsRef }; catch optional failures explicitly. Uncaught failures terminate the workflow and drain siblings. Helpers never convert failures to null. Context options: {mode:"none"} (default), {mode:"recent",turns:N} (positive integer, includes current user turn), or {mode:"full"} (available context after compaction). All children share invocation-time context/settings; earlier child results must still be passed explicitly. Context excludes thinking/system instructions and pending calls; images and snapshots over 1 MiB fail without truncation. Context sharing cannot be combined with resume. Use schema for results that control branching or aggregation. Imports, filesystem globals, Date APIs, and Math.random() are unavailable.
97
98
 
98
- Background and supervision: blocking by default — an ordinary call waits for its child and returns the result, run everything this way unless the parent has other work to do first. Set background:true only when the parent can proceed before completion, then use external_runs on the returned stable run ID while session-owned work continues. Parallel same-harness work (e.g. three agy workers) runs concurrently under the shared maxConcurrentSubagents cap: call parallel([() => agent(...), ...]) in a workflow, or issue separate Agent calls with background:true in one turn, then external_runs wait/inspect/cancel. Awaiting agent() calls sequentially stays serial by construction. external_runs actions are list (optional workflowRunId/cursor/workflowCursor/limit), inspect (runId, view summary|output|diagnostics, optional opaque cursor/limitBytes), wait (runId or runIds, mode any|all), and cancel (runId, optional reason). Follow nextCursor/nextWorkflowCursor for complete results. Wait returns selected terminal outcomes and pending IDs, never cancels pending work, and returns an unsuccessful workflow early; interrupting wait stops only the wait. Cancelling a workflow stops active children, while cancelling one child is a catchable workflow error. Blocking-call interruption cancels that call; background work survives tool return but is cancelled on orderly owning-session shutdown. It is not a daemon: crashes leave unfinished evidence interrupted/uncertain, restart does not adopt work, and live steering is unavailable.
99
+ Background and supervision: blocking by default — an ordinary call waits for its child and returns the result, run everything this way unless the parent has other work to do first. Set background:true only when the parent can proceed before completion, then use external_runs on the returned stable run ID while session-owned work continues. Parallel same-harness work (e.g. three agy workers) runs concurrently under the shared maxConcurrentSubagents cap: call parallel([() => agent(...), ...]) in a workflow, or issue separate Agent calls with background:true in one turn, then external_runs wait/inspect/cancel. Awaiting agent() calls sequentially stays serial by construction. external_runs selects targets with runIds. list takes optional workflowRunId, cursor, workflowCursor, and limit. inspect with one id reads any view (summary, output, diagnostics, or final) and pages with an opaque cursor and limitBytes; several ids (up to 20) batch summary only. wait takes runIds in mode any or all (up to 100). cancel takes one id and an optional reason. Follow nextCursor/nextWorkflowCursor for complete results. Wait returns selected terminal outcomes and pending IDs, never cancels pending work, and returns an unsuccessful workflow early; interrupting wait stops only the wait. Cancelling a workflow stops active children, while cancelling one child is a catchable workflow error. Blocking-call interruption cancels that call; background work survives tool return but is cancelled on orderly owning-session shutdown. It is not a daemon: crashes leave unfinished evidence interrupted/uncertain, restart does not adopt work, and live steering is unavailable.
99
100
 
100
101
  Replay: resumeFromRunId works only with persisted scriptPath and starts an explicit new attempt. It reuses the longest unchanged prefix of successful child calls; the first changed/failed/cancelled/timed-out call and its suffix run again. Script recomposition is cheap, but child reruns can cost money or repeat side effects. There is no automatic repaired-script replay.
101
102
 
@@ -126,25 +127,27 @@ export function createExternalHelpTool(
126
127
  return defineTool({
127
128
  name: "external_help",
128
129
  label: "External Help",
129
- description: "Read-only help on demand for external roles, permission behavior, and workflow usage (including background runs, external_runs supervision syntax, and replay) or saved-workflow discovery.",
130
+ description: "Read-only help on demand: usage playbook, role details, permission behavior, and workflow usage (including background runs, external_runs supervision syntax, and replay) or saved-workflow discovery.",
130
131
  promptSnippet: EXTERNAL_HELP_PROMPT_SNIPPET,
131
132
  parameters: externalHelpParameters,
132
133
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
133
134
  // A schema-conversion layer downstream of this tool's declaration may
134
135
  // present `harness` as required (#62); treat a blank/whitespace
135
- // placeholder as omitted. Furthermore, do not reject an explicit
136
- // harness on topic "workflow": workflows orchestrate across all
137
- // harnesses, so passing a harness filter gracefully returns workflow
138
- // guidance without error.
136
+ // placeholder as omitted. usage and workflow describe every harness, so
137
+ // an explicit harness is not a filter and does not error.
139
138
  const harness = params.harness?.trim() ? params.harness.trim() : undefined;
140
139
  let text: string;
141
140
  const catalog = loadExternalCatalog(getAgentDir());
142
141
  const harnessConfigs = catalog.harnessConfigs;
143
142
  const configuredPiHarnesses = new Set(harnessConfigs.keys());
144
- if (params.topic !== "workflow") {
143
+ // usage and workflow describe every harness. A supplied harness is not a
144
+ // filter, including a blank placeholder forced by a downstream schema.
145
+ if (params.topic === "roles" || params.topic === "permissions") {
145
146
  validateHarnessFilter(harness, configuredPiHarnesses);
146
147
  }
147
- if (params.topic === "roles") {
148
+ if (params.topic === "usage") {
149
+ text = formatUsagePlaybook();
150
+ } else if (params.topic === "roles") {
148
151
  text = catalog.blocked ? catalog.diagnostics.join(" ") : formatExternalRoleHelp(catalog.profiles, options.getDefaultHarness(ctx), harness);
149
152
  } else if (params.topic === "permissions") {
150
153
  text = permissionHelp(harness, configuredPiHarnesses);
@@ -57,7 +57,7 @@ const MAX_CONCURRENT_SUBAGENTS_FLAG = "max-concurrent-subagents";
57
57
  const SUBAGENT_TIMEOUT_MS_FLAG = "subagent-timeout-ms";
58
58
  const STATUS_KEY = "pi-flow";
59
59
 
60
- const agentToolParameters = Type.Object({
60
+ export const agentToolParameters = Type.Object({
61
61
  context: Type.Optional(parentContextSchema),
62
62
  description: Type.String({
63
63
  description: "A short 3-5 word description of the task, used for UI display and routing context.",
package/src/profiles.ts CHANGED
@@ -455,6 +455,12 @@ const DEFAULT_RESOLVE_OPTIONS: Required<ResolveExternalProfileOptions> = {
455
455
  harnessConfigs: NO_HARNESS_CONFIGS,
456
456
  };
457
457
 
458
+ /** Unknown exact identity. Directs the caller back to role plus harness, including a registered pi-* name. */
459
+ export function unknownExternalProfileMessage(subagentType: string, names: Iterable<string>): string {
460
+ const available = [...names].join(", ") || "none";
461
+ return `Unknown external subagent_type "${subagentType}". Available external profiles: ${available}. Select role and an optional harness (agy, claude, codex, grok, muse, or a registered pi-* name).`;
462
+ }
463
+
458
464
  export function resolveExternalProfile(
459
465
  profiles: Map<string, SubagentProfile>,
460
466
  selection: ExternalAgentSelection,
@@ -473,9 +479,7 @@ export function resolveExternalProfile(
473
479
  }
474
480
  const profile = profiles.get(subagentType);
475
481
  if (!profile) {
476
- throw new Error(
477
- `Unknown external subagent_type "${subagentType}". Available external profiles: ${[...profiles.keys()].join(", ") || "none"}. Use the native subagent system for Pi-backed agents.`,
478
- );
482
+ throw new Error(unknownExternalProfileMessage(subagentType, profiles.keys()));
479
483
  }
480
484
  if (profile.configurationError) throw new Error(profile.configurationError);
481
485
  return reconcilePiProfileWithHarness(profile, harnessConfigs);
package/src/prompts.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { SavedWorkflow } from "./workflow/registry.ts";
2
- import { EXTERNAL_HARNESSES, type SubagentProfile } from "./types.ts";
2
+ import { EXTERNAL_HARNESSES, type PermissionTier, type SubagentProfile } from "./types.ts";
3
3
  import { externalProfileRole, externalRoleAvailability } from "./profiles.ts";
4
4
 
5
5
  export const AGENT_PROMPT_SNIPPET =
@@ -9,7 +9,7 @@ export const WORKFLOW_PROMPT_SNIPPET =
9
9
  "Run requested multi-agent orchestration with external roles; use external_help for syntax and saved workflows.";
10
10
 
11
11
  export const EXTERNAL_HELP_PROMPT_SNIPPET =
12
- "Show external role details, permission behavior, or workflow guidance on demand.";
12
+ "Show the delegation playbook, role details, permission behavior, or workflow guidance on demand.";
13
13
 
14
14
  export const EXTERNAL_RUNS_PROMPT_SNIPPET =
15
15
  "List, inspect (single or batch summary via runIds, up to 20), wait for, or cancel session-owned external runs; follow cursors for complete output. view: final returns only a verified terminal answer, empty until one exists.";
@@ -92,5 +92,142 @@ export function buildCoordinatorPrompt(
92
92
 
93
93
  ${formatExternalRoleCatalog(profiles, defaultHarness, configuredHarnessNames)}
94
94
 
95
- Catalog availability reflects built-in and authored roles, not CLI installation or authentication. External delegation tools use external CLIs and registered Pi harnesses; use native Pi subagents for Pi-backed work. Give each child a clear task, absolute paths, and read-only/edit intent; nested agents may start elsewhere. Share parent context explicitly when a child needs it: context: {mode:"recent", turns:N} for the last N user turns (including the current turn), {mode:"full"} for available post-compaction conversation, or omit it for independent tasks. Prefer the smallest sufficient snapshot; snapshots exclude thinking/system instructions and pending calls, fail on images or over 1 MiB, and may contain sensitive data sent to the external harness. Use resume to follow up on an existing child; never combine it with sharing. This is text transfer, not a native session clone or guaranteed cache reuse. Blocking by default; set background:true only when the parent can proceed before completion, then use external_runs on the returned run ID. Parallel same-harness work shares one maxConcurrentSubagents cap: use parallel([() => agent(...), ...]) or separate background Agent calls in one turn; sequential awaits stay serial. External CLIs have host access, and agy always runs unsandboxed (readonly/edit are advisory). Role selection never falls back. Do not retry failed runs: agy alone may make one disclosed infrastructure retry. Use external_help for role descriptions, permission details, workflow syntax, supervision syntax, and saved workflows.`;
95
+ Own the result and verify it yourself. Do the work directly unless a separate harness, a fresh context, or a bounded parallel review earns the overhead. A role is the job. A harness (agy, claude, codex, grok, muse, or a registered pi-* name) is the environment. Prefer the configured default harness unless the user asks for another or the task needs a capability the default lacks. Select both through Agent or workflow. Follow the user's own approval policy before widening a delegation. This extension has no fixed multi-agent approval threshold.
96
+
97
+ Give every child an objective, the context it needs, the changes it may make, a deliverable, and the check you will run. Use absolute paths and say whether the task is read-only or may edit. Permission is the call's tier, otherwise defaultPermission (danger unless changed). Omit it when the child should keep that default, including a reviewer that may use a shell and still must not edit. A danger tier still has to stay inside the authorization the user gave you. agy accepts only danger and rejects readonly and edit.
98
+
99
+ Choose a fresh child or resume on purpose. Omit context for independent work. context {mode:"recent", turns:N} shares the last N user turns, including the current turn; {mode:"full"} shares the available post-compaction conversation. Snapshots drop thinking, system instructions, and pending calls, reject images and payloads over 1 MiB, and are sent to that harness. resume continues the same CLI child and cannot be combined with sharing. A named pi-* harness has no persisted session and no enforced budget. Prefer resume for the same task and a new child for an independent review.
100
+
101
+ Calls block unless background:true. Then supervise with external_runs and judge view "final" yourself. Same-harness parallel work shares one maxConcurrentSubagents cap: parallel([() => agent(...), ...]) or separate background Agent calls in one turn. Sequential awaits stay serial. Report milestones. Retry a failed run, or switch its model or harness, only when the user authorized that step. agy alone may make one disclosed infrastructure retry. Nested agents may start in another workspace.
102
+
103
+ Catalog availability reflects built-in and authored roles, not CLI installation or authentication. Role selection never falls back. external_help topic usage is the playbook; roles, permissions, and workflow are the references.`;
104
+ }
105
+
106
+ export interface UsageAgentExample {
107
+ description: string;
108
+ prompt: string;
109
+ role: string;
110
+ harness: string;
111
+ /** Omit on ordinary calls so defaultPermission applies. Set only to confine the tier. */
112
+ permission?: PermissionTier;
113
+ background?: boolean;
114
+ resume?: string;
115
+ context?: { mode: "none" } | { mode: "recent"; turns: number } | { mode: "full" };
116
+ }
117
+
118
+ /** One self-contained Agent call. Valid against the Agent tool schema. Permission stays omitted. */
119
+ export const USAGE_ONE_AGENT: UsageAgentExample = {
120
+ description: "Map the repository",
121
+ prompt: "Map /absolute/repo. Read only. Return the important files and how they fit together. Do not edit.",
122
+ role: "explorer",
123
+ harness: "claude",
124
+ };
125
+
126
+ /** Fresh reviewer on the default tier, so the shell stays available. The prompt forbids edits. */
127
+ export const USAGE_INDEPENDENT_REVIEW: UsageAgentExample = {
128
+ description: "Independent review",
129
+ prompt: "Review /absolute/repo read-only. Report defects with file paths. Do not edit.",
130
+ role: "reviewer",
131
+ harness: "codex",
132
+ context: { mode: "none" },
133
+ };
134
+
135
+ /** Returns after registration. The parent reads the verified answer through external_runs. */
136
+ export const USAGE_BACKGROUND_AGENT: UsageAgentExample = {
137
+ description: "Background map",
138
+ prompt: "Map /absolute/repo read-only and return a short file list. Do not edit.",
139
+ role: "explorer",
140
+ harness: "claude",
141
+ background: true,
142
+ };
143
+
144
+ /** Single-id batch inspect. The executor accepts runIds with view final for one id. */
145
+ export const USAGE_RUNS_FINAL = {
146
+ action: "inspect" as const,
147
+ runIds: ["run_example"],
148
+ view: "final" as const,
149
+ };
150
+
151
+ /** Continues a CLI child that stored a session id. Named pi-* harnesses cannot resume. */
152
+ export const USAGE_RESUME_AGENT: UsageAgentExample = {
153
+ description: "Continue the map",
154
+ prompt: "Continue the map of /absolute/repo. Add the missing test entry points you already found. Do not expand scope.",
155
+ role: "explorer",
156
+ harness: "claude",
157
+ resume: "run_example",
158
+ };
159
+
160
+ /** The one call that sets permission. Use it to confine a tier the harness can enforce. */
161
+ export const USAGE_RESTRICTED_AGENT: UsageAgentExample = {
162
+ description: "Confined lookup",
163
+ prompt: "List the public entry points in /absolute/repo. Read only. Do not edit.",
164
+ role: "explorer",
165
+ harness: "claude",
166
+ permission: "readonly",
167
+ };
168
+
169
+ export const USAGE_WORKFLOW_CALLS: { prompt: string; options: { description: string; role: string; harness: string; permission?: PermissionTier } }[] = [
170
+ {
171
+ prompt: "Map /absolute/repo read-only. Return important paths. Do not edit.",
172
+ options: { description: "map", role: "explorer", harness: "claude" },
173
+ },
174
+ {
175
+ prompt: "Review /absolute/repo read-only. Report defects with paths. Do not edit.",
176
+ options: { description: "review", role: "reviewer", harness: "codex" },
177
+ },
178
+ ];
179
+
180
+ /** Parallel workflow whose branches catch ChildRunError so one failure does not drain the other. */
181
+ export function usageWorkflowScript(): string {
182
+ const branches = USAGE_WORKFLOW_CALLS.map((call) => ` async () => {
183
+ try {
184
+ return { ok: true, value: await agent(${JSON.stringify(call.prompt)}, ${JSON.stringify(call.options)}) };
185
+ } catch (error) {
186
+ return { ok: false, runId: error.runId, outcome: error.outcome, message: error.message };
187
+ }
188
+ }`);
189
+ return [
190
+ 'export const meta = { apiVersion: 1, name: "parallel-review", description: "Review in parallel and keep a sibling failure" };',
191
+ "const settled = await parallel([",
192
+ branches.join(",\n"),
193
+ "]);",
194
+ "return { settled };",
195
+ "",
196
+ ].join("\n");
197
+ }
198
+
199
+ function jsonBlock(value: unknown): string {
200
+ return JSON.stringify(value, null, 2);
201
+ }
202
+
203
+ /** Worked playbook served by external_help topic "usage". Examples are the exported constants. */
204
+ export function formatUsagePlaybook(): string {
205
+ const script = usageWorkflowScript();
206
+ return `Practical delegation playbook. A role is the work. A harness is the environment: ${EXTERNAL_HARNESSES.join(", ")}, or a registered pi-* name. Agent and workflow select every one of those harnesses the same way. Prefer the configured default harness. Name another when the user asks for it or the task needs a capability the default lacks. Do the task directly unless a separate environment, an independent context, or bounded parallel work is worth the coordination. You still own the outcome: read the result and check the deliverable. Follow the user's approval policy before widening a delegation. There is no fixed team-size threshold here.
207
+
208
+ A named pi-* harness runs in-process on the SDK's built-in tools. Preset minimal leaves skills unloaded. Preset skills loads installed skills, and project skills only when the project is trusted. Those presets select skill loading. They are not this extension's tools and they are not web search. Resume does not continue a pi-* child, and a budget there is recorded without enforcement.
209
+
210
+ Tell each child the objective, the context it needs, what it may change, the deliverable, and how you will verify it. Use absolute paths. Say read-only or edit in the prompt. Omit permission to keep defaultPermission (danger unless changed), including a reviewer that may use a shell and still must not edit. Pass permission only to confine the call below that default. A danger tier stays inside the authorization the user already gave. Antigravity accepts only danger. Topic permissions is the harness-boundary reference. Topic roles lists descriptions. Topic workflow is the syntax and saved-workflow reference.
211
+
212
+ Choose fresh context or resume deliberately. Omit context, or pass {"mode":"none"}, for independent work such as a review. {"mode":"recent","turns":N} and {"mode":"full"} share parent conversation and cannot be combined with resume. resume continues one prior CLI run that recorded a session id, on the same backend: claude, codex, agy, grok, or muse. A named pi-* harness has no persisted session, so resume does not continue it. Prefer resume for the same task and a new child when you want an independent judgment.
213
+
214
+ Calls block until the child finishes. background true returns a run id while the originating session still owns the work. Use external_runs to wait, then read view final, and judge that answer yourself. A failed or aborted run stays failed. Retry it, or switch model or harness, only when the user authorized that step. agy may make one disclosed infrastructure retry; that exception is not yours to extend.
215
+
216
+ One Agent call. Permission stays omitted; the prompt carries the no-edit intent:
217
+ ${jsonBlock(USAGE_ONE_AGENT)}
218
+
219
+ Independent review. Fresh context, a reviewer role, the default tier, no resume:
220
+ ${jsonBlock(USAGE_INDEPENDENT_REVIEW)}
221
+
222
+ Background work, then the verified final answer. A one-element runIds list is valid for view final:
223
+ ${jsonBlock(USAGE_BACKGROUND_AGENT)}
224
+ ${jsonBlock(USAGE_RUNS_FINAL)}
225
+
226
+ Parallel workflow. Catch the child error inside each branch so one failure does not drain its sibling. Pass this string as the workflow tool's script:
227
+ ${script}
228
+ Resume, only for the same task on a CLI harness that stored a session id. Do not combine it with context:
229
+ ${jsonBlock(USAGE_RESUME_AGENT)}
230
+
231
+ Confine the tier only when the task must stay below defaultPermission. Antigravity rejects readonly and edit:
232
+ ${jsonBlock(USAGE_RESTRICTED_AGENT)}`;
96
233
  }
@@ -20,7 +20,7 @@ import { runRecordsDirectory } from "../core/retention.ts";
20
20
  import { captureParentContext } from "../core/parent-context.ts";
21
21
  import { RunRegistry } from "../core/run-registry.ts";
22
22
  import { OUTPUT_PREVIEW_CHARS } from "../core/progress.ts";
23
- import { loadExternalCatalog, resolveExternalProfile, selectorHarness } from "../profiles.ts";
23
+ import { loadExternalCatalog, resolveExternalProfile, selectorHarness, unknownExternalProfileMessage } from "../profiles.ts";
24
24
  import { effectivePiResourcePreset, loadHarnessConfigs } from "../harnesses.ts";
25
25
  import { WORKFLOW_PROMPT_SNIPPET } from "../prompts.ts";
26
26
  import { EXTERNAL_HARNESSES, type PermissionTier, type SubagentProfile, type SubagentToolDetails, type SubagentUsage, type WorkflowAgentSnapshot, type WorkflowToolDetails } from "../types.ts";
@@ -200,9 +200,7 @@ export function createWorkflowTool(
200
200
  const profile = profiles.get(call.subagentType);
201
201
  if (profile?.configurationError) throw new Error(profile.configurationError);
202
202
  if (!profile) {
203
- throw new Error(
204
- `Unknown external subagent_type "${call.subagentType}". Available external agents: ${[...profiles.keys()].join(", ")}. Use the native subagent system for Pi-backed agents.`,
205
- );
203
+ throw new Error(unknownExternalProfileMessage(call.subagentType, profiles.keys()));
206
204
  }
207
205
  const model = models.get(call.subagentType);
208
206
  if (usesPiBackend(profile) && !model) {
@@ -80,7 +80,7 @@ export interface WorkflowAgentCall {
80
80
  subagentType: string;
81
81
  /** JSON Schema for structured output from the child subagent. */
82
82
  schema?: unknown;
83
- /** Permission tier for this child (call > profile > settings default). */
83
+ /** Permission tier for this child: the call's permission, otherwise settings defaultPermission. A role does not set this. */
84
84
  permission?: import("../types.ts").PermissionTier;
85
85
  /** USD budget cap for this child, when any. */
86
86
  maxBudgetUsd?: number;