@gotgenes/pi-subagents 18.0.1 → 18.0.3

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.
Files changed (268) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/docs/architecture/architecture.md +165 -203
  3. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  4. package/package.json +6 -7
  5. package/src/config/default-agents.ts +3 -0
  6. package/src/lifecycle/subagent.ts +25 -14
  7. package/src/observation/notification.ts +23 -10
  8. package/src/observation/subagent-events-observer.ts +1 -3
  9. package/src/runtime.ts +2 -1
  10. package/src/service/service-adapter.ts +20 -17
  11. package/src/session/model-resolver.ts +31 -30
  12. package/src/tools/agent-tool.ts +17 -13
  13. package/src/tools/get-result-report.ts +60 -0
  14. package/src/tools/get-result-tool.ts +25 -40
  15. package/src/tools/helpers.ts +21 -5
  16. package/src/tools/spawn-config.ts +5 -5
  17. package/src/tools/steer-tool.ts +37 -27
  18. package/src/types.ts +4 -1
  19. package/src/ui/session-navigator.ts +0 -2
  20. package/.prettierignore +0 -5
  21. package/AGENTS.md +0 -7
  22. package/docs/plans/0048-implement-subagents-api.md +0 -304
  23. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  24. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  25. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  26. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  27. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  28. package/docs/plans/0057-structured-debug-logging.md +0 -154
  29. package/docs/plans/0061-session-format-transcript.md +0 -284
  30. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  31. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  32. package/docs/plans/0070-extract-event-handlers.md +0 -306
  33. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  34. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  35. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  36. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  37. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  38. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  39. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  40. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  41. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  42. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  43. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  44. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  45. package/docs/plans/0109-extract-settings-manager.md +0 -276
  46. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  47. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  48. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  49. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  50. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  51. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  52. package/docs/plans/0116-type-housekeeping.md +0 -351
  53. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  54. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  55. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  56. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  57. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  58. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  59. package/docs/plans/0135-extract-display-helpers.md +0 -182
  60. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  61. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  62. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  63. package/docs/plans/0146-narrow-ui-context.md +0 -319
  64. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  65. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  66. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  67. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  68. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  69. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  70. package/docs/plans/0167-narrow-runner-io.md +0 -150
  71. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  72. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  73. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  74. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  75. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  76. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  77. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  78. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  79. package/docs/plans/0192-define-session-context-interface.md +0 -107
  80. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  81. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  82. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  83. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  84. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  85. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  86. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  87. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  88. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  89. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  90. package/docs/plans/0216-decompose-start-agent.md +0 -255
  91. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  92. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  93. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  94. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  95. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  96. package/docs/plans/0229-agent-born-complete.md +0 -564
  97. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  98. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  99. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  100. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  101. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  102. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  103. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  104. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  105. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  106. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  107. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  108. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  109. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  110. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  111. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  112. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  113. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  114. package/docs/plans/0373-extract-subagent-state.md +0 -250
  115. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  116. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  117. package/docs/plans/0376-extract-manager-observer.md +0 -232
  118. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  119. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  120. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  121. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  122. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  123. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  124. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  125. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  126. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  127. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  128. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  129. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  130. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  131. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  132. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  133. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  134. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  135. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  136. package/docs/plans/0443-consolidate-remaining-test-clone-families.md +0 -268
  137. package/docs/plans/0444-shrink-widget-to-background-agents.md +0 -156
  138. package/docs/plans/0445-native-session-navigation.md +0 -365
  139. package/docs/plans/0446-spike-session-navigation-entry-criteria.md +0 -171
  140. package/docs/plans/0447-extract-subagents-settings-command.md +0 -220
  141. package/docs/plans/0448-block-disabled-agent-spawn.md +0 -207
  142. package/docs/plans/0462-navigation-renderer-tui-components.md +0 -338
  143. package/docs/plans/0463-file-snapshot-source-evicted-agents.md +0 -306
  144. package/docs/plans/0470-refresh-readme-command-surface.md +0 -159
  145. package/docs/retro/0048-implement-subagents-api.md +0 -44
  146. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  147. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  148. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  149. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  150. package/docs/retro/0057-structured-debug-logging.md +0 -77
  151. package/docs/retro/0061-session-format-transcript.md +0 -41
  152. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  153. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  154. package/docs/retro/0070-extract-event-handlers.md +0 -49
  155. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  156. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  157. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  158. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  159. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  160. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  161. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  162. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  163. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  164. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  165. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  166. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  167. package/docs/retro/0109-extract-settings-manager.md +0 -55
  168. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  169. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  170. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  171. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  172. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  173. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  174. package/docs/retro/0116-type-housekeeping.md +0 -42
  175. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  176. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  177. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  178. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  179. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  180. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  181. package/docs/retro/0135-extract-display-helpers.md +0 -38
  182. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  183. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  184. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  185. package/docs/retro/0146-narrow-ui-context.md +0 -70
  186. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  187. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  188. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  189. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  190. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  191. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  192. package/docs/retro/0167-narrow-runner-io.md +0 -63
  193. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  194. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  195. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  196. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  197. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  198. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  199. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  200. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  201. package/docs/retro/0192-define-session-context-interface.md +0 -59
  202. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  203. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  204. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  205. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  206. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  207. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  208. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  209. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  210. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  211. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  212. package/docs/retro/0216-decompose-start-agent.md +0 -80
  213. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  214. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  215. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  216. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  217. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  218. package/docs/retro/0229-agent-born-complete.md +0 -89
  219. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  220. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  221. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  222. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  223. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  224. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  225. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  226. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  227. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  228. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  229. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  230. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  231. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  232. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  233. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  234. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  235. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  236. package/docs/retro/0373-extract-subagent-state.md +0 -94
  237. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  238. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  239. package/docs/retro/0376-extract-manager-observer.md +0 -81
  240. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  241. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  242. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  243. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  244. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  245. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  246. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  247. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  248. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  249. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  250. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  251. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  252. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  253. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  254. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  255. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  256. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -97
  257. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  258. package/docs/retro/0443-consolidate-remaining-test-clone-families.md +0 -28
  259. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  260. package/docs/retro/0445-native-session-navigation.md +0 -122
  261. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  262. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  263. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  264. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  265. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  266. package/docs/retro/0470-refresh-readme-command-surface.md +0 -40
  267. package/src/observation/notification-state.ts +0 -27
  268. package/vitest.config.ts +0 -14
@@ -1,288 +0,0 @@
1
- ---
2
- issue: 228
3
- issue_title: "Convert startAgent to async/await, move run lifecycle to Agent (Phase 15, Step 2)"
4
- ---
5
-
6
- # Convert startAgent to async/await, dissolve RunHandle into Agent
7
-
8
- ## Problem Statement
9
-
10
- `startAgent` is synchronous and uses `.then()`/`.catch()` to handle the runner promise.
11
- This forces a promise-chain callback style even though `Agent` (as of #227) already owns per-agent behavior.
12
-
13
- `RunHandle` is a private class in `agent-manager.ts` that does 6 things — 5 of which are Agent concerns (status transitions, worktree cleanup, execution state updates, listener lifecycle, signal wiring).
14
- The only non-Agent concern is `onFinished`, a callback that connects to the manager's concurrency queue drain.
15
-
16
- `resume()` duplicates the same pattern manually: subscribe observer, try/catch with `markCompleted`/`markError`, finally unsub.
17
- Issue #232 wants to unify resume with the run lifecycle, and the architecture doc says "resume becomes a 4-line delegation."
18
- If we just move `RunHandle` to `Agent` as a separate class, `resume()` still can't use it naturally — the signatures differ.
19
- But if we dissolve `RunHandle` into Agent methods, both paths use the same primitives.
20
-
21
- ## Goals
22
-
23
- - Zero `.then()`/`.catch()` in `agent-manager.ts`.
24
- - Dissolve `RunHandle` into Agent methods: `completeRun`, `failRun`, `wireSignal`, `attachObserver`, `releaseListeners`, `onRunFinished` setter.
25
- - `startAgent` is a straightforward async method: setup → await → handle result.
26
- - `spawn()` assigns `record.promise = this.startAgent(...)`.
27
- - Prepare the ground for #232 (resume unification) by giving Agent the run lifecycle primitives that `resume()` can reuse.
28
-
29
- ## Non-Goals
30
-
31
- - **Resume unification** — deferred to #232.
32
- That issue will use the new Agent methods to simplify `AgentManager.resume()`.
33
- - **`onSessionCreated` observer** — deferred to #229.
34
- The `onSessionCreated` callback in `startAgent` stays as-is.
35
- - **`ConcurrencyQueue` extraction** — deferred to #230.
36
- - **Relay deps** — deferred to #231.
37
-
38
- ## Background
39
-
40
- ### Relevant modules
41
-
42
- | Module | LOC | Relationship to this change |
43
- | -------------------------------------- | --- | ------------------------------------------------------------- |
44
- | `src/lifecycle/agent-manager.ts` | 492 | Loses `RunHandle` class (~85 LOC), `startAgent` becomes async |
45
- | `src/lifecycle/agent.ts` | 260 | Gains run lifecycle methods (~80 LOC) |
46
- | `src/lifecycle/agent-runner.ts` | — | Exports `RunResult` type, now imported by `agent.ts` |
47
- | `test/lifecycle/agent.test.ts` | 501 | Gains ~120 LOC of run lifecycle tests |
48
- | `test/lifecycle/agent-manager.test.ts` | 768 | One assertion update (`Promise<void>`) |
49
-
50
- ### What RunHandle does today
51
-
52
- | Concern | RunHandle method | Who should own it |
53
- | ---------------------------------------------------------------------- | -------------------- | ----------------------------------------------- |
54
- | Listener lifecycle (unsub + detachFn) | `releaseListeners()` | Agent — per-run cleanup handles |
55
- | Run completion (worktree cleanup, status transition, execution update) | `complete(result)` | Agent — all state mutations target Agent fields |
56
- | Run failure (error marking, best-effort worktree cleanup) | `fail(err)` | Agent — same |
57
- | Signal wiring (parent abort → child abort) | `wireSignal()` | Agent — per-run handle, released on completion |
58
- | Observer attachment (session event subscription) | `attachObserver()` | Agent — per-run handle, released on completion |
59
- | onFinished callback (concurrency drain) | `fireOnFinished()` | Manager concern, but just a stored `() => void` |
60
-
61
- Five of six are Agent concerns.
62
- RunHandle reaches into `this.record` for every operation and talks through `this.record.worktreeState` to a stranger.
63
-
64
- ### Dependency flow (no cycles)
65
-
66
- `agent.ts` gains a type-only import of `RunResult` from `agent-runner.ts`.
67
- `agent-runner.ts` imports from `agent-manager.ts` (not `agent.ts`), so no cycle is created.
68
-
69
- ### Constraints from AGENTS.md
70
-
71
- - `promise` type change from `Promise<string>` to `Promise<void>` is internal — `Agent` is not exported from `package.json`.
72
- - Worktree setup hoist preserves the synchronous-throw contract in `spawn()` (callers rely on catching `isolation: "worktree"` errors synchronously).
73
-
74
- ## Design Overview
75
-
76
- ### Dissolve RunHandle into Agent methods
77
-
78
- Agent gains per-run listener fields and run lifecycle methods:
79
-
80
- ```typescript
81
- class Agent {
82
- // --- Per-run listener state (released on completion or resume reset) ---
83
- private _unsub?: () => void;
84
- private _detachFn?: () => void;
85
- private _onRunFinished?: () => void;
86
-
87
- /** Wire a parent AbortSignal so it stops this agent when fired. */
88
- wireSignal(signal: AbortSignal | undefined, onAbort: () => void): void;
89
-
90
- /** Store the record-observer unsubscribe handle. */
91
- attachObserver(unsub: () => void): void;
92
-
93
- /** Release observer + signal listener handles. */
94
- releaseListeners(): void;
95
-
96
- /** Set the callback fired once when the run finishes (for concurrency drain). */
97
- setOnRunFinished(fn: () => void): void;
98
-
99
- /** Complete a run: release listeners, worktree cleanup, status transition,
100
- execution update, fire onRunFinished. */
101
- completeRun(result: RunResult, worktrees: WorktreeManager): void;
102
-
103
- /** Fail a run: mark error, release listeners, best-effort worktree cleanup,
104
- fire onRunFinished. */
105
- failRun(err: unknown, worktrees: WorktreeManager): void;
106
- }
107
- ```
108
-
109
- `completeRun` and `failRun` take `worktrees: WorktreeManager` as a parameter rather than storing it on Agent.
110
- Worktrees are only needed at run end — storing the reference would widen Agent's dependency surface for a single use.
111
-
112
- Consumer call-site after the change (`startAgent`):
113
-
114
- ```typescript
115
- record.setOnRunFinished(
116
- options.isBackground ? () => this.finalizeBackgroundRun(record) : undefined,
117
- );
118
- record.wireSignal(options.signal, () => this.abort(id));
119
- try {
120
- const result = await this.runner.run(...);
121
- record.completeRun(result, this.worktrees);
122
- } catch (err) {
123
- record.failRun(err, this.worktrees);
124
- }
125
- ```
126
-
127
- ### Narrow `promise` to `Promise<void>`
128
-
129
- The resolved string value of `record.promise` is dead — every consumer just `await`s it and reads `record.result`.
130
- One test asserts `resolves.toBe("done")`; all others use `await record.promise`.
131
- Narrowing to `Promise<void>` first makes the async conversion clean (async `startAgent` naturally returns `Promise<void>`).
132
-
133
- ### Hoist worktree setup from `startAgent` to callers
134
-
135
- `record.setupWorktree()` can throw synchronously (strict isolation failure).
136
- `spawn()` catches this and removes the orphan record.
137
- `drainQueue()` catches it and marks the record as errored.
138
-
139
- If `startAgent` becomes `async`, synchronous throws become rejected promises — neither caller catches them.
140
- Fix: move `record.setupWorktree()` into the callers' existing try-catch blocks before calling async `startAgent`.
141
- `startAgent` reads `record.worktreeState?.path` for the cwd instead.
142
-
143
- ### `resetForResume` releases listeners
144
-
145
- After dissolution, `resetForResume` must call `releaseListeners()` and clear `_onRunFinished` to prevent stale handles from a previous run leaking into the resumed run.
146
-
147
- ## Module-Level Changes
148
-
149
- ### `src/lifecycle/agent.ts`
150
-
151
- 1. Add per-run listener fields: `_unsub`, `_detachFn`, `_onRunFinished`.
152
- 2. Add `wireSignal(signal, onAbort)` — logic from `RunHandle.wireSignal`.
153
- 3. Add `attachObserver(unsub)` — logic from `RunHandle.attachObserver`.
154
- 4. Add `releaseListeners()` — logic from `RunHandle.releaseListeners` (public).
155
- 5. Add `setOnRunFinished(fn)` — stores the callback.
156
- 6. Add private `fireOnRunFinished()` — idempotent clear-then-call pattern from `RunHandle.fireOnFinished`.
157
- 7. Add `completeRun(result, worktrees)` — logic from `RunHandle.complete`, returns `void` (not `string`).
158
- 8. Add `failRun(err, worktrees)` — logic from `RunHandle.fail`.
159
- 9. Update `resetForResume` — call `releaseListeners()` and clear `_onRunFinished`.
160
- 10. Change `promise` type from `Promise<string>` to `Promise<void>` (on both `AgentInit` and the class field).
161
- 11. Add imports: `type RunResult` from `agent-runner`, `debugLog` from `debug`.
162
-
163
- ### `src/lifecycle/agent-manager.ts`
164
-
165
- 1. Delete `RunHandle` class (~85 lines).
166
- 2. Remove `import type { RunResult }` (moved to `agent.ts`; `AgentRunner` import stays).
167
- 3. Convert `startAgent` to `async`, returning `Promise<void>`.
168
- 4. Replace RunHandle creation with Agent method calls: `record.setOnRunFinished(...)`, `record.wireSignal(...)`.
169
- 5. Replace `handle.attachObserver(...)` with `record.attachObserver(...)` in `onSessionCreated`.
170
- 6. Replace `.then()`/`.catch()` chain with `try { await ...; record.completeRun(...) } catch { record.failRun(...) }`.
171
- 7. Remove `record.promise = this.runner.run(...)` assignment — `record.promise` is now assigned by `spawn`/`drainQueue`.
172
- 8. In `spawn()`: hoist `record.setupWorktree(...)` before `startAgent` call (inside existing try-catch); assign `record.promise = this.startAgent(...)`.
173
- 9. In `drainQueue()`: hoist `record.setupWorktree(...)` before `startAgent` call (inside existing try-catch); assign `record.promise = this.startAgent(...)`.
174
- 10. In `startAgent`: remove `record.setupWorktree()` call; read `record.worktreeState?.path` for cwd.
175
- 11. Update `waitForAll` filter: `Promise<string>` → `Promise<void>`.
176
-
177
- ### `test/lifecycle/agent.test.ts`
178
-
179
- 1. Add `describe("Agent — completeRun")` — status transitions (completed/aborted/steered), worktree cleanup with branch append, execution state update, `onRunFinished` fires once, listeners released.
180
- 2. Add `describe("Agent — failRun")` — marks error, best-effort worktree cleanup, `onRunFinished` fires once, listeners released.
181
- 3. Add `describe("Agent — wireSignal")` — connects parent signal to abort callback, `releaseListeners` detaches.
182
- 4. Add `describe("Agent — attachObserver / releaseListeners")` — stores unsub, calls it on release, idempotent.
183
- 5. Update `describe("Agent — resetForResume")` — verify listeners are released and `_onRunFinished` is cleared.
184
-
185
- ### `test/lifecycle/agent-manager.test.ts`
186
-
187
- 1. Update one assertion: `resolves.toBe("done")` → `resolves.toBeUndefined()`.
188
-
189
- ### `packages/pi-subagents/docs/architecture/architecture.md`
190
-
191
- 1. Update Phase 15 smell table — mark `startAgent` callback row as resolved.
192
- 2. Update Step 2 description to note RunHandle dissolution (not just async conversion).
193
- 3. Update Step 6 (#232) description — RunHandle no longer exists; Agent already has `completeRun`/`failRun`/`releaseListeners` that `resume()` can use directly.
194
-
195
- ## Test Impact Analysis
196
-
197
- ### New unit tests enabled by the dissolution
198
-
199
- 1. **`Agent.completeRun()`** — isolated tests for run completion logic (status transitions based on `RunResult` flags, worktree cleanup, execution update, onRunFinished firing) without needing a full `AgentManager` scaffold with a mock runner.
200
- 2. **`Agent.failRun()`** — isolated tests for error handling and best-effort cleanup.
201
- 3. **`Agent.wireSignal()` / `Agent.attachObserver()` / `Agent.releaseListeners()`** — isolated tests for listener lifecycle without spawning a real agent.
202
-
203
- These behaviors were previously only testable through `AgentManager` integration tests that required setting up a mock runner, worktrees, and observer.
204
-
205
- ### Existing tests that must stay
206
-
207
- 1. All `AgentManager — spawn/spawnAndWait` tests — they verify the full spawn flow including async orchestration.
208
- 2. All worktree isolation tests — they verify the synchronous-throw contract in `spawn()`.
209
- 3. All queue/concurrency tests — they verify the manager's orchestration around `drainQueue`.
210
- 4. All completion/notification tests — they verify end-to-end flow through the observer.
211
-
212
- ### Existing tests that change
213
-
214
- 1. One assertion in `agent-manager.test.ts`: `resolves.toBe("done")` → `resolves.toBeUndefined()` (promise type narrowing).
215
-
216
- ## TDD Order
217
-
218
- 1. **Narrow `Agent.promise` from `Promise<string>` to `Promise<void>`**
219
- - Change `AgentInit.promise` and `Agent.promise` field types.
220
- - In `startAgent`: wrap `.then()` callback body in braces (discard `handle.complete` return); remove `return ""` from `.catch()` callback.
221
- - Update `waitForAll` filter type guard.
222
- - Update one test assertion: `resolves.toBe("done")` → `resolves.toBeUndefined()`.
223
- - Run `pnpm run check` + `pnpm vitest run`.
224
- - Commit: `refactor(pi-subagents): narrow Agent.promise to Promise<void>`
225
-
226
- 2. **Red/Green: add run lifecycle methods to Agent**
227
- - Red: add tests in `agent.test.ts` for `completeRun`, `failRun`, `wireSignal`, `attachObserver`/`releaseListeners`, `resetForResume` listener cleanup.
228
- - Green: implement the methods on `Agent` — `wireSignal`, `attachObserver`, `releaseListeners`, `setOnRunFinished`, `fireOnRunFinished`, `completeRun`, `failRun`; update `resetForResume`.
229
- - Add `import type { RunResult }` and `import { debugLog }` to `agent.ts`.
230
- - Run `pnpm run check` + `pnpm vitest run`.
231
- - Commit: `feat(pi-subagents): add run lifecycle methods to Agent`
232
-
233
- 3. **Replace RunHandle with Agent methods in `startAgent`, delete RunHandle**
234
- - Replace `new RunHandle(record, this.worktrees, onFinished)` with `record.setOnRunFinished(onFinished)`.
235
- - Replace `handle.wireSignal(...)` with `record.wireSignal(...)`.
236
- - Replace `handle.attachObserver(...)` with `record.attachObserver(...)`.
237
- - Replace `handle.complete(result)` with `record.completeRun(result, this.worktrees)`.
238
- - Replace `handle.fail(err)` with `record.failRun(err, this.worktrees)`.
239
- - Delete `RunHandle` class.
240
- - Remove `import type { RunResult }` from `agent-manager.ts` (moved to `agent.ts`).
241
- - Run `pnpm run check` + `pnpm vitest run`.
242
- - Commit: `refactor(pi-subagents): replace RunHandle with Agent run lifecycle methods`
243
-
244
- 4. **Hoist worktree setup from `startAgent` to callers**
245
- - In `spawn()`: move `record.setupWorktree(this.worktrees, options.isolation)` before `this.startAgent()`, inside the existing try-catch.
246
- - In `drainQueue()`: move `record.setupWorktree(this.worktrees, next.args.options.isolation)` before `this.startAgent()`, inside its try-catch.
247
- - In `startAgent`: remove `record.setupWorktree()` call; use `record.worktreeState?.path` for `context.cwd`.
248
- - Existing worktree isolation tests pass unchanged.
249
- - Run `pnpm run check` + `pnpm vitest run`.
250
- - Commit: `refactor(pi-subagents): hoist worktree setup from startAgent to callers`
251
-
252
- 5. **Convert `startAgent` to async/await**
253
- - Make `startAgent` async, returning `Promise<void>`.
254
- - Replace `.then()`/`.catch()` chain with `try { const result = await this.runner.run(...); record.completeRun(result, this.worktrees); } catch (err) { record.failRun(err, this.worktrees); }`.
255
- - Remove `record.promise = this.runner.run(...)` assignment from inside `startAgent`.
256
- - In `spawn()`: assign `record.promise = this.startAgent(id, record, args)`.
257
- - In `drainQueue()`: assign `record.promise = this.startAgent(next.id, record, next.args)`.
258
- - Run `pnpm run check` + `pnpm vitest run`.
259
- - Commit: `refactor(pi-subagents): convert startAgent to async/await`
260
-
261
- 6. **Update architecture docs**
262
- - Mark Phase 15 Step 2 smell row as resolved.
263
- - Update Step 2 description to note RunHandle dissolution.
264
- - Update Step 6 (#232) description: RunHandle no longer exists; Agent has `completeRun`/`failRun`/`releaseListeners` that `resume()` can use directly.
265
- - Commit: `docs(pi-subagents): update architecture for async startAgent`
266
-
267
- ## Risks and Mitigations
268
-
269
- 1. **`resetForResume` must release listeners** — If not updated, resumed agents retain stale listener handles from the previous run.
270
- Mitigated by step 2 explicitly updating `resetForResume` to call `releaseListeners()` and clear `_onRunFinished`, with a test.
271
-
272
- 2. **Worktree hoist changes observer-throw semantics** — Currently, if `observer.onAgentStarted()` throws inside `startAgent`, `spawn()`'s try-catch catches it and removes the record.
273
- After async conversion, that throw becomes a rejected promise.
274
- This is a pre-existing inconsistency (`onAgentCompleted` is already wrapped in try-catch, `onAgentStarted` is not) and observers should not throw.
275
- Mitigated by noting the inconsistency; a future step could add try-catch around `onAgentStarted`.
276
-
277
- 3. **Agent grows by ~80 LOC** — Dissolving RunHandle adds methods to an already-substantial class.
278
- Mitigated by the fact that these methods replace logic that already operated on Agent's fields — they belong here by SRP.
279
- The net effect on `agent-manager.ts` is -85 LOC (RunHandle deletion), so the total codebase shrinks.
280
-
281
- 4. **`completeRun` takes `worktrees` parameter instead of storing it** — This means every caller must pass worktrees.
282
- Mitigated by there being exactly two callers today (startAgent and the future resume), both of which already have access to worktrees.
283
- Storing it would widen Agent's dependency surface for a single use.
284
-
285
- ## Open Questions
286
-
287
- None — the design direction (dissolve rather than move) is settled.
288
- The `worktrees` parameter vs. stored-reference question is resolved in favor of the parameter (ISP).