pi-herdr-agents 3.0.3 → 3.2.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/AGENTS.md CHANGED
@@ -27,12 +27,13 @@ Former bundled roles, `/plan`, its plan skill, and `/skill:orchestrate` now belo
27
27
  - `pi-extension/subagents/index.ts` — Pi composition root: public tools/commands, role-pack event bridge, host policy, widgets, and parent delivery
28
28
  - `pi-extension/subagents/model-registry.ts`, `config-path.ts` — permanent host-local SDK capability glue and configuration-path conventions
29
29
  - `maestro/core/` — seam interfaces and types; activity/lifecycle/status projection, routing, wake-ups, and supervision
30
+ - `plugins/sidebar/` — optional standalone Node companion Herdr plugin; it imports nothing from the extension
30
31
  - `maestro/core/roles/discovery.ts`, `maestro/core/config/` — role parsing/discovery, injected-directory config loaders, and task-model init prompt construction
31
32
  - `maestro/core/worktree.ts`, `worktree-cleanup.ts` — manifest schema/state, handoff types, and cleanup eligibility/formatting
32
33
  - `maestro/adapters/pi/` — `PiHarnessAdapter`, launch transactions, completion evidence, session I/O, activity files, SDK model glue, and task-model registry projection
33
34
  - `maestro/adapters/pi/child/subagent-done.ts` — child protocol: `caller_ping`, `subagent_done`, and activity recording
34
- - `maestro/surfaces/herdr/` — `HerdrSurfaceProvider`, Herdr CLI driver, and terminal scripts/placement
35
- - `maestro/runtime/` — `RunSession`, Pi composition, run ownership, controls/retries, observation, delivery-gated cleanup, worktree operations/handoff, and task-model init composition
35
+ - `maestro/surfaces/herdr/` — `HerdrSurfaceProvider`, Herdr CLI driver, and terminal scripts/placement, plus `herdr-socket.ts` for socket pane metadata
36
+ - `maestro/runtime/` — `RunSession`, Pi composition, run ownership, controls/retries, observation, delivery-gated cleanup, worktree operations/handoff, and task-model init composition, plus `delegated-marker.ts` for delegated-child sidebar markers
36
37
  - `maestro/adapters/fake/`, `maestro/surfaces/fake/`, `test/maestro/` — conformance fakes, seam tests, and the dependency-rule test
37
38
  - `CONTEXT.md` — orchestration-domain glossary
38
39
  - `docs/adr/` — hard-to-reverse architectural decisions; [ADR-0012](docs/adr/0012-adopt-maestro-seams-in-repo.md) records the maestro seams decision
package/CHANGELOG.md CHANGED
@@ -7,10 +7,27 @@ 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
- ## [v3.0.3](https://github.com/giuseppecrj/pi-herdr-agents/compare/v3.0.2...v3.0.3)
10
+ ## [v3.2.0](https://github.com/giuseppecrj/pi-herdr-agents/compare/v3.1.0...v3.2.0)
11
+
12
+ ### Commits
13
+
14
+ - feat: add optional Herdr sidebar focus plugin [`9d2fb03`](https://github.com/giuseppecrj/pi-herdr-agents/commit/9d2fb039b006e0d389d19b1105dd54e277f695ba)
15
+ - fix: update dependencies to resolve security advisories [`028ebd6`](https://github.com/giuseppecrj/pi-herdr-agents/commit/028ebd62fa8c8e967c15a9677c0d4a1abc52505a)
16
+ - docs: match sidebar README to main package style [`918db68`](https://github.com/giuseppecrj/pi-herdr-agents/commit/918db68bd5f56782198182aa48eb6586a46a0ef7)
17
+
18
+ ## [v3.1.0](https://github.com/giuseppecrj/pi-herdr-agents/compare/v3.0.3...v3.1.0) - 2026-10-09
19
+
20
+ ### Commits
21
+
22
+ - feat: route task-model init through pack approval [`1b6bacd`](https://github.com/giuseppecrj/pi-herdr-agents/commit/1b6bacd0b5eecb6f83ffba1d5a5ee57a8b0fa944)
23
+ - test: dispatch all Pi lifecycle handlers in placement fixtures [`f96e59d`](https://github.com/giuseppecrj/pi-herdr-agents/commit/f96e59de8890d39ce6c79be8de245d287fc2485e)
24
+ - chore: release v3.1.0 [`0bb8bcd`](https://github.com/giuseppecrj/pi-herdr-agents/commit/0bb8bcdc328d152c5cab5eaaf4d261f109055ee4)
25
+
26
+ ## [v3.0.3](https://github.com/giuseppecrj/pi-herdr-agents/compare/v3.0.2...v3.0.3) - 2026-10-08
11
27
 
12
28
  ### Merged
13
29
 
30
+ - Release 3.0.3 [`#82`](https://github.com/giuseppecrj/pi-herdr-agents/pull/82)
14
31
  - test(integration): re-read worktree row before asserting its open workspace [`#81`](https://github.com/giuseppecrj/pi-herdr-agents/pull/81)
15
32
  - Fix Pi readiness check when Pi rewrites argv [`#80`](https://github.com/giuseppecrj/pi-herdr-agents/pull/80)
16
33
  - Fix tsc errors and git-version-sensitive index test; document lint Node floor [`#79`](https://github.com/giuseppecrj/pi-herdr-agents/pull/79)
@@ -25,6 +42,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
25
42
  - fix: settle stranded supervision waits and test wake retain accounting [`d18df1c`](https://github.com/giuseppecrj/pi-herdr-agents/commit/d18df1cb83bedf86951d2f9702a4a6185b44a8e8)
26
43
  - test: bound the identity-capture loop anchor and close supervision test gaps [`4a0e71a`](https://github.com/giuseppecrj/pi-herdr-agents/commit/4a0e71a97ebc9ba3f21a5377504f24c3d837d9a2)
27
44
  - Treat an unreadable Pi environment as unproven, not fatal [`b69151a`](https://github.com/giuseppecrj/pi-herdr-agents/commit/b69151ae85bd13c45bdf3e980c0b36e315c7c863)
45
+ - chore: release v3.0.3 [`1c75adb`](https://github.com/giuseppecrj/pi-herdr-agents/commit/1c75adb0864af9b922d1471734b086943435f238)
28
46
  - test(worktree-cleanup): make the index-restore test hermetic and deterministic [`16840f6`](https://github.com/giuseppecrj/pi-herdr-agents/commit/16840f64b9481983cf44fc928061fd4e6822434d)
29
47
  - test(worktree-cleanup): clear inherited git repository variables in the fixture [`323e1fb`](https://github.com/giuseppecrj/pi-herdr-agents/commit/323e1fbf6e6240eb5e7000d63fc84e23b391259a)
30
48
  - test(worktrees): expect launch snapshots in scoped inventory [`f733040`](https://github.com/giuseppecrj/pi-herdr-agents/commit/f73304088c8a781b1ed7333a5a09175ec7c896e9)
package/CONTEXT.md CHANGED
@@ -65,12 +65,30 @@ taken first stays authoritative.
65
65
  _Avoid_: Interrupt, suppression, persistent stop, pane-close fallback
66
66
 
67
67
  **Process identity**:
68
- A managed worktree child's Pi process named by immutable kernel facts (PID,
69
- start time, boot, and PID namespace), recorded by the child at launch and
70
- verified by the parent against the Herdr pane. A worktree cancel signals and
71
- judges exit only by it.
68
+ A child's Pi process named by immutable kernel facts (PID, start time, boot,
69
+ and PID namespace), recorded by the child at launch and verified by the parent
70
+ against the Herdr pane. Every managed worktree child records it; other children
71
+ record it when sidebar markers are on. A worktree cancel signals and judges
72
+ exit only by it, and a delegated marker renews only while it is alive.
72
73
  _Avoid_: argv match, foreground process, shell visibility
73
74
 
75
+ **Delegated marker**:
76
+ The display-only pane token `piha_delegated_v1=live` that a parent reports on
77
+ a delegated child's pane when `sidebar.enabled` is on. It means the parent
78
+ classified the pane's process as its child within the last 15 seconds, not
79
+ that the process is alive when Herdr draws the row. One stable source, a
80
+ 15-second TTL, and increasing sequence numbers bound a leftover marker;
81
+ renewal requires the original process identity. A `/worktree` handoff is
82
+ never marked.
83
+ _Avoid_: liveness proof, ownership record, security boundary, per-run source
84
+
85
+ **Sidebar Focus**:
86
+ The Herdr agent view that the optional `pi-herdr-agents.sidebar` plugin sets
87
+ on request. It hides rows whose delegated marker is `live` while their status
88
+ is idle, working, or done, and keeps blocked, unknown, and unmarked rows. All
89
+ clears only this plugin's view and reports a view that another tool owns.
90
+ _Avoid_: hiding processes, notification filter, automatic view, popup
91
+
74
92
  **Pi startup confirmation**:
75
93
  The bounded check that a `/worktree` handoff's Pi is running before its
76
94
  workspace is focused: a Pi process in the root pane with the worktree cwd whose
@@ -210,7 +228,9 @@ architecture to planning and diagnosis, coding to workers, review to reviewers,
210
228
  QA to software and test runners, and docs to documentation workers. Categories
211
229
  describe work, not complexity. `/subagents-init [preferences]` drafts them from
212
230
  the active extension-loaded registry's synchronous snapshot and existing saved
213
- choices, with source-based research when available. A dynamic provider awaiting
231
+ choices, with source-based research when available. A single loaded extension
232
+ that offers on the task-model init approval event owns approval and the write
233
+ instead of the host writer. A dynamic provider awaiting
214
234
  its initial catalog refresh might be absent. `task:<category>` is a subagent
215
235
  model selector, not a command or parent model change. Ordered authenticated
216
236
  candidate plans resolve before launch; ordinary nonpersistent runs can retry
@@ -227,6 +247,14 @@ the independence boundary; project policy may separately require a
227
247
  different provider.
228
248
  _Avoid_: Generic tier, reviewer-family enforcement, per-step routing
229
249
 
250
+ **Ranking basis**:
251
+ What a task-model proposal's ranking rests on, as the proposing model submitted
252
+ it: `registry-only`, or `research` with the http(s) sources consulted in that
253
+ run, how each influenced the ranking, and the remaining uncertainty. It travels
254
+ with one write for review and is never saved; `tasksMeta.method` keeps its kind.
255
+ Code checks its shape, not that a source was read.
256
+ _Avoid_: Verified research, evidence store
257
+
230
258
  **Loop template**:
231
259
  A future reusable orchestration definition beside `models`, describing stages,
232
260
  task categories, and a termination/report contract. Loop templates are not
package/README.md CHANGED
@@ -17,6 +17,7 @@ Delegate investigation, implementation, and review without blocking the parent s
17
17
  - **Conversation handoff** — continue the active Pi conversation in a new worktree with `/worktree` while preserving the parent session.
18
18
  - **Pack-neutral roles** — use project or global definitions and installable role packs; this package ships no default roles or workflows.
19
19
  - **Persistent specialists** — retain one policy-bound Pi session for sequential, turn-based tasks.
20
+ - **Optional sidebar focus** — the companion [Pi Herdr Agents Sidebar](plugins/sidebar/README.md) Herdr plugin hides marked delegated children from Herdr's Agents view while you run Focus. Markers are display-only and follow the pane, so see its [limits](plugins/sidebar/README.md#reusing-a-delegated-pane).
20
21
 
21
22
  ## Requirements
22
23
 
@@ -57,7 +58,7 @@ Restart or `/reload` Pi after installation. Review package source before install
57
58
  - [Requirements](#requirements), [Install](#install), [Safety and uninstall](#safety-and-uninstall)
58
59
  - [Quick start](#quick-start), [Release notes](#release-notes), [How it works](#how-it-works)
59
60
  - [What's Included](#whats-included): tools, commands, the operating skill, role packs
60
- - [Async Subagent Flow](#async-subagent-flow): status, configuration, model routing
61
+ - [Async Subagent Flow](#async-subagent-flow): status, configuration, model routing, [sidebar markers](#sidebar-markers)
61
62
  - [Spawning Subagents](#spawning-subagents), [Persistent specialists](#persistent-specialists), [Interrupting](#interrupting-a-running-subagent), [Cancelling](#cancelling-a-running-subagent)
62
63
  - [The `/worktree` Workflow](#the-worktree-workflow), [Custom Agents](#custom-agents), [Tool Access Control](#tool-access-control)
63
64
  - [Development](#development), [License](#license)
@@ -67,7 +68,8 @@ Restart or `/reload` Pi after installation. Review package source before install
67
68
  - Child agents are real Pi processes running with your user account's permissions, inside Herdr panes. Worktrees isolate Git checkouts, not processes or permissions.
68
69
  - The extension creates Herdr panes, tabs and managed worktrees only when a launch asks for them. It never pushes, merges, opens pull requests, deletes branches or removes worktrees on its own; cleanup is an explicit parent action ([worktree cleanup](#explicit-worktree-cleanup)).
69
70
  - What it writes: `$PI_CODING_AGENT_DIR/herdr-agents/config.json` (only through `/subagents-init` or the writer tool, never on startup); per-launch artifacts under the parent session's `artifacts/<session-id>/` directory beside Pi's session store, which hold the child's full task text and any `systemPrompt` as Markdown files, activity snapshots and worktree manifests; and the child's own Pi session file. A managed worktree that carries its own `.pi/agent` directory receives that child's session inside the checkout. Treat task text as potentially sensitive when you share or inspect those files.
70
- - To uninstall: first list and remove any retained worktrees while the extension is still loaded (`/worktree list`, then `/worktree remove <target>`), because those commands leave with the package. Then run `pi remove npm:pi-herdr-agents`, and delete `$PI_CODING_AGENT_DIR/herdr-agents/` and the `artifacts/` directories above if you no longer want the configuration and launch records.
71
+ - Managed worktree children record their PID, process start time, boot ID, and PID namespace in `<session>.process.json` beside their session. With [`sidebar.enabled`](#sidebar-markers), every other child records the same facts, and a resumed child records them under `artifacts/<session-id>/process-identity/`. The parent then reports one display-only pane token, `piha_delegated_v1`, on each child's pane through the Herdr socket. With the setting off, the extension writes no pane metadata.
72
+ - To uninstall: first list and remove any retained worktrees while the extension is still loaded (`/worktree list`, then `/worktree remove <target>`), because those commands leave with the package. If you installed the optional sidebar plugin, remove it with `herdr plugin uninstall pi-herdr-agents.sidebar` (or `herdr plugin unlink` for a linked copy). Then run `pi remove npm:pi-herdr-agents`, and delete `$PI_CODING_AGENT_DIR/herdr-agents/` and the `artifacts/` directories above if you no longer want the configuration and launch records.
71
73
 
72
74
  ## Quick start
73
75
 
@@ -192,7 +194,7 @@ Subagent tabs, panes, and worktree workspaces are created without stealing keybo
192
194
  | `worktree_list` | Parent-only inspect-only inventory of managed worktrees and cleanup blockers |
193
195
  | `worktree_remove` | Parent-only explicit removal by `target` path, branch, or workspace ID; optional `preserve: true` commits dirty state first |
194
196
  | `subagent_resume` | Resume a previous Pi-backed sub-agent session in a new ordinary pane (async) |
195
- | `subagents_write_task_models` | Parent-only internal tool that validates and atomically writes `models.tasks` preferences, optionally conditional on `expectedConfigRevision` |
197
+ | `subagents_write_task_models` | Parent-only internal tool that validates and atomically writes `models.tasks` preferences, optionally conditional on `expectedConfigRevision` and carrying an unsaved ranking `basis` |
196
198
 
197
199
  | Skill | Description |
198
200
  | ----- | ----------- |
@@ -207,7 +209,7 @@ Subagent tabs, panes, and worktree workspaces are created without stealing keybo
207
209
  | -------------------------- | ------------------------------------ |
208
210
  | `/worktree <name> [task]` | Continue this session in a new managed worktree (`/worktree list` lists them) |
209
211
  | `/subagent <agent> <task>` | Spawn a named agent directly (`/subagent list` lists available agents) |
210
- | `/subagents-init [preferences]` | Draft task-category model preferences from the live authenticated registry, with optional ranking preferences |
212
+ | `/subagents-init [preferences]` | Draft task-category model preferences from the live authenticated registry, with optional ranking preferences; a loaded approval extension can own the write |
211
213
 
212
214
  ### Taxonomy and discovery
213
215
 
@@ -461,7 +463,10 @@ JSON without truncating models and reports its model count and JSON character
461
463
  count (not a token estimate); large catalogs still consume context. This is the
462
464
  current synchronous snapshot: a dynamic provider whose initial catalog refresh
463
465
  has not completed might be absent. Init does not refresh providers or probe the
464
- network for availability.
466
+ network for availability. The brief also includes `configRevision`, the
467
+ revision of the exact config bytes that supplied the saved preferences. Init
468
+ does not start while a turn runs or messages are queued, and an empty registry
469
+ ends with a notice instead of a prompt. Neither case writes anything.
465
470
 
466
471
  The draft considers current saved task, default, and per-agent preferences.
467
472
  Optional command arguments set ranking preferences. Otherwise it favors
@@ -482,8 +487,23 @@ sources, disclose uncertainty and notable exclusions, and avoid duplicate
482
487
  upstream models across routes unless deliberate redundancy is explained. Display
483
488
  names help identify candidates but, like aliases, do not prove upstream
484
489
  equivalence; research is still required. Price or context size alone is not
485
- quality evidence. It reports `registry-only` when
486
- search is unavailable or yields no usable evidence; no live model probes run.
490
+ quality evidence. No live model probes run.
491
+
492
+ The proposal carries a ranking `basis`. It is `{"kind":"registry-only"}` when
493
+ search is unavailable or yields no usable evidence. It is `{"kind":"research"}`
494
+ only with the http(s) `sources` consulted in this run, the `influence` each had
495
+ on the ranking, and the remaining `uncertainty`. Code checks the basis's shape,
496
+ not whether a source was read, so the basis is labeled as submitted and
497
+ unverified.
498
+
499
+ While init runs, it emits `pi-herdr-subagents:task-models:init:approval:v1`.
500
+ With no offer, the prompt directs the model to `subagents_write_task_models`
501
+ with `expectedConfigRevision`. When exactly one loaded extension offers, init
502
+ opens that extension's flow, and the extension's instructions replace the
503
+ writer instruction; the extension then owns approval and the write. Two or more
504
+ offers, or an invalid one, stop init before any extension opens or a prompt is
505
+ sent. Once an offer is recorded, a refusal or failure also stops init: it never
506
+ falls back to the direct writer. See [Task-model init events](#task-model-init-events).
487
507
 
488
508
  The writer validates and atomically replaces `models.tasks` and `tasksMeta`,
489
509
  preserving unrelated settings. Its tool schema accepts partial nonempty
@@ -491,9 +511,15 @@ categories (omitted categories are removed), rejects empty `tasks: {}` input,
491
511
  and rejects exact duplicate refs within a category after trimming;
492
512
  IDs remain case-sensitive. Its result includes normalized saved `tasks`,
493
513
  `tasksMeta`, `configPath`, `missingCategories`, and `configRevision`. Init requests all six categories
494
- and a before/after table based on that saved result, not the unsaved draft. It
495
- must explain missing categories or changed choices; with no available models,
496
- it must report the limitation without writing.
514
+ and a before/after table based on that saved result, not the unsaved draft, and
515
+ must explain missing categories or changed choices.
516
+
517
+ Optional `basis` must have the same kind as `tasksMeta.method`. A research basis
518
+ needs at least one source with an http(s) URL that has a host and a nonblank
519
+ `influence`, plus a nonblank `uncertainty`. The writer validates it before
520
+ writing, returns it in the result, and never saves it; only `tasksMeta.method`
521
+ persists. Calls without `basis`, including research calls, keep the earlier
522
+ contract.
497
523
 
498
524
  Optional `expectedConfigRevision` makes a write conditional on the config the
499
525
  proposal was read from. A revision is `sha256:` followed by 64 lowercase hex
@@ -549,6 +575,128 @@ rewrites the file. Other values remain configuration errors, as do unknown keys
549
575
  under `roles`. Registered role packs are the entire package layer, and global and
550
576
  project definitions keep their precedence over them.
551
577
 
578
+ ### Task-model init events
579
+
580
+ Two versioned `pi.events` channels let another extension take part in
581
+ `/subagents-init` without importing this package. Both carry the invoking
582
+ command's live `ExtensionCommandContext`, so they are for trusted extensions in
583
+ the same process. The context is current only while the event is emitted.
584
+
585
+ The host emits `pi-herdr-subagents:task-models:init:approval:v1` while init
586
+ runs:
587
+
588
+ ```typescript
589
+ type ApprovalRequest = {
590
+ apiVersion: 1;
591
+ brief: TaskModelBrief; // frozen: operatorPreferences, categories, configRevision, current, models (never empty)
592
+ context: ExtensionCommandContext;
593
+ offer(offer: { owner: string; open(): OpenResult }): "recorded" | "closed";
594
+ };
595
+ type OpenResult =
596
+ | {
597
+ kind: "ready";
598
+ destination: { toolName: string; instructions: string };
599
+ cancel(): void; // closes the flow this open created
600
+ }
601
+ | { kind: "blocked"; reason: string };
602
+ ```
603
+
604
+ - Call `offer` synchronously, before the listener's first `await`. After `emit`
605
+ returns, `offer` answers `"closed"` and records nothing.
606
+ - Offer whenever your extension owns task-model approval, even when it is busy,
607
+ and refuse from `open`. `owner` is one printable token without spaces.
608
+ - The host opens nothing until collection ends. Two offers (even with the same
609
+ owner) or an invalid offer stop init. With one offer, the host calls `open()`
610
+ once, and it must return synchronously.
611
+ - `ready` must leave `toolName` active. Its `instructions` replace the host's
612
+ writer instruction in the prompt the host sends.
613
+ - `ready` must include `cancel`. When the host does not hand the prompt to Pi
614
+ (the tool is inactive, or `pi.sendUserMessage` throws), it calls `cancel()`
615
+ once before reporting `not-started`. `cancel` must close only the flow that
616
+ this `open` created, never a later one. A `cancel` that throws is reported
617
+ with the outcome.
618
+ - A throw, a promise, an invalid result (including a missing `cancel`), or
619
+ `blocked` stops init with a notice and no prompt. The host cannot undo what
620
+ `open` changed before failing.
621
+ - Pi accepts the prompt asynchronously. It can still reject it after `start`
622
+ reports `started`, for example when the selected model has no configured
623
+ auth; the host refuses up front only when no model is selected. Pi delivers
624
+ the prompt to `input` handlers (source `"extension"`) before that check and
625
+ emits `before_agent_start` and `agent_start` only after it passes. An
626
+ extension whose flow outlives one prompt should close it when other input or
627
+ a run arrives before those events, so a later request cannot inherit it.
628
+ - Pi's event bus logs and swallows listener exceptions. A listener that throws
629
+ before offering looks the same as no listener, so init uses the direct
630
+ writer. An extension that guards the writer still refuses that write.
631
+
632
+ Another extension's command emits `pi-herdr-subagents:task-models:init:start:v1`
633
+ to start the same init:
634
+
635
+ ```typescript
636
+ type StartRequest = {
637
+ apiVersion: 1;
638
+ context: ExtensionCommandContext;
639
+ preferences: string; // ranking preferences, passed through as typed
640
+ offer(offer: { owner: string; start(): InitOutcome }): "recorded" | "closed";
641
+ };
642
+ type InitOutcome =
643
+ | { kind: "started"; destination: string }
644
+ | { kind: "not-started"; reason: string };
645
+ ```
646
+
647
+ The host offers synchronously as `pi-herdr-agents` and starts nothing until the
648
+ emitter calls `start()`. Call it only when exactly one host offered. `start`
649
+ runs init with the request's context and returns synchronously; the emitter
650
+ shows a `not-started` reason. The host does not listen in subagent sessions
651
+ and unsubscribes at `session_shutdown`. Unsubscribe an approval listener there
652
+ too, as the role-pack bridge does.
653
+
654
+ ### Sidebar markers
655
+
656
+ Set `sidebar.enabled` to `true` to mark delegated children for the optional
657
+ [Pi Herdr Agents Sidebar](plugins/sidebar/README.md) Herdr plugin, whose Focus
658
+ action hides marked children from Herdr's Agents view. The setting defaults to
659
+ `false`.
660
+
661
+ ```json
662
+ {
663
+ "sidebar": {
664
+ "enabled": true
665
+ }
666
+ }
667
+ ```
668
+
669
+ Add the key to your existing `config.json`. The extension rejects a
670
+ `config.json` without its `status` section, so copy `config.json.example`
671
+ first if the file does not exist. Run `/reload` after changing the setting.
672
+ Children launched before the change stay unmarked.
673
+
674
+ Setting `sidebar.enabled` to `false` and running `/reload` retires every
675
+ existing marker and clears the markers of running children. A clear can fail to
676
+ reach Herdr, so a token still expires at most 15 seconds after the last write
677
+ Herdr accepted. Setting it back to `true` marks only later launches; a child
678
+ that was already running when you turned it off is never marked again.
679
+
680
+ With markers on, every fresh, resumed, and persistent child records its own
681
+ process identity, as managed worktree children always do. After the parent
682
+ verifies that identity, it reports the pane token `piha_delegated_v1` with the
683
+ value `live`, a 15-second time-to-live, and an increasing sequence number
684
+ through `HERDR_SOCKET_PATH`. It renews the token on supervision checks while
685
+ that exact process is alive. It clears the token when the run finalizes or is
686
+ suppressed, or when the process exits or can no longer be verified. A
687
+ `/worktree` handoff session is never marked.
688
+
689
+ Marker writes never fail a launch or keep Pi running. Each write has a 2-second
690
+ timeout on an unreferenced socket, failed writes are not retried, and three
691
+ failures in a row stop that child's marker. Process identity reads Linux
692
+ `/proc`, so on other systems, and outside Herdr, nothing is marked. After a
693
+ parent crash, a marker can remain for up to 15 seconds after the last write
694
+ Herdr applied. A marker belongs to the pane, so a different process started by
695
+ hand in a delegated pane stays hidden by Focus while the original process is
696
+ alive and supervised, even if it is suspended. Run All first or use a fresh
697
+ pane. See [what a marker proves](plugins/sidebar/README.md#what-a-marker-proves)
698
+ and [reusing a delegated pane](plugins/sidebar/README.md#reusing-a-delegated-pane).
699
+
552
700
  ### Supervision transport
553
701
 
554
702
  On supported local filesystems, supervision uses file wake-ups plus one shared
@@ -1247,11 +1395,15 @@ provider; fakes are for conformance tests only.
1247
1395
  - `maestro/adapters/pi/` — Pi launch, completion, session I/O, activity files,
1248
1396
  model SDK glue, and registry projection behind `PiHarnessAdapter`;
1249
1397
  `child/subagent-done.ts` implements the child protocol.
1250
- - `maestro/surfaces/herdr/` — `HerdrSurfaceProvider`, Herdr CLI driver, and
1251
- terminal scripts/placement.
1398
+ - `maestro/surfaces/herdr/` — `HerdrSurfaceProvider`, Herdr CLI driver,
1399
+ terminal scripts/placement, and `herdr-socket.ts`, the bounded socket
1400
+ client for pane metadata.
1252
1401
  - `maestro/runtime/` — run ownership, controls/retries, observation,
1253
- delivery-gated cleanup, Pi composition, worktree operations/handoff, and
1254
- task-model init composition.
1402
+ delivery-gated cleanup, Pi composition, worktree operations/handoff,
1403
+ task-model init composition, and sidebar markers (`delegated-marker.ts`).
1404
+ - `plugins/sidebar/` — the optional Herdr plugin. Its `sidebar.mjs` uses Node
1405
+ built-ins only and imports nothing from the extension, so Herdr can install
1406
+ the directory by itself.
1255
1407
  - `maestro/adapters/fake/`, `maestro/surfaces/fake/`, `test/maestro/` —
1256
1408
  conformance fakes, seam tests, and the import dependency-rule test.
1257
1409
 
package/RELEASING.md CHANGED
@@ -10,6 +10,8 @@ The published version must be unique on npm.
10
10
 
11
11
  `3.0.0` is the pack-neutral baseline. It is a major release because it removes the bundled roles, `/plan`, `/skill:orchestrate`, `/iterate`, `/btw`, and `/btw-close`, and makes `roles.bundled` a deprecated no-op. Its hand-written breaking changes and migration notes are in the README [Release notes](README.md#release-notes) section, because `npm version` regenerates `CHANGELOG.md` and would discard hand edits there. Record later breaking-release notes in the same section.
12
12
 
13
+ `3.1.0` is a compatible minor release. It adds the request-scoped task-model init events that let a loaded pack approve `/subagents-init` proposals; with no pack offering, the direct writer is unchanged. It was prepared from feature commit `1b6bacd0b5eecb6f83ffba1d5a5ee57a8b0fa944`. It is not published until the release workflow succeeds.
14
+
13
15
  Do not design a release that creates a GitHub Release without a successful npm publish for a new version. The workflow publishes first, then tags and creates the GitHub Release.
14
16
 
15
17
  ## Prerequisites
@@ -35,7 +37,7 @@ Manual deterministic Herdr integration (required before you push a release commi
35
37
  npm run test:integration
36
38
  ```
37
39
 
38
- Run that suite from inside Herdr. It uses real Pi and Herdr processes with the local deterministic provider, so it needs no provider credentials or network access.
40
+ Run that suite from inside Herdr. It uses real Pi and Herdr processes with the local deterministic provider, so it needs no provider credentials or network access. `test/integration/sidebar.test.ts` also starts two disposable Herdr servers with their own HOME, configuration, sockets, and plugin registry under the system temporary directory, and removes them afterwards. It needs Linux and skips elsewhere.
39
41
 
40
42
  The optional live-provider smoke test is not a release gate:
41
43
 
@@ -43,7 +45,7 @@ The optional live-provider smoke test is not a release gate:
43
45
  PI_TEST_MODEL="openai-codex/gpt-5.6-luna" PI_TEST_TIMEOUT=180000 npm run test:integration:live
44
46
  ```
45
47
 
46
- Do not release from skipped Herdr tests. Confirm the package preview includes `README.md`, `CHANGELOG.md`, `AGENTS.md`, `docs/`, `config.json.example`, and `examples/role-pack/`, contains no `agents/` resources, no `skills/` resources other than the host-owned `skills/pi-herdr-agents/SKILL.md`, and no `pi-extension/subagents/plan-skill.md`, and excludes `pi-extension/subagents/workflow-worker.js`. Confirm it excludes plans, journals, sessions, prototypes, generated evidence, local `config.json` and `openspec/`, and that the worktree integration tests leave no test workspace behind. Durable user configuration is `$PI_CODING_AGENT_DIR/herdr-agents/config.json`; package-root configuration is ignored, so users must move an older file manually or re-run `/subagents-init`.
48
+ Do not release from skipped Herdr tests. Confirm the package preview includes `README.md`, `CHANGELOG.md`, `AGENTS.md`, `docs/`, `config.json.example`, `examples/role-pack/`, and exactly `README.md`, `herdr-plugin.toml`, and `sidebar.mjs` under `plugins/sidebar/`, excludes the active `docs/sidebar-plan.md`, contains no `agents/` resources, no `skills/` resources other than the host-owned `skills/pi-herdr-agents/SKILL.md`, and no `pi-extension/subagents/plan-skill.md`, and excludes `pi-extension/subagents/workflow-worker.js`. Confirm it excludes plans, journals, sessions, prototypes, generated evidence, local `config.json` and `openspec/`, and that the worktree integration tests leave no test workspace behind. Durable user configuration is `$PI_CODING_AGENT_DIR/herdr-agents/config.json`; package-root configuration is ignored, so users must move an older file manually or re-run `/subagents-init`.
47
49
 
48
50
  ## npm authentication
49
51
 
package/docs/README.md CHANGED
@@ -11,6 +11,9 @@ are evidence, not shipped contracts, when a later ADR supersedes them.
11
11
  - [`../CONTEXT.md`](../CONTEXT.md) — orchestration glossary.
12
12
  - [`worktree-subagents.md`](worktree-subagents.md) — worktree operation,
13
13
  review, recovery, and cleanup.
14
+ - [`../plugins/sidebar/README.md`](../plugins/sidebar/README.md) — the
15
+ optional Herdr sidebar plugin: installation, Focus and All, the delegated
16
+ marker contract, and its staleness limits.
14
17
 
15
18
  The package is a pack-neutral execution host: it launches asynchronous Pi
16
19
  children in Herdr and supports managed worktrees for writing tasks, but ships no
@@ -66,6 +69,12 @@ real implementations; in-memory fakes are conformance fixtures only.
66
69
  | [`0014`](adr/0014-operator-cancel-terminal-intent.md) | Accepted | Operator cancel records terminal intent before owned termination: no fallback, one confirmed cancelled result, unconfirmed runs stay live. |
67
70
  | [`0015`](adr/0015-report-opened-primary-workspace.md) | Accepted | Report, and never automatically close, a primary workspace that worktree creation opened. |
68
71
 
72
+ ## Active design
73
+
74
+ - `sidebar-plan.md` — local plan and audit record for the sidebar plugin and
75
+ its marker bridge. It is excluded from the npm package, so it has no link
76
+ here. It is not a shipped contract; the plugin README is.
77
+
69
78
  ## Historical material
70
79
 
71
80
  - [`orchestrated-review-workflow-plan.md`](orchestrated-review-workflow-plan.md)
@@ -194,6 +194,7 @@ The extension never pushes, creates a PR, merges, cherry-picks, or changes the p
194
194
  - **Ordinary launch failure:** if a fresh launch or `subagent_resume` fails after the extension creates its pane, the extension closes that pane and preserves the original launch error. It does not close a caller-supplied surface.
195
195
  - **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.
196
196
  - **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.
197
+ - **Sidebar marker:** with `sidebar.enabled`, the worktree child's root pane carries the delegated marker only while the child's own Pi process runs. The parent clears it when the run finalizes, so the retained shell, and any agent you later start in it, is not marked. After a parent crash the marker expires within 15 seconds of the last write Herdr applied. See the [sidebar plugin README](../plugins/sidebar/README.md#what-a-marker-proves).
197
198
  - **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.
198
199
  - **Operator cancel:** `subagent_cancel` never closes the retained root pane or workspace. At launch, the child's protocol extension records its own process identity (PID, `/proc` start time, boot ID, and PID-namespace link) in `<session>.process.json`, and the parent accepts it only while that process is alive in the parent's PID namespace as the Herdr pane shell or a descendant of it. Cancel sends SIGTERM only to that identity, re-verified (PID and start time) immediately before the signal, then confirms that the identity no longer exists, or that the pane is gone while it is not known alive. Command-line text and Herdr's foreground list are never evidence: Pi rewrites its process title, and Herdr lists foreground processes only. A live identity (such as a suspended Pi with SIGTERM pending), or one whose SIGTERM fails while it is still alive, leaves termination `unconfirmed` even if the pane is gone. An identity that was not captured (including on non-Linux hosts), is unreadable, belongs to another boot or PID namespace, or whose PID now has another start time is never signalled and also leaves it `unconfirmed` unless pane absence confirms termination. The PID could still be reused between the final check and the signal, because Node has no pidfd signal. Every Herdr query is bounded by the cancel deadline, and an answer that arrives after the deadline, even before the timer runs, never changes the reported outcome. The run stays live, and retrying `subagent_cancel` re-checks; if the launch capture expired, the retry captures the identity again within its own deadline. On confirmation, the manifest is marked `cancelled` and the normal Git handoff is delivered once. A parent shutdown while termination is unconfirmed records the plain shutdown state, never `cancelled`. No Git cleanup, branch change, or removal happens.
199
200
  - **`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.
@@ -54,6 +54,8 @@ export interface FreshPiLaunchRequest {
54
54
  fork?: boolean;
55
55
  handoff?: { leafId: string };
56
56
  surface?: string;
57
+ /** Ordinary children record their process identity too (sidebar markers). */
58
+ recordProcessIdentity?: boolean;
57
59
  parent: {
58
60
  cwd: string;
59
61
  invocationCwd?: string;
@@ -86,6 +88,8 @@ export interface ResumePiLaunchRequest {
86
88
  name: string;
87
89
  sessionFile: string;
88
90
  message?: string;
91
+ /** Record the resumed process's identity (sidebar markers). */
92
+ recordProcessIdentity?: boolean;
89
93
  parent: {
90
94
  sessionId: string;
91
95
  sessionDir: string;
@@ -125,9 +129,15 @@ export interface PiRunningChild {
125
129
  stopState?: "requested" | "pending" | "failed";
126
130
  stopFailure?: string;
127
131
  crashNotified?: boolean;
128
- /** Managed worktree children: where the child records its own process identity. */
132
+ /**
133
+ * Where the child records its own process identity: every managed worktree
134
+ * child, and other children when the launch asks for it.
135
+ */
129
136
  processIdentityFile?: string;
130
- /** Verified at launch readiness; a worktree cancel's only signal and exit evidence. */
137
+ /**
138
+ * Verified at launch readiness. A worktree cancel's only signal and exit
139
+ * evidence; sidebar markers renew only while it is alive.
140
+ */
131
141
  processIdentity?: PiProcessIdentity;
132
142
  /** Settles when launch-readiness capture verifies or gives up; never rejects. */
133
143
  processIdentityCapture?: Promise<PiProcessIdentity | undefined>;
@@ -718,7 +728,8 @@ function prepareChildSession(
718
728
  activityFile,
719
729
  // A handoff Pi loads no child protocol extension, so it records nothing.
720
730
  processIdentityFile:
721
- surface.worktree && !resolved.request.handoff
731
+ (surface.worktree || resolved.request.recordProcessIdentity) &&
732
+ !resolved.request.handoff
722
733
  ? getSubagentProcessIdentityFile(sessionFile)
723
734
  : undefined,
724
735
  };
@@ -1034,6 +1045,11 @@ async function launchResumedPiSubagent(
1034
1045
  await operations.waitForShellReady(surface);
1035
1046
  const activityFile = getSubagentActivityFile(artifactDir, id);
1036
1047
  mkdirSync(dirname(activityFile), { recursive: true });
1048
+ // The session file is reused, and its sidecar keeps the first identity
1049
+ // ever published for it, so each resumed run records under its own ID.
1050
+ const processIdentityFile = request.recordProcessIdentity
1051
+ ? join(artifactDir, "process-identity", `${id}.json`)
1052
+ : undefined;
1037
1053
 
1038
1054
  let messageFile: string | undefined;
1039
1055
  if (request.message) {
@@ -1058,6 +1074,9 @@ async function launchResumedPiSubagent(
1058
1074
  `PI_SUBAGENT_ID=${shellQuote(id)}`,
1059
1075
  `PI_SUBAGENT_ACTIVITY_FILE=${shellQuote(activityFile)}`,
1060
1076
  `PI_SUBAGENT_AUTO_EXIT=${autoExit ? "1" : "0"}`,
1077
+ ...(processIdentityFile
1078
+ ? [`PI_SUBAGENT_PROCESS_FILE=${shellQuote(processIdentityFile)}`]
1079
+ : []),
1061
1080
  ];
1062
1081
  const toolAllowlist = buildSubagentToolAllowlist(
1063
1082
  policy.tools?.join(","),
@@ -1093,7 +1112,7 @@ async function launchResumedPiSubagent(
1093
1112
  ].join("\n"),
1094
1113
  },
1095
1114
  );
1096
- return {
1115
+ const running: PiRunningChild = {
1097
1116
  id,
1098
1117
  name: request.name,
1099
1118
  task: request.message ?? "resumed session",
@@ -1105,7 +1124,10 @@ async function launchResumedPiSubagent(
1105
1124
  interactive,
1106
1125
  runtimePlan: undefined,
1107
1126
  lifecycle: createLifecycle(startTime),
1127
+ processIdentityFile,
1108
1128
  };
1129
+ startProcessIdentityCapture(running, operations);
1130
+ return running;
1109
1131
  } catch (error) {
1110
1132
  try {
1111
1133
  await operations.closePane(surface);
@@ -97,6 +97,8 @@ export interface PiHarnessAdapterOptions {
97
97
  worktreeOperations?: WorktreeOperations;
98
98
  /** Kernel process facts for a retained worktree child's identity; tests inject it. */
99
99
  processProbe?: ProcessIdentityProbe;
100
+ /** Ordinary and resumed children also record their identity (sidebar markers). */
101
+ recordProcessIdentity?: boolean;
100
102
  /** Bound for confirming a retained worktree child's exit. Default: 5000ms. */
101
103
  killTimeoutMs?: number;
102
104
  /** Explicit local evidence/lifecycle bridge. The host still owns delivery and deduplication. */
@@ -209,6 +211,7 @@ export class PiHarnessAdapter implements HarnessAdapter {
209
211
  : undefined
210
212
  : undefined,
211
213
  surface: opts.surface?.id,
214
+ recordProcessIdentity: this.options.recordProcessIdentity,
212
215
  parent: this.options.parent,
213
216
  runtimePlan:
214
217
  resolved?.runtimePlan ??
@@ -294,6 +297,7 @@ export class PiHarnessAdapter implements HarnessAdapter {
294
297
  name: opts.name,
295
298
  sessionFile: opts.sessionId,
296
299
  message: opts.message,
300
+ recordProcessIdentity: this.options.recordProcessIdentity,
297
301
  parent: this.options.parent,
298
302
  behavior: { autoExit: opts.autoExit },
299
303
  },