@henryqw/pi-subagent 6.1.1 → 7.0.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/CONTEXT.md +2 -2
- package/README.md +4 -4
- package/dist/index.d.ts +8 -2
- package/dist/index.js +7 -2
- package/docs/orchestration.md +74 -49
- package/extensions/subagent.ts +5 -3
- package/package.json +2 -2
- package/skills/pi-subagent-delegated-development/SKILL.md +2 -0
package/CONTEXT.md
CHANGED
|
@@ -28,10 +28,10 @@ Provide validated built-in and user Roles, shared task-model Pi launch policy, g
|
|
|
28
28
|
- Up to five active ephemeral `delegate_task` children run per Main by default, configurable via `maxSubagents` in `~/.pi/agent/config/pi-subagent/pi-subagent.json` or the `PI_SUBAGENT_MAX_SUBAGENTS` environment variable; excess calls wait FIFO. Queued calls do not start a child or consume child timeout. Managed Herdr workers are unaffected.
|
|
29
29
|
- Ambient child extensions and Skills stay disabled. Every Role requires `tools`, `extensions`, and `skills` YAML arrays, and every launch installs the Role tool policy. `tools: []` activates no base built-ins but does activate all tools from explicitly selected trusted extension bundles and explicit caller tool additions; `skills: []` selects no separately named Role Skills but trusted selected extension Skills still load; `extensions: []` selects no Role extension bundle. A Role/caller explicitly selected extension is a trusted atomic capability bundle: all tools it registers and all Skills supplied through its Pi package metadata or dynamic `resources_discover` load alongside separately named Role Skills. This intentionally includes the extension's executable lifecycle/prompt behavior; pi-subagent does not infer or externally narrow undocumented dependencies, and loading an extension is not sandboxing. Scope children by selecting fewer trusted extensions; finer granularity requires separate entry points/configuration or an upstream split. Explicit Role/caller tool names still verify against the final filtered registry, while parent-only recursive orchestration tools remain excluded.
|
|
30
30
|
- Role Skill names resolve through Main's effective Pi Skill registry; unavailable names warn and skip without blocking delegation. Explicit Role/caller tool names verify against the final filtered child registry after explicit provider `session_start` handlers, and unavailable names fail before the first turn.
|
|
31
|
-
- Main policy populates direct `model` and `thinking` only for explicit user overrides; otherwise it selects only `modelClass` (`fast` normally, `balanced` upfront for obvious complexity). This has no provenance tracking or runtime enforcement. An omitted class uses
|
|
31
|
+
- Main policy populates direct `model` and `thinking` only for explicit user overrides; otherwise it selects only `modelClass` (`fast` normally, `balanced` upfront for obvious complexity). This has no provenance tracking or runtime enforcement. An omitted class uses pi-subagent's local `pi-subagent/delegateTask` Model Task declaration (default `fast`); library callers select a Role plus their own Model Task declaration.
|
|
32
32
|
- The selected profile resolves primary then fallback only before launch when a route, model, or thinking level is unavailable. If neither route is usable, launch rejects with `Run /task-models`; a started child is never retried by this package.
|
|
33
33
|
- User Role Markdown files and Subagent JSON config live only in the user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models.json`. Package-shipped built-in Roles (`implementer`, `reviewer`) resolve from the package's own `examples/roles/` Markdown through the same parser; a same-named user file explicitly overrides a built-in for `delegate_task` and `delegate_flow`.
|
|
34
|
-
- `delegate_flow` accepts 1–8 independent units with direct validation commands, optional `modelClass`, and optional non-empty `review` judgment text. Omitted classes use
|
|
34
|
+
- `delegate_flow` accepts 1–8 independent units with direct validation commands, optional `modelClass`, and optional non-empty `review` judgment text. Omitted classes use pi-subagent's local `pi-subagent/delegateTask` declaration (default `fast`); each unit's current class resolves through its shared profile route for its Implementer and, when `review` exists, Reviewer. At Flow start it always resolves/freezes the effective Implementer and resolves/freezes a Reviewer only when at least one requested unit has `review`. One active Flow creates every Unit Worktree before launching Implementers in parallel, then processes settled units in declared order. For each unit Flow rebases in place when earlier units advanced Main, inspects committed Git state, and runs declared validation. Validation is authoritative for objective verification: units without `review` fast-forward their exact validated tip; units with `review` send the exact Review Packet to the Reviewer in the same worktree and require exact `PASS` before the same guarded `git merge --ff-only` path.
|
|
35
35
|
- Flow is memory-only. Only a post-rebase commit drop produces a no-op (`base === tip`); it validates, skips Reviewer and merge, then cleans up ordinarily. Initial zero-commit implementations block. Implementer, validation, or reviewer blocks allow one `delegate_flow_continue({ guidance, modelClass? })` repair in the same worktree; omission retains the Unit's current class and presence replaces it for that repair. A second block is terminal. Rebase and evidence/Reviewer/infrastructure failures retain worktrees. A reported fast-forward failure retains its worktree unless Main is clean at the exact integrated tip, which completes with the merge diagnostic as a warning. Cleanup uses non-forced worktree removal and branch deletion; cleanup refusal is a completion warning.
|
|
36
36
|
- Flow has no graph, saved recovery, automatic retry, aggregate review, or post-merge validation. It never changes generic `delegate_task` Role resolution, isolation, non-Git fallback, or ordinary direct plan/file review.
|
|
37
37
|
- Numbered Codex routes prefer Main's active account slot and explicitly load the multi-Codex child extension.
|
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Select exactly one shape:
|
|
|
47
47
|
{ chain: [{ role, task, model?, modelClass?, thinking? }], background? }
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
`model` is `provider/modelId` and overrides `modelClass`. Main populates `model` and `thinking` only for an explicit user override; otherwise it chooses only `modelClass`—`fast` normally, or `balanced` upfront for obviously complex work. This is Main policy only: the runtime records no provenance and does not enforce it. `modelClass` is `fast`, `balanced`, `frontier`, or `fav`; omission uses
|
|
50
|
+
`model` is `provider/modelId` and overrides `modelClass`. Main populates `model` and `thinking` only for an explicit user override; otherwise it chooses only `modelClass`—`fast` normally, or `balanced` upfront for obviously complex work. This is Main policy only: the runtime records no provenance and does not enforce it. `modelClass` is `fast`, `balanced`, `frontier`, or `fav`; omission uses pi-subagent's local `pi-subagent/delegateTask` Model Task declaration (default `fast`), which shared config can explicitly override. `background` applies to the entire selected mode and is never a per-delegation field.
|
|
51
51
|
|
|
52
52
|
Parallel mode starts entries concurrently, waits for every entry, and reports them in input order. Chain mode is sequential and fail-fast; every literal `{previous}` receives only the immediately preceding successful assistant output. Foreground failures throw after retaining bounded sibling and recovery evidence. One tool call has one aggregate 50 KiB Main-visible transport cap, not 50 KiB per child.
|
|
53
53
|
|
|
@@ -70,7 +70,7 @@ delegate_flow_continue({ guidance, modelClass? })
|
|
|
70
70
|
|
|
71
71
|
Objective verification is authoritative. Flow always inspects committed Git state and runs declared validation. A unit without `review` skips review evidence and Reviewer launch, then fast-forwards its exact validated tip through the existing guarded `git merge --ff-only` path. Add `review` only for an explicit judgment that automation cannot establish; that unit retains the exact `{base, tip, patchPath}` protocol and requires exact `PASS` before the same integration path.
|
|
72
72
|
|
|
73
|
-
One memory-only Flow may be active. At start it resolves/freezes the effective `implementer` Role, including a same-named user override, and resolves/freezes the effective `reviewer` only if at least one requested unit has `review`. Omitted `modelClass` uses
|
|
73
|
+
One memory-only Flow may be active. At start it resolves/freezes the effective `implementer` Role, including a same-named user override, and resolves/freezes the effective `reviewer` only if at least one requested unit has `review`. Omitted `modelClass` uses pi-subagent's local `pi-subagent/delegateTask` declaration (default `fast`); a selected class resolves through its shared profile model-and-thinking route for the unit's Implementer and, when applicable, Reviewer. It creates one Unit Worktree per unit, runs Implementers in parallel, then processes settled results in declared order. It removes the worktree and branch non-forcibly after integration; a refusal is a completion warning with the retained worktree path and/or branch.
|
|
74
74
|
|
|
75
75
|
A rebase that drops all unit commits is a no-op: Flow validates it, skips Reviewer and merge, then cleans up ordinarily. Implementer, validation, or review blocks can be repaired once through `delegate_flow_continue` in the same worktree. Omitted continuation `modelClass` retains the blocked unit's current class; a supplied class replaces it for that one repair. Rebase and infrastructure failures are terminal. A reported fast-forward failure completes with its diagnostic as a warning only when Git left Main clean at the exact integrated tip; otherwise it is terminal and retains the affected worktree. Flow has no graph, saved recovery, automatic retry, aggregate review, or post-merge gate.
|
|
76
76
|
|
|
@@ -90,7 +90,7 @@ A rebase that drops all unit commits is a no-op: Flow validates it, skips Review
|
|
|
90
90
|
|
|
91
91
|
## Config
|
|
92
92
|
|
|
93
|
-
pi-subagent owns the extension-named config directory `~/.pi/agent/config/pi-subagent/`, which holds two kinds of user-owned configuration: one Markdown file per Role (see [Roles](#roles)) and its own optional JSON file below. Model routing is *not* configured here; children resolve routes through the shared `@henryqw/pi-task-models` config at `~/.pi/agent/config/pi-task-models.json
|
|
93
|
+
pi-subagent owns the extension-named config directory `~/.pi/agent/config/pi-subagent/`, which holds two kinds of user-owned configuration: one Markdown file per Role (see [Roles](#roles)) and its own optional JSON file below. Model routing is *not* configured here; children resolve routes through the shared `@henryqw/pi-task-models` config at `~/.pi/agent/config/pi-task-models.json`, which stores only explicit task overrides. The local `pi-subagent/delegateTask` declaration supplies the omitted-class default.
|
|
94
94
|
|
|
95
95
|
`~/.pi/agent/config/pi-subagent/pi-subagent.json` controls the ephemeral child pool and timeouts. All fields are optional; a missing file uses defaults.
|
|
96
96
|
|
|
@@ -155,4 +155,4 @@ A Role explicitly owns base tools, extensions, named Skills, instructions, and o
|
|
|
155
155
|
|
|
156
156
|
The package root exports Role loading and launch resolution, `createEphemeralSubagentExecutor`, worktree helpers, and generic managed Herdr lifecycle helpers. The ephemeral executor is for code already running inside active Pi; it does not provide standalone Node.js Pi discovery or launch support. After Pi itself exits, it drains inherited stdout/stderr normally but destroys streams still held by escaped descendants after a short inactivity deadline or one-second hard deadline, so they cannot retain a pool permit.
|
|
157
157
|
|
|
158
|
-
Use [`docs/orchestration.md`](./docs/orchestration.md#public-role-and-executor-api) for exact API behavior and a post-permit `prepare` example using `resolveRoleLaunch` against the latest Pi context.
|
|
158
|
+
Use [`docs/orchestration.md`](./docs/orchestration.md#public-role-and-executor-api) for exact API behavior and a post-permit `prepare` example using `resolveRoleLaunch` with a caller-owned Model Task declaration against the latest Pi context.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { type HerdrExecutor } from "@henryqw/pi-herdr";
|
|
3
|
-
import { type AvailableModel, type ProfileName, type ResolvedTaskRoute, type ThinkingLevel } from "@henryqw/pi-task-models";
|
|
3
|
+
import { type AvailableModel, type ModelTask, type ProfileName, type ResolvedTaskRoute, type ThinkingLevel } from "@henryqw/pi-task-models";
|
|
4
4
|
export { addUsage, capEphemeralSubagentOutput, createEphemeralSubagentExecutor, EphemeralSubagentError, formatDuration, type EphemeralSubagentActivityEvent, type EphemeralSubagentErrorCode, type EphemeralSubagentExecutor, type EphemeralSubagentExecutorOptions, type EphemeralSubagentResult, type EphemeralSubagentRunInput, type EphemeralSubagentTimeout, } from "./ephemeral.ts";
|
|
5
5
|
export { createChildWorktree, finalizeChildWorktree, inspectIndexFlags, inspectWorktreeDirty, WorktreeSetupError, worktreeContextNote, type WorktreeDirtyInspection, type WorktreeInfo, type WorktreePayload, } from "./worktree.ts";
|
|
6
6
|
export { prepareExactReviewEvidence, REVIEW_MAX_PATCH_BYTES, REVIEW_MAX_PATHS, type PreparedReviewEvidence, type PrepareExactReviewEvidenceInput, } from "./review-evidence.ts";
|
|
7
7
|
export declare const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
|
|
8
8
|
export declare const CHILD_EXCLUDED_TOOL_NAMES: readonly ["delegate_task", "delegate_flow", "delegate_flow_continue", "ask_question"];
|
|
9
9
|
export declare const CHILD_EXCLUDED_TOOLS: string;
|
|
10
|
+
export declare const DELEGATE_TASK: {
|
|
11
|
+
readonly id: "pi-subagent/delegateTask";
|
|
12
|
+
readonly label: "Subagent delegation";
|
|
13
|
+
readonly purpose: "Launch an isolated Pi subagent.";
|
|
14
|
+
readonly defaultProfile: "fast";
|
|
15
|
+
};
|
|
10
16
|
export interface Role {
|
|
11
17
|
name: string;
|
|
12
18
|
description: string;
|
|
@@ -33,7 +39,7 @@ export interface CreateRoleLaunchInput {
|
|
|
33
39
|
env?: Readonly<Record<string, string>>;
|
|
34
40
|
}
|
|
35
41
|
export interface ResolveRoleLaunchInput extends Omit<CreateRoleLaunchInput, "route"> {
|
|
36
|
-
|
|
42
|
+
task: ModelTask;
|
|
37
43
|
agentDir?: string;
|
|
38
44
|
}
|
|
39
45
|
export interface ResolvedRoleSkills {
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,12 @@ export const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
|
|
|
15
15
|
export const CHILD_EXCLUDED_TOOL_NAMES = ["delegate_task", "delegate_flow", "delegate_flow_continue", "ask_question"];
|
|
16
16
|
export const CHILD_EXCLUDED_TOOLS = CHILD_EXCLUDED_TOOL_NAMES.join(",");
|
|
17
17
|
const CHILD_IDENTITY_POLICY = "You are a delegated Pi Subagent, not Main. Execute the assigned Role and task directly. Main-only delegation rules do not apply. Recursive delegation is unavailable; do not seek or invoke delegation tools.";
|
|
18
|
+
export const DELEGATE_TASK = {
|
|
19
|
+
id: "pi-subagent/delegateTask",
|
|
20
|
+
label: "Subagent delegation",
|
|
21
|
+
purpose: "Launch an isolated Pi subagent.",
|
|
22
|
+
defaultProfile: "fast",
|
|
23
|
+
};
|
|
18
24
|
const cleanText = (value, field, source) => {
|
|
19
25
|
if (typeof value !== "string" || !value.trim() || value.includes("\0")) {
|
|
20
26
|
throw new Error(`${source}: ${field} must be non-empty text.`);
|
|
@@ -186,10 +192,9 @@ export function createRoleLaunch(pi, ctx, input) {
|
|
|
186
192
|
};
|
|
187
193
|
}
|
|
188
194
|
export function resolveRoleLaunch(pi, ctx, input) {
|
|
189
|
-
const taskId = cleanText(input.taskId, "task ID", "Role launch");
|
|
190
195
|
return createRoleLaunch(pi, ctx, {
|
|
191
196
|
...input,
|
|
192
|
-
route: resolveConfiguredTaskRoute(ctx,
|
|
197
|
+
route: resolveConfiguredTaskRoute(ctx, input.task, input.agentDir),
|
|
193
198
|
});
|
|
194
199
|
}
|
|
195
200
|
function launchEnvironmentArgs(launch) {
|
package/docs/orchestration.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
```text
|
|
6
6
|
Role (built-in or user override) + latest Pi registries ── resolveRoleLaunch ──> PiLaunch
|
|
7
7
|
│
|
|
8
|
-
caller-owned
|
|
8
|
+
caller-owned Model Task declaration, cwd, signal ────────┤
|
|
9
9
|
v
|
|
10
10
|
active-Pi ephemeral executor
|
|
11
11
|
```
|
|
@@ -68,7 +68,7 @@ Single mode puts one delegation's fields at the top level.
|
|
|
68
68
|
| `role` | yes | Name of a Role in the user's effective `config/pi-subagent` directory or a package-shipped built-in (`implementer`, `reviewer`); a same-named user file overrides the built-in. |
|
|
69
69
|
| `task` | yes | Non-empty bounded task packet. |
|
|
70
70
|
| `model` | no | Designated `provider/modelId`; takes precedence over `modelClass`, and Main supplies it only for an explicit user override. |
|
|
71
|
-
| `modelClass` | no | `fast`, `balanced`, `frontier`, or `fav`; Main normally chooses `fast`, may choose `balanced` upfront for obvious complexity, and omission uses
|
|
71
|
+
| `modelClass` | no | `fast`, `balanced`, `frontier`, or `fav`; Main normally chooses `fast`, may choose `balanced` upfront for obvious complexity, and omission uses pi-subagent's local Model Task declaration. |
|
|
72
72
|
| `thinking` | no | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`; Main supplies it only for an explicit user override. Route selection skips models that cannot honor it. |
|
|
73
73
|
|
|
74
74
|
Those five fields are the complete delegation object. The direct-model/thinking rule is Main-facing policy only: the runtime adds no provenance tracking or enforcement. `tasks`, `chain`, and `background` cannot be nested. Route fallback occurs only before launch; a started child is never retried by this package.
|
|
@@ -85,7 +85,7 @@ All Main-visible text for one tool call shares one aggregate 50 KiB UTF-8 transp
|
|
|
85
85
|
|
|
86
86
|
`delegate_flow({ units })` accepts 1–8 units with unique non-empty `id` and `task`, one or more direct `{command, args}` validation commands, optional `modelClass`, and optional non-empty `review` text. `delegate_flow_continue({ guidance, modelClass? })` is available only for the one blocked unit of the active Flow.
|
|
87
87
|
|
|
88
|
-
A Flow is memory-only and permits one active Flow. At start it always resolves/freezes the effective `implementer` Role, including a same-named user override. It resolves/freezes the effective `reviewer` only if at least one requested unit declares `review`. Omitted unit classes use
|
|
88
|
+
A Flow is memory-only and permits one active Flow. At start it always resolves/freezes the effective `implementer` Role, including a same-named user override. It resolves/freezes the effective `reviewer` only if at least one requested unit declares `review`. Omitted unit classes use pi-subagent's local `pi-subagent/delegateTask` declaration (default `fast`); a selected class resolves through its existing `pi-task-models` profile model-and-thinking route for the unit's Implementer and, when applicable, Reviewer. It requires clean committed Git Main and creates every Unit Worktree before launching work; setup failure launches no Implementer. Each unit gets exactly one worktree and one Implementer. Implementers run in parallel and all settle. Flow then processes units in declared order:
|
|
89
89
|
|
|
90
90
|
```text
|
|
91
91
|
Implementers (parallel, one Unit Worktree each)
|
|
@@ -149,12 +149,12 @@ The package root exports the following mechanism-level APIs:
|
|
|
149
149
|
| --- | --- |
|
|
150
150
|
| `loadRoles(agentDir?)` | Validate and load package-shipped built-in and user Role Markdown. |
|
|
151
151
|
| `resolveRoleSkills(pi, role)` | Resolve Role Skill names from Pi's effective registry. |
|
|
152
|
-
| `resolveRoleLaunch(pi, ctx, input)` | Resolve a
|
|
152
|
+
| `resolveRoleLaunch(pi, ctx, input)` | Resolve a caller-owned Model Task route and produce `ResolvedRoleLaunch`. |
|
|
153
153
|
| `createRoleLaunch(pi, ctx, input)` | Produce the same launch from a caller-supplied resolved route. |
|
|
154
154
|
| `createEphemeralSubagentExecutor(options)` | Queue and run one prepared no-session child per `run`. |
|
|
155
155
|
| `createChildWorktree` / `finalizeChildWorktree` | Optional caller-managed worktree lifecycle. |
|
|
156
156
|
|
|
157
|
-
A loaded `Role` contains `name`, `description`, required normalized `tools`, `extensions`, and `skills` arrays, optional `isolation`, and `systemPrompt`. `resolveRoleLaunch` accepts `role`, `
|
|
157
|
+
A loaded `Role` contains `name`, `description`, required normalized `tools`, `extensions`, and `skills` arrays, optional `isolation`, and `systemPrompt`. `resolveRoleLaunch` accepts `role`, a caller-owned `task` Model Task declaration, and optional caller `agentDir`, `extensions`, `tools`, and `env`. At extension load, callers invoke `registerModelTask(pi, task)` from `@henryqw/pi-task-models` once to expose that declaration in the shared control plane. Its result is a `PiLaunch` (`{ env, args }`) plus the selected `model`, `thinkingLevel`, and `missingSkills`.
|
|
158
158
|
|
|
159
159
|
`createEphemeralSubagentExecutor` requires:
|
|
160
160
|
|
|
@@ -178,54 +178,69 @@ import {
|
|
|
178
178
|
createEphemeralSubagentExecutor,
|
|
179
179
|
resolveRoleLaunch,
|
|
180
180
|
} from "@henryqw/pi-subagent";
|
|
181
|
+
import { registerModelTask } from "@henryqw/pi-task-models";
|
|
181
182
|
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
const MODEL_TASK = {
|
|
184
|
+
id: "your-package/delegate",
|
|
185
|
+
label: "Package delegation",
|
|
186
|
+
purpose: "Run one package-owned delegated task.",
|
|
187
|
+
defaultProfile: "fast",
|
|
188
|
+
};
|
|
186
189
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
pi
|
|
190
|
-
latestCtx = { ...ctx, model: event.model };
|
|
191
|
-
});
|
|
192
|
-
pi.on("agent_settled", (_event, ctx) => { latestCtx = ctx; });
|
|
190
|
+
export function createRunRole(pi) {
|
|
191
|
+
// Register the Model Task at extension load so /task-models discovery works.
|
|
192
|
+
registerModelTask(pi, MODEL_TASK);
|
|
193
193
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
194
|
+
const executor = createEphemeralSubagentExecutor({
|
|
195
|
+
maxConcurrency: 4,
|
|
196
|
+
timeout: { idleMs: 10 * 60_000, maxMs: 30 * 60_000 },
|
|
197
|
+
});
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
extensions = [],
|
|
204
|
-
tools,
|
|
205
|
-
env = {},
|
|
206
|
-
} = options;
|
|
207
|
-
|
|
208
|
-
return executor.run({
|
|
209
|
-
signal,
|
|
210
|
-
prepare: async () => {
|
|
211
|
-
// prepare runs only after this delegation owns a FIFO permit.
|
|
212
|
-
const ctx = latestContext();
|
|
213
|
-
const launch = resolveRoleLaunch(pi, ctx, {
|
|
214
|
-
role,
|
|
215
|
-
taskId: "your-package/delegate",
|
|
216
|
-
extensions,
|
|
217
|
-
tools,
|
|
218
|
-
env,
|
|
219
|
-
});
|
|
220
|
-
if (launch.missingSkills.length && ctx.hasUI) {
|
|
221
|
-
ctx.ui.notify(
|
|
222
|
-
`Skipped unavailable Skills: ${launch.missingSkills.join(", ")}`,
|
|
223
|
-
"warning",
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
return { launch, task, cwd: cwd ?? ctx.cwd };
|
|
227
|
-
},
|
|
199
|
+
let latestCtx;
|
|
200
|
+
pi.on("session_start", (_event, ctx) => { latestCtx = ctx; });
|
|
201
|
+
pi.on("model_select", (event, ctx) => {
|
|
202
|
+
latestCtx = { ...ctx, model: event.model };
|
|
228
203
|
});
|
|
204
|
+
pi.on("agent_settled", (_event, ctx) => { latestCtx = ctx; });
|
|
205
|
+
|
|
206
|
+
function latestContext() {
|
|
207
|
+
if (!latestCtx) throw new Error("Pi session has not started.");
|
|
208
|
+
return latestCtx;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function runRole(role, task, options = {}) {
|
|
212
|
+
const {
|
|
213
|
+
signal,
|
|
214
|
+
cwd,
|
|
215
|
+
extensions = [],
|
|
216
|
+
tools,
|
|
217
|
+
env = {},
|
|
218
|
+
} = options;
|
|
219
|
+
|
|
220
|
+
return executor.run({
|
|
221
|
+
signal,
|
|
222
|
+
prepare: async () => {
|
|
223
|
+
// prepare runs only after this delegation owns a FIFO permit.
|
|
224
|
+
const ctx = latestContext();
|
|
225
|
+
const launch = resolveRoleLaunch(pi, ctx, {
|
|
226
|
+
role,
|
|
227
|
+
task: MODEL_TASK,
|
|
228
|
+
extensions,
|
|
229
|
+
tools,
|
|
230
|
+
env,
|
|
231
|
+
});
|
|
232
|
+
if (launch.missingSkills.length && ctx.hasUI) {
|
|
233
|
+
ctx.ui.notify(
|
|
234
|
+
`Skipped unavailable Skills: ${launch.missingSkills.join(", ")}`,
|
|
235
|
+
"warning",
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
return { launch, task, cwd: cwd ?? ctx.cwd };
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return { runRole };
|
|
229
244
|
}
|
|
230
245
|
```
|
|
231
246
|
|
|
@@ -249,7 +264,17 @@ Generic managed Herdr exports (`managedSubagentWorkspaceId`, reconciliation help
|
|
|
249
264
|
|
|
250
265
|
## JavaScript composition
|
|
251
266
|
|
|
252
|
-
The examples below use caller-selected `Role` objects and the `runRole`
|
|
267
|
+
The examples below use caller-selected `Role` objects and the `runRole` function returned by the package's initializer. A consuming Pi extension calls the initializer once at startup:
|
|
268
|
+
|
|
269
|
+
```js
|
|
270
|
+
import { createRunRole } from "your-package";
|
|
271
|
+
|
|
272
|
+
export default function yourExtension(pi) {
|
|
273
|
+
const { runRole } = createRunRole(pi);
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Variable names such as `reviewRole` are local bindings, not reserved Role names. The executor's `maxConcurrency` bounds launches; callers must also bound collections and loops.
|
|
253
278
|
|
|
254
279
|
A small caller-owned failure policy keeps the examples readable:
|
|
255
280
|
|
package/extensions/subagent.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
availableTaskModels,
|
|
7
7
|
type ThinkingLevel,
|
|
8
8
|
modelReference,
|
|
9
|
+
registerModelTask,
|
|
9
10
|
resolveAvailableModel,
|
|
10
11
|
resolveConfiguredTaskRoute,
|
|
11
12
|
type ResolvedTaskRoute,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
capEphemeralSubagentOutput as capOutput,
|
|
16
17
|
createChildWorktree,
|
|
17
18
|
createEphemeralSubagentExecutor,
|
|
19
|
+
DELEGATE_TASK,
|
|
18
20
|
createRoleLaunch,
|
|
19
21
|
EphemeralSubagentError,
|
|
20
22
|
finalizeChildWorktree,
|
|
@@ -54,7 +56,6 @@ import {
|
|
|
54
56
|
type WorkflowEntry,
|
|
55
57
|
} from "./workflow.ts";
|
|
56
58
|
|
|
57
|
-
const SUBAGENT_TASK = "pi-subagent/delegateTask";
|
|
58
59
|
const WIDGET_KEY = "subagent-status";
|
|
59
60
|
const WIDGET_INTERVAL_MS = 80;
|
|
60
61
|
const MAX_WIDGET_ITEMS = 8;
|
|
@@ -304,6 +305,7 @@ export default function subagentExtension(
|
|
|
304
305
|
pi: ExtensionAPI,
|
|
305
306
|
overrideTimeoutPolicy?: TimeoutPolicy,
|
|
306
307
|
): void {
|
|
308
|
+
registerModelTask(pi, DELEGATE_TASK);
|
|
307
309
|
const widgetItems = new Map<string, WidgetItem>();
|
|
308
310
|
// Each child is a full Pi process issuing its own model calls; cap parallel
|
|
309
311
|
// spend. Precedence: PI_SUBAGENT_MAX_SUBAGENTS env > config/pi-subagent/pi-subagent.json
|
|
@@ -584,7 +586,7 @@ export default function subagentExtension(
|
|
|
584
586
|
return createRoleLaunch(pi, launchCtx, {
|
|
585
587
|
role,
|
|
586
588
|
route: modelClass === undefined
|
|
587
|
-
? resolveConfiguredTaskRoute(launchCtx,
|
|
589
|
+
? resolveConfiguredTaskRoute(launchCtx, DELEGATE_TASK)
|
|
588
590
|
: resolveTaskRoute(launchCtx, modelClass),
|
|
589
591
|
});
|
|
590
592
|
},
|
|
@@ -661,7 +663,7 @@ export default function subagentExtension(
|
|
|
661
663
|
route: delegation.model !== undefined
|
|
662
664
|
? resolveDesignatedRoute(launchCtx(), delegation.model, delegation.thinking)
|
|
663
665
|
: delegation.modelClass === undefined
|
|
664
|
-
? resolveConfiguredTaskRoute(launchCtx(),
|
|
666
|
+
? resolveConfiguredTaskRoute(launchCtx(), DELEGATE_TASK, undefined, delegation.thinking)
|
|
665
667
|
: resolveTaskRoute(launchCtx(), delegation.modelClass, undefined, delegation.thinking),
|
|
666
668
|
});
|
|
667
669
|
const notifyMissingSkills = (role: Role, launch: ReturnType<typeof resolveLaunch>) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henryqw/pi-subagent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Delegate bounded single, parallel, or chained tasks to isolated Pi roles.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -66,6 +66,6 @@
|
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@henryqw/pi-herdr": "^0.4.0",
|
|
68
68
|
"@henryqw/pi-multi-codex": "^0.3.8",
|
|
69
|
-
"@henryqw/pi-task-models": "^
|
|
69
|
+
"@henryqw/pi-task-models": "^3.0.0"
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -9,6 +9,8 @@ You are Main, the planner/orchestrator: slice work and call `delegate_flow`. Do
|
|
|
9
9
|
|
|
10
10
|
## Slice
|
|
11
11
|
|
|
12
|
+
Before slicing, identify applicable repository prohibitions. If the request or plan conflicts with them, stop and resolve the conflict before delegation. Copy them into every affected task and into `review` when automated validation cannot establish compliance; never replace repository policy with generic preservation or migration assumptions. When compatibility is disallowed, require deletion of replaced paths and forbid legacy readers, aliases, adapters, dual schemas, deprecation paths, and compatibility fallbacks.
|
|
13
|
+
|
|
12
14
|
Use the fewest cohesive units. `delegate_flow` is for independent units expected to commute; combine or sequence work that overlaps files, APIs, schemas, generated output, package metadata, lockfiles, or invariants. Dependent work remains outside Flow, in one task or ordinary caller-controlled sequencing.
|
|
13
15
|
|
|
14
16
|
Give every unit a bounded objective, owned scope and exclusions, and its direct validation command/argument array. Do not pass the parent request unchanged. Use `modelClass: "fast"` normally; use `"balanced"` upfront only for obviously complex work. Add non-empty `review` only for an explicit judgment that automated validation cannot establish. Call `delegate_flow` with 1–8 units; the runtime always supplies the effective Implementer and supplies the Reviewer only when a unit needs review.
|