pi-subagents 0.52.0 → 0.52.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.52.1] - 2026-08-20
6
+
7
+ ### Highlights
8
+ - Model setup errors now point to the right alternate provider when there is one clear match.
9
+ - Surf's optional `gpt-pro` package agent has a smoother path to run ChatGPT Pro web jobs through the external-job bridge when the user is logged in.
10
+ - External-job providers can add metadata or extra operations without breaking provider discovery.
11
+ - The packaged skill now includes a concise guide for coordinating multiple tasks, worktrees, and repositories.
12
+ - Pi extension worktrees now have clearer guidance to avoid duplicate auto-loaded tools and shortcuts.
13
+
14
+ ### Fixed
15
+ - Suggest the unique alternate provider model when an explicit qualified subagent model is unavailable, without resolving across providers. Thanks to [@lallenlowe](https://github.com/lallenlowe) for #1280.
16
+ - Accept extra fields on registered external-job providers, such as `kind`, `wakeChannels`, or additional operations. This keeps integrations such as Surf's `gpt-pro` package agent from breaking provider discovery as they add browser-backed job metadata, while job payload validation stays strict.
17
+
18
+ ### Changed
19
+ - Add a pi-subagents reference for coordinating multiple tasks, worktrees, and repositories, including guidance for keeping Pi extension worktrees outside auto-discovered extension directories.
20
+
5
21
  ## [0.52.0] - 2026-08-19
6
22
 
7
23
  ### Highlights
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-subagents",
3
- "version": "0.52.0",
3
+ "version": "0.52.1",
4
4
  "description": "Pi extension for single-agent delegation and scripted multi-agent workflows",
5
5
  "author": "Nico Bailon",
6
6
  "license": "MIT",
@@ -22,6 +22,7 @@ Read the matching reference file before acting. Paths are relative to this `SKIL
22
22
  | --- | --- |
23
23
  | Decide whether to delegate, choose agents, compare tool versus slash commands, apply prompt techniques, or understand builtin roles | `references/prompting-and-roles.md` |
24
24
  | Run one-child, scripted, async, scheduled, mission-backed, forked, watchdog, oracle, or intercom-coordinated workflows | `references/execution-controls.md` |
25
+ | Coordinate several independent tasks, worktrees, repositories, or writer lanes | `references/multi-lane-orchestration.md` |
25
26
  | List/create/update/delete/eject/disable agents, inspect legacy chain records, edit agent files, use prompt-template integration, or expose extension RPC | `references/management-authoring-rpc.md` |
26
27
  | Check safety constraints, best practices, standard workflows, or error handling | `references/constraints-and-recipes.md` |
27
28
 
@@ -30,6 +31,7 @@ For broad or uncertain requests, read more than one reference. For complex work,
30
31
  ## Always-on constraints
31
32
 
32
33
  - Keep the parent as orchestrator and final decision-maker.
34
+ - Before multiple mutation-capable lanes, record a lane board and each lane's isolation path.
33
35
  - For plan, design, or architecture advice that asks to consult, discuss with, or come to agreement with `oracle`, use a short same-session consultation loop: read the first result, resume once with a targeted challenge when material tradeoffs remain, then synthesize the parent decision. Keep explicit one-shot, trivial, and fully settled consultations one-shot.
34
36
  - Use one writer per cwd/worktree unless isolated worktrees are intentional.
35
37
  - For cross-codebase work, record the target repo, explicit `cwd`, authority boundary, and expected output before launch. Do not assume the parent session cwd is the child repo.
@@ -0,0 +1,39 @@
1
+ # Multi-Lane Orchestration
2
+
3
+ Use this reference when several independent tasks need coordinated workers, worktrees, or repositories. It defines lane ownership; use the other pi-subagents references for run controls, prompts, and mission details. The parent remains the final decision-maker.
4
+
5
+ ## Lane board and authority
6
+
7
+ Before multiple mutation-capable lanes start, record this board in the parent context:
8
+
9
+ `Lane | repo/cwd | exact decision | claimed files or contract | isolation path | authority | next gate | handoff | why independent`
10
+
11
+ Record the isolation path before the first mutation. Do not split one source seam or decision into duplicate lanes. Make overlapping work one lane with one source of truth.
12
+
13
+ For every lane, record the delivery target, allowed actions, required validation, and review rigor. For cross-repository work, name the shared contract and which repository changes first. A blocked decision is a lane state: record the owner, options, recommended default, and evidence needed to continue.
14
+
15
+ ## Partitioned runs
16
+
17
+ Use one writer per repo/cwd or worktree. Mutation lanes need distinct isolation paths and explicit `cwd` values. Set `worktree: true` when a run needs managed worktree isolation within one repository. Read-only runs can share a checkout only when they cannot change state or create generated files.
18
+
19
+ For Pi extension repositories, keep lane worktrees outside auto-discovered extension directories such as `~/.pi/agent/extensions`. A stale extension worktree there can auto-load duplicate tools and shortcuts. Remove or move it only after its handoff is durable, the worktree is clean, and no run owns it.
20
+
21
+ Partition fanout by repository, source seam, decision, or review angle. Each run needs a stable key, lane-specific task, and durable output path. Do not launch prompts that differ only by item name or broad file glob.
22
+
23
+ Use one async `workflowScript` for a coordinated wave. Use `runs.all` for independent lanes and `runs.run` for dependent lane stages. Give cross-repository runs explicit `cwd` values and lane-qualified outputs. Use `outputMode: "file-only"` when a report must survive the run or feed a later stage.
24
+
25
+ ## Keep independent work moving
26
+
27
+ While one lane waits, run safe independent preparation, validation, or fresh read-only review lanes. Do not block the parent just because a run is active. If no safe lane remains, record the blocker and the event that will reopen work.
28
+
29
+ An ordinary coordinated workflow has one mission. Use its durable state, artifacts, run records, and receipts for recovery. Treat a receipt as evidence, not as authority or acceptance.
30
+
31
+ After a writer produces a candidate, run the required fresh-context, read-only reviewer. The reviewer inspects the exact worktree and returns evidence-backed findings. The parent decides which findings are in scope and whether the lane is ready. Send accepted fixes to that lane's sole writer, then rerun only the affected gate.
32
+
33
+ ## Handoff, cleanup, and recovery
34
+
35
+ Use stable lane-qualified paths for reports and review output. A handoff states the lane status, repository and worktree, changed files, validation, open decisions, next action, and artifact or receipt paths.
36
+
37
+ Keep a worktree until its handoff is durable, no run owns it, and no later gate needs it. Clean up only inside the recorded authority boundary. If a run stops or needs attention, preserve its worktree and artifacts, record the last known state and recovery owner, then resume that run or create one replacement lane from the handoff. Do not start another writer while worktree ownership is uncertain.
38
+
39
+ Before completion, inspect the board. Every lane must be terminal or blocked with a named next action. Confirm one writer per repo/cwd or worktree, required validation, required fresh read-only review, and a durable handoff. The parent reports outcomes, evidence, residual risks, and the next decision.
@@ -146,8 +146,9 @@ export function validateExternalJobResult(provider: string, value: unknown, fiel
146
146
  function validateProvider(value: unknown): ExternalJobProvider {
147
147
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("External-job provider must be an object.");
148
148
  const provider = value as Record<string, unknown>;
149
- const unknownFields = Object.keys(provider).filter((key) => !["name", "start", "status", "result", "reattach"].includes(key));
150
- if (unknownFields.length > 0) throw new Error(`External-job provider has unknown fields: ${unknownFields.join(", ")}.`);
149
+ // Tolerate extra provider fields (for example kind, wakeChannels, or future
150
+ // operations) so one evolving provider cannot poison registry reads for all
151
+ // providers. Payload validation stays strict.
151
152
  const name = validateString(provider.name, "External-job provider name", MAX_PROVIDER_NAME_LENGTH);
152
153
  for (const op of ["start", "status", "result", "reattach"] as const) {
153
154
  if (typeof provider[op] !== "function") throw new Error(`External-job provider '${name}' must expose ${op}().`);
@@ -341,7 +341,7 @@ const SubagentParamProperties = {
341
341
  })),
342
342
  outputMode: Type.Optional(OutputModeOverride),
343
343
  skill: Type.Optional(SkillOverride),
344
- model: Type.Optional(Type.String({ description: "Default child model override (e.g. 'anthropic/claude-sonnet-4')" })),
344
+ model: Type.Optional(Type.String({ description: "Default child model override. Full provider/id values are accepted; bare ids resolve from the active registry." })),
345
345
  outputSchema: Type.Optional(JsonSchemaObject),
346
346
  agentContract: Type.Optional(AgentContractOverride),
347
347
  acceptance: Type.Optional(AcceptanceOverride),
@@ -205,6 +205,21 @@ function resolveSubagentModelCandidate(
205
205
  return resolvedBase ? `${resolvedBase}${thinkingSuffix}` : undefined;
206
206
  }
207
207
 
208
+ function suggestAlternateProviderModel(
209
+ model: string,
210
+ availableModels: AvailableModelInfo[] | undefined,
211
+ ): string | undefined {
212
+ if (!availableModels || availableModels.length === 0) return undefined;
213
+ const { baseModel, thinkingSuffix } = splitThinkingSuffix(model);
214
+ const { queryProvider, queryIdRaw } = splitQualifiedModelQuery(baseModel, availableModels);
215
+ if (queryProvider === undefined) return undefined;
216
+ const suggestion = resolveBaseModelCandidate(queryIdRaw, availableModels);
217
+ if (!suggestion) return undefined;
218
+ const matched = availableModels.find((entry) => entry.fullId === suggestion);
219
+ if (!matched || normalizeModelSegment(matched.provider) === queryProvider) return undefined;
220
+ return `${suggestion}${thinkingSuffix}`;
221
+ }
222
+
208
223
  function resolveRequiredSubagentModelCandidate(
209
224
  model: string,
210
225
  availableModels: AvailableModelInfo[] | undefined,
@@ -212,7 +227,10 @@ function resolveRequiredSubagentModelCandidate(
212
227
  ): string {
213
228
  const resolved = resolveSubagentModelCandidate(model, availableModels, preferredProvider);
214
229
  if (resolved) return resolved;
215
- throw new Error(`Unknown subagent model '${model}' in the active Pi model registry.`);
230
+ const suggestion = suggestAlternateProviderModel(model, availableModels);
231
+ throw new Error(
232
+ `Unknown subagent model '${model}' in the active Pi model registry.${suggestion ? ` Did you mean '${suggestion}'?` : ""}`,
233
+ );
216
234
  }
217
235
 
218
236
  export interface ResolveSubagentModelOverrideOptions {