@gotgenes/pi-subagents 10.0.1 → 10.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/CHANGELOG.md CHANGED
@@ -5,6 +5,47 @@ 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
+ ## [10.2.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v10.1.0...pi-subagents-v10.2.0) (2026-05-27)
9
+
10
+
11
+ ### Features
12
+
13
+ * **pi-subagents:** add run lifecycle methods to Agent ([2a378f1](https://github.com/gotgenes/pi-packages/commit/2a378f1c82e977bdfee25931ab449757e364d589))
14
+
15
+
16
+ ### Documentation
17
+
18
+ * **pi-subagents:** update architecture for async startAgent ([941eb10](https://github.com/gotgenes/pi-packages/commit/941eb109e71e4c51d5bb37a2a46ffc12f618d949))
19
+ * plan async startAgent and RunHandle dissolution ([#228](https://github.com/gotgenes/pi-packages/issues/228)) ([647adf8](https://github.com/gotgenes/pi-packages/commit/647adf853fec63ea53afd63bc8204c89a6194bbe))
20
+ * **retro:** add planning stage notes for issue [#228](https://github.com/gotgenes/pi-packages/issues/228) ([8dd9f8a](https://github.com/gotgenes/pi-packages/commit/8dd9f8ab7082c08e424b1b4a9557253af2ce584b))
21
+ * **retro:** add retro notes for issue [#227](https://github.com/gotgenes/pi-packages/issues/227) ([78a4d64](https://github.com/gotgenes/pi-packages/commit/78a4d645f524465c64bf0b6ba1bcca37858e8721))
22
+ * **retro:** add TDD stage notes for issue [#228](https://github.com/gotgenes/pi-packages/issues/228) ([ab497c5](https://github.com/gotgenes/pi-packages/commit/ab497c57723666d0635a0a08f9eecc06576da549))
23
+
24
+ ## [10.1.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v10.0.1...pi-subagents-v10.1.0) (2026-05-27)
25
+
26
+
27
+ ### Features
28
+
29
+ * **pi-subagents:** add abort() to AgentRecord ([bbc9dc7](https://github.com/gotgenes/pi-packages/commit/bbc9dc779d2ff845680f6de34d0ef33c10cdf124))
30
+ * **pi-subagents:** add setupWorktree() to AgentRecord ([1786fdb](https://github.com/gotgenes/pi-packages/commit/1786fdb939d3e858d6453b71a43b9fb3c3346a88))
31
+ * **pi-subagents:** add steer buffering to AgentRecord ([a22d8c7](https://github.com/gotgenes/pi-packages/commit/a22d8c77e8074e1fe9f396305267380d9f3558b3))
32
+
33
+
34
+ ### Bug Fixes
35
+
36
+ * **pi-subagents:** remove unused AgentInit/AgentStatus re-exports from types.ts ([6789cb7](https://github.com/gotgenes/pi-packages/commit/6789cb72e26eefd54da4ff6ee5dd847c4fa78385))
37
+
38
+
39
+ ### Documentation
40
+
41
+ * **pi-subagents:** archive Phase 14, advance to Phase 15 ([a8147fb](https://github.com/gotgenes/pi-packages/commit/a8147fb3f6be9af6d73089bbb42b40080e8e04a7))
42
+ * **pi-subagents:** update architecture for Agent rename ([#227](https://github.com/gotgenes/pi-packages/issues/227)) ([f9afc88](https://github.com/gotgenes/pi-packages/commit/f9afc88dcc21213abe453baa032563ff37499ca9))
43
+ * plan evolve AgentRecord into Agent with behavior ([#227](https://github.com/gotgenes/pi-packages/issues/227)) ([d56ff97](https://github.com/gotgenes/pi-packages/commit/d56ff97408063de6467149dc332e35d4078dd137))
44
+ * replace \n with <br/> in Mermaid node labels ([3312a45](https://github.com/gotgenes/pi-packages/commit/3312a4559100cf9ae923f67819653b5a99fceb12))
45
+ * **retro:** add planning stage notes for issue [#227](https://github.com/gotgenes/pi-packages/issues/227) ([ccd9788](https://github.com/gotgenes/pi-packages/commit/ccd9788ac619ae9f0380cb4b0c0b632efb0faf68))
46
+ * **retro:** add retro notes for issue [#239](https://github.com/gotgenes/pi-packages/issues/239) ([58a19a1](https://github.com/gotgenes/pi-packages/commit/58a19a1e65d93c753ef7ba9c24e34d4ebb6f172d))
47
+ * **retro:** add TDD stage notes for issue [#227](https://github.com/gotgenes/pi-packages/issues/227) ([66cf314](https://github.com/gotgenes/pi-packages/commit/66cf314aa4bef736a689d547e75e8bede1757f85))
48
+
8
49
  ## [10.0.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v10.0.0...pi-subagents-v10.0.1) (2026-05-27)
9
50
 
10
51
 
@@ -34,44 +34,44 @@ The extension is organized around six domains, each responsible for one aspect o
34
34
  flowchart TB
35
35
  subgraph config["Config domain"]
36
36
  direction TB
37
- AgentTypeRegistry["AgentTypeRegistry\n(registry of agent types)"]
38
- DefaultAgents["default-agents\n(built-in types)"]
39
- CustomAgents["custom-agents\n(user .md files)"]
40
- InvocationConfig["invocation-config\n(per-call merge)"]
37
+ AgentTypeRegistry["AgentTypeRegistry<br/>(registry of agent types)"]
38
+ DefaultAgents["default-agents<br/>(built-in types)"]
39
+ CustomAgents["custom-agents<br/>(user .md files)"]
40
+ InvocationConfig["invocation-config<br/>(per-call merge)"]
41
41
  end
42
42
 
43
43
  subgraph session["Session domain"]
44
44
  direction TB
45
- SessionConfig["assembleSessionConfig\n(pure assembler)"]
46
- Prompts["prompts\n(system prompt)"]
47
- Context["context\n(parent history)"]
48
- SafeFs["safe-fs\n(symlink/name guards)"]
49
- SkillLoader["skill-loader\n(preload skills)"]
50
- Env["env\n(git/platform)"]
51
- ModelResolver["model-resolver\n(fuzzy match)"]
45
+ SessionConfig["assembleSessionConfig<br/>(pure assembler)"]
46
+ Prompts["prompts<br/>(system prompt)"]
47
+ Context["context<br/>(parent history)"]
48
+ SafeFs["safe-fs<br/>(symlink/name guards)"]
49
+ SkillLoader["skill-loader<br/>(preload skills)"]
50
+ Env["env<br/>(git/platform)"]
51
+ ModelResolver["model-resolver<br/>(fuzzy match)"]
52
52
  end
53
53
 
54
54
  subgraph lifecycle["Lifecycle domain"]
55
55
  direction TB
56
- AgentManager["AgentManager\n(spawn, queue, abort)"]
57
- AgentRunner["agent-runner\n(session, turns, results)"]
58
- AgentRecord["AgentRecord\n(status state machine)"]
59
- ParentSnapshot["ParentSnapshot\n(frozen parent state)"]
60
- Worktree["worktree\n(git isolation)"]
56
+ AgentManager["AgentManager<br/>(spawn, queue, abort)"]
57
+ AgentRunner["agent-runner<br/>(session, turns, results)"]
58
+ Agent["Agent<br/>(status, behavior: abort/steer/worktree/run lifecycle)"]
59
+ ParentSnapshot["ParentSnapshot<br/>(frozen parent state)"]
60
+ Worktree["worktree<br/>(git isolation)"]
61
61
  end
62
62
 
63
63
  subgraph observation["Observation domain"]
64
64
  direction TB
65
- RecordObserver["record-observer\n(stats via events)"]
66
- Notification["notification\n(completion nudges)"]
67
- UIObserver["ui-observer\n(streaming state)"]
65
+ RecordObserver["record-observer<br/>(stats via events)"]
66
+ Notification["notification<br/>(completion nudges)"]
67
+ UIObserver["ui-observer<br/>(streaming state)"]
68
68
  end
69
69
 
70
70
  subgraph tools["Tools domain"]
71
71
  direction TB
72
- AgentTool["subagent tool\n(dispatch)"]
73
- ResultRenderer["result-renderer\n(pure rendering)"]
74
- SpawnConfig["spawn-config\n(resolve params)"]
72
+ AgentTool["subagent tool<br/>(dispatch)"]
73
+ ResultRenderer["result-renderer<br/>(pure rendering)"]
74
+ SpawnConfig["spawn-config<br/>(resolve params)"]
75
75
  FgRunner["foreground-runner"]
76
76
  BgSpawner["background-spawner"]
77
77
  GetResult["get_subagent_result"]
@@ -80,9 +80,9 @@ flowchart TB
80
80
 
81
81
  subgraph ui["UI domain"]
82
82
  direction TB
83
- Widget["agent-widget\n(live status)"]
84
- ConvViewer["conversation-viewer\n(session overlay)"]
85
- Menu["agent-menu\n(slash command)"]
83
+ Widget["agent-widget<br/>(live status)"]
84
+ ConvViewer["conversation-viewer<br/>(session overlay)"]
85
+ Menu["agent-menu<br/>(slash command)"]
86
86
  end
87
87
 
88
88
  AgentTool --> AgentManager
@@ -101,11 +101,11 @@ flowchart TB
101
101
 
102
102
  ```mermaid
103
103
  classDiagram
104
- class AgentRecord {
104
+ class Agent {
105
105
  +id: string
106
106
  +type: SubagentType
107
107
  +description: string
108
- +status: AgentRecordStatus
108
+ +status: AgentStatus
109
109
  +result?: string
110
110
  +error?: string
111
111
  +toolUses: number
@@ -120,16 +120,25 @@ classDiagram
120
120
  +markError()
121
121
  +markStopped()
122
122
  +resetForResume()
123
+ +abort(): boolean
124
+ +queueSteer(message)
125
+ +flushPendingSteers(session)
126
+ +setupWorktree(worktrees, isolation)
127
+ +completeRun(result, worktrees)
128
+ +failRun(err, worktrees)
129
+ +wireSignal(signal, onAbort)
130
+ +attachObserver(unsub)
131
+ +releaseListeners()
132
+ +setOnRunFinished(fn)
123
133
  }
124
134
 
125
135
  class AgentManager {
126
136
  +spawn(snapshot, type, prompt, config)
127
137
  +spawnAndWait(snapshot, type, prompt, config)
128
138
  +resume(id, snapshot, exec)
129
- +getRecord(id): AgentRecord
130
- +listAgents(): AgentRecord[]
139
+ +getRecord(id): Agent
140
+ +listAgents(): Agent[]
131
141
  +abort(id)
132
- +queueSteer(id, message)
133
142
  }
134
143
 
135
144
  class AgentTypeRegistry {
@@ -157,7 +166,7 @@ classDiagram
157
166
  +hasRunning(): boolean
158
167
  }
159
168
 
160
- AgentManager --> AgentRecord : creates/manages
169
+ AgentManager --> Agent : creates/manages
161
170
  AgentManager --> ParentSnapshot : receives at spawn
162
171
  SubagentsService --> AgentManager : wraps via adapter
163
172
  AgentManager --> AgentTypeRegistry : resolves types
@@ -216,10 +225,10 @@ sequenceDiagram
216
225
  Asm-->>Runner: SessionConfig
217
226
  Runner->>Child: create session + run turn loop
218
227
  Child-->>Runner: result text
219
- Runner-->>Mgr: update AgentRecord
220
- Note over Mgr: record-observer subscribes to session events for stats
228
+ Runner-->>Mgr: update Agent
229
+ Note over Mgr: agent-observer subscribes to session events for stats
221
230
  Note over Mgr: ui-observer subscribes for streaming state
222
- Mgr-->>Tool: AgentRecord
231
+ Mgr-->>Tool: Agent
223
232
  Tool-->>LLM: formatted result
224
233
  ```
225
234
 
@@ -259,11 +268,10 @@ src/
259
268
  ├── lifecycle/ agent execution and state tracking
260
269
  │ ├── agent-manager.ts spawn, queue, abort, resume, concurrency
261
270
  │ ├── agent-runner.ts session creation, turn loop, tool filtering
262
- │ ├── agent-record.ts status state machine
271
+ │ ├── agent.ts status state machine, per-agent behavior (abort, steer, worktree)
263
272
  │ ├── parent-snapshot.ts immutable spawn-time parent state
264
273
  │ ├── execution-state.ts session/output phase state
265
274
  │ ├── permission-bridge.ts optional bridge to pi-permission-system registry
266
- │ ├── run-handle.ts per-run cleanup lifecycle
267
275
  │ ├── worktree.ts git worktree isolation
268
276
  │ ├── worktree-state.ts worktree phase state
269
277
  │ └── usage.ts token usage tracking
@@ -504,7 +512,7 @@ Bags with 10+ fields are the highest priority for decomposition.
504
512
  | `AgentToolDeps` | 8 | agent-tool | ✓ done |
505
513
  | `AgentMenuDeps` | 8 | agent-menu | ✓ done |
506
514
  | `ConversationViewerOptions` | 8 | conversation-viewer | Low |
507
- | `AgentRecordInit` | 8 | agent-record | Low |
515
+ | `AgentInit` | 8 | agent | Low |
508
516
 
509
517
  ### Complexity hotspots
510
518
 
@@ -665,103 +673,11 @@ Phase 13 addressed remaining closure factories, the last fallow refactoring targ
665
673
  All six steps are closed: [#214], [#215], [#216], [#217], [#218], [#219].
666
674
  See [phase-13-remaining-smells.md](history/phase-13-remaining-smells.md) for details.
667
675
 
668
- ## Improvement roadmap (Phase 14 — strip policy from core)
676
+ ## Phase 14 (complete)
669
677
 
670
- Phase 14 removes tool and extension policy enforcement from pi-subagents.
671
- This code duplicates what pi-permission-system already provides with richer semantics (allow/ask/deny vs. binary hide).
672
- Removing it simplifies `runAgent`, shrinks `AgentConfig` and `SessionConfig`, and makes the Phase 15 domain-model work operate on a cleaner codebase.
673
-
674
- ### Findings summary
675
-
676
- | Finding | Category | Impact | Risk | Priority |
677
- | ----------------------------------------------------------------------------------------- | ------------- | ------ | ---- | -------- |
678
- | `disallowed_tools` duplicates pi-permission-system's `permission:` frontmatter | A: Overlap | 4 | 2 | 12 |
679
- | `extensions: string[]` allowlist is tool filtering disguised as lifecycle control | A: Overlap | 3 | 2 | 9 |
680
- | `filterActiveTools` ran twice (pre-bind + post-bind) to catch extension-registered tools | B: Complexity | 3 | 1 | ✓ done |
681
- | `ToolFilterConfig` existed solely to carry filtering state through the runner | C: Accidental | 2 | 1 | ✓ done |
682
- | `Agent` tool name is PascalCase — misaligns with Pi's lowercase built-in convention | D: Convention | 2 | 1 | 3 |
683
-
684
- ### Step 1: Remove `disallowed_tools` — [#237] ✅ Complete
685
-
686
- Remove the `disallowedTools` field from `AgentConfig` and all code that processes it.
687
-
688
- 1. Remove `disallowedTools` from the `AgentConfig` interface in `types.ts`.
689
- 2. Remove `disallowed_tools` parsing from custom agent frontmatter in `custom-agents.ts`.
690
- 3. Remove `disallowedSet` from `ToolFilterConfig` in `session-config.ts`.
691
- 4. Remove the `disallowedSet` construction in `assembleSessionConfig`.
692
- 5. Remove the `disallowedSet` branch from `filterActiveTools` in `agent-runner.ts`.
693
- 6. Remove `disallowed_tools` from the agent config editor UI.
694
- 7. Remove `disallowed_tools` from the agent creation wizard.
695
- 8. Update tests.
696
-
697
- - Target: `types.ts`, `custom-agents.ts`, `session-config.ts`, `agent-runner.ts`, `ui/agent-config-editor.ts`, `ui/agent-creation-wizard.ts`
698
- - Smell: A (responsibility overlap with pi-permission-system)
699
- - Outcome: users migrate to `permission:` frontmatter for tool restrictions; single source of truth for access control
700
-
701
- ### Step 2: Remove `extensions` filtering — [#238] ✅ Complete
702
-
703
- Remove the `extensions: string[]` allowlist and simplify the field to a boolean.
704
- The `extensions: false` case (used by `isolated`) is retained in this step and removed in Phase 16.
705
-
706
- 1. Change `extensions` type from `true | string[] | false` to `boolean` in `AgentConfig`.
707
- 2. Remove the `extensions` array branch from `filterActiveTools` in `agent-runner.ts`.
708
- 3. Remove `extensions` from the agent config editor and creation wizard.
709
- 4. Update custom agent frontmatter parsing to treat array values as `true` (with a warning).
710
- 5. Update tests.
711
-
712
- - Target: `types.ts`, `agent-runner.ts`, `session-config.ts`, `ui/agent-config-editor.ts`, `ui/agent-creation-wizard.ts`
713
- - Smell: A (tool filtering disguised as extension lifecycle control)
714
- - Outcome: `filterActiveTools` reduces to two concerns: recursion guard and `extensions: false` passthrough
715
-
716
- ### Step 3: Collapse `filterActiveTools` to recursion guard — [#239] ✅ Complete
717
-
718
- With Steps 1–2 complete, `filterActiveTools` had only two remaining branches: the `EXCLUDED_TOOL_NAMES` recursion guard and the `extensions === false` passthrough.
719
- Inlined the `extensions === false` passthrough into the callsite and reduced the function to its essential purpose.
720
-
721
- 1. Simplified `filterActiveTools` to filter only `EXCLUDED_TOOL_NAMES`.
722
- 2. Removed `ToolFilterConfig` — the function no longer needs a config bag.
723
- 3. Removed the pre-bind filter call — extension tools aren't in the active set pre-bind, so the guard is a no-op there.
724
- 4. Kept a single post-bind filter call for the recursion guard.
725
- 5. Flattened `SessionConfig` — removed `toolFilter: ToolFilterConfig`; `toolNames` and `extensions` are top-level fields.
726
- 6. Updated tests.
727
-
728
- - Target: `agent-runner.ts`, `session-config.ts`
729
- - Smell: B (accidental complexity), C (two-pass filter dance)
730
- - Outcome: `filterActiveTools` is a one-liner; `SessionConfig` loses one nested type; the pre-bind/post-bind dance is gone
731
-
732
- ### Step 4: Rename `Agent` tool to `subagent` — [#242] ✅ Complete
733
-
734
- Rename the `Agent` tool to `subagent` to align with Pi's built-in tool naming convention (all lowercase: `read`, `bash`, `write`, `edit`, `find`, `grep`, `ls`).
735
- The PascalCase name was inherited from tintinweb/pi-subagents (mimicking Claude Code's convention), but Pi uses lowercase for all built-in tools.
736
- The companion tools are already lowercase snake_case (`get_subagent_result`, `steer_subagent`), and nicobailon/pi-subagents already uses `subagent`.
737
-
738
- 1. Rename tool name from `"Agent"` to `"subagent"` in `agent-tool.ts`.
739
- 2. Update `label` and `promptSnippet` in the tool definition.
740
- 3. Update `EXCLUDED_TOOL_NAMES` in `agent-runner.ts`.
741
- 4. Update the fallback display name in `agent-tool.ts`.
742
- 5. Update architecture docs (tool references, domain diagrams, cross-extension section).
743
- 6. Update pi-permission-system docs that reference the `Agent` tool name.
744
- 7. Update tests.
745
-
746
- - Target: `tools/agent-tool.ts`, `lifecycle/agent-runner.ts`, `docs/`, `../pi-permission-system/docs/`
747
- - Smell: D (convention mismatch — PascalCase in a lowercase ecosystem)
748
- - Outcome: all three tools use consistent lowercase naming; aligns with Pi's built-in convention
749
-
750
- ### Step dependency diagram
751
-
752
- ```mermaid
753
- flowchart LR
754
- S1["Step 1\nRemove disallowed_tools"]
755
- S2["Step 2\nRemove extensions filtering"]
756
- S3["Step 3\nCollapse filterActiveTools"]
757
- S4["Step 4\nRename Agent to subagent"]
758
-
759
- S1 --> S3
760
- S2 --> S3
761
- ```
762
-
763
- Steps 1, 2, and 4 are independent and can proceed in parallel.
764
- Step 3 depends on Steps 1 and 2.
678
+ Phase 14 removed tool and extension policy enforcement from pi-subagents, eliminating overlap with pi-permission-system.
679
+ All four steps are closed: [#237], [#238], [#239], [#242].
680
+ See [phase-14-strip-policy.md](history/phase-14-strip-policy.md) for details.
765
681
 
766
682
  [#237]: https://github.com/gotgenes/pi-packages/issues/237
767
683
  [#238]: https://github.com/gotgenes/pi-packages/issues/238
@@ -773,23 +689,24 @@ Step 3 depends on Steps 1 and 2.
773
689
  Phase 15 addresses the anemic domain model in the lifecycle layer.
774
690
  `AgentRecord` is a data bag — identity, status transitions, and stats — but no behavior.
775
691
  `AgentManager` reaches into records 37 times, doing work that belongs on the agent.
776
- Per-agent state (pending steers, abort logic, run lifecycle) is scattered across the manager, `RunHandle`, and a manager-level Map.
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.
777
694
 
778
695
  The scheduling concern (queue, concurrency counter, drain) is tangled into `AgentManager` alongside collection management and run orchestration.
779
696
  `notifyConcurrencyChanged()` is a scheduling method exposed as a public API so settings can poke the queue — a cross-concern leak.
780
697
 
781
698
  ### Findings summary
782
699
 
783
- | Finding | Category | Impact | Risk | Priority |
784
- | ------------------------------------------------------------- | ------------ | ------ | ---- | -------- |
785
- | `AgentRecord` is anemic — no behavior, manager reaches in 37× | B: Oversized | 5 | 3 | 15 |
786
- | Scheduling tangled into `AgentManager` (3 fields, 3 methods) | A: Coupling | 4 | 2 | 12 |
787
- | `startAgent` uses `.then()`/`.catch()` instead of async/await | C: Callbacks | 3 | 2 | 10 |
788
- | `onSessionCreated` callback flows through 3 layers | C: Callbacks | 3 | 2 | 10 |
789
- | `resume()` duplicates observer subscribe/unsubscribe pattern | A: Redundant | 2 | 1 | 8 |
790
- | `exec`/`registry` relay-only deps on `AgentManager` | C: Coupling | 2 | 1 | 6 |
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 |
791
708
 
792
- ### Step 1: Evolve AgentRecord into Agent with behavior — [#227]
709
+ ### Step 1: Evolve AgentRecord into Agent with behavior — [#227] ✅ Complete
793
710
 
794
711
  Rename `AgentRecord` → `Agent` (or wrap it).
795
712
  Move per-agent behavior from `AgentManager` into the agent:
@@ -802,15 +719,17 @@ Move per-agent behavior from `AgentManager` into the agent:
802
719
  - Smell: B (anemic domain model) + C (manager reaching into records)
803
720
  - Outcome: `AgentManager` delegates via Tell-Don't-Ask; per-agent state lives on the agent
804
721
 
805
- ### Step 2: Convert startAgent to async/await — [#228]
722
+ ### Step 2: Convert startAgent to async/await — [#228] ✅ Complete
806
723
 
807
- Convert `startAgent` from synchronous (returns void, assigns `record.promise` to a `.then()`/`.catch()` chain) to `async` (returns `Promise<void>`, uses try/catch).
724
+ Converted `startAgent` to `async` with `try/catch` and dissolved `RunHandle` into `Agent` methods.
808
725
  `spawn()` assigns `record.promise = this.startAgent(...)` instead of calling `startAgent()` synchronously.
726
+ `Agent` gained run lifecycle methods: `completeRun`, `failRun`, `wireSignal`, `attachObserver`, `releaseListeners`, `setOnRunFinished`.
727
+ Worktree setup was hoisted to callers (`spawn`, `drainQueue`) to preserve the synchronous-throw contract.
809
728
 
810
729
  - Depends on: #227
811
- - Target: `src/lifecycle/agent-manager.ts`
730
+ - Target: `src/lifecycle/agent-manager.ts`, `src/lifecycle/agent.ts`
812
731
  - Smell: C (raw promise callbacks)
813
- - Outcome: zero `.then()`/`.catch()` in `agent-manager.ts`
732
+ - Outcome: zero `.then()`/`.catch()` in `agent-manager.ts`; `RunHandle` deleted; Agent owns run lifecycle
814
733
 
815
734
  ### Step 3: Replace onSessionCreated callback with observer method — [#229]
816
735
 
@@ -840,10 +759,10 @@ Move them to `ConcreteAgentRunner` construction.
840
759
  - Smell: C (relay-only dependencies)
841
760
  - Outcome: `AgentManager` loses 2 fields; `AgentManagerOptions` shrinks from 7 to 5 fields
842
761
 
843
- ### Step 6: Unify resume() with RunHandle pattern — [#232]
762
+ ### Step 6: Unify resume() with Agent run lifecycle methods — [#232]
844
763
 
845
- After #227 moves `RunHandle` ownership to the `Agent`, `resume()` on `AgentManager` becomes a 4-line delegation to `agent.resume(runner, prompt, signal)`.
846
- The agent manages its own observer subscription lifecycle.
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.
847
766
 
848
767
  - Depends on: #227, #228
849
768
  - Target: `src/lifecycle/agent-manager.ts`
@@ -854,12 +773,12 @@ The agent manages its own observer subscription lifecycle.
854
773
 
855
774
  ```mermaid
856
775
  flowchart LR
857
- S1["Step 1\nAgent with behavior"]
858
- S2["Step 2\nasync startAgent"]
859
- S3["Step 3\nonSessionCreated observer"]
860
- S4["Step 4\nConcurrencyQueue"]
861
- S5["Step 5\nrelay deps"]
862
- S6["Step 6\nresume unification"]
776
+ S1["Step 1<br/>Agent with behavior"]
777
+ 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"]
863
782
 
864
783
  S1 --> S2
865
784
  S1 --> S6
@@ -896,7 +815,7 @@ By this point the core is minimal and stable — the API boundary has been prove
896
815
 
897
816
  ## Refactoring history
898
817
 
899
- Phases 1–5 and 7–12 are complete.
818
+ Phases 1–5, 7–14 are complete.
900
819
  Phase 6 (UI extraction to a separate package) is deferred.
901
820
  Detailed records are preserved in per-phase history files:
902
821
 
@@ -915,7 +834,7 @@ Detailed records are preserved in per-phase history files:
915
834
  | 11 | Closure factories to classes | Complete | [phase-11-closure-to-class.md](history/phase-11-closure-to-class.md) |
916
835
  | 12 | Complexity reduction and test fixture extraction | Complete | [phase-12-complexity-test-fixtures.md](history/phase-12-complexity-test-fixtures.md) |
917
836
  | 13 | Remaining structural smells | Complete | [phase-13-remaining-smells.md](history/phase-13-remaining-smells.md) |
918
- | 14 | Strip policy from core | Planned | |
837
+ | 14 | Strip policy from core | Complete | [phase-14-strip-policy.md](history/phase-14-strip-policy.md) |
919
838
  | 15 | Domain model evolution | Planned | — |
920
839
  | 16 | Invert dependencies | Planned | — |
921
840
  | 17 | Extract UI to separate package | Planned | — |
@@ -936,7 +855,7 @@ Detailed records are preserved in per-phase history files:
936
855
  | Phase 11 | #192, #193, #194, #195, #196 | SessionContext, runtime queries, interface alignment, tool classes, runner/menu classes, index.ts simplification |
937
856
  | Phase 12 | #205, #206, #207, #208 | renderWidgetLines, showAgentDetail, widget update, shared test fixtures |
938
857
  | Phase 13 | #214, #215, #216, #217, #218, #219 | Closure-to-class, buildParentContext, startAgent decomp, overwrite guard, settings SDK, test duplication |
939
- | Phase 14 | #237, #238, #239 | Remove disallowed_tools, remove extensions filtering, collapse filterActiveTools |
858
+ | Phase 14 | #237, #238, #239, #242 | Remove disallowed_tools, remove extensions filtering, collapse filterActiveTools, rename Agent to subagent |
940
859
  | Phase 15 | #227, #228, #229, #230, #231, #232 | Agent domain model, async startAgent, onSessionCreated observer, ConcurrencyQueue, relay deps, resume unification |
941
860
 
942
861
  The remaining open issue is #22 (parent-session resolution), a cross-extension track that does not gate the structural work.
@@ -0,0 +1,49 @@
1
+ # Phase 14: Strip policy from core
2
+
3
+ ## Summary
4
+
5
+ Phase 14 removed tool and extension policy enforcement from pi-subagents.
6
+ This code duplicated what pi-permission-system already provides with richer semantics (allow/ask/deny vs. binary hide).
7
+ Removing it simplified `runAgent`, shrunk `AgentConfig` and `SessionConfig`, and prepared a cleaner codebase for Phase 15's domain-model work.
8
+
9
+ All four steps are closed: [#237], [#238], [#239], [#242].
10
+
11
+ ## Steps
12
+
13
+ ### Step 1: Remove `disallowed_tools` — [#237]
14
+
15
+ Removed the `disallowedTools` field from `AgentConfig` and all code that processed it.
16
+ Users migrate to `permission:` frontmatter for tool restrictions.
17
+
18
+ - Target: `types.ts`, `custom-agents.ts`, `session-config.ts`, `agent-runner.ts`, `ui/agent-config-editor.ts`, `ui/agent-creation-wizard.ts`
19
+ - Outcome: single source of truth for access control in pi-permission-system
20
+
21
+ ### Step 2: Remove `extensions` filtering — [#238]
22
+
23
+ Removed the `extensions: string[]` allowlist and simplified the field to a boolean.
24
+ The `extensions: false` case (used by `isolated`) was retained for Phase 16.
25
+
26
+ - Target: `types.ts`, `agent-runner.ts`, `session-config.ts`, `ui/agent-config-editor.ts`, `ui/agent-creation-wizard.ts`
27
+ - Outcome: `filterActiveTools` reduced to two concerns: recursion guard and `extensions: false` passthrough
28
+
29
+ ### Step 3: Collapse `filterActiveTools` to recursion guard — [#239]
30
+
31
+ With Steps 1–2 complete, `filterActiveTools` was reduced to its essential purpose: filtering `EXCLUDED_TOOL_NAMES` to prevent recursive agent spawning.
32
+
33
+ - Removed `ToolFilterConfig` — the function no longer needs a config bag.
34
+ - Removed the pre-bind filter call — extension tools aren't in the active set pre-bind.
35
+ - Flattened `SessionConfig` — removed `toolFilter: ToolFilterConfig`; `toolNames` and `extensions` are top-level fields.
36
+ - Target: `agent-runner.ts`, `session-config.ts`
37
+ - Outcome: `filterActiveTools` is a one-liner; the pre-bind/post-bind dance is gone
38
+
39
+ ### Step 4: Rename `Agent` tool to `subagent` — [#242]
40
+
41
+ Renamed the `Agent` tool to `subagent` to align with Pi's built-in tool naming convention (all lowercase).
42
+
43
+ - Target: `tools/agent-tool.ts`, `lifecycle/agent-runner.ts`, `docs/`, `../pi-permission-system/docs/`
44
+ - Outcome: all three tools use consistent lowercase naming
45
+
46
+ [#237]: https://github.com/gotgenes/pi-packages/issues/237
47
+ [#238]: https://github.com/gotgenes/pi-packages/issues/238
48
+ [#239]: https://github.com/gotgenes/pi-packages/issues/239
49
+ [#242]: https://github.com/gotgenes/pi-packages/issues/242