@gotgenes/pi-subagents 10.2.0 → 11.0.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/CHANGELOG.md CHANGED
@@ -5,6 +5,42 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [11.0.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v10.2.1...pi-subagents-v11.0.0) (2026-05-28)
9
+
10
+
11
+ ### ⚠ BREAKING CHANGES
12
+
13
+ * AgentSpawnConfig.onSessionCreated is replaced by AgentSpawnConfig.observer (AgentLifecycleObserver). Callers that used onSessionCreated must use observer.onSessionCreated instead.
14
+
15
+ ### Features
16
+
17
+ * add Agent.run() encapsulating full execution lifecycle ([#229](https://github.com/gotgenes/pi-packages/issues/229)) ([780cb72](https://github.com/gotgenes/pi-packages/commit/780cb72ea7a6981ee283a9de791d5fb65cabaa28))
18
+ * add AgentLifecycleObserver interface ([#229](https://github.com/gotgenes/pi-packages/issues/229)) ([c0f08a4](https://github.com/gotgenes/pi-packages/commit/c0f08a40ce36afa3a7876345709db56e192a893b))
19
+ * AgentManager.spawn() creates complete Agent, deletes startAgent ([#229](https://github.com/gotgenes/pi-packages/issues/229)) ([4d83c6d](https://github.com/gotgenes/pi-packages/commit/4d83c6d583df905054066ed841a67795753928d4))
20
+ * expand AgentInit with run-config, deps, and self-created AbortController ([#229](https://github.com/gotgenes/pi-packages/issues/229)) ([e522f23](https://github.com/gotgenes/pi-packages/commit/e522f23232e4b447ffa39e36a19cf70c7e5cbaae))
21
+
22
+
23
+ ### Documentation
24
+
25
+ * mark Phase 15 Step 4 complete, update architecture ([#229](https://github.com/gotgenes/pi-packages/issues/229)) ([29b0da8](https://github.com/gotgenes/pi-packages/commit/29b0da8435aaa5be51fc073612fc09d9a22004bc))
26
+ * plan Agent born complete — Agent.run() absorbs startAgent ([#229](https://github.com/gotgenes/pi-packages/issues/229)) ([c1588b5](https://github.com/gotgenes/pi-packages/commit/c1588b58ae697d22db1bf30b8997e5502626c33b))
27
+ * **retro:** add planning stage notes for issue [#229](https://github.com/gotgenes/pi-packages/issues/229) ([21243d5](https://github.com/gotgenes/pi-packages/commit/21243d564691daab5dcddff23809a82db56c0660))
28
+ * **retro:** add retro notes for issue [#231](https://github.com/gotgenes/pi-packages/issues/231) ([249cce0](https://github.com/gotgenes/pi-packages/commit/249cce0e1c7642d8c85da67e8f3c92f210735e7b))
29
+ * **retro:** add TDD stage notes for issue [#229](https://github.com/gotgenes/pi-packages/issues/229) ([047cd9e](https://github.com/gotgenes/pi-packages/commit/047cd9e2816f6a25d93bbba33fc93b228989f272))
30
+
31
+ ## [10.2.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v10.2.0...pi-subagents-v10.2.1) (2026-05-27)
32
+
33
+
34
+ ### Documentation
35
+
36
+ * **pi-subagents:** renumber Phase 15 steps to match execution order ([598bb65](https://github.com/gotgenes/pi-packages/commit/598bb653ac8b63756e8c00dfcf19d3167e2dbc37))
37
+ * **pi-subagents:** revise Phase 15 roadmap for Agent-born-complete vision ([e04583e](https://github.com/gotgenes/pi-packages/commit/e04583e75bfc1314674a6f3181762a26733fb830))
38
+ * plan push exec/registry relay deps to runner construction ([#231](https://github.com/gotgenes/pi-packages/issues/231)) ([646b4d5](https://github.com/gotgenes/pi-packages/commit/646b4d5085e0f7d36a397b43b3b46e0537c3141f))
39
+ * **retro:** add planning stage notes for issue [#231](https://github.com/gotgenes/pi-packages/issues/231) ([dc0daee](https://github.com/gotgenes/pi-packages/commit/dc0daee634c17cf2a40336e27f551bfa2ce0e249))
40
+ * **retro:** add retro notes for issue [#228](https://github.com/gotgenes/pi-packages/issues/228) ([d5b563b](https://github.com/gotgenes/pi-packages/commit/d5b563b6484cbd6a89cd7e9e87ebd431aed128fc))
41
+ * **retro:** add TDD stage notes for issue [#231](https://github.com/gotgenes/pi-packages/issues/231) ([28094ae](https://github.com/gotgenes/pi-packages/commit/28094ae812141ea1c93a22be50ed29d31b7a979a))
42
+ * update architecture for runner self-contained ([#231](https://github.com/gotgenes/pi-packages/issues/231)) ([80dd339](https://github.com/gotgenes/pi-packages/commit/80dd339d7dee9b312b52af2b74756c5748619a49))
43
+
8
44
  ## [10.2.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v10.1.0...pi-subagents-v10.2.0) (2026-05-27)
9
45
 
10
46
 
@@ -266,9 +266,9 @@ src/
266
266
  │ └── session-dir.ts session directory derivation
267
267
 
268
268
  ├── lifecycle/ agent execution and state tracking
269
- │ ├── agent-manager.ts spawn, queue, abort, resume, concurrency
269
+ │ ├── agent-manager.ts collection manager + concurrency controller
270
270
  │ ├── agent-runner.ts session creation, turn loop, tool filtering
271
- │ ├── agent.ts status state machine, per-agent behavior (abort, steer, worktree)
271
+ │ ├── agent.ts owns full execution lifecycle (run, abort, steer, worktree)
272
272
  │ ├── parent-snapshot.ts immutable spawn-time parent state
273
273
  │ ├── execution-state.ts session/output phase state
274
274
  │ ├── permission-bridge.ts optional bridge to pi-permission-system registry
@@ -599,23 +599,21 @@ export interface ParentSessionInfo {
599
599
 
600
600
  `AgentSpawnConfig` now carries `parentSession?: ParentSessionInfo` instead of three flat optional fields.
601
601
 
602
- #### RunOptions (12 fields → extract RunContext) — done ([#169][169])
602
+ #### RunOptions (12 fields → extract RunContext) — done ([#169][169]), updated by [#231]
603
603
 
604
- The `RunOptions` bag mixes execution parameters with context information.
605
- `RunContext` was extracted and nested as `RunOptions.context`:
604
+ `RunContext` was extracted and nested as `RunOptions.context` in #169.
605
+ Issue #231 moved the two static dependencies (`exec`, `registry`) to `RunnerDeps` on `ConcreteAgentRunner`, leaving `RunContext` with only per-call fields:
606
606
 
607
607
  ```typescript
608
- /** Parent execution context — where/who is running. */
608
+ /** Per-call execution context — fields that vary per spawn. */
609
609
  export interface RunContext {
610
- exec: ShellExec;
611
- registry: AgentConfigLookup;
612
610
  cwd?: string;
613
611
  parentSession?: ParentSessionInfo;
614
612
  }
615
613
  ```
616
614
 
617
615
  The remaining `RunOptions` fields (`model`, `maxTurns`, `signal`, `isolated`, `thinkingLevel`, `defaultMaxTurns`, `graceTurns`, `onSessionCreated`) are genuine execution parameters.
618
- `RunOptions` now has 9 fields: 1 nested `context: RunContext` plus 8 flat execution fields.
616
+ `RunOptions` now has 9 fields: 1 nested `context: RunContext` (2 per-call fields) plus 8 flat execution fields.
619
617
 
620
618
  #### SessionConfig (11 fields → extract ToolFilterConfig) — done ([#168][168])
621
619
 
@@ -686,25 +684,66 @@ See [phase-14-strip-policy.md](history/phase-14-strip-policy.md) for details.
686
684
 
687
685
  ## Improvement roadmap (Phase 15 — domain model evolution)
688
686
 
689
- Phase 15 addresses the anemic domain model in the lifecycle layer.
690
- `AgentRecord` is a data bag — identity, status transitions, and stats — but no behavior.
691
- `AgentManager` reaches into records 37 times, doing work that belongs on the agent.
692
- Per-agent state (pending steers, abort logic, run lifecycle) was scattered across the manager, `RunHandle`, and a manager-level Map.
693
- `RunHandle` has been dissolved into `Agent` methods see Step 2.
687
+ Phase 15 evolves `Agent` from a passive state machine into an object that **owns its entire execution lifecycle**.
688
+
689
+ Steps 1–2 (complete) moved per-agent behavior from `AgentManager` onto `Agent`: abort, steer buffering, worktree setup, and run lifecycle methods (`completeRun`, `failRun`).
690
+ However, Agent still cannot *run itself*.
691
+ `AgentManager.startAgent()` orchestrates the entire execution: calling the runner, handling session creation, wiring observers, and cleaning up worktrees.
692
+ The manager reaches into Agent 10 times across `spawn()` + `startAgent()` — writing to `notification`, `execution`, and `promise` after construction, passing its own `worktrees` and `runner` as method arguments, and threading `onSessionCreated` callbacks through three layers.
693
+
694
+ The remaining steps address this by making **Agent born complete**: constructed with all dependencies and configuration, owning its entire execution lifecycle.
695
+
696
+ ### Architecture target
697
+
698
+ Agent receives three concerns at construction:
699
+
700
+ | Concern | Fields | Lifetime |
701
+ | ----------- | ----------------------------------------------------------------------------- | ------------------------- |
702
+ | Identity | id, type, description, invocation | Immutable |
703
+ | Run config | snapshot, prompt, model, isolation, maxTurns, thinking, signal, parentSession | Immutable per-run |
704
+ | Shared deps | runner, worktrees | Shared service references |
705
+
706
+ `Agent.run()` encapsulates the full execution lifecycle:
707
+
708
+ 1. Set up worktree internally (knows its own isolation mode, has worktrees).
709
+ 2. Call `this.runner.run()` (has the runner).
710
+ 3. Handle session creation internally: set `execution`, flush pending steers, attach record-observer.
711
+ 4. Notify lifecycle observer (started, session created, completed, compacted).
712
+ 5. Clean up worktree on completion or error.
713
+ 6. Transition status.
714
+
715
+ `AgentManager` becomes a collection manager + concurrency controller:
716
+
717
+ - Creates complete Agent objects, stores them in the map.
718
+ - Decides when to run (immediate or queue) and calls `agent.run()`.
719
+ - Provides high-level actions: abort, list, cleanup.
720
+ - Does *not* own the runner, worktrees, or any run-orchestration logic.
721
+
722
+ The queue stores agent IDs, not `SpawnArgs`.
723
+ When capacity opens, the manager looks up the agent and calls `agent.run()` — the agent already has everything.
724
+
725
+ The `onSessionCreated` callback that currently threads through `AgentSpawnConfig` → `startAgent` → `RunOptions` → runner disappears.
726
+ Agent handles session creation internally during `run()` and notifies external observers via the lifecycle observer pattern.
727
+
728
+ The synchronous-throw contract for worktree failure (introduced in Step 2's hoist) is replaced by a uniform async error surface.
729
+ Worktree failures inside `agent.run()` propagate through the promise.
730
+ For background agents, errors surface via `get_subagent_result` and appear in `/agents`.
731
+ For foreground agents, `spawnAndWait` awaits the promise naturally.
694
732
 
695
733
  The scheduling concern (queue, concurrency counter, drain) is tangled into `AgentManager` alongside collection management and run orchestration.
696
734
  `notifyConcurrencyChanged()` is a scheduling method exposed as a public API so settings can poke the queue — a cross-concern leak.
697
735
 
698
736
  ### Findings summary
699
737
 
700
- | Finding | Category | Impact | Risk | Priority |
701
- | ----------------------------------------------------------------- | ------------ | ------ | ---- | -------- |
702
- | `AgentRecord` is anemic — no behavior, manager reaches in 37× | B: Oversized | 5 | 3 | 15 |
703
- | Scheduling tangled into `AgentManager` (3 fields, 3 methods) | A: Coupling | 4 | 2 | 12 |
704
- | ~~`startAgent` uses `.then()`/`.catch()` instead of async/await~~ | C: Callbacks | 3 | 2 | |
705
- | `onSessionCreated` callback flows through 3 layers | C: Callbacks | 3 | 2 | 10 |
706
- | `resume()` duplicates observer subscribe/unsubscribe pattern | A: Redundant | 2 | 1 | 8 |
707
- | `exec`/`registry` relay-only deps on `AgentManager` | C: Coupling | 2 | 1 | 6 |
738
+ | Finding | Category | Impact | Risk | Priority |
739
+ | ------------------------------------------------------------------ | ------------ | ------ | ---- | -------- |
740
+ | ~~`AgentRecord` is anemic — no behavior, manager reaches in 37×~~ | B: Oversized | 5 | 3 | |
741
+ | Agent cannot run itself manager orchestrates 10 external touches | C: Coupling | 5 | 3 | 15 |
742
+ | Scheduling tangled into `AgentManager` (3 fields, 3 methods) | A: Coupling | 4 | 2 | 12 |
743
+ | ~~`startAgent` uses `.then()`/`.catch()` instead of async/await~~ | C: Callbacks | 3 | 2 | |
744
+ | ~~`onSessionCreated` callback flows through 3 layers~~ | C: Callbacks | 3 | 2 | subsumed |
745
+ | `resume()` duplicates observer subscribe/unsubscribe pattern | A: Redundant | 2 | 1 | 8 |
746
+ | `exec`/`registry` relay-only deps on `AgentManager` | C: Coupling | 2 | 1 | 6 |
708
747
 
709
748
  ### Step 1: Evolve AgentRecord into Agent with behavior — [#227] ✅ Complete
710
749
 
@@ -731,43 +770,55 @@ Worktree setup was hoisted to callers (`spawn`, `drainQueue`) to preserve the sy
731
770
  - Smell: C (raw promise callbacks)
732
771
  - Outcome: zero `.then()`/`.catch()` in `agent-manager.ts`; `RunHandle` deleted; Agent owns run lifecycle
733
772
 
734
- ### Step 3: Replace onSessionCreated callback with observer method — [#229]
773
+ ### Step 3: Push exec/registry relay deps to runner construction — [#231]
735
774
 
736
- Add `onSessionCreated(agent, session)` to `AgentManagerObserver`.
737
- Remove the `onSessionCreated` callback from `AgentSpawnConfig`.
738
- Tool-layer code subscribes via the observer pattern instead of passing callbacks through the spawn config.
775
+ `exec` and `registry` moved from `AgentManager` to `ConcreteAgentRunner` via a new `RunnerDeps` interface.
776
+ `RunContext` shrunk from 4 to 2 per-call fields (`cwd`, `parentSession`).
777
+ `AgentManagerOptions` shrunk from 7 to 5 fields.
739
778
 
740
- - Target: `src/lifecycle/agent-manager.ts`, `src/tools/background-spawner.ts`, `src/tools/foreground-runner.ts`
741
- - Smell: C (callback flowing through 3 layers)
742
- - Outcome: `AgentSpawnConfig` loses one callback field; session notification uses the observer pattern
779
+ - Target: `src/lifecycle/agent-manager.ts`, `src/lifecycle/agent-runner.ts`, `src/index.ts`
780
+ - Smell: C (relay-only dependencies)
781
+ - Outcome: `AgentManager` loses 2 fields; `AgentManagerOptions` shrinks from 7 to 5 fields; runner is self-contained
782
+
783
+ ### Step 4: Agent born complete — Agent.run() absorbs startAgent — [#229] ✅
784
+
785
+ Agent receives `runner`, `worktrees`, and a lifecycle observer at construction.
786
+ Agent creates its own `AbortController` and `NotificationState` from `parentSession.toolCallId` — no external writes.
787
+ `Agent.run()` encapsulates the entire execution lifecycle: worktree setup, runner invocation, session-creation handling, observer wiring, worktree cleanup, and status transitions.
788
+ `startAgent` is deleted from `AgentManager`.
789
+ The `onSessionCreated` callback is removed from `AgentSpawnConfig` — replaced by `AgentLifecycleObserver` passed at construction.
790
+ `SpawnArgs` is deleted — Agent has its config from construction.
791
+ The queue is simplified from `{ id, args }[]` to `string[]` (agent IDs only).
792
+
793
+ `AgentManager.spawn()` becomes: create complete Agent, put in map, call `agent.run()` or queue the agent ID.
794
+
795
+ - Depends on: #228, #231
796
+ - Target: `src/lifecycle/agent.ts`, `src/lifecycle/agent-manager.ts`, `src/tools/background-spawner.ts`, `src/tools/foreground-runner.ts`
797
+ - Smell: C (manager orchestrates 10 external touches on Agent) + C (callback flowing through 3 layers)
798
+ - Outcome: Agent owns its entire execution lifecycle; `startAgent`, `SpawnArgs`, `onSessionCreated` callback deleted; zero post-construction writes from `AgentManager`
743
799
 
744
- ### Step 4: Extract ConcurrencyQueue from AgentManager — [#230]
800
+ ### Step 5: Extract ConcurrencyQueue from AgentManager — [#230]
745
801
 
746
802
  Extract `queue[]`, `runningBackground`, `_getMaxConcurrent`, `drainQueue()`, `finalizeBackgroundRun()` into a `ConcurrencyQueue` class.
803
+ The queue stores agent IDs — not `SpawnArgs`.
804
+ Drain calls `agent.run()` directly — no worktree setup, no args threading.
747
805
  `SettingsManager` talks to the queue directly — `notifyConcurrencyChanged()` is eliminated from `AgentManager`.
748
806
 
807
+ - Depends on: #229
749
808
  - Target: new `src/lifecycle/concurrency-queue.ts`, `src/lifecycle/agent-manager.ts`, `src/index.ts`
750
809
  - Smell: A (tangled concerns) + C (cross-concern leak via `notifyConcurrencyChanged`)
751
- - Outcome: `AgentManager` loses 3 fields, 3 methods (~40 lines); scheduling is independently testable
752
-
753
- ### Step 5: Push exec/registry relay deps to runner construction — [#231]
754
-
755
- `AgentManager` receives `exec` and `registry` in its constructor but only relays them to `runner.run()` via `context`.
756
- Move them to `ConcreteAgentRunner` construction.
757
-
758
- - Target: `src/lifecycle/agent-manager.ts`, `src/lifecycle/agent-runner.ts`, `src/index.ts`
759
- - Smell: C (relay-only dependencies)
760
- - Outcome: `AgentManager` loses 2 fields; `AgentManagerOptions` shrinks from 7 to 5 fields
810
+ - Outcome: `AgentManager` loses 3 fields, 3 methods (~40 lines); scheduling is independently testable; queue interface is trivial (agent has everything)
761
811
 
762
- ### Step 6: Unify resume() with Agent run lifecycle methods — [#232]
812
+ ### Step 6: Agent.resume() with internal observer lifecycle — [#232]
763
813
 
764
- After #228 dissolved `RunHandle` into Agent methods (`completeRun`, `failRun`, `releaseListeners`), `resume()` on `AgentManager` becomes a short delegation to `agent.resume(runner, prompt, signal)`.
765
- The agent manages its own observer subscription lifecycle using the same methods that `startAgent` uses.
814
+ Agent has the runner from construction.
815
+ `Agent.resume(prompt, signal)` manages its own observer subscription lifecycle using the same internal wiring as `run()`.
816
+ `AgentManager.resume()` becomes a one-liner delegation to `agent.resume(prompt, signal)` — no manual `subscribeRecordObserver` / try-finally.
766
817
 
767
- - Depends on: #227, #228
768
- - Target: `src/lifecycle/agent-manager.ts`
818
+ - Depends on: #229
819
+ - Target: `src/lifecycle/agent.ts`, `src/lifecycle/agent-manager.ts`
769
820
  - Smell: A (duplicated observer subscribe/unsubscribe pattern)
770
- - Outcome: no manual `subscribeRecordObserver` / try-finally in the manager
821
+ - Outcome: `AgentManager.resume()` is a 4-line delegation; observer lifecycle is Agent-internal
771
822
 
772
823
  ### Step dependency diagram
773
824
 
@@ -775,28 +826,32 @@ The agent manages its own observer subscription lifecycle using the same methods
775
826
  flowchart LR
776
827
  S1["Step 1<br/>Agent with behavior"]
777
828
  S2["Step 2<br/>async startAgent"]
778
- S3["Step 3<br/>onSessionCreated observer"]
779
- S4["Step 4<br/>ConcurrencyQueue"]
780
- S5["Step 5<br/>relay deps"]
781
- S6["Step 6<br/>resume unification"]
829
+ S3["Step 3<br/>runner self-contained"]
830
+ S4["Step 4<br/>Agent.run()"]
831
+ S5["Step 5<br/>ConcurrencyQueue"]
832
+ S6["Step 6<br/>Agent.resume()"]
782
833
 
783
834
  S1 --> S2
784
- S1 --> S6
785
- S2 --> S6
786
- S3 ~~~ S4
787
- S4 ~~~ S5
835
+ S2 --> S4
836
+ S3 --> S4
837
+ S4 --> S5
838
+ S4 --> S6
788
839
  ```
789
840
 
790
841
  ### Tracks
791
842
 
792
- 1. **Track A — Domain model** (Steps 1, 2, 6): Agent with behavior, async runs, resume unification.
793
- Sequential each depends on the previous.
794
- 2. **Track B — Decoupling** (Steps 3, 4, 5): independent, can proceed in parallel with Track A.
843
+ 1. **Track A — Foundation** (Step 3): Runner becomes self-contained.
844
+ No dependencies on other Phase 15 steps; can start immediately.
845
+ 2. **Track B — Agent lifecycle** (Steps 4, 6): Agent born complete, owns run + resume.
846
+ Step 4 depends on Track A + Step 2.
847
+ Step 6 depends on Step 4.
848
+ 3. **Track C — Scheduling** (Step 5): ConcurrencyQueue extraction.
849
+ Depends on Step 4 (queue drains via `agent.run()`).
795
850
 
796
851
  ## Improvement roadmap (Phase 16 — invert dependencies)
797
852
 
798
853
  Phase 16 completes the architectural inversion by removing the outbound permission bridge and the `extensions: false` / `isolated` concepts.
799
- It depends on Phase 15's observer pattern (#229) as the replacement mechanism.
854
+ It depends on Phase 15's lifecycle observer (#229) as the replacement mechanism.
800
855
 
801
856
  Phase 16 is scoped but not yet broken into steps.
802
857
  Key changes:
@@ -856,7 +911,7 @@ Detailed records are preserved in per-phase history files:
856
911
  | Phase 12 | #205, #206, #207, #208 | renderWidgetLines, showAgentDetail, widget update, shared test fixtures |
857
912
  | Phase 13 | #214, #215, #216, #217, #218, #219 | Closure-to-class, buildParentContext, startAgent decomp, overwrite guard, settings SDK, test duplication |
858
913
  | Phase 14 | #237, #238, #239, #242 | Remove disallowed_tools, remove extensions filtering, collapse filterActiveTools, rename Agent to subagent |
859
- | Phase 15 | #227, #228, #229, #230, #231, #232 | Agent domain model, async startAgent, onSessionCreated observer, ConcurrencyQueue, relay deps, resume unification |
914
+ | Phase 15 | #227, #228, #231, #229, #230, #232 | Agent domain model, async startAgent, runner self-contained, Agent.run(), ConcurrencyQueue, Agent.resume() |
860
915
 
861
916
  The remaining open issue is #22 (parent-session resolution), a cross-extension track that does not gate the structural work.
862
917