feat(pi): Agent tool for sub-agents on the pi runtime - #6756
Conversation
PR Summary by Qodofeat(pi): add Agent/Task sub-agent support
AI Description
Diagram
High-Level Assessment
Files changed (18)
|
81e5855 to
4e91c29
Compare
|
🤖 Review · Commit: |
Site previewPreview: https://1aeb9eb3-site.fullsend-ai.workers.dev Commit: |
|
🤖 Finished Review · ✅ Success · Started 6:11 PM UTC · Completed 6:29 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $12.82 |
Code Review by Qodo
1.
|
|
Risk Assessment: moderate (2/5) DetailsModerate risk. The PR is large (3876 lines, 24 files) giving a high change-size score, but strong mitigating factors keep overall risk moderate: no protected paths, no security-sensitive files, no CI or dependency changes, solid test ratio (0.38), and the author is experienced. Most additions are new files rather than modifications to shared hot paths. Elevated Tier 2 churn and coupling scores are driven by hub files (run.go, runtime.go) where this PR's changes are minor. Clear issue lineage (#6527, #6464) confirms well-scoped planned feature work. Re-review anchoring: Tier 1 signals unchanged from prior assessment; score of 2 confirmed. Previous runRisk Assessment: moderate (2/5) DetailsModerate risk. The PR is large (3873 lines, 24 files) giving a high change-size score, but strong mitigating factors keep overall risk moderate: no protected paths, no security-sensitive files, no CI or dependency changes, solid test ratio (0.38), and the author is highly familiar with the internal/runtime/pi subsystem. Most additions are new files rather than modifications to shared hot paths, reducing merge conflict and regression risk. Clear issue lineage (#6527, #6464) confirms well-scoped planned feature work. Re-review anchoring: signals consistent with prior score of 2. Previous run (2)Risk Assessment: moderate (2/5) DetailsModerate risk. While the PR is large (1908 lines, 18 files), the high change-size score is offset by strong mitigating factors: no protected paths, no security-sensitive files, no CI/dependency changes, decent test ratio (0.39), known non-bot non-first-time author, and well-scoped feature work with clear issue lineage (#6527, #6464). The high-churn files (run.go, runtimes.md) are touched only lightly. Six files are entirely new. |
ReviewFindingsLow
Next steps:
Previous runReviewFindingsLow
Next steps:
Previous run (2)ReviewFindingsLow
Next steps:
Previous run (3)ReviewFindingsMedium
Low
Next steps:
Previous run (4)ReviewFindingsHigh
Medium
Low
Next steps:
|
4e91c29 to
67e8ed5
Compare
|
🤖 Finished Review · ✅ Success · Started 7:03 PM UTC · Completed 7:21 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $8.84 |
67e8ed5 to
7fa2def
Compare
7fa2def to
cf9800e
Compare
|
🤖 Review · Commit: |
|
🤖 Finished Review · ✅ Success · Started 8:17 PM UTC · Completed 8:36 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $11.56 |
a007629 to
7331170
Compare
|
🤖 Finished Review · ✅ Success · Started 8:53 PM UTC · Completed 9:14 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $8.71 |
An agent definition that lists Agent or Task under tools: now activates both pi tool names (the extension registers Agent with Task as its legacy alias) instead of reporting them as unsupported. Adds the enablement predicate (no tools: entry, or Agent/Task listed) and the read-only Explore tool set shared with the extension via the manifest. Refs: #6527, #6464 Assisted-by: Claude Signed-off-by: Wayne Sun <gsun@redhat.com>
A pi extension that registers Claude Code's Agent tool (and its legacy alias Task) with the same contract — prompt, description, model, subagent_type, run_in_background accepted and ignored — so the fleet's pr-review and retro-analysis skills dispatch unchanged on the pi runtime. Each call runs one child pi --print --mode json to completion with the parent's flag set (trust off, --no-extensions plus the manifest's provider extensions and the hook adapter, strict --tools, its own session dir) and FULLSEND_SUBAGENT_DEPTH=1; the extension refuses to register when that variable is already set, so recursion is impossible. Models are translated through the manifest alias table (opus|sonnet| haiku, Claude ids with an @suffix, anthropic/ and xai/ direct-API forms) and anything the run cannot serve — an invented Claude id, a provider without credentials — is rejected with the accepted forms rather than passed through. Explore gives the read-only tool set; a concurrency cap, a timeout that kills the child's process group, and a usage.jsonl line per child (for metrics.json) complete it. Failed children surface as isError with the child's message. Refs: #6527, #6464 Assisted-by: Claude Signed-off-by: Wayne Sun <gsun@redhat.com>
Bootstrap now enables the Agent tool when the agent definition has no tools: frontmatter or lists Agent/Task: it uploads fullsend-agent.js, probes the sandbox for the pi binary and the vendored provider extension directories that exist, and writes the `agent` manifest block the extension reads — child extensions (providers present, then the hook adapter when security is on), the model alias table (default = the agent's model translated as for the parent; opus|sonnet|haiku on the Anthropic Vertex provider), thinking (FULLSEND_PI_SUBAGENT_THINKING when it names a pi level, else medium), the child and Explore tool sets, the concurrency cap, timeout and usage file. The hook adapter's tool-name table gains Agent/Task so the scripts see Claude vocabulary, and APPEND_SYSTEM.md gets a note describing the tool and that parallel dispatch is several Agent calls in one message; an agent whose tools: leaves Agent out keeps the single-context note. Refs: #6527, #6464 Assisted-by: Claude Signed-off-by: Wayne Sun <gsun@redhat.com>
Partial: the run-command guard and -e wiring; transcripts, metrics folding and docs follow in the next commits. Assisted-by: Claude Signed-off-by: Wayne Sun <gsun@redhat.com>
A child dispatched through the Agent tool is a separate pi process, so none of its tokens reach the parent's --mode json stream and its session file lands in its own `sessions/agent-<seq>/` directory. Without this the run's artifacts and metrics.json show the orchestrator's cost only, and a review whose spend is dominated by its roster looks nearly free. ExtractTranscripts now names child sessions `<agent>-sub<seq>-<basename>` (the sequence number keeps children with equal session basenames apart) and downloads the extension's usage file beside them. Run reads that file after the iteration and folds it into RunMetrics: the totals grow by what the children spent, and per_model_usage attributes them, with the parent's own iteration as one entry so the breakdown sums to the totals. Runs that dispatch nothing keep metrics.json byte-identical. ClearIterationArtifacts removes the usage file, which sits outside the sessions dir the glob already clears, so a retry does not re-count the previous iteration's children. Assisted-by: Claude Signed-off-by: Wayne Sun <gsun@redhat.com>
…acts The pi runtime pages still said sub-agents were not wired and that review/retro must stay on Claude Code, which is no longer true and would send readers to the single-context workaround. pi.md gains a Sub-agents section: the tool contract, what a child inherits (hooks, providers, trust off, tool allowlist), the model alias table and why an unservable model is rejected rather than passed through, the medium thinking default with its env override and the review-budget reason for it, and where child transcripts, the usage file and per_model_usage land. runtimes.md's matrix row and the "stay on claude for sub-agents" advice follow. runtime-implementation.md gets an Agent tool contract section with the manifest fields, the depth guard, the shared exit-97 integrity check and the process-group kill, plus the two new paths in the sandbox layout tree. Assisted-by: Claude Signed-off-by: Wayne Sun <gsun@redhat.com>
… contributing Same split as the extensions section: the `Sub-agents` section on the pi runtime page was written as security design notes — "Prompt delivery", "Stopping a child", "`.env` is the same trust class here as everywhere else", the manifest/adapter digest re-hash sequencing — so an agent author who just wants to use `Agent` could not read it straight through. - `docs/runtimes/pi.md` § Sub-agents is now a walkthrough: a parameter table, choosing a model (four accepted forms and the one rule that rejects the rest), running children in parallel, what a child does and does not inherit, thinking level, where the output lands, turning it off, and a symptom/cause/fix table. 118 lines down to 102. - `docs/contributing/runtime-implementation.md` § Agent tool contract is renamed "Pi sub-agents: the Agent tool contract" so it sits beside the Pi extensions subsection, and gains the two facts the user page no longer carries: the usage file is consumed as it is read (`<usageFile>.read`, capped at 1 MiB) so a retry cannot double-count children, and the `.env` trust discussion around `FULLSEND_SUBAGENT_DEPTH`. Everything else the user page dropped — stdin prompt delivery and the argv hazards, the SIGTERM-then-SIGKILL stop sequence and process groups, the per-dispatch digest re-checks — was already documented there and is now linked rather than restated. `docs/cli/run.md` § Per-model usage already read cleanly and is unchanged. Assisted-by: Claude Signed-off-by: Wayne Sun <gsun@redhat.com>
7331170 to
ca12f63
Compare
|
🤖 Finished Review · ✅ Success · Started 9:19 PM UTC · Completed 10:00 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $13.22 |
| // reached the stream just parsed; the extension's usage file is the | ||
| // only record of what they spent. A read failure is not fatal — | ||
| // losing the breakdown must not fail an iteration that succeeded. | ||
| if usage, _, _, uerr := sandbox.Exec(params.SandboxName, piSubagentUsageReadCommand(m.Agent.UsageFile), 10*time.Second); uerr != nil { |
There was a problem hiding this comment.
[low] error-handling-gap
When the sub-agent usage file read fails (uerr != nil), foldPiSubagentUsage is never called because it sits inside the else branch. The parent's own entry is not recorded in PerModelUsage. In a retry run where one iteration dispatched children and another's read fails, per_model_usage stops summing to the totals — breaking the documented invariant. Practical impact is low because piSubagentUsageReadCommand ends with 2>/dev/null || true, so uerr would only be non-nil from a transport-level sandbox.Exec failure.
Suggested fix: Move the foldPiSubagentUsage call (with empty data) outside the else branch so the parent entry is always added when the Agent tool is enabled.
|
|
||
| // piManifestHash returns the digest recorded for a sandbox, or "" when | ||
| // Bootstrap did not run in this process. | ||
| func piManifestHash(sandboxName string) string { |
There was a problem hiding this comment.
[low] fail-open
When Bootstrap and Run execute in separate processes, piManifestHash returns an empty string, causing Run to silently omit both the manifest integrity shell guard and the FULLSEND_PI_MANIFEST_SHA256 export. Nothing logs or warns when the guard is not emitted, making this silent fail-open invisible to operators.
Suggested fix: Add a log line in Run when piManifestHash returns empty and the agent tool is enabled, so operators who inadvertently separate Bootstrap and Run get a warning that the manifest integrity chain is not active.
| @@ -23,6 +26,14 @@ const ( | |||
| // sandbox hook scripts; loaded explicitly with -e, never auto-discovered | |||
There was a problem hiding this comment.
[low] naming-consistency
piAgentUsageFile is named with a File suffix but its value is a relative path with a directory component (subagents/usage.jsonl). The codebase convention uses File for bare filenames (piHooksExtensionFile, piManifestFile, piAgentExtensionFile) and Path for values with directory segments. The existing method piAgentUsagePath() creates a File-constant / Path-method pair that inverts the usual naming.
Suggested fix: Rename piAgentUsageFile to piAgentUsageRelPath and rename the method piAgentUsagePath() to piAgentUsageAbsPath(), or accept the minor inconsistency.
| # Choose an agent runtime | ||
|
|
||
| > **Claude Code is the stable default.** The fleet agents have run on Claude Code in production for a long time; it is what a new installation gets unless you ask for something else. **pi is in its enablement (experimental) phase** — it works end to end for `triage`, `prioritize`, `code` and `fix`, has no sub-agent tool yet (`review`/`retro` run in a single context), and its fleet pilot is still in progress. Unless you are taking part in that pilot, keep the default. | ||
| > **Claude Code is the stable default.** The fleet agents have run on Claude Code in production for a long time; it is what a new installation gets unless you ask for something else. **pi is in its enablement (experimental) phase** — it works end to end for `triage`, `prioritize`, `code` and `fix`, and `review`/`retro` now dispatch their real sub-agent roster through a fullsend-supplied `Agent`/`Task` tool, but its fleet pilot is still in progress. Unless you are taking part in that pilot, keep the default. |
There was a problem hiding this comment.
Remove the specific phrase:
| > **Claude Code is the stable default.** The fleet agents have run on Claude Code in production for a long time; it is what a new installation gets unless you ask for something else. **pi is in its enablement (experimental) phase** — it works end to end for `triage`, `prioritize`, `code` and `fix`, and `review`/`retro` now dispatch their real sub-agent roster through a fullsend-supplied `Agent`/`Task` tool, but its fleet pilot is still in progress. Unless you are taking part in that pilot, keep the default. | |
| > **Claude Code is the stable default.** The fleet agents have run on Claude Code in production for a long time; it is what a new installation gets unless you ask for something else. **pi is in its enablement (experimental) phase** — it works end to end for `triage`, `prioritize`, `code` and `fix`, and `review`/`retro`. Unless you are taking part in the `pi` pilot, keep the default. |
| |---------|--------|-------------|-------------| | ||
| | `claude` | **Stable (default)** | Claude Code on Vertex AI | Every production deployment — mature, full sub-agent support for `review`/`retro` | | ||
| | `pi` | Experimental (enablement phase) | [Pi](https://github.com/earendil-works/pi) — Claude on Vertex by default; any provider pi supports by model name (e.g. Gemini on Vertex with the same credentials) | Opt-in pilots only; no sub-agent tool yet, so `review`/`retro` run single-context; see [Runtimes](../../runtimes.md) for known constraints | | ||
| | `pi` | Experimental (enablement phase) | [Pi](https://github.com/earendil-works/pi) — Claude on Vertex by default; any provider pi supports by model name (e.g. Gemini on Vertex with the same credentials) | Opt-in pilots only; `Agent`/`Task` sub-agents come from a fullsend extension (children are `pi` processes) rather than from pi itself; see [Runtimes](../../runtimes.md) for known constraints | |
There was a problem hiding this comment.
Too much detail somewhere it does not belong, probably you are already documenting this somewhere else. Remove this detail.
| | | | | ||
| |---|---| | ||
| | Roles | All, including `review` and `retro` — they need sub-agents | | ||
| | Roles | All, including `review` and `retro` — they need sub-agents (pi covers these too, through a fullsend extension: [pi § Sub-agents](pi.md#sub-agents)) | |
There was a problem hiding this comment.
Again, too much detail where it does not belong.
| Claude Code's exact framing can read differently there. | ||
| - **Native sub-agents** via the `Agent` tool, which is why `review` and `retro` are Claude-only | ||
| today. | ||
| - **Native sub-agents** via the `Agent` tool. This is no longer Claude-only: pi serves the same `Agent`/`Task` contract from a runner-owned extension that runs each child as its own `pi` process ([pi § Sub-agents](pi.md#sub-agents)). What stays Claude-specific is that the sub-agents are *native* — no child process, no separate session dir, no per-child provider hygiene. |
There was a problem hiding this comment.
Again, comenting pi details on the claude page.
| - **Native sub-agents** via the `Agent` tool. This is no longer Claude-only: pi serves the same `Agent`/`Task` contract from a runner-owned extension that runs each child as its own `pi` process ([pi § Sub-agents](pi.md#sub-agents)). What stays Claude-specific is that the sub-agents are *native* — no child process, no separate session dir, no per-child provider hygiene. | |
| - **Native sub-agents** via the `Agent` tool. |
| |---|---|---| | ||
| | Models | Anthropic on Vertex | Claude, **Grok** and **Gemini** on Vertex; **GPT** via OpenAI WIF (opt-in, [not yet exercised live](runtimes/pi.md#models-and-providers)) | | ||
| | Sub-agents | Native (`Agent` tool) | Not wired — agents execute sub-agent definitions inline ([#6527](https://github.com/fullsend-ai/fullsend/issues/6527)) | | ||
| | Sub-agents | Native (`Agent` tool) | `Agent`/`Task` via a fullsend extension — children are `pi` processes with the same hooks, providers and tool allowlist ([pi runtime § Sub-agents](runtimes/pi.md#sub-agents)) | |
There was a problem hiding this comment.
Details where they are not needed.
| | Sub-agents | Native (`Agent` tool) | `Agent`/`Task` via a fullsend extension — children are `pi` processes with the same hooks, providers and tool allowlist ([pi runtime § Sub-agents](runtimes/pi.md#sub-agents)) | | |
| | Sub-agents | Native (`Agent` tool) | `Agent`/`Task` via a fullsend extension | |
| | Sub-agents | Native (`Agent` tool) | `Agent`/`Task` via a fullsend extension — children are `pi` processes with the same hooks, providers and tool allowlist ([pi runtime § Sub-agents](runtimes/pi.md#sub-agents)) | | ||
| | Fallback model chain | `FULLSEND_FALLBACK_MODELS`, tried in order | Ignored with a warning | | ||
| | Roles | All | `review`/`retro` stay on Claude Code — they rely on sub-agent rosters | | ||
| | Roles | All | All; `review`/`retro` run their real sub-agent roster, at `--thinking medium` by default | |
There was a problem hiding this comment.
| | Roles | All | All; `review`/`retro` run their real sub-agent roster, at `--thinking medium` by default | | |
| | Roles | All | All; `review`/`retro` at `--thinking medium` by default | |
rh-hemartin
left a comment
There was a problem hiding this comment.
I think the extension may live elsewhere and then install them on the image, but for now that looks good to me. Probably a followup to move the extensions would be good.
Summary
Stacked on #6754 (base branch
pi-extensions-key) → #6752. Retarget as the stack merges.Gives the pi runtime Claude Code's
Agenttool (aliasTask) soreviewandretrorun their real sub-agent rosters on pi instead of the single-context fallback. The contract is Claude Code's, soskills/pr-reviewandskills/retro-analysisdispatch unchanged:prompt,description,model(Claude alias/id orprovider/id, translated through the manifest alias table; an unknown or Claude-shaped id is rejected with a clear tool error rather than passed through),subagent_type(Explore→read,grep,find,ls; otherwise the parent's built-in set minusAgent/Task);run_in_backgroundaccepted and ignored — pi runs sibling tool calls concurrently, which is the parallel dispatch the skills ask for.Design was verified empirically first (2026-08-29, with a community stand-in): the full pr-review roster ran as parallel children carrying the hook adapter and both provider extensions, mixed providers per child (Grok 4.6 / Sonnet 5 / Haiku), and a Grok orchestrator finished a review in ~16 min. This PR replaces the stand-in with a fullsend-owned extension and closes the gaps that run exposed (model spec bound by the runtime, per-child thinking default
medium, child transcripts and cost accounted).Mechanics.
internal/runtime/pi_extension/fullsend-agent.js(ESM,go:embed, SHA-256-checked in the run command like the hook adapter — shared exit 97): registersAgent/Task; children spawn aspi --print --mode json --no-approve --no-extensions --no-prompt-templates --no-themes --session-dir <sessions>/agent-<seq> -e <every provider extension present in the image> -e fullsend-hooks.js --tools <set> --model <spec> --thinking <level> --append-system-prompt <child note>with the prompt delivered over stdin (pi's option parser treats a positional starting with-/--as a flag and@…as a file even after--, and argv caps at 128 KiB — reproduced with pi 0.84.4) andFULLSEND_SUBAGENT_DEPTH=1; the extension refuses to register when that variable is already set and children never receive-e fullsend-agent.js(no recursion by construction); concurrency semaphore (4) with shutdown-drained waiters, per-child timeout/abort/session_shutdown→SIGTERM(pi reaps its detached bash grandchildren) thenSIGKILLafter 3 s; per-child provider env hygiene (the sameANTHROPIC_*/XAI_*scrubs and project pinsbuildPiRunCommandapplies, chosen by the child's resolved provider); children get a child-only--append-system-promptinstead of the orchestrator'sAPPEND_SYSTEM.md; result = the child's final text (64 KB cap),isErroronstopReasonerror/aborted, non-zero exit, timeout or missingagent_end; one usage line per child appended to/sandbox/pi-config/subagents/usage.jsonl; stderr[fullsend-agent] #<seq> <model> start/donelines.agentblock (enabled when the agent listsAgent/Taskor has notools:; provider extensions probed withtest -d; alias table viatranslatePiModelincl.claude-sonnet-4-6@default-style ids;thinking=FULLSEND_PI_SUBAGENT_THINKINGormedium) and swaps the "no sub-agent tool" runtime note for an "Agent is available; parallel dispatch = several Agent calls in one message" note.-e fullsend-agent.jsafter the hook adapter (PreToolUse hooks seeAgentcalls, as on Claude Code);Agent,Taskadded to--toolswhen the agent declares them.<agent>-sub<seq>-<basename>.jsonlplus<agent>-subagents-usage.jsonl; child tokens/cost are folded intoRunMetricstotals and a newper_model_usagebreakdown (parent iteration included, so the map sums to the totals);ClearIterationArtifactsremoves child sessions and the usage file so retries don't double-count.docs/runtimes/pi.md"Sub-agents" (replaces "not supported"),docs/runtimes.mdmatrix row,docs/contributing/runtime-implementation.md"Agent tool contract" + manifest fields.Closes the "pi
Agenttool (Track E)" item of #6527; refs #6464.Review round 1 → fixes (head
67e8ed5c)Claude + Grok found one HIGH (prompt as positional argv) and the MEDIUMs now fixed: stdin prompt delivery (verified against real pi 0.84.4:
- a leading dashexits 1 as argv, arrives as the user message over stdin);detacheddropped andSIGTERM→SIGKILLsequence (test reaps a detached grandchild); abort signal wired,shuttingDownre-checked after the semaphore, waiters drained on shutdown; empty child tool list →--no-builtin-tools; per-provider child env;:thinkingsuffix stripped from model specs; the parent's live provider allowed for inherited specs; the manifestagentblock is now SHA-256-verified in the run command (pre-.envand post-.env, exit 95) so the extension no longer trusts an agent-writable file;fullsend-agentreserved as an extension name; child-only system prompt (--append-system-promptreplacesAPPEND_SYSTEM.mddiscovery); 1 MiB stdout-line cap,head -c 1MiBusage read, 512-bytedescriptioncap (record stays underPIPE_BUF);per_model_usagefolds the parent entry on every iteration and the usage file is renamed after reading (idempotent across retries; malformed lines counted and warned);Exploretools intersected with the parent's set; docs reconciled (choosing-a-runtime.md,runtimes/claude.md,runtime-implementation.mdflowchart + guards,docs/cli/run.mdper_model_usage, "what a child does not inherit").Review round 2 → fixes (head
a007629b, rebased onto #6754's current head)Claude + Grok round 2: the semaphore over-admitted one child when a queued dispatch was aborted while other waiters remained (a queued ticket claimed a slot in
unqueue; itsrelease()then handed that slot on without decrementing) — tickets nowgrant()/evict()explicitly and only an acquired ticket releases, with a maxConcurrent=2 regression test that failed on the old code; the manifest's integrity is now enforced for the whole iteration, not just at launch: the run command exportsFULLSEND_PI_MANIFEST_SHA256after.env,fullsend-hooks.jsrefuses to load a manifest that does not match it (so a child whose adapter starts later dies and the tool reportsisError), and the Agent extension re-hashes the manifest immediately before each dispatch;resolveModelaccepts only a closed set (manifest alias values, the parent's own spec, and a Bootstrap-writtenproviderModelslist taken verbatim from pi 0.84.4'sgoogle-vertexcatalog — 13 ids;gemini-3.7-prodoes not exist there); the usage-file download goes through the sameOpenRootcontainment as transcripts (a../agent label escapedoutputDirbefore); usage figures are clamped at zero and half-records (missingseqorusage) are counted as skipped; the Agent-extension guard has its own exit code 94 (97 = hook adapter, 95 = manifest, 96 = declared extensions, 98 = provider config); comments/docs corrected (pi does honour--, but@positionals stay file arguments and argv is capped — stdin stays;descriptioncapped in the log line;per_model_usagefield list; parent entry on every Agent-enabled iteration; dash note onunset -f test [ …; all roles supported on pi;-e fullsend-agent.jsin the command template;providerModelsmanifest row).Review round 3 (verification) → fixes (head
7331170c)Both reviewers confirmed every round-2 fix. Two residuals closed: the per-dispatch drift check now also re-hashes the hook adapter file children load (
agent.extensionDigests, written by Bootstrap assha256(fullsend-hooks.js)— the same digest the launch guard checks — so a parent that rewrites the adapter mid-iteration getshook adapter changed since load; refusing to dispatch); and thexai/xai-vertexbranch ofresolveModelonly normalises the spelling now and then goes through the same closed set as every other provider (providerModels["xai-vertex"] = ["xai/grok-4.6"], the single id pi-xai-vertex v0.2.0 registers). Also:+Infcost clamped, the--sentence in runtime-implementation.md corrected,-e fullsend-agent.jsin the literal command template, the new model lists added to thePI_VERSION-bump re-check table, and a note that Bootstrap and Run must share a process for the manifest digest guard. Node: 58/58 with a real pi 0.84.4 (57 + 1 skipped without).Test plan
go build ./...;go test ./...— all packages pass except the known environmental failures (TestDummyRuntime_*with a local gateway,TestResolveAgentSource_OverrideOnlyEntryUsesAgentsRepoFallbackandTestListTriggeredHarnesses_BaseCompositionon macOS/varsymlinks); new:piToolsFormapping,piAgentManifestFor(enabled/disabled, aliases, extensions present/absent),buildPiRunCommandgoldens (-eorder,--tools, guard adjacency),ExtractTranscriptschild sessions,foldPiSubagentUsage/parsePiSubagentUsage, metrics aggregation across iterationsnode --teston both extension test files — 55 (54 pass, 1 skipped withoutFULLSEND_TEST_PI_BIN; 55/55 with it pointing at a pi 0.84.4 install, incl. the 200 KiB stdin prompt and the positional negative control) (fakepistub: success text, error stopReason → isError, timeout → group kill, concurrency cap,Exploretool set, model translation/rejection, depth refusal, missing manifest → registers nothing)pre-commit run --from-ref pi-extensions-key --to-ref HEAD— all hooks passfullsend run review --runtime pion the stacked branch against a real PR (Grok 4.6 orchestrator viaxai-vertex,pr-reviewskill roster): 7Agentdispatches (3 Opus, 4 Sonnet; up to 4 concurrent), every childstop, agent exit 0 in one iteration;transcripts/review-sub1..7-*.jsonl+review-subagents-usage.jsonlextracted;metrics.jsonper_model_usage=xai-vertex/xai/grok-4.6$2.34 /anthropic-vertex/claude-opus-4-6$0.95 (3 req) /anthropic-vertex/claude-sonnet-4-6$0.85 (4 req), summing tototal_cost_usd$4.15