pi-herdr-agents 1.5.0 → 1.5.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 +16 -1
- package/CONTEXT.md +1 -1
- package/README.md +43 -15
- package/docs/adr/0003-installable-role-packs.md +12 -2
- package/docs/orchestrated-review-workflow-plan.md +2 -2
- package/docs/worktree-subagents.md +2 -2
- package/package.json +1 -1
- package/pi-extension/subagents/completion.ts +11 -6
- package/pi-extension/subagents/index.ts +393 -166
- package/pi-extension/subagents/launch.ts +22 -0
- package/pi-extension/subagents/session.ts +152 -1
- package/pi-extension/subagents/workflow.ts +30 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
9
9
|
|
|
10
|
-
## [v1.5.
|
|
10
|
+
## [v1.5.1](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.5.0...v1.5.1)
|
|
11
|
+
|
|
12
|
+
### Merged
|
|
13
|
+
|
|
14
|
+
- Fix subagent lifecycle, resume permissions, and failure reporting [`#30`](https://github.com/giuseppecrj/pi-herdr-agents/pull/30)
|
|
15
|
+
|
|
16
|
+
### Commits
|
|
17
|
+
|
|
18
|
+
- fix(subagents): preserve capabilities across resume (#26) [`9b46c5c`](https://github.com/giuseppecrj/pi-herdr-agents/commit/9b46c5c612c299ca9bb2ad3b6511b0af53438814)
|
|
19
|
+
- fix(subagents): preserve model failure evidence and fallback delivery (#24) [`a6366ee`](https://github.com/giuseppecrj/pi-herdr-agents/commit/a6366eea08ec6679535f3f4fa9dbd6c9201254e9)
|
|
20
|
+
- fix(roles): reject malformed capability declarations (#27) [`0d263b8`](https://github.com/giuseppecrj/pi-herdr-agents/commit/0d263b8867e1708288302e0f9a5c9b806c810345)
|
|
21
|
+
- fix(workflows): join child cleanup before terminal delivery (#25) [`e928817`](https://github.com/giuseppecrj/pi-herdr-agents/commit/e9288172b8037b39ed04107ad0729292ea596a8f)
|
|
22
|
+
- test(integration): assert cwd and session delivery structurally (#28) [`ec277ff`](https://github.com/giuseppecrj/pi-herdr-agents/commit/ec277ff9ddf4b309edb1f12942acac68ee295eb2)
|
|
23
|
+
|
|
24
|
+
## [v1.5.0](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.4.2...v1.5.0) - 2026-09-05
|
|
11
25
|
|
|
12
26
|
### Merged
|
|
13
27
|
|
|
@@ -28,6 +42,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
|
28
42
|
- feat: allow disabling bundled agent roles [`e866ade`](https://github.com/giuseppecrj/pi-herdr-agents/commit/e866ade49f35c7bd51931c1e216fd9749abca2e8)
|
|
29
43
|
- fix: distinguish role-pack replacements from bundled warnings [`d4452c8`](https://github.com/giuseppecrj/pi-herdr-agents/commit/d4452c8994c61607d2c1d1259679c1b7f7be8780)
|
|
30
44
|
- fix: address evaluation validator review findings [`406d1ab`](https://github.com/giuseppecrj/pi-herdr-agents/commit/406d1ab9e1a129d336ed0b850508d335b130e156)
|
|
45
|
+
- chore: release v1.5.0 [`d77714c`](https://github.com/giuseppecrj/pi-herdr-agents/commit/d77714c79123db91d4741838587b4851527c4615)
|
|
31
46
|
|
|
32
47
|
## [v1.4.2](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.4.1...v1.4.2) - 2026-08-31
|
|
33
48
|
|
package/CONTEXT.md
CHANGED
|
@@ -33,7 +33,7 @@ The runner-owned append-only `run.jsonl` that starts with approval binding the w
|
|
|
33
33
|
_Avoid_: User-authored plan, mutable audit log, duplicated result
|
|
34
34
|
|
|
35
35
|
**Terminal lifecycle**:
|
|
36
|
-
|
|
36
|
+
Every workflow terminal path stops queued work and accounts for active read and review panes before checkout disposal and final delivery. Completed panes close after result capture while child session files and run evidence remain. Unconfirmed child exit retains the checkout and ends failed with `cancel_termination_failed`. Writer-worktree retention belongs to a deferred writer workflow.
|
|
37
37
|
_Avoid_: Retaining every clean pane, deleting review evidence
|
|
38
38
|
|
|
39
39
|
**Restart boundary**:
|
package/README.md
CHANGED
|
@@ -342,12 +342,25 @@ followed by agent frontmatter, per-agent config, the global default, and finally
|
|
|
342
342
|
the parent model. Model values must be exact authenticated `provider/model-id`
|
|
343
343
|
references. A value can contain an ordered comma-separated fallback list, for
|
|
344
344
|
example `provider/preferred, provider/fallback`. The extension validates every
|
|
345
|
-
candidate before launch,
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
345
|
+
candidate before launch, then launches later candidates only after the selected
|
|
346
|
+
child settles with a provider/agent error. Pi owns any automatic transient
|
|
347
|
+
retrying inside that child; the extension does not infer retry counts or
|
|
348
|
+
permanence from the error text. A completed child result, including a negative
|
|
349
|
+
task result, never switches models. Completion metadata reports the requested
|
|
350
|
+
candidate, every attempted candidate, the model actually used, and each raw
|
|
351
|
+
model failure in attempt order when fallbacks are tried. Workflow metadata accepts one exact
|
|
352
|
+
model only, to keep approved workflow runtimes deterministic.
|
|
353
|
+
|
|
354
|
+
A catalog-listed model and configured authentication do not prove that the
|
|
355
|
+
active provider account can use that model. Providers may reject an account /
|
|
356
|
+
model combination only when the request is made. The completion preserves each
|
|
357
|
+
raw provider reason with its model and suggests checking account access,
|
|
358
|
+
spawning a new subagent with a supported model, or choosing an appropriate
|
|
359
|
+
configured fallback. `subagent_resume` does not select a model and should be
|
|
360
|
+
used only after the session's stored model is usable. The completion does not
|
|
361
|
+
claim a permanent failure or a retry count that Pi has not exposed. Reliable
|
|
362
|
+
structured permanence and retry counts require an upstream Pi/ExtensionAPI
|
|
363
|
+
diagnostics seam for final provider errors and retry outcomes.
|
|
351
364
|
|
|
352
365
|
`config.json` is gitignored in the source tree so local overrides are not
|
|
353
366
|
committed from a checkout. On an installed package root, treat it as disposable
|
|
@@ -478,6 +491,8 @@ Parameters:
|
|
|
478
491
|
- If process identity cannot be captured for an active pane, the pane remains present after close, or any captured process still lives after the bounded wait, the checkout is retained and the run ends `failed` with `cancel_termination_failed`. Successful cancellation is not reported in that case.
|
|
479
492
|
- A successful cancel writes one `cancelled` terminal journal event and one result-free delivery. Repeated cancel is idempotent and returns the authoritative terminal outcome (including a prior fail-closed result).
|
|
480
493
|
|
|
494
|
+
Every terminal path—normal completion, early script return, script or Worker failure, deadline, interruption, and explicit cancellation—stops queued work and accounts for active workflow children before checkout disposal or final delivery. If active-child exit cannot be confirmed, the checkout is retained and the authoritative outcome is `failed` with `cancel_termination_failed`.
|
|
495
|
+
|
|
481
496
|
There is no list, status, resume, or history action in v1. Workflow ownership and the Worker survive `/reload` in the same Pi process, and the latest parent API receives one final delivery. A full process restart reconciles interruption without replay: startup marks only the last known running journal event as `interrupted`, leaves sessions, journals, and reader checkouts in place, and requires a new approved run.
|
|
482
497
|
|
|
483
498
|
### Bundled `orchestrate` skill
|
|
@@ -490,7 +505,7 @@ The script and journal retain every original child envelope. Synthesis receives
|
|
|
490
505
|
|
|
491
506
|
The runner-owned checkout contains only the pinned commit. Parent staged, unstaged, and untracked state is not review evidence. Effective child tools are the resolved role allowlist intersected with the runner maximum (`read`, `grep`, `find`, and `ls`) and deny rules. Public `subagent` results can be abbreviated above 16,000 characters, but workflow scripts receive complete child reports within their explicit bounds. Operational failures are preserved without silent fallback; recovery is a new exact approved run.
|
|
492
507
|
|
|
493
|
-
The parent calls `herdr_workflow prepare`, presents its packet unchanged, and waits for the exact `APPROVE <8-character lowercase hash prefix>` reply before calling `start`. After start, one final delivery is sent without polling.
|
|
508
|
+
The parent calls `herdr_workflow prepare`, presents its packet unchanged, and waits for the exact `APPROVE <8-character lowercase hash prefix>` reply before calling `start`. After start, one final delivery is sent without polling. Every terminal path is fail-closed: it accounts for queued and active children before checkout disposal and delivery, retaining evidence when process exit cannot be confirmed. Same-process `/reload` preserves ownership; full restart records interruption without replay, restart, cleanup, or history. Workflow JavaScript runs in a Worker-hosted `vm` for event-loop availability only; neither the Worker nor `vm` is a security boundary, and worktrees do not provide process or security isolation.
|
|
494
509
|
|
|
495
510
|
---
|
|
496
511
|
|
|
@@ -509,6 +524,8 @@ The `caller_ping` tool lets a Pi-backed subagent request help from its parent ag
|
|
|
509
524
|
- `message` (optional): Follow-up prompt to send after resuming
|
|
510
525
|
- `autoExit` (optional): Whether the resumed session should auto-exit after its next response fully settles. Defaults to `true` for autonomous follow-up work; set `false` when resuming for an interactive handoff.
|
|
511
526
|
|
|
527
|
+
Each public child stores a session-adjacent versioned launch-policy sidecar. Public resume restores its resolved tool allowlist and denied subagent tools rather than looking up the current role, so later role changes cannot widen a child. An intentionally unrestricted launch remains unrestricted (no `--tools` argument); a restricted launch restores its exact allowlist. The `autoExit` override still controls whether `subagent_done` is available, while `caller_ping` remains available. Missing, malformed, or unsupported policy fails closed before a pane is created with recovery guidance. Public resume also rejects workflow-owned and managed-worktree child sessions; use their retained workflow evidence or workspace instead.
|
|
528
|
+
|
|
512
529
|
**Interaction flow:**
|
|
513
530
|
|
|
514
531
|
1. Child calls `caller_ping({ message: "Not sure which schema to use" })`
|
|
@@ -632,8 +649,11 @@ specific exact authenticated `provider/model-id`.
|
|
|
632
649
|
|
|
633
650
|
`tools` is passed to Pi's `--tools` allowlist and may name any registered
|
|
634
651
|
built-in, extension, or custom tool. Listing a tool does not install its
|
|
635
|
-
extension.
|
|
636
|
-
|
|
652
|
+
extension. Use one non-empty inline comma-separated scalar, such as
|
|
653
|
+
`tools: read, grep`; do not use YAML lists, containers, quotes, or comments.
|
|
654
|
+
Omitting `tools` intentionally leaves the role unrestricted. Likewise, `skills`
|
|
655
|
+
names must already be discoverable by Pi; this package does not install role
|
|
656
|
+
prerequisites.
|
|
637
657
|
|
|
638
658
|
### 3. Verify and launch
|
|
639
659
|
|
|
@@ -736,9 +756,17 @@ collision rules, and rejected alternatives.
|
|
|
736
756
|
- Generic roles omit `model` unless a particular runtime is functionally required.
|
|
737
757
|
- `/subagent list` shows the expected source and a smoke launch succeeds.
|
|
738
758
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
and
|
|
759
|
+
Capability declarations are strict: use the unquoted, unindented keys
|
|
760
|
+
`tools:`, `deny-tools:`, and `spawning:` exactly once when present. Declare
|
|
761
|
+
`tools` and `deny-tools` as non-empty inline comma-separated scalars, and
|
|
762
|
+
`spawning` as exactly `true` or `false`. YAML lists, containers, multiline
|
|
763
|
+
values, quotes, comments, empty values, duplicates, noncanonical key spelling,
|
|
764
|
+
and invalid booleans are rejected. A role with an invalid capability declaration
|
|
765
|
+
is excluded from discovery, and an exact-name launch reports the diagnostic
|
|
766
|
+
before creating a Herdr pane or worktree. Other unsupported or unknown
|
|
767
|
+
frontmatter may still be ignored.
|
|
768
|
+
Compare definitions against the reference below and verify them with
|
|
769
|
+
`/subagent list` plus a smoke launch.
|
|
742
770
|
|
|
743
771
|
### Frontmatter Reference
|
|
744
772
|
|
|
@@ -749,11 +777,11 @@ and verify them with `/subagent list` plus a smoke launch.
|
|
|
749
777
|
| `model` | string | Optional exact authenticated Pi model default or ordered comma-separated fallback list; omit to use per-agent config, global config, then the parent |
|
|
750
778
|
| `thinking` | string | Optional Pi thinking default (`off` through `max`); omit to inherit the parent |
|
|
751
779
|
| `system-prompt` | string | `append` passes the agent body through Pi's appended system prompt; `replace` replaces Pi's default system prompt. Without this field, the body is included in the task wrapper |
|
|
752
|
-
| `tools` | string |
|
|
780
|
+
| `tools` | string | One non-empty inline comma-separated Pi `--tools` allowlist under the exact unquoted key `tools:`; may contain any registered built-in, extension, or custom tool name. Omit to leave unrestricted. YAML lists, containers, multiline values, quotes, comments, noncanonical keys, and duplicates are rejected. |
|
|
753
781
|
| `skills` | string | Comma-separated installed skill names to auto-load. Use this plural form for new definitions; legacy project/global definitions using singular `skill` remain compatible. |
|
|
754
782
|
| `session-mode` | string | Default child-session mode: `standalone`, `lineage-only`, or `fork` |
|
|
755
|
-
| `spawning` | boolean | Set `false` to deny all subagent-spawning tools
|
|
756
|
-
| `deny-tools` | string |
|
|
783
|
+
| `spawning` | boolean | Set exactly `false` to deny all subagent-spawning tools under the exact unquoted key `spawning:`. Only one `true` or `false` declaration is accepted. |
|
|
784
|
+
| `deny-tools` | string | One non-empty inline comma-separated `pi-herdr-agents` tool list to suppress under the exact unquoted key `deny-tools:`; this is not a universal cross-extension deny list. YAML lists, containers, multiline values, quotes, comments, noncanonical keys, and duplicates are rejected. |
|
|
757
785
|
| `auto-exit` | boolean | Auto-shutdown after Pi fully settles when the latest assistant turn does not end with `stopReason: "aborted"` — no `subagent_done` call needed. User input does not permanently disable auto-exit. Recommended for autonomous agents (scout, worker); not for interactive ones (planner). Also determines the default value of `interactive` (see below). |
|
|
758
786
|
| `interactive` | boolean | Override whether stall/recovery transitions wake the parent session. Defaults to the inverse of `auto-exit`: autonomous agents (`auto-exit: true`) are non-interactive and get stall pings; agents without `auto-exit` are interactive and stay quiet. Explicit values take precedence. |
|
|
759
787
|
| `cwd` | string | Default working directory. Absolute paths are unambiguous; relative agent-frontmatter paths resolve from Pi's agent config directory (`PI_CODING_AGENT_DIR` or `~/.pi/agent`), not the project root |
|
|
@@ -29,7 +29,14 @@ roles are listed or launched.
|
|
|
29
29
|
|
|
30
30
|
Role packs use the existing agent-definition format. The filename stem is the
|
|
31
31
|
canonical role name; `name` frontmatter is optional and, when present, must
|
|
32
|
-
match the stem. `description` is required for contributed roles.
|
|
32
|
+
match the stem. `description` is required for contributed roles. Capability
|
|
33
|
+
declarations are strict: use the unquoted, unindented keys `tools:`,
|
|
34
|
+
`deny-tools:`, and `spawning:` exactly once when present. `tools` and
|
|
35
|
+
`deny-tools` must each be one non-empty inline comma-separated scalar, and
|
|
36
|
+
`spawning` must be exactly `true` or `false`. YAML lists, containers,
|
|
37
|
+
multiline or empty values, quotes, comments, noncanonical key spelling, and
|
|
38
|
+
duplicate declarations are invalid; omit `tools` to intentionally leave a role
|
|
39
|
+
unrestricted.
|
|
33
40
|
|
|
34
41
|
## Why
|
|
35
42
|
|
|
@@ -80,7 +87,10 @@ Within the package layer:
|
|
|
80
87
|
|
|
81
88
|
Invalid registrations do not suppress unrelated roles. Listing surfaces report
|
|
82
89
|
concise diagnostics, and an exact-name launch reports the matching diagnostic
|
|
83
|
-
instead of treating an invalid contribution as a bare agent.
|
|
90
|
+
instead of treating an invalid contribution as a bare agent. An invalid
|
|
91
|
+
capability declaration makes that role name unavailable at its precedence layer
|
|
92
|
+
rather than falling through to a lower-priority role, and launch rejects it
|
|
93
|
+
before Herdr creates a pane or worktree.
|
|
84
94
|
|
|
85
95
|
## Reload and security
|
|
86
96
|
|
|
@@ -19,7 +19,7 @@ Shipped:
|
|
|
19
19
|
- bounded parallel review, fresh synthesis, and explicit non-retryable failure evidence for parent-guided recovery;
|
|
20
20
|
- a preferred adversarial procedure in the existing authoring skill, with risk-based discovery and candidate-dependent verification;
|
|
21
21
|
- one final parent delivery; and
|
|
22
|
-
- fail-closed
|
|
22
|
+
- fail-closed terminal cleanup with process-exit confirmation for every outcome before checkout disposal.
|
|
23
23
|
|
|
24
24
|
Package and runtime slices are shipped. Remaining work is limited to deferred writer behavior and any future workflow extensions.
|
|
25
25
|
|
|
@@ -146,7 +146,7 @@ The LLM cannot authorize execution by passing approval text in tool arguments. `
|
|
|
146
146
|
7. dispose the reader checkout only after termination is confirmed;
|
|
147
147
|
8. append one `cancelled` terminal event and deliver one bounded cancellation receipt.
|
|
148
148
|
|
|
149
|
-
If a captured process remains after the bounded wait, retain the checkout and append `failed` with `cancel_termination_failed`; never report successful cancellation or clean up underneath a live child. All completion, failure, interruption, and cancellation paths use the same compare-and-set terminal gate. Repeated cancellation is idempotent.
|
|
149
|
+
If a captured process remains after the bounded wait, retain the checkout and append `failed` with `cancel_termination_failed`; never report successful cancellation or clean up underneath a live child. All completion, early return, failure, interruption, deadline, and cancellation paths use the same compare-and-set terminal gate and terminate queued or active children before checkout disposal and delivery. Repeated cancellation is idempotent.
|
|
150
150
|
|
|
151
151
|
## Workflow metadata
|
|
152
152
|
|
|
@@ -190,11 +190,11 @@ The extension never pushes, creates a PR, merges, cherry-picks, or changes the p
|
|
|
190
190
|
- **Creation failure:** the manifest is marked failed. If Herdr created the branch but returned an incomplete response, the extension reconciles a unique branch match through `/worktree list` and records any recovered workspace/path.
|
|
191
191
|
- **Launch failure after creation:** the manifest is marked failed and the workspace, forked session, and path are retained. The destination is not focused unless Pi startup is confirmed.
|
|
192
192
|
- **Worker failure:** summary and available Git state are returned; the workspace remains open. Auto-exit waits until Pi is fully settled, so a transient provider error followed by automatic compaction or retry does not end the worker early.
|
|
193
|
-
- **`caller_ping`:** the child exits with `needs_help`; continue worktree-bound follow-up in the retained workspace rather than through `subagent_resume`.
|
|
193
|
+
- **`caller_ping`:** the child exits with `needs_help`; continue worktree-bound follow-up in the retained workspace rather than through `subagent_resume`. Public `subagent_resume` rejects managed-worktree child sessions before creating a pane so it cannot silently lose worktree ownership or policy.
|
|
194
194
|
- **Parent `/reload`, `/new`, `/resume`, or `/fork`:** active in-memory watchers transfer to the replacement parent session.
|
|
195
195
|
- **Full process restart or crash:** the worktree remains, but v1 does not automatically rediscover and resume its watcher.
|
|
196
196
|
|
|
197
|
-
`subagent_resume`
|
|
197
|
+
`subagent_resume` rejects managed-worktree sessions. It does not reattach the managed worktree lifecycle or produce a new worktree handoff. For worktree follow-up, focus the retained workspace and resume manually from its shell:
|
|
198
198
|
|
|
199
199
|
```bash
|
|
200
200
|
herdr workspace focus <workspace-id>
|
package/package.json
CHANGED
|
@@ -86,7 +86,7 @@ function completionArtifact(
|
|
|
86
86
|
return consumeExitSidecar(options.sessionFile);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
async function
|
|
89
|
+
async function waitForDelayedSidecar(
|
|
90
90
|
signal: AbortSignal,
|
|
91
91
|
options: CompletionOptions,
|
|
92
92
|
): Promise<CompletionResult | null> {
|
|
@@ -137,7 +137,15 @@ export async function waitForCompletion(
|
|
|
137
137
|
|
|
138
138
|
try {
|
|
139
139
|
const exitCode = terminalExitCode(await options.readTerminalTail());
|
|
140
|
-
if (exitCode !== null)
|
|
140
|
+
if (exitCode !== null) {
|
|
141
|
+
// The shell sentinel can become readable just before the child writes
|
|
142
|
+
// its authoritative error sidecar. Preserve that error metadata.
|
|
143
|
+
if (exitCode !== 0) {
|
|
144
|
+
const racedCompletion = await waitForDelayedSidecar(signal, options);
|
|
145
|
+
if (racedCompletion) return racedCompletion;
|
|
146
|
+
}
|
|
147
|
+
return { reason: "sentinel", exitCode };
|
|
148
|
+
}
|
|
141
149
|
} catch {
|
|
142
150
|
// Terminal reads are only sentinel/output probes; Herdr status is polled
|
|
143
151
|
// independently below, even when terminal reads succeed.
|
|
@@ -155,10 +163,7 @@ export async function waitForCompletion(
|
|
|
155
163
|
if (inspection.kind === "missing") {
|
|
156
164
|
// Pane closure and atomic artifact publication are separate operations.
|
|
157
165
|
// Allow a short bounded grace window before declaring evidence lost.
|
|
158
|
-
const racedCompletion = await
|
|
159
|
-
signal,
|
|
160
|
-
options,
|
|
161
|
-
);
|
|
166
|
+
const racedCompletion = await waitForDelayedSidecar(signal, options);
|
|
162
167
|
if (racedCompletion) return racedCompletion;
|
|
163
168
|
return {
|
|
164
169
|
reason: "error",
|