@maxiaochao/pi-toolkit 0.11.6 → 0.11.8
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 +11 -0
- package/README.md +2 -1
- package/package.json +5 -3
- package/packages/herdr-subagents/ARCHITECTURE.md +82 -378
- package/packages/herdr-subagents/CHANGELOG.md +4 -0
- package/packages/herdr-subagents/README.md +52 -321
- package/packages/herdr-subagents/TESTING.md +36 -53
- package/packages/herdr-subagents/extensions/herdr-subagents.ts +24 -158
- package/packages/herdr-subagents/extensions/subagent-report.ts +21 -7
- package/packages/herdr-subagents/package.json +4 -3
- package/packages/herdr-subagents/src/batch-runner.ts +5 -3
- package/packages/herdr-subagents/src/herdr.ts +11 -0
- package/packages/herdr-subagents/src/live-agent.ts +49 -114
- package/packages/herdr-subagents/src/parameters.ts +2 -6
- package/packages/herdr-subagents/src/protocol.ts +4 -5
- package/packages/herdr-subagents/src/records.ts +2 -0
- package/packages/herdr-subagents/src/tool.ts +9 -51
- package/packages/herdr-subagents/src/validation.ts +0 -4
- package/packages/herdr-subagents/tests/core-state-machine.mjs +178 -0
- package/packages/herdr-subagents/tests/extension-sources-load.mjs +7 -3
- package/packages/openai-account-pool/CHANGELOG.md +11 -0
- package/packages/openai-account-pool/README.md +67 -0
- package/packages/openai-account-pool/TESTING.md +28 -0
- package/packages/openai-account-pool/docs/demo.gif +0 -0
- package/packages/openai-account-pool/docs/screenshot.png +0 -0
- package/packages/openai-account-pool/extensions/openai-account-pool.ts +368 -0
- package/packages/openai-account-pool/package.json +45 -0
- package/packages/openai-account-pool/src/auth.ts +418 -0
- package/packages/openai-account-pool/src/availability.ts +230 -0
- package/packages/openai-account-pool/src/command-completions.ts +18 -0
- package/packages/openai-account-pool/src/enrollment.ts +339 -0
- package/packages/openai-account-pool/src/lease.ts +128 -0
- package/packages/openai-account-pool/src/lock.ts +171 -0
- package/packages/openai-account-pool/src/login-slot.ts +158 -0
- package/packages/openai-account-pool/src/policy.ts +176 -0
- package/packages/openai-account-pool/src/provider.ts +511 -0
- package/packages/openai-account-pool/src/status-widget.ts +62 -0
- package/packages/openai-account-pool/src/store.ts +259 -0
- package/packages/openai-account-pool/src/usage.ts +291 -0
- package/packages/openai-account-pool/tests/command-completions.mjs +10 -0
- package/packages/openai-account-pool/tests/enrollment.mjs +322 -0
- package/packages/openai-account-pool/tests/failover-stream.mjs +554 -0
- package/packages/openai-account-pool/tests/helpers/fixtures.mjs +40 -0
- package/packages/openai-account-pool/tests/helpers/native-credential-store.mjs +58 -0
- package/packages/openai-account-pool/tests/helpers/pi-runtime.mjs +24 -0
- package/packages/openai-account-pool/tests/helpers/refresh-child.mjs +29 -0
- package/packages/openai-account-pool/tests/integration-login-ownership.mjs +299 -0
- package/packages/openai-account-pool/tests/login-slot.mjs +131 -0
- package/packages/openai-account-pool/tests/multiprocess.mjs +73 -0
- package/packages/openai-account-pool/tests/selection.mjs +286 -0
- package/packages/openai-account-pool/tests/store-ownership.mjs +369 -0
- package/packages/openai-account-pool/tests/usage.mjs +428 -0
|
@@ -1,363 +1,94 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Herdr Subagents
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run focused Pi subagents in visible, focusable Herdr panes and collect exactly one terminal result per task.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The implementation is intentionally small: one task state machine, one derived run state, and one bounded worker pool. Herdr provides visible panes and navigation; the package only dispatches serial or read-only parallel work, waits for a report, and records the result. It does not add a second workflow engine around Pi.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+

|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Demo
|
|
10
10
|
|
|
11
11
|
https://github.com/user-attachments/assets/8ddf3ae6-a11a-492b-9cae-d9496ba65b67
|
|
12
12
|
|
|
13
|
-
The
|
|
14
|
-
|
|
15
|
-
## Requirements and install
|
|
16
|
-
|
|
17
|
-
Run Pi inside Herdr, then install the package:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
pi install npm:@maxiaochao/pi-herdr-subagents
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Update an existing standalone installation with:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
pi update npm:@maxiaochao/pi-herdr-subagents
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
The full `@maxiaochao/pi-toolkit` package also bundles this extension. Install the full toolkit or this standalone child package, not both.
|
|
30
|
-
|
|
31
|
-
Inside a Herdr workspace, the package registers `herdr_subagents`, `herdr_subagents_control`, and `/herdr-subagents`. Outside Herdr (or without a workspace ID), the extension registers none of them, so these capabilities are absent from the model context. Start Pi inside Herdr or reload the extension there to enable them.
|
|
32
|
-
|
|
33
|
-
The design replaces hidden print-mode subprocesses with ordinary interactive Pi Agents managed through Herdr, so delegated work stays visible, inspectable, resumable, and durably recorded.
|
|
34
|
-
|
|
35
|
-
See [ARCHITECTURE.md](ARCHITECTURE.md) for the current execution architecture, the proposed decision-pause state machine, and configuration-normalization boundaries.
|
|
36
|
-
|
|
37
|
-
## Usage
|
|
38
|
-
|
|
39
|
-
Discover the effective personas first:
|
|
40
|
-
|
|
41
|
-
```text
|
|
42
|
-
herdr_subagents_control({ action: "list" })
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Then run one self-contained task:
|
|
46
|
-
|
|
47
|
-
```text
|
|
48
|
-
herdr_subagents({
|
|
49
|
-
label: "auth-review",
|
|
50
|
-
tasks: [
|
|
51
|
-
{ name: "auth", agent: "worker", prompt: "Inspect the authentication flow and summarize the risks" }
|
|
52
|
-
]
|
|
53
|
-
})
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Or submit an ordered foreground batch:
|
|
57
|
-
|
|
58
|
-
```text
|
|
59
|
-
herdr_subagents({
|
|
60
|
-
label: "security-review",
|
|
61
|
-
concurrency: 3,
|
|
62
|
-
tasks: [
|
|
63
|
-
{ name: "auth", agent: "reviewer", prompt: "Review authentication" },
|
|
64
|
-
{ name: "sessions", agent: "explorer", prompt: "Trace session storage" }
|
|
65
|
-
]
|
|
66
|
-
})
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
`tasks` is always a non-empty array: one item is a single task and multiple items form a batch. Set `background: true` for either form to return a stable run ID immediately while the queue continues. The dispatcher starts batches FIFO, with one batch holding its active slot at a time; a failed foreground handoff releases that slot as described below. A blocked run retains the slot so its queued siblings can continue after an answer. While that slot is blocked, a new foreground submission fails immediately with the blocking run ID instead of waiting indefinitely; a background submission remains queued. Cancelling a foreground tool call while it is queued removes it from the queue immediately. Each background run sends one compact parent notification only when the whole run completes, partially fails, fails, or blocks. Individual successful tasks do not wake the parent model.
|
|
70
|
-
|
|
71
|
-
A foreground concurrent batch normally waits for the whole batch. If any task fails while siblings are still running, the call immediately returns the stable `runId` and that failure's details, then durably continues the same batch in the background without cancelling its siblings. The handed-off batch releases its dispatcher slot immediately, so a later submission can start while its surviving tasks are still running. The detached batch remains cancellable and is still joined during session shutdown. Its final aggregate is delivered exactly once through the same follow-up notification used by an explicit background run.
|
|
72
|
-
|
|
73
|
-
Background work normally relies on that terminal notification. When the user asks for progress or the parent must confirm a review gate before continuing, query only that run's compact snapshot:
|
|
74
|
-
|
|
75
|
-
```text
|
|
76
|
-
herdr_subagents_control({ action: "status", runId: "<run-id>" })
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
The snapshot reports the aggregate run state and every task's state, including a blocked question when present. It checks durable run state before queued state, never returns unrelated runs, and does not wait or trigger a parent turn. Do not poll full `history` for progress.
|
|
80
|
-
|
|
81
|
-
Run controls accept the complete run UUID, the saved run-directory name, or any unique UUID prefix. Results always return the canonical complete UUID.
|
|
82
|
-
|
|
83
|
-
Every child reports exactly one of three outcomes. `completed` and `failed` are terminal; `blocked` means the child is still alive in its pane but needs the parent agent to answer or decide something before it can go on. A blocked task keeps its Herdr tab, pane, partial report, and Pi session, and returns control to the parent with an exact question. Answer through the control tool to continue the original session:
|
|
84
|
-
|
|
85
|
-
```text
|
|
86
|
-
herdr_subagents_control({ action: "respond", runId: "<run-id>", task: 1, answer: "Use the main branch." })
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Blocked is a normal control state, not a failed tool call. Only terminal failure, cancellation, or a partial aggregate is returned as a tool error.
|
|
90
|
-
|
|
91
|
-
Blocking works the same way at any concurrency: a blocked task never holds up its independent siblings, which keep running and settling normally. A blocked run does keep the dispatcher slot until you respond or cancel it, so another foreground run started meanwhile is refused with the blocking run ID and a background run stays queued. That is what keeps a second write-capable run from editing the repository behind an unanswered question.
|
|
92
|
-
|
|
93
|
-
A failed task is not a dead end. Name it explicitly to continue its original Pi session with another message, even while the rest of its batch is still running:
|
|
94
|
-
|
|
95
|
-
```text
|
|
96
|
-
herdr_subagents_control({ action: "respond", runId: "<run-id>", task: 2, answer: "Use the staging credentials and retry." })
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
The task resumes in its own live pane when that pane still hosts its child, and otherwise restarts from the saved session in a new pane. `task` is always required so an answer never picks a task implicitly. A task whose child never produced a saved Pi session cannot be continued and says so; submit a new run instead. Continuing a failed task never starts its batch's queued siblings, which the running batch still owns. While a continued task is working, its run stays `running` and its Herdr tab stays open even if the batch that created it has already settled.
|
|
100
|
-
|
|
101
|
-
Blocked work always belongs to a live parent session. A historical follow-up may block too, keeping its shared resume tab until you answer it. Startup reconciliation cancels blocked work whose parent session disappeared, so no blocked run is ever left without an owner.
|
|
102
|
-
|
|
103
|
-
Cancel one active or queued run with `herdr_subagents_control({ action: "cancel", runId: "<run-id>" })`. Cancellation aborts its live child prompts, archives active or queued tasks as cancelled, closes its run tab, and leaves unrelated runs alone.
|
|
104
|
-
|
|
105
|
-
While a batch is active, a compact Fleet monitor stays below the editor. Its divider/title row summarizes the run, and each task row shows a hollow or filled status circle, the real persona and task name, business status or current runtime telemetry, live active duration, and cumulative child Pi token usage in one compact shared metrics column after the longest row rather than at the terminal edge. Herdr `blocked` is shown neutrally as `waiting_interaction` while the task remains `running`; only a child `subagent_report` with `status=blocked` makes the business task `blocked`. Prompt-delivery timeouts appear as `delivery_unknown` while a working live child is still allowed to publish its authoritative report. Running work uses Pi's warning/yellow semantic color and completed work uses success/green. Token totals use the child session's persisted assistant, tool-result, standalone usage, compaction, and branch-summary usage; they are never estimated. Queued tasks show neither fabricated time nor tokens. The monitor follows Pi theme semantics, truncates by visible terminal columns, and refreshes session usage from both file events and the running one-second timer.
|
|
106
|
-
|
|
107
|
-
With an empty editor, press `↓` to enter the list, `↑`/`↓` to choose a task, `Enter` to focus its exact pane, and `Esc` to return. `/herdr-subagents focus <task-number>` remains available as a fallback. Inside every child Agent, press `Alt+P` or run `/parent` to return to the exact parent Agent pane; the shortcut is also shown in the child footer.
|
|
108
|
-
|
|
109
|
-
## History
|
|
110
|
-
|
|
111
|
-
Run `/herdr-subagents history` or `herdr_subagents_control({ action: "history" })` to list the full compact project-local run and task archive after Herdr tabs close. History does not accept a run ID and is not a status interface. Full results, described documents, artifacts, and persistent Pi sessions remain separate on disk and are not loaded into parent context.
|
|
112
|
-
|
|
113
|
-
Continue a saved conversation with `herdr_subagents_control({ action: "resume", runId: "<run-id>", task: 1, prompt: "Follow up..." })`. Different settled tasks from the same run may resume concurrently in distinct panes of one shared resume tab; the same task remains single-writer and rejects a second resume. Remove only a selected archive with `herdr_subagents_control({ action: "cleanup", runId: "<run-id>" })`. History has no automatic count-based eviction. The extension adds the runtime path to local Git excludes when available (and otherwise uses `.pi/.gitignore`) without overwriting unrelated rules.
|
|
114
|
-
|
|
115
|
-
Read-only tasks start FIFO up to the configured concurrency limit. A pane is created only when its task starts, belongs exclusively to that task, and is never reused. Completed panes remain inspectable while another task in the batch is unsettled. Results remain in input order, and one failed task does not cancel its siblings. Any batch containing a write-capable persona is forced to concurrency one.
|
|
13
|
+
The reproducible source recording is retained as [`docs/demo.webm`](docs/demo.webm).
|
|
116
14
|
|
|
117
|
-
|
|
15
|
+
## Execution contract
|
|
118
16
|
|
|
119
|
-
|
|
17
|
+
`herdr_subagents` accepts an ordered, non-empty `tasks` array.
|
|
120
18
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
- `completed`, `
|
|
126
|
-
-
|
|
127
|
-
- paths to useful documents
|
|
128
|
-
- the task-record location
|
|
129
|
-
|
|
130
|
-
The complete result is not copied into the parent model context. Each run is retained under:
|
|
131
|
-
|
|
132
|
-
```text
|
|
133
|
-
.pi/herdr-subagents/runs/<run>/tasks/<task>/
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
The task directory contains the full result, canonical structured report, attempt-scoped reports and lifecycle markers, task metadata, system prompt, and persistent Pi session. Run-level metadata is stored in the parent run directory.
|
|
137
|
-
|
|
138
|
-
## Configuration and personas
|
|
139
|
-
|
|
140
|
-
Herdr Subagents loads settings and persona definitions from separate files:
|
|
141
|
-
|
|
142
|
-
| Scope | Settings | Persona definitions |
|
|
143
|
-
| --- | --- | --- |
|
|
144
|
-
| Project | `.pi/herdr-subagents.json` | `.pi/herdr-subagents/agents/*.md` |
|
|
145
|
-
| Global | `~/.pi/agent/herdr-subagents.json` | `~/.pi/agent/herdr-subagents/agents/*.md` |
|
|
146
|
-
| Package | built-in defaults | installed package `agents/*.md` |
|
|
147
|
-
|
|
148
|
-
Project JSON values recursively override global JSON values. The built-in setting defaults are `defaultConcurrency: 1`, `maxConcurrency: 4`, and `stalledWarningSeconds: 300`. `defaultModel` and `defaultThinking` are unset, so the parent Pi model and its thinking level are inherited unless another setting wins.
|
|
149
|
-
|
|
150
|
-
### Settings file option reference
|
|
151
|
-
|
|
152
|
-
These are all supported top-level fields in `herdr-subagents.json`:
|
|
153
|
-
|
|
154
|
-
| Option | Accepted value | Default | Effect |
|
|
155
|
-
| --- | --- | --- | --- |
|
|
156
|
-
| `defaultConcurrency` | Positive integer | `1` | Number of read-only tasks allowed to run together when a batch call omits `concurrency`. It is capped to `maxConcurrency`. |
|
|
157
|
-
| `maxConcurrency` | Positive integer | `4` | Hard ceiling for requested/default batch concurrency. A batch containing any write-capable persona still runs serially. |
|
|
158
|
-
| `stalledWarningSeconds` | Positive integer | `300` | Seconds before a still-running child emits a warning. This is not a timeout and does not stop the child. |
|
|
159
|
-
| `defaultModel` | Non-empty Pi model string, normally `provider/model` | Unset | Model used when neither JSON persona settings nor persona Markdown select one. If unset, the parent model is inherited. Use `/herdr-subagents models` to list authenticated models. |
|
|
160
|
-
| `defaultThinking` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` | Unset | Thinking level below persona-specific settings. Provider/model support can vary. If unset, parent thinking is inherited only when the parent model is also inherited. |
|
|
161
|
-
| `personas` | Object keyed by an existing persona name | `{}` | Per-persona JSON overrides described below. This does not create a persona; a matching Markdown definition must exist. |
|
|
162
|
-
|
|
163
|
-
Known fields with an invalid JSON type, an empty required string, or a non-positive integer are ignored with a diagnostic shown by `/herdr-subagents settings`. Model IDs, thinking-level compatibility, and skill/extension source availability are ultimately handled by Pi when the child starts. When `defaultConcurrency` exceeds `maxConcurrency`, the effective default is reduced to the maximum.
|
|
164
|
-
|
|
165
|
-
Each `personas.<name>` object supports exactly these overrides:
|
|
166
|
-
|
|
167
|
-
| Option | Accepted value | Effect |
|
|
168
|
-
| --- | --- | --- |
|
|
169
|
-
| `model` | Non-empty Pi model string | Overrides the model from the effective persona Markdown and `defaultModel`. |
|
|
170
|
-
| `thinking` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` | Overrides persona Markdown and `defaultThinking`. The value is passed to Pi's `--thinking` option. |
|
|
171
|
-
| `skills` | Array of non-empty strings | Replaces the entire lower-precedence explicit skill list. Use `[]` to clear that list. Each item is passed to child Pi as `--skill`. Package-provided skills are separate; see below. |
|
|
172
|
-
| `extensions` | Array of non-empty Pi source strings | Replaces the entire lower-precedence extension-source list. Use `[]` to clear it. Each item is passed as `--extension` after the mandatory child extensions. Accepts local files/directories, `npm:` and `git:` Pi packages, and `builtin:` sources. |
|
|
173
|
-
|
|
174
|
-
JSON persona overrides cannot change `name`, `description`, `access`, `tools`, or the system prompt; define those in persona Markdown.
|
|
175
|
-
|
|
176
|
-
Every child Pi explicitly loads and enables `codemode`. Built-in personas include it in their tool allowlists. Custom personas may omit `tools` and inherit Pi's defaults plus `codemode`, or list `codemode` explicitly alongside their allowed tools. A read-only persona remains read-only because codemode can call only the tools enabled for that child session.
|
|
177
|
-
|
|
178
|
-
A global configuration can establish shared defaults:
|
|
179
|
-
|
|
180
|
-
```json
|
|
181
|
-
{
|
|
182
|
-
"defaultConcurrency": 2,
|
|
183
|
-
"maxConcurrency": 4,
|
|
184
|
-
"stalledWarningSeconds": 300,
|
|
185
|
-
"defaultModel": "anthropic/claude-sonnet-4-5",
|
|
186
|
-
"defaultThinking": "medium",
|
|
187
|
-
"personas": {
|
|
188
|
-
"explorer": {
|
|
189
|
-
"model": "anthropic/claude-haiku-4-5",
|
|
190
|
-
"thinking": "medium",
|
|
191
|
-
"skills": ["code-search"]
|
|
192
|
-
},
|
|
193
|
-
"reviewer": {
|
|
194
|
-
"thinking": "high",
|
|
195
|
-
"skills": ["code-review"]
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
The project file can override only the fields that differ. Persona fields are merged by persona name:
|
|
19
|
+
- `concurrency: 1` runs tasks strictly in input order.
|
|
20
|
+
- `concurrency: N` runs at most N read-only tasks at once.
|
|
21
|
+
- A run containing a write-capable persona must use `concurrency: 1`; unsafe parallel writes are rejected.
|
|
22
|
+
- One task failing does not cancel its siblings.
|
|
23
|
+
- The tool returns only after every task is `completed`, `failed`, or `cancelled`.
|
|
24
|
+
- There is no background queue, blocked/respond flow, automatic resume, or early failure detach.
|
|
202
25
|
|
|
203
26
|
```json
|
|
204
27
|
{
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
208
|
-
|
|
209
|
-
"
|
|
210
|
-
|
|
211
|
-
},
|
|
212
|
-
"reviewer": {
|
|
213
|
-
"skills": ["security-review", "code-review"]
|
|
214
|
-
}
|
|
215
|
-
}
|
|
28
|
+
"label": "review",
|
|
29
|
+
"concurrency": 2,
|
|
30
|
+
"tasks": [
|
|
31
|
+
{ "name": "api", "prompt": "Review the API boundary.", "agent": "reviewer" },
|
|
32
|
+
{ "name": "tests", "prompt": "Find missing test coverage.", "agent": "explorer" }
|
|
33
|
+
]
|
|
216
34
|
}
|
|
217
35
|
```
|
|
218
36
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
| Persona | Access | Built-in tools | Intended use |
|
|
222
|
-
| --- | --- | --- | --- |
|
|
223
|
-
| `worker` | write | `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls` | Focused implementation work |
|
|
224
|
-
| `explorer` | read | `read`, `bash`, `grep`, `find`, `ls` | Fast codebase investigation |
|
|
225
|
-
| `reviewer` | read | `read`, `bash`, `grep`, `find`, `ls` | Correctness and maintainability review |
|
|
226
|
-
| `analyst` | read | `read`, `bash`, `grep`, `find`, `ls` | Neutral analysis with the method and role supplied by each task |
|
|
227
|
-
|
|
228
|
-
`analyst` deliberately does not bind a model or review method. Use the task-level `model` field when independent tasks need different models, and put roles such as auditor, replicator, or adversary in each task prompt.
|
|
229
|
-
|
|
230
|
-
Persona definitions use Markdown frontmatter followed by the child system prompt. For example, save this as `.pi/herdr-subagents/agents/security-reviewer.md`:
|
|
231
|
-
|
|
232
|
-
```md
|
|
233
|
-
---
|
|
234
|
-
name: security-reviewer
|
|
235
|
-
description: Read-only reviewer for security risks
|
|
236
|
-
access: read
|
|
237
|
-
model: anthropic/claude-sonnet-4-5
|
|
238
|
-
thinking: high
|
|
239
|
-
tools: read, bash, grep, find, ls
|
|
240
|
-
skills: security-review, code-review
|
|
241
|
-
---
|
|
242
|
-
|
|
243
|
-
Review the assigned scope without modifying files. Report concrete risks and evidence.
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
Persona Markdown supports these frontmatter fields:
|
|
247
|
-
|
|
248
|
-
| Field | Accepted value | Required/default | Effect |
|
|
249
|
-
| --- | --- | --- | --- |
|
|
250
|
-
| `name` | Non-empty string | Required | Persona identifier used by `agent`. Names must be unique within one persona directory. |
|
|
251
|
-
| `description` | Non-empty string | Required | Description shown to the parent when agents are listed. |
|
|
252
|
-
| `access` | `read` or `write` | `write` | Scheduling declaration. Any batch containing `write` is forced to concurrency one. `read` permits parallel scheduling but does not itself remove tools. |
|
|
253
|
-
| `model` | Non-empty Pi model string | Unset | Persona model default. |
|
|
254
|
-
| `thinking` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` | Unset | Persona thinking default passed to Pi's `--thinking` option. |
|
|
255
|
-
| `tools` | Non-empty comma-separated list, optionally bracketed | Child Pi defaults | Restricts built-in tools. Supported names are `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`, and `powershell`. `subagent_report` is always added. |
|
|
256
|
-
| `skills` | Comma-separated list, optionally bracketed | None | Skills loaded explicitly into child Pi; automatic skill discovery is disabled. |
|
|
257
|
-
|
|
258
|
-
The Markdown body after the frontmatter is the child system prompt and may be empty. To make a custom persona operationally read-only, set `access: read` **and** choose an appropriate `tools` allowlist; `access` alone is not a tool sandbox. Model names and skill values are passed to Pi, so they must be available in the installed Pi environment.
|
|
259
|
-
|
|
260
|
-
Persona Markdown definitions use replacement precedence, not field merging. A same-name project definition replaces the entire global or package definition:
|
|
37
|
+
Each task starts a single-task interactive Pi process in its own Herdr pane. Herdr first confirms the interactive Pi is ready, then the prompt is delivered exactly once. Prompt acknowledgement never decides task completion. Panes remain ordinary Herdr panes: they can be focused and inspected while work is running.
|
|
261
38
|
|
|
262
|
-
|
|
263
|
-
2. global `~/.pi/agent/herdr-subagents/agents/*.md`
|
|
264
|
-
3. package built-ins
|
|
39
|
+
## Result protocol
|
|
265
40
|
|
|
266
|
-
|
|
41
|
+
The child must call `subagent_report` once with a terminal status:
|
|
267
42
|
|
|
268
|
-
|
|
43
|
+
- `completed`: the requested work finished.
|
|
44
|
+
- `failed`: it cannot be completed; include `error`.
|
|
269
45
|
|
|
270
|
-
|
|
271
|
-
2. project `personas.<name>.model` JSON setting
|
|
272
|
-
3. global `personas.<name>.model` JSON setting
|
|
273
|
-
4. `model` in the effective persona Markdown frontmatter
|
|
274
|
-
5. effective `defaultModel` JSON setting (project overrides global)
|
|
275
|
-
6. parent Pi model
|
|
46
|
+
The complete result is stored on disk. The parent receives only the compact summary and document paths.
|
|
276
47
|
|
|
277
|
-
|
|
48
|
+
An atomically renamed attempt report is the authoritative completion signal. Herdr states such as `idle`, `blocked`, and `done` are wake-up hints only. A prompt transport error or Pi retry cannot fail a task while its child process remains alive. If the process exits without a valid report, the task fails explicitly.
|
|
278
49
|
|
|
279
|
-
|
|
280
|
-
2. global `personas.<name>.thinking` JSON setting
|
|
281
|
-
3. `thinking` in the effective persona Markdown frontmatter
|
|
282
|
-
4. effective `defaultThinking` JSON setting (project overrides global)
|
|
283
|
-
5. parent Pi thinking level when the child also inherits the parent model
|
|
50
|
+
Duplicate identical reports are idempotent. A conflicting report for the same task attempt is rejected. Terminal task records are immutable.
|
|
284
51
|
|
|
285
|
-
|
|
52
|
+
## State model
|
|
286
53
|
|
|
287
|
-
|
|
54
|
+
The same small state machine is used for every task; there are no separate foreground, background, blocked, resumed, or retry lifecycles.
|
|
288
55
|
|
|
289
|
-
|
|
56
|
+
New task records use:
|
|
290
57
|
|
|
291
|
-
```md
|
|
292
|
-
---
|
|
293
|
-
name: routed-explorer
|
|
294
|
-
description: Explorer with a provider routing hook
|
|
295
|
-
access: read
|
|
296
|
-
tools: read, bash, grep, find, ls, codemode
|
|
297
|
-
extensions: ~/.pi/agent/extensions/gateway-routing.ts
|
|
298
|
-
---
|
|
299
|
-
Inspect the requested code and report your findings.
|
|
300
58
|
```
|
|
301
|
-
|
|
302
|
-
Frontmatter accepts a single source or a comma-separated inline list, optionally enclosed in brackets. Use `extensions: []` to clear the list. Like the existing persona format, it does not support multiline YAML lists.
|
|
303
|
-
|
|
304
|
-
JSON accepts an array only. For example, override the built-in explorer with a local hook and an npm Pi package:
|
|
305
|
-
|
|
306
|
-
```json
|
|
307
|
-
{
|
|
308
|
-
"personas": {
|
|
309
|
-
"explorer": {
|
|
310
|
-
"extensions": [
|
|
311
|
-
"~/.pi/agent/extensions/gateway-routing.ts",
|
|
312
|
-
"npm:@example/pi-tools@1.0.0"
|
|
313
|
-
]
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
59
|
+
queued -> running -> completed | failed | cancelled
|
|
317
60
|
```
|
|
318
61
|
|
|
319
|
-
|
|
62
|
+
A run is `running` while any task is queued or running. Settled aggregate states are `completed`, `failed`, `partial`, or `cancelled`.
|
|
320
63
|
|
|
321
|
-
|
|
64
|
+
Older records containing `starting` or `blocked` remain readable for history and cleanup, but new runs never write those states.
|
|
322
65
|
|
|
323
|
-
|
|
66
|
+
## Durable records
|
|
324
67
|
|
|
325
|
-
|
|
68
|
+
Records are stored under:
|
|
326
69
|
|
|
327
|
-
Load only trusted extension code. `access: read` controls scheduling; it is not a sandbox for extension hooks, which can perform side effects outside model tools. `PI_HERDR_SUBAGENTS_CHILD` prevents **this package** from registering recursively, not arbitrary third-party extensions. Loading a package does not broaden an explicit `tools` allowlist. Persona `tools` still accepts only the existing built-in names; adding extension-defined tools to that schema is a separate feature.
|
|
328
|
-
|
|
329
|
-
Override the model for one task:
|
|
330
|
-
|
|
331
|
-
```text
|
|
332
|
-
herdr_subagents({
|
|
333
|
-
tasks: [
|
|
334
|
-
{ name: "authorization", agent: "reviewer", model: "anthropic/claude-opus-4-1:high", prompt: "Review the authorization boundary" }
|
|
335
|
-
]
|
|
336
|
-
})
|
|
337
70
|
```
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
]
|
|
348
|
-
})
|
|
71
|
+
.pi/herdr-subagents/runs/<sortable-run-id>/
|
|
72
|
+
run.json
|
|
73
|
+
tasks/<order>-<task-id>/
|
|
74
|
+
task.json
|
|
75
|
+
session.jsonl
|
|
76
|
+
system-prompt.md
|
|
77
|
+
report.json
|
|
78
|
+
reports/<attempt-id>.json
|
|
79
|
+
results/<attempt-id>.md
|
|
349
80
|
```
|
|
350
81
|
|
|
351
|
-
|
|
82
|
+
The orchestrator is the only writer of `run.json`, `task.json`, and promoted `report.json`. The child only writes its attempt-scoped result and report.
|
|
352
83
|
|
|
353
|
-
##
|
|
84
|
+
## Control and UI
|
|
354
85
|
|
|
355
|
-
|
|
86
|
+
`herdr_subagents_control` supports `list`, `status`, `history`, `cancel`, and `cleanup`. Follow-up work is a new task or run; settled tasks are never revived.
|
|
356
87
|
|
|
357
|
-
|
|
88
|
+
Use `/herdr-subagents focus <task-number>` to focus a running pane. `/herdr-subagents active`, `history`, `agents`, `models`, and `settings` provide compact inspection.
|
|
358
89
|
|
|
359
|
-
|
|
90
|
+
## Personas and settings
|
|
360
91
|
|
|
361
|
-
|
|
92
|
+
Bundled personas are `worker`, `explorer`, `analyst`, and `reviewer`. Project personas can be added under `.pi/agents/` using the same frontmatter format as the bundled files. Access is declared as `read` or `write` and drives concurrency safety.
|
|
362
93
|
|
|
363
|
-
|
|
94
|
+
Settings are read from the existing package configuration. `defaultConcurrency`, `maxConcurrency`, and `stalledWarningSeconds` remain supported.
|
|
@@ -1,53 +1,36 @@
|
|
|
1
|
-
# Herdr Subagents
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- Verify `status(runId)` isolates one run, projects queue-only state, prefers durable state over a stale queue record, exposes blocked questions, marks terminal states settled, rejects unknown IDs, and leaves full `history` semantics unchanged.
|
|
38
|
-
- Verify historical follow-ups allow different tasks to share one resume tab while preserving one session writer per task, and active resume membership keeps aggregate status running before the sibling task record is persisted.
|
|
39
|
-
- Verify Herdr `blocked` projects only `waiting_interaction`, prompt-delivery timeout projects `delivery_unknown` and accepts a late report, terminal API/process errors without reports fail, and `retrying` remains a display-only running runtime state until a reliable lifecycle source exists.
|
|
40
|
-
- Simulate a false screen-settled return while the authoritative Pi lifecycle remains working, then write the report later and verify the task completes without a transient failed state.
|
|
41
|
-
- Verify reports are bound to unique attempt IDs, blocked results are not tool errors, duplicate blocked responses are serialized, mixed failed/cancelled aggregates remain partial, and cleanup failures never overwrite business outcomes.
|
|
42
|
-
- Verify settling a batch merges run state written concurrently under the run lock instead of overwriting `run.json` from a stale in-memory copy.
|
|
43
|
-
- Wait for deferred cleanup commits/notifications rather than fixed sleeps. Hold the real run-state lock across the old assertion point to reproduce both successful-cleanup and error-persistence races, then require the expected durable state after release. Real smoke failures retain evidence before teardown.
|
|
44
|
-
- Verify an explicitly named failed task continues from its saved Pi session: it reuses its own live pane while its batch still runs, restarts in a new pane when that pane is gone, refuses to continue without a saved session, is never selected by an unqualified response, and does not start queued siblings that the running batch still owns.
|
|
45
|
-
- Verify a settling batch adopts the task records on disk, so a continued task keeps its run `running` and its Herdr tab open instead of being overwritten by the batch's own stale view.
|
|
46
|
-
- Verify a continuation claims its task before touching any pane, so a sibling continuation that finishes first leaves the shared tab open, and only the last one to settle closes it.
|
|
47
|
-
- Verify a sibling that is still queued is reported as running work rather than as an invented input request with no question.
|
|
48
|
-
- Verify a continued task may ask for input, becomes an ordinary blocked task, and is then answerable without naming its number.
|
|
49
|
-
- Verify `respond` refuses completed, cancelled, queued, running, and nonexistent tasks without touching Herdr or the run record.
|
|
50
|
-
|
|
51
|
-
## Latest baseline
|
|
52
|
-
|
|
53
|
-
All 266 deterministic scheduler, recovery, process-boundary, schema-validation, navigation, metrics, responsive-formatting, lifecycle, attempt-isolation, and persona cases, the controlled cleanup-timing regression, and the extension-contract lifecycle simulations must pass. The startup regression test reproduces the real ordering where `before_agent_start` records the attempt as working while Herdr can still report stale idle. Every release also runs the real single-Agent and concurrent read-only Herdr smoke; a skipped smoke is not a passing release check.
|
|
1
|
+
# Testing Herdr Subagents
|
|
2
|
+
|
|
3
|
+
Run the core suite:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm test --workspace @maxiaochao/pi-herdr-subagents
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
It verifies:
|
|
10
|
+
|
|
11
|
+
- strict serial start order at concurrency one;
|
|
12
|
+
- bounded read-only parallelism;
|
|
13
|
+
- ordered results despite concurrent completion;
|
|
14
|
+
- sibling continuation after failure and all-failed aggregation;
|
|
15
|
+
- explicit rejection of parallel write-capable tasks;
|
|
16
|
+
- waiting through a delayed authoritative report after a prompt transport error while the Pi process is alive;
|
|
17
|
+
- explicit failure when the process exits without a report;
|
|
18
|
+
- parent cancellation of running and queued tasks;
|
|
19
|
+
- immutable terminal task records;
|
|
20
|
+
- idempotent duplicate reports and rejection of conflicting replacements.
|
|
21
|
+
|
|
22
|
+
The previous broad lifecycle suite remains available temporarily as `npm run test:legacy --workspace @maxiaochao/pi-herdr-subagents` for migration auditing. Scenarios for background queues, blocked/respond, and historical resume intentionally no longer describe the public contract.
|
|
23
|
+
|
|
24
|
+
Run extension source loading. The direct-Pi fixture validates explicit file/package sources; Herdr's `builtin:codemode` alias is validated by the real-Herdr smoke because the alias is translated by `herdr agent start`:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npm run load-test --workspace @maxiaochao/pi-herdr-subagents
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Run the real Herdr smoke test from a Herdr session:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm run test:real-herdr --workspace @maxiaochao/pi-herdr-subagents
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
For real smoke testing, verify that each child appears in a visible pane, can be focused and inspected, exits after one terminal report, and that the parent returns only after all requested tasks settle. Also verify cancellation closes the run tab and does not overwrite a report already atomically published.
|