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 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.0](https://github.com/giuseppecrj/pi-herdr-agents/compare/v1.4.2...v1.5.0)
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
- Completed read and review panes close after result capture while child session files and run evidence remain. Writer-worktree retention belongs to a deferred writer workflow.
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, retries the preferred model normally, then launches
346
- later candidates only after a provider/agent request failure. A completed child
347
- result, including a negative task result, never switches models. Completion
348
- metadata and the status widget report the model actually used; an exhausted
349
- list reports every attempted model. Workflow metadata accepts one exact model
350
- only, to keep approved workflow runtimes deterministic.
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. Cancellation is fail-closed and retains 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.
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. Likewise, `skills` names must already be discoverable by Pi; this
636
- package does not install role prerequisites.
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
- The current parser is permissive: unsupported or unknown frontmatter may be
740
- ignored rather than rejected. Compare definitions against the reference below
741
- and verify them with `/subagent list` plus a smoke launch.
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 | Comma-separated Pi `--tools` allowlist; may contain any registered built-in, extension, or custom tool name |
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 | Comma-separated `pi-herdr-agents` tool names to suppress; this is not a universal cross-extension deny list |
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 cancellation with process-exit confirmation before checkout cleanup.
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` resumes a session in a new ordinary Herdr pane. 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:
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-herdr-agents",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "Asynchronous Pi subagents and approved review workflows in Herdr, with optional isolated Git worktrees",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -86,7 +86,7 @@ function completionArtifact(
86
86
  return consumeExitSidecar(options.sessionFile);
87
87
  }
88
88
 
89
- async function waitForDisappearanceArtifacts(
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) return { reason: "sentinel", exitCode };
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 waitForDisappearanceArtifacts(
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",