@henryqw/pi-subagent 3.0.2 → 3.1.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 +8 -4
- package/README.md +54 -62
- package/dist/ephemeral.d.ts +50 -0
- package/dist/ephemeral.js +651 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +11 -3
- package/docs/adr/001-composable-ephemeral-execution.md +19 -0
- package/docs/orchestration.md +342 -0
- package/examples/roles/implementer.md +14 -0
- package/examples/roles/reviewer.md +11 -0
- package/examples/roles/scout.md +17 -0
- package/examples/roles/synthesizer.md +18 -0
- package/extensions/result-transport.ts +213 -0
- package/extensions/role-tools.ts +1 -1
- package/extensions/subagent.ts +366 -573
- package/extensions/workflow.ts +202 -0
- package/package.json +4 -2
package/CONTEXT.md
CHANGED
|
@@ -2,27 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
Provide validated user Roles, shared task-model Pi launch policy, generic managed Herdr Subagent hosting, and a `delegate_task` extension that runs one bounded
|
|
5
|
+
Provide validated user Roles, shared task-model Pi launch policy, generic managed Herdr Subagent hosting, and a `delegate_task` extension that runs one selected bounded workflow in one or more ephemeral child processes.
|
|
6
6
|
|
|
7
7
|
## Domain glossary
|
|
8
8
|
|
|
9
9
|
- **Main**: Pi session delegating work.
|
|
10
10
|
- **Subagent**: isolated Pi child process handling one task.
|
|
11
|
-
- **Role**: user-owned Markdown
|
|
11
|
+
- **Role**: user-owned Markdown definition of a reusable responsibility, with a name, description, system instructions, optional base tool allowlist, extensions, and Skill names.
|
|
12
12
|
- **Model Class**: `fast`, `balanced`, `frontier`, or `fav`, assigned in shared task-model settings or overridden by Main from task complexity.
|
|
13
13
|
- **Route**: configured model and thinking-level pair selected from a shared Model Class profile; the primary route precedes its optional fallback.
|
|
14
14
|
- **Delegated Task**: one bounded work request sent from Main to one Role.
|
|
15
|
+
- **Workflow**: orchestration of one or more Delegated Tasks; `delegate_task` owns its selected mode, while library callers compose executor runs in JavaScript.
|
|
16
|
+
- **Workflow Mode**: `delegate_task` tool policy selected per call for `single`, `parallel`, or `chain` execution; not a Role property or executor API.
|
|
17
|
+
- **Resource Policy**: Role ownership of base tools, extensions, and Skill names, plus explicit caller additions of tools, extensions, and environment through `createRoleLaunch`.
|
|
15
18
|
- **Pi Launch**: reusable `{env,args}` policy for one Role, resolved model route, explicit caller resources, and project trust.
|
|
19
|
+
- **Ephemeral Executor**: mechanism that receives a prepared Pi Launch, runs one bounded Delegated Task in one no-session child process, and returns its result without discovering resources or composing a Workflow.
|
|
16
20
|
- **Managed Subagent**: Pi agent hosted in a reconciled Herdr tab or pane; lifecycle orchestration remains with the caller.
|
|
17
21
|
|
|
18
22
|
## Invariants
|
|
19
23
|
|
|
20
24
|
- One Delegated Task creates one ephemeral child process and no saved session. Timeout behavior: `deadline = min(last recognized Pi JSON event + idle timeout, child start + maximum runtime)` (recognized Pi events renew; raw bytes do not; max always terminates). Configurable via the `timeout` object in `~/.pi/agent/config/pi-subagent/pi-subagent.json` (`idleMinutes`, `maxMinutes`; defaults 10/30).
|
|
21
25
|
- 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.
|
|
22
|
-
- Ambient child extensions and Skills stay disabled; Role explicitly selects extension sources and named Skills. Pi loads Skills supplied by those extension packages or their resource discovery.
|
|
26
|
+
- Ambient child extensions and Skills stay disabled; Role explicitly selects extension sources and named Skills. Pi loads Skills supplied by those extension packages or their resource discovery. With Role and caller tools omitted, no allowlist is installed and Pi uses its defaults. Caller tools with omitted Role tools snapshot Main's effective active built-ins into the installed policy; explicit Role tools set the base. Loaded extension tools activate automatically in every case.
|
|
23
27
|
- Role Skill names resolve through Main's effective Pi Skill registry; unavailable names warn and skip without blocking delegation.
|
|
24
28
|
- Main selects Role and may override Model Class and thinking level per task; omitted class uses shared `pi-subagent/delegateTask` assignment, initially `balanced`. Library callers select Role plus their own shared task ID.
|
|
25
29
|
- 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.
|
|
26
|
-
- Role and Subagent JSON config live only in the user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models.json`; repository roles do not execute.
|
|
30
|
+
- 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`; repository roles do not execute.
|
|
27
31
|
- Numbered Codex routes prefer Main's active account slot and explicitly load the multi-Codex child extension.
|
|
28
32
|
- Generic Herdr host functions validate workspace ownership and provisioning identity while callers retain domain state, prompts, and lifecycle decisions.
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# `@henryqw/pi-subagent`
|
|
2
2
|
|
|
3
|
-
Delegate
|
|
3
|
+
Delegate bounded work to isolated Pi child processes. The `delegate_task` tool has one flat contract with exactly one selected mode: single, parallel, or chain. Package authors can reuse the same Role launch policy and active-Pi executor from JavaScript.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
6
|
|
|
7
|
-
- **Created for**:
|
|
8
|
-
- **Advantage**:
|
|
7
|
+
- **Created for**: Pi users who need to delegate bounded work to isolated child Pi processes without losing Main's context.
|
|
8
|
+
- **Advantage**: One flat `delegate_task` tool contract with single, parallel, and chain modes, plus reusable Role launch policies for package authors.
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -19,86 +19,78 @@ pi install npm:@henryqw/pi-subagent
|
|
|
19
19
|
|
|
20
20
|
| Package | Why |
|
|
21
21
|
| --- | --- |
|
|
22
|
-
| `@henryqw/pi-task-models` | Required
|
|
23
|
-
| `@henryqw/pi-multi-codex` | Required
|
|
22
|
+
| `@henryqw/pi-task-models` | Required shared `fast` / `balanced` / `frontier` / `fav` routes. |
|
|
23
|
+
| `@henryqw/pi-multi-codex` | Required so children can use Main's active Codex slot. |
|
|
24
24
|
|
|
25
25
|
## Use
|
|
26
26
|
|
|
27
27
|
| Surface | Type | Purpose |
|
|
28
28
|
| --- | --- | --- |
|
|
29
|
-
| `delegate_task` | tool |
|
|
29
|
+
| `delegate_task` | tool | Delegate bounded work to one or more isolated Pi child processes. |
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
### `delegate_task`
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
Select exactly one shape:
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
```text
|
|
36
|
+
// Single
|
|
37
|
+
{ role, task, model?, modelClass?, thinking?, background? }
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
// Parallel: 1–8 independent delegations
|
|
40
|
+
{ tasks: [{ role, task, model?, modelClass?, thinking? }], background? }
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
// Chain: 1–8 dependent delegations
|
|
43
|
+
{ chain: [{ role, task, model?, modelClass?, thinking? }], background? }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`model` is `provider/modelId` and overrides `modelClass`. `modelClass` is `fast`, `balanced`, `frontier`, or `fav`; omission uses the shared `pi-subagent/delegateTask` assignment. `background` applies to the entire selected mode and is never a per-delegation field.
|
|
47
|
+
|
|
48
|
+
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.
|
|
49
|
+
|
|
50
|
+
Background workflows are session-scoped. Session shutdown or reload aborts them and may deliver only recoverable-work evidence or no follow-up message.
|
|
51
|
+
|
|
52
|
+
Each delegation resolves its own user Role, resources, route, and optional worktree request. When available, `isolation: worktree` gives each entry a deterministic separate worktree; non-Git or unborn-`HEAD` contexts may use Main's cwd. Siblings and chain steps never implicitly share one created worktree.
|
|
53
|
+
|
|
54
|
+
See [Orchestration, isolation, and the public API](./docs/orchestration.md) for the complete contract and JavaScript composition examples.
|
|
40
55
|
|
|
41
56
|
## Config
|
|
42
57
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
extensions:
|
|
51
|
-
- ~/.pi/agent/extensions/review-tools.ts
|
|
52
|
-
skills:
|
|
53
|
-
- code-review
|
|
54
|
-
- security
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
Review only requested change. Return ranked findings with file and line evidence.
|
|
58
|
-
Do not edit files.
|
|
58
|
+
`~/.pi/agent/config/pi-subagent/pi-subagent.json` controls the ephemeral child pool and timeouts:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"maxSubagents": 5,
|
|
63
|
+
"timeout": { "idleMinutes": 10, "maxMinutes": 30 }
|
|
64
|
+
}
|
|
59
65
|
```
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
| --- | --- | --- |
|
|
63
|
-
| `name` | yes | Role selected by Main |
|
|
64
|
-
| `description` | yes | Tells Main when to use the role |
|
|
65
|
-
| `tools` | no | Omit for Pi defaults; when present, base tools are listed and every loaded Role/caller extension tool is added automatically. `[]` leaves extension tools only. |
|
|
66
|
-
| `extensions` | no | Absolute/`~/` paths or package sources. Package-declared Skills and Pi `resources_discover` Skill paths load automatically. Repository-relative paths are rejected. |
|
|
67
|
-
| `skills` | no | Additional effective Pi Skill names, resolved from Main's registry |
|
|
68
|
-
| `isolation` | no | Set to `worktree` to give each delegated child its own git worktree branched from Main's current `HEAD` |
|
|
69
|
-
| Markdown body | yes | Role system instructions |
|
|
67
|
+
Excess children wait FIFO without consuming child timeout. `PI_SUBAGENT_MAX_SUBAGENTS` overrides the configured concurrency for the session.
|
|
70
68
|
|
|
71
|
-
|
|
69
|
+
## Roles
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
Roles are user-owned Markdown files in `~/.pi/agent/config/pi-subagent/`. This repository includes inert samples:
|
|
74
72
|
|
|
75
|
-
|
|
73
|
+
- [`scout`](./examples/roles/scout.md): read-only discovery
|
|
74
|
+
- [`implementer`](./examples/roles/implementer.md): focused edits requesting worktree isolation
|
|
75
|
+
- [`reviewer`](./examples/roles/reviewer.md): read-only correctness review
|
|
76
|
+
- [`synthesizer`](./examples/roles/synthesizer.md): reconcile supplied reports
|
|
77
|
+
|
|
78
|
+
Copy them manually from the repository root:
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
import {
|
|
81
|
-
loadRoles,
|
|
82
|
-
resolveRoleLaunch,
|
|
83
|
-
managedSubagentWorkspaceId,
|
|
84
|
-
reconcileManagedSubagentTab,
|
|
85
|
-
startManagedSubagent,
|
|
86
|
-
} from "@henryqw/pi-subagent";
|
|
87
|
-
|
|
88
|
-
const role = loadRoles().find(({ name }) => name === "reviewer")!;
|
|
89
|
-
const launch = resolveRoleLaunch(pi, ctx, {
|
|
90
|
-
role,
|
|
91
|
-
taskId: "your-package/review",
|
|
92
|
-
extensions: [adapterExtensionPath],
|
|
93
|
-
tools: ["submit_review"],
|
|
94
|
-
});
|
|
95
|
-
const workspaceId = await managedSubagentWorkspaceId(ctx.cwd, mainPane, { execute });
|
|
96
|
-
const host = { cwd: ctx.cwd, workspaceId };
|
|
97
|
-
const tab = await reconcileManagedSubagentTab(host, { cwd: worktree, launch, label }, { execute });
|
|
98
|
-
await startManagedSubagent(host, agentName, tab.paneId, launch, { execute });
|
|
80
|
+
```bash
|
|
81
|
+
mkdir -p ~/.pi/agent/config/pi-subagent
|
|
82
|
+
cp packages/pi-subagent/examples/roles/*.md ~/.pi/agent/config/pi-subagent/
|
|
99
83
|
```
|
|
100
84
|
|
|
101
|
-
|
|
85
|
+
The package never installs or writes Role configuration. Sample names are not built-ins; after copying, edit or replace them as your own Roles.
|
|
86
|
+
|
|
87
|
+
A Role owns its base tools, extensions, named Skills, instructions, and optional `isolation: worktree`. Ambient extension and Skill discovery is disabled in children. With neither Role tools nor caller tools, Pi defaults remain; caller tools with omitted Role tools snapshot Main's effective active built-ins and install the child policy. Loaded Role/caller extension tools still activate, parent-only tools stay excluded, and unavailable named Skills warn and skip.
|
|
88
|
+
|
|
89
|
+
## Library API
|
|
90
|
+
|
|
91
|
+
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.
|
|
92
|
+
|
|
93
|
+
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.
|
|
102
94
|
|
|
103
95
|
## Remove
|
|
104
96
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Usage } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { PiLaunch } from "./index.ts";
|
|
3
|
+
export interface EphemeralSubagentTimeout {
|
|
4
|
+
idleMs: number;
|
|
5
|
+
maxMs: number;
|
|
6
|
+
}
|
|
7
|
+
export interface EphemeralSubagentExecutorOptions {
|
|
8
|
+
maxConcurrency: number;
|
|
9
|
+
timeout: EphemeralSubagentTimeout;
|
|
10
|
+
}
|
|
11
|
+
export interface EphemeralSubagentRunInput {
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
onUpdate?: (text: string) => void;
|
|
14
|
+
onTokens?: (tokens: number) => void;
|
|
15
|
+
prepare: () => Promise<{
|
|
16
|
+
launch: PiLaunch;
|
|
17
|
+
task: string;
|
|
18
|
+
cwd: string;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
interface EphemeralSubagentResultBase {
|
|
22
|
+
exitCode: number;
|
|
23
|
+
output: string;
|
|
24
|
+
stderr: string;
|
|
25
|
+
stopReason?: string;
|
|
26
|
+
errorMessage?: string;
|
|
27
|
+
usage?: Usage;
|
|
28
|
+
}
|
|
29
|
+
export type EphemeralSubagentResult = (EphemeralSubagentResultBase & {
|
|
30
|
+
outcome: "success";
|
|
31
|
+
}) | (EphemeralSubagentResultBase & {
|
|
32
|
+
outcome: "failure";
|
|
33
|
+
});
|
|
34
|
+
export type EphemeralSubagentErrorCode = "aborted" | "timeout" | "spawn" | "protocol" | "prepare" | "callback";
|
|
35
|
+
export declare class EphemeralSubagentError extends Error {
|
|
36
|
+
name: string;
|
|
37
|
+
readonly code: EphemeralSubagentErrorCode;
|
|
38
|
+
readonly usage?: Usage;
|
|
39
|
+
constructor(code: EphemeralSubagentErrorCode, message: string, cause?: unknown, usage?: Usage);
|
|
40
|
+
}
|
|
41
|
+
export interface EphemeralSubagentExecutor {
|
|
42
|
+
run(input: EphemeralSubagentRunInput): Promise<EphemeralSubagentResult>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates a bounded ephemeral executor for callers already running inside Pi.
|
|
46
|
+
* It reuses the active Pi process invocation; it does not resolve a standalone Pi installation.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createEphemeralSubagentExecutor(options: EphemeralSubagentExecutorOptions): EphemeralSubagentExecutor;
|
|
49
|
+
export declare function capEphemeralSubagentOutput(text: string): string;
|
|
50
|
+
export {};
|