@gotgenes/pi-subagents 18.0.1 → 18.0.2

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 (260) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/docs/architecture/architecture.md +157 -204
  3. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  4. package/package.json +5 -6
  5. package/src/lifecycle/subagent.ts +5 -9
  6. package/src/observation/notification.ts +23 -10
  7. package/src/observation/subagent-events-observer.ts +1 -3
  8. package/src/tools/get-result-report.ts +60 -0
  9. package/src/tools/get-result-tool.ts +25 -40
  10. package/src/types.ts +1 -1
  11. package/src/ui/session-navigator.ts +0 -2
  12. package/.prettierignore +0 -5
  13. package/AGENTS.md +0 -7
  14. package/docs/plans/0048-implement-subagents-api.md +0 -304
  15. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  16. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  17. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  18. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  19. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  20. package/docs/plans/0057-structured-debug-logging.md +0 -154
  21. package/docs/plans/0061-session-format-transcript.md +0 -284
  22. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  23. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  24. package/docs/plans/0070-extract-event-handlers.md +0 -306
  25. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  26. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  27. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  28. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  29. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  30. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  31. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  32. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  33. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  34. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  35. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  36. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  37. package/docs/plans/0109-extract-settings-manager.md +0 -276
  38. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  39. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  40. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  41. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  42. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  43. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  44. package/docs/plans/0116-type-housekeeping.md +0 -351
  45. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  46. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  47. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  48. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  49. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  50. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  51. package/docs/plans/0135-extract-display-helpers.md +0 -182
  52. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  53. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  54. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  55. package/docs/plans/0146-narrow-ui-context.md +0 -319
  56. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  57. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  58. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  59. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  60. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  61. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  62. package/docs/plans/0167-narrow-runner-io.md +0 -150
  63. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  64. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  65. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  66. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  67. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  68. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  69. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  70. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  71. package/docs/plans/0192-define-session-context-interface.md +0 -107
  72. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  73. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  74. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  75. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  76. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  77. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  78. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  79. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  80. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  81. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  82. package/docs/plans/0216-decompose-start-agent.md +0 -255
  83. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  84. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  85. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  86. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  87. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  88. package/docs/plans/0229-agent-born-complete.md +0 -564
  89. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  90. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  91. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  92. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  93. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  94. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  95. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  96. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  97. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  98. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  99. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  100. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  101. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  102. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  103. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  104. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  105. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  106. package/docs/plans/0373-extract-subagent-state.md +0 -250
  107. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  108. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  109. package/docs/plans/0376-extract-manager-observer.md +0 -232
  110. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  111. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  112. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  113. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  114. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  115. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  116. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  117. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  118. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  119. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  120. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  121. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  122. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  123. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  124. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  125. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  126. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  127. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  128. package/docs/plans/0443-consolidate-remaining-test-clone-families.md +0 -268
  129. package/docs/plans/0444-shrink-widget-to-background-agents.md +0 -156
  130. package/docs/plans/0445-native-session-navigation.md +0 -365
  131. package/docs/plans/0446-spike-session-navigation-entry-criteria.md +0 -171
  132. package/docs/plans/0447-extract-subagents-settings-command.md +0 -220
  133. package/docs/plans/0448-block-disabled-agent-spawn.md +0 -207
  134. package/docs/plans/0462-navigation-renderer-tui-components.md +0 -338
  135. package/docs/plans/0463-file-snapshot-source-evicted-agents.md +0 -306
  136. package/docs/plans/0470-refresh-readme-command-surface.md +0 -159
  137. package/docs/retro/0048-implement-subagents-api.md +0 -44
  138. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  139. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  140. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  141. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  142. package/docs/retro/0057-structured-debug-logging.md +0 -77
  143. package/docs/retro/0061-session-format-transcript.md +0 -41
  144. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  145. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  146. package/docs/retro/0070-extract-event-handlers.md +0 -49
  147. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  148. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  149. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  150. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  151. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  152. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  153. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  154. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  155. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  156. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  157. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  158. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  159. package/docs/retro/0109-extract-settings-manager.md +0 -55
  160. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  161. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  162. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  163. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  164. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  165. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  166. package/docs/retro/0116-type-housekeeping.md +0 -42
  167. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  168. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  169. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  170. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  171. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  172. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  173. package/docs/retro/0135-extract-display-helpers.md +0 -38
  174. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  175. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  176. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  177. package/docs/retro/0146-narrow-ui-context.md +0 -70
  178. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  179. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  180. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  181. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  182. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  183. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  184. package/docs/retro/0167-narrow-runner-io.md +0 -63
  185. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  186. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  187. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  188. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  189. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  190. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  191. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  192. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  193. package/docs/retro/0192-define-session-context-interface.md +0 -59
  194. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  195. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  196. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  197. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  198. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  199. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  200. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  201. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  202. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  203. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  204. package/docs/retro/0216-decompose-start-agent.md +0 -80
  205. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  206. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  207. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  208. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  209. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  210. package/docs/retro/0229-agent-born-complete.md +0 -89
  211. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  212. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  213. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  214. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  215. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  216. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  217. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  218. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  219. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  220. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  221. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  222. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  223. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  224. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  225. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  226. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  227. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  228. package/docs/retro/0373-extract-subagent-state.md +0 -94
  229. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  230. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  231. package/docs/retro/0376-extract-manager-observer.md +0 -81
  232. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  233. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  234. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  235. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  236. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  237. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  238. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  239. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  240. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  241. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  242. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  243. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  244. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  245. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  246. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  247. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  248. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -97
  249. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  250. package/docs/retro/0443-consolidate-remaining-test-clone-families.md +0 -28
  251. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  252. package/docs/retro/0445-native-session-navigation.md +0 -122
  253. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  254. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  255. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  256. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  257. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  258. package/docs/retro/0470-refresh-readme-command-surface.md +0 -40
  259. package/src/observation/notification-state.ts +0 -27
  260. package/vitest.config.ts +0 -14
@@ -1,265 +0,0 @@
1
- ---
2
- issue: 230
3
- issue_title: "Extract ConcurrencyQueue from AgentManager (Phase 15, Step 5)"
4
- ---
5
-
6
- # Extract ConcurrencyQueue from AgentManager
7
-
8
- ## Problem Statement
9
-
10
- `AgentManager` tangles two concerns: agent collection management and scheduling.
11
- The scheduling concern — `queue[]`, `runningBackground`, `_getMaxConcurrent`, `drainQueue()`, `finalizeBackgroundRun()`, `notifyConcurrencyChanged()` — is 3 fields + 3 methods of cohesive, separable logic.
12
- `notifyConcurrencyChanged()` is a scheduling method exposed as a public API on the wrong object so that `SettingsManager` can poke the queue after a concurrency limit change.
13
- This cross-concern leak violates SRP and prevents independent testing of the queue.
14
-
15
- ## Goals
16
-
17
- - Extract scheduling logic into a `ConcurrencyQueue` class in `src/lifecycle/concurrency-queue.ts`.
18
- - Delete `notifyConcurrencyChanged()` from `AgentManager` — settings triggers drain on the queue directly via the existing callback wiring.
19
- - Make scheduling independently testable with fast, focused unit tests.
20
- - `AgentManager` becomes a pure collection manager (agents Map, lookup, cleanup, iteration) + observer wiring.
21
-
22
- ## Non-Goals
23
-
24
- - Changing `SettingsManager` internals — `onMaxConcurrentChanged` callback stays; only the wiring target changes (queue.drain instead of manager.notifyConcurrencyChanged).
25
- - Extracting `Agent.resume()` — tracked in #232.
26
- - Changing the concurrency semantics (limits, drain order, foreground bypass).
27
-
28
- ## Background
29
-
30
- ### Dependencies
31
-
32
- Both dependencies are implemented:
33
-
34
- - Issue #229 (Agent.run()) — ✅ closed.
35
- Agent owns its full execution lifecycle; `startAgent` and `SpawnArgs` are gone.
36
- - Issue #231 (runner self-contained) — ✅ closed.
37
- Agent holds the runner at construction.
38
-
39
- ### Current queue surface in AgentManager
40
-
41
- | Member | Kind | Purpose |
42
- | --------------------------------- | ------ | ------------------------------------------------ |
43
- | `queue: string[]` | field | IDs of background agents waiting to start |
44
- | `runningBackground: number` | field | Count of currently running background agents |
45
- | `_getMaxConcurrent: () => number` | field | Injected getter for the concurrency limit |
46
- | `drainQueue()` | method | Start queued agents up to the limit |
47
- | `finalizeBackgroundRun()` | method | Decrement counter, notify observer, drain |
48
- | `notifyConcurrencyChanged()` | method | Public entry point for settings to trigger drain |
49
-
50
- These 6 members form a cohesive unit — they only reference each other and the agents Map (for status checks during drain).
51
-
52
- ### Callers of queue logic in AgentManager
53
-
54
- - `spawn()` — checks `runningBackground >= getMaxConcurrent()`, pushes to `queue` or starts.
55
- - `buildObserver().onStarted` — increments `runningBackground`.
56
- - `buildObserver().onRunFinished` — calls `finalizeBackgroundRun()`.
57
- - `abort()` — filters `queue` to remove an aborted ID.
58
- - `abortAll()` — iterates `queue`, clears it.
59
- - `waitForAll()` — calls `drainQueue()`.
60
- - `dispose()` — clears `queue`.
61
-
62
- ### Agent comment to update
63
-
64
- `agent.ts` line 366 has a comment: "Queue removal stays on AgentManager until #230 extracts ConcurrencyQueue."
65
- This comment should be updated to remove the forward reference.
66
-
67
- ## Design Overview
68
-
69
- ### ConcurrencyQueue class
70
-
71
- ```typescript
72
- export class ConcurrencyQueue {
73
- private queue: string[] = [];
74
- private running = 0;
75
-
76
- constructor(
77
- private readonly getMaxConcurrent: () => number,
78
- private readonly startAgent: (id: string) => void,
79
- ) {}
80
-
81
- isFull(): boolean;
82
- enqueue(id: string): void;
83
- dequeue(id: string): boolean;
84
- markStarted(): void;
85
- markFinished(): void; // running--, drain()
86
- drain(): void;
87
- get queuedIds(): readonly string[];
88
- clear(): void;
89
- }
90
- ```
91
-
92
- ### Design decision: stored start callback
93
-
94
- The issue proposes `drain(start: (id: string) => void)` with the callback as a parameter.
95
- However, the issue also proposes `markFinished()` as no-arg with "running--, drain()" semantics — which contradicts `drain` requiring a callback parameter.
96
-
97
- Resolution: store the `startAgent` callback at construction.
98
- This makes `drain()` and `markFinished()` both no-arg, follows Tell-Don't-Ask (the queue is a self-contained unit), and avoids requiring callers to pass the same callback repeatedly.
99
-
100
- The `startAgent` callback is provided by the wiring layer (`index.ts`) using the established forward-reference-via-closure pattern already used for `onMaxConcurrentChanged`:
101
-
102
- ```typescript
103
- // index.ts
104
- const queue = new ConcurrencyQueue(
105
- () => settings.maxConcurrent,
106
- (id) => {
107
- const agent = manager.getRecord(id);
108
- if (agent?.status !== "queued") return;
109
- agent.promise = agent.run();
110
- },
111
- );
112
- ```
113
-
114
- ### Ordering note
115
-
116
- `markFinished()` calls `drain()` internally.
117
- The current `finalizeBackgroundRun()` order is: decrement → observer notification → drain.
118
- After extraction: `queue.markFinished()` (decrement + drain) → observer notification.
119
- Drain fires before the observer notification.
120
-
121
- This reordering is safe: `drain()` only starts promises (no await), and the observer notification (`onAgentCompleted`) processes the completed agent's data without referencing queue state.
122
-
123
- ### AgentManager after extraction
124
-
125
- ```typescript
126
- export interface AgentManagerOptions {
127
- runner: AgentRunner;
128
- worktrees: WorktreeManager;
129
- queue: ConcurrencyQueue; // was: getMaxConcurrent
130
- getRunConfig?: () => RunConfig;
131
- observer?: AgentManagerObserver;
132
- }
133
- ```
134
-
135
- `AgentManager` loses `queue`, `runningBackground`, `_getMaxConcurrent`, `drainQueue()`, `finalizeBackgroundRun()`, `notifyConcurrencyChanged()`.
136
-
137
- ### Settings wiring
138
-
139
- Before:
140
-
141
- ```typescript
142
- onMaxConcurrentChanged: () => manager.notifyConcurrencyChanged(),
143
- ```
144
-
145
- After:
146
-
147
- ```typescript
148
- onMaxConcurrentChanged: () => queue.drain(),
149
- ```
150
-
151
- `SettingsManager` itself does not change — it still invokes the stored callback.
152
- The callback wiring in `index.ts` targets the queue directly instead of the manager.
153
-
154
- ### Consumer call site (AgentManager.buildObserver)
155
-
156
- ```typescript
157
- private buildObserver(options: AgentSpawnConfig): AgentLifecycleObserver {
158
- return {
159
- onStarted: (agent) => {
160
- if (options.isBackground) this.queue.markStarted();
161
- this.observer?.onAgentStarted(agent);
162
- },
163
- onRunFinished: (agent) => {
164
- if (options.isBackground) {
165
- this.queue.markFinished();
166
- try { this.observer?.onAgentCompleted(agent); }
167
- catch (err) { debugLog("onAgentCompleted observer", err); }
168
- }
169
- },
170
- // onSessionCreated, onCompacted unchanged
171
- };
172
- }
173
- ```
174
-
175
- ### Test helper (createManager)
176
-
177
- ```typescript
178
- function createManager(overrides?: { ...; getMaxConcurrent?: () => number; }) {
179
- let mgr: AgentManager;
180
- const queue = new ConcurrencyQueue(
181
- overrides?.getMaxConcurrent ?? (() => 4),
182
- (id) => {
183
- const record = mgr.getRecord(id);
184
- if (record?.status !== "queued") return;
185
- record.promise = record.run();
186
- },
187
- );
188
- mgr = new AgentManager({ ..., queue });
189
- return { manager: mgr, ..., queue };
190
- }
191
- ```
192
-
193
- The forward-reference-via-closure is safe because `drain()` is never called during construction.
194
- The `getMaxConcurrent` parameter name stays in the test helper for readability; it's passed to `ConcurrencyQueue`.
195
-
196
- ## Module-Level Changes
197
-
198
- | File | Change |
199
- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
200
- | `src/lifecycle/concurrency-queue.ts` | **Add** — new `ConcurrencyQueue` class |
201
- | `src/lifecycle/agent-manager.ts` | **Change** — remove 3 fields, 3 methods; add `queue: ConcurrencyQueue` to options; update `buildObserver`, `spawn`, `abort`, `abortAll`, `waitForAll`, `dispose` |
202
- | `src/lifecycle/agent.ts` | **Change** — update comment on `abort()` (remove #230 forward reference) |
203
- | `src/index.ts` | **Change** — create `ConcurrencyQueue`, pass to manager, wire settings to `queue.drain()` |
204
- | `test/lifecycle/concurrency-queue.test.ts` | **Add** — unit tests for ConcurrencyQueue |
205
- | `test/lifecycle/agent-manager.test.ts` | **Change** — update `createManager` helper to construct ConcurrencyQueue; no queue-behavior tests removed (they remain as integration tests) |
206
- | `docs/architecture/architecture.md` | **Change** — add `concurrency-queue.ts` to layout listing; update agent-manager description |
207
-
208
- ## Test Impact Analysis
209
-
210
- ### New unit tests enabled by extraction
211
-
212
- 1. `isFull()` boundary — returns false when running < max, true when running >= max.
213
- 2. `enqueue()` / `dequeue()` — add/remove from queue, dequeue returns false for missing ID.
214
- 3. `markStarted()` / `markFinished()` — increment/decrement running count.
215
- 4. `drain()` — calls `startAgent` for each queued ID until full; skips when already full; handles empty queue.
216
- 5. `markFinished()` auto-drain — decrement triggers drain of next queued agent.
217
- 6. `clear()` — empties queue without starting agents.
218
- 7. `queuedIds` — snapshot of queue for iteration.
219
-
220
- These tests were previously impossible because queue logic was interleaved with agent creation, observer notifications, and session management in `AgentManager`.
221
-
222
- ### Existing tests that stay as-is
223
-
224
- - "queueing and concurrency with injected stubs" — integration tests verifying end-to-end spawn→queue→drain through the full AgentManager stack.
225
- They still provide value as wiring tests.
226
- - All observer notification tests — test observer wiring which stays in AgentManager.
227
- - Bug race condition tests, worktree tests, execution state tests, lifecycle observer forwarding tests — independent of queue.
228
-
229
- ### Existing tests that need updating
230
-
231
- - `createManager` helper — accepts `getMaxConcurrent` but passes it to `ConcurrencyQueue` constructor instead of `AgentManagerOptions`.
232
-
233
- ## TDD Order
234
-
235
- 1. **Red→Green: ConcurrencyQueue class + tests.**
236
- New `src/lifecycle/concurrency-queue.ts` with `isFull`, `enqueue`, `dequeue`, `markStarted`, `markFinished`, `drain`, `clear`, `queuedIds`.
237
- New `test/lifecycle/concurrency-queue.test.ts` covering: full boundary, enqueue/dequeue, start/finish counting, drain ordering, markFinished auto-drain, clear, empty-queue no-op.
238
- Commit: `feat(pi-subagents): add ConcurrencyQueue class (#230)`
239
-
240
- 2. **Red→Green: Migrate AgentManager to use ConcurrencyQueue.**
241
- Update `AgentManagerOptions`: replace `getMaxConcurrent` with `queue: ConcurrencyQueue`.
242
- Update constructor, `buildObserver`, `spawn`, `abort`, `abortAll`, `waitForAll`, `dispose`.
243
- Delete: `queue` field, `runningBackground` field, `_getMaxConcurrent` field, `notifyConcurrencyChanged()`, `drainQueue()`, `finalizeBackgroundRun()`.
244
- Update `test/lifecycle/agent-manager.test.ts`: revise `createManager` helper to construct `ConcurrencyQueue` internally.
245
- Update `src/index.ts`: construct `ConcurrencyQueue`, pass to `AgentManager`, wire `onMaxConcurrentChanged` to `queue.drain()`.
246
- Update `src/lifecycle/agent.ts`: remove #230 forward-reference comment on `abort()`.
247
- Run `pnpm run check` after this step.
248
- Commit: `refactor(pi-subagents): replace inline queue with ConcurrencyQueue (#230)`
249
-
250
- 3. **Docs: Update architecture.**
251
- Update `docs/architecture/architecture.md`: add `concurrency-queue.ts` to layout listing under `lifecycle/`, update `agent-manager.ts` description from "collection manager + concurrency controller" to "collection manager + observer wiring".
252
- Commit: `docs(pi-subagents): update architecture for ConcurrencyQueue extraction (#230)`
253
-
254
- ## Risks and Mitigations
255
-
256
- | Risk | Mitigation |
257
- | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
258
- | Forward-reference-via-closure in test helper and index.ts could break if drain is called during construction | ConcurrencyQueue constructor does not call drain; drain is only called after agents exist. Same pattern already used for `onMaxConcurrentChanged`. |
259
- | `markFinished()` auto-drain changes ordering (drain before observer notification) | Verified: observer notification only processes the completed agent's data and does not reference queue state. Drain starts promises without awaiting — no observable behavior change. |
260
- | `markStarted()` called synchronously inside drain loop could miscount | Verified: `Agent.run()` calls `observer.onStarted()` synchronously before the first await, so `markStarted()` fires before control returns to the drain while-loop. The running count is always current. |
261
- | Integration tests in agent-manager.test.ts break after migration | Tests continue to work because the `createManager` helper constructs the ConcurrencyQueue internally with the same `getMaxConcurrent` semantics. Queue behavior is preserved. |
262
-
263
- ## Open Questions
264
-
265
- None — the issue's proposed change is unambiguous and both dependencies are implemented.
@@ -1,245 +0,0 @@
1
- ---
2
- issue: 231
3
- issue_title: "Push exec/registry relay deps to runner construction (Phase 15, Step 3)"
4
- ---
5
-
6
- # Push exec/registry relay deps to runner construction
7
-
8
- ## Problem Statement
9
-
10
- `AgentManager` receives `exec` and `registry` in its constructor but never uses them directly.
11
- They are stored as fields solely to relay them into `runner.run()` via the `RunContext` parameter.
12
- This makes `AgentManager` wider than necessary and prevents the runner from being self-contained — a prerequisite for #229 (Agent.run() absorbs startAgent).
13
-
14
- ## Goals
15
-
16
- - Move `exec` and `registry` from `AgentManager` construction to `ConcreteAgentRunner` construction.
17
- - Remove `exec` and `registry` from `AgentManagerOptions` (7 → 5 fields).
18
- - Remove `exec` and `registry` from `RunContext` (4 → 2 fields).
19
- - Group runner-owned dependencies in a `RunnerDeps` interface: `{ io, exec, registry }`.
20
- - Replace `runAgent()`'s `io: RunnerIO` parameter with `deps: RunnerDeps`.
21
-
22
- ## Non-Goals
23
-
24
- - Dissolving `RunContext` entirely — it shrinks to `{ cwd?, parentSession? }`, which is still a coherent per-call grouping.
25
- Issue #229 will likely dissolve it when `Agent.run()` calls the runner directly.
26
- - Changing the `AgentRunner` interface's `run()` signature — callers continue to pass `RunOptions` with `context: RunContext`.
27
- `ConcreteAgentRunner` merges its stored deps before calling `runAgent()`.
28
- - Touching `resume()` or `resumeAgent()` — they don't use `exec` or `registry`.
29
-
30
- ## Background
31
-
32
- Issue #169 extracted `RunContext` from `RunOptions` to group the 4 parent-context fields: `exec`, `registry`, `cwd`, `parentSession`.
33
- The doc comment describes them as "parent environment and identity" fields.
34
- However, 2 of the 4 fields (`exec`, `registry`) are static — identical across every `run()` call — while the other 2 (`cwd`, `parentSession`) vary per spawn.
35
- The static pair are relay-only dependencies on `AgentManager`: stored at construction, never read, only forwarded.
36
-
37
- From the code-design skill, this is a **parameter relay** smell: intermediaries (`AgentManager`) carry fields they never use, only to thread them to the endpoint (`runAgent`).
38
- The fix: put them on the object the endpoint owns — the runner.
39
-
40
- ### Key references
41
-
42
- - `src/lifecycle/agent-manager.ts` — stores `exec` and `registry`, relays them at lines 193–194.
43
- - `src/lifecycle/agent-runner.ts` — `RunContext` interface (line 125), `ConcreteAgentRunner` class (line 189), `runAgent()` free function (line 236).
44
- - `src/index.ts` — constructs both `ConcreteAgentRunner` and `AgentManager` (lines 148–157).
45
- - Phase 15 roadmap in `docs/architecture/architecture.md` § Step 3.
46
-
47
- ## Design Overview
48
-
49
- ### RunnerDeps — grouping runner-owned dependencies
50
-
51
- A new `RunnerDeps` interface groups the three dependencies that the runner owns:
52
-
53
- ```typescript
54
- export interface RunnerDeps {
55
- io: RunnerIO;
56
- exec: ShellExec;
57
- registry: AgentConfigLookup;
58
- }
59
- ```
60
-
61
- `ConcreteAgentRunner` takes `RunnerDeps` at construction:
62
-
63
- ```typescript
64
- export class ConcreteAgentRunner implements AgentRunner {
65
- constructor(private readonly deps: RunnerDeps) {}
66
-
67
- run(snapshot, type, prompt, options) {
68
- return runAgent(snapshot, type, prompt, options, this.deps);
69
- }
70
- }
71
- ```
72
-
73
- `runAgent()` changes its last parameter from `io: RunnerIO` to `deps: RunnerDeps`:
74
-
75
- ```typescript
76
- export async function runAgent(
77
- snapshot: ParentSnapshot,
78
- type: SubagentType,
79
- prompt: string,
80
- options: RunOptions,
81
- deps: RunnerDeps,
82
- ): Promise<RunResult> {
83
- const effectiveCwd = options.context?.cwd ?? snapshot.cwd;
84
- const env = await deps.io.detectEnv(deps.exec, effectiveCwd);
85
- // ...
86
- const cfg = assembleSessionConfig(type, ..., deps.registry, deps.io.assemblerIO);
87
- // ...
88
- }
89
- ```
90
-
91
- ### RunContext shrinks
92
-
93
- `RunContext` loses `exec` and `registry`:
94
-
95
- ```typescript
96
- export interface RunContext {
97
- /** Override working directory (e.g. for worktree isolation). */
98
- cwd?: string;
99
- /** Parent session identity (file path + session ID). */
100
- parentSession?: ParentSessionInfo;
101
- }
102
- ```
103
-
104
- The `AgentRunner.run()` interface is unchanged — callers still pass `RunOptions` with `context: RunContext`.
105
- `ConcreteAgentRunner.run()` reads `exec` and `registry` from its own `deps` instead of from `options.context`.
106
-
107
- ### AgentManager loses 2 fields
108
-
109
- `AgentManagerOptions` removes `exec` and `registry`.
110
- `AgentManager` removes the corresponding private fields and the `this.exec` / `this.registry` relay in `startAgent()`.
111
- The `context` object constructed in `startAgent()` shrinks from 4 fields to 2:
112
-
113
- ```typescript
114
- context: {
115
- cwd: record.worktreeState?.path,
116
- parentSession: options.parentSession,
117
- },
118
- ```
119
-
120
- ### Wiring in index.ts
121
-
122
- ```typescript
123
- const runner = new ConcreteAgentRunner({
124
- io: runnerIO,
125
- exec: (cmd, args, opts) => pi.exec(cmd, args, opts),
126
- registry,
127
- });
128
-
129
- const manager = new AgentManager({
130
- runner,
131
- worktrees: new GitWorktreeManager(process.cwd()),
132
- observer,
133
- getMaxConcurrent: () => settings.maxConcurrent,
134
- getRunConfig: () => settings,
135
- });
136
- ```
137
-
138
- ## Module-Level Changes
139
-
140
- ### `src/lifecycle/agent-runner.ts`
141
-
142
- 1. Add `RunnerDeps` interface (exported): `{ io: RunnerIO; exec: ShellExec; registry: AgentConfigLookup }`.
143
- 2. Remove `exec` and `registry` from `RunContext`.
144
- Update doc comment to reflect the 2 remaining per-call fields.
145
- 3. Update `ConcreteAgentRunner` constructor: accept `RunnerDeps` instead of `RunnerIO`.
146
- 4. Update `ConcreteAgentRunner.run()`: pass `this.deps` to `runAgent()`.
147
- 5. Update `runAgent()`: change last parameter from `io: RunnerIO` to `deps: RunnerDeps`.
148
- Replace `io.` references with `deps.io.`, `options.context.exec` with `deps.exec`, `options.context.registry` with `deps.registry`.
149
-
150
- ### `src/lifecycle/agent-manager.ts`
151
-
152
- 1. Remove `exec: ShellExec` and `registry: AgentTypeRegistry` from `AgentManagerOptions`.
153
- 2. Remove `private readonly exec` and `private readonly registry` fields from `AgentManager`.
154
- 3. Remove assignment of `this.exec` and `this.registry` in the constructor.
155
- 4. Remove `exec: this.exec` and `registry: this.registry` from the `context` object in `startAgent()`.
156
- 5. Remove `ShellExec` and `AgentTypeRegistry` imports (verify no other references first).
157
-
158
- ### `src/index.ts`
159
-
160
- 1. Move `exec` and `registry` from the `AgentManager` constructor call to `ConcreteAgentRunner`:
161
- `new ConcreteAgentRunner({ io: runnerIO, exec: ..., registry })`.
162
- 2. Remove `exec` and `registry` from the `AgentManager({...})` constructor argument.
163
-
164
- ### `test/lifecycle/agent-runner.test.ts`
165
-
166
- 1. Update all `runAgent(..., io)` calls to `runAgent(..., { io, exec, registry: mockAgentLookup })`.
167
- 2. Remove `exec` and `registry` from `context:` objects in `RunOptions`.
168
- `context: { exec, registry: mockAgentLookup }` → `context: {}` or `{}`.
169
-
170
- ### `test/lifecycle/agent-runner-extension-tools.test.ts`
171
-
172
- 1. Same pattern as `agent-runner.test.ts`: update `runAgent(..., io)` last param and strip `exec`/`registry` from `context:`.
173
-
174
- ### `test/lifecycle/concrete-agent-runner.test.ts`
175
-
176
- 1. Update `new ConcreteAgentRunner(io)` → `new ConcreteAgentRunner({ io, exec: vi.fn(), registry })`.
177
- 2. Remove `exec` and `registry` from the `context:` in `runner.run()` call options.
178
-
179
- ### `test/lifecycle/agent-manager.test.ts`
180
-
181
- 1. Remove `exec: vi.fn()` and `registry: testRegistry` from `createManager()` factory.
182
- 2. Remove the `testRegistry` construction and `AgentTypeRegistry` import if no other references exist.
183
-
184
- ### `test/helpers/runner-io.ts`
185
-
186
- 1. No structural changes needed — `createRunnerIO()` returns the `RunnerIO` shape, which is unchanged.
187
- However, add a `createRunnerDeps()` convenience factory that bundles `{ io: createRunnerIO(), exec: vi.fn(), registry: createAgentLookup() }` for runner test files.
188
-
189
- ### `docs/architecture/architecture.md`
190
-
191
- 1. Update the `RunContext` code block in § "RunOptions (12 fields → extract RunContext)" to show only `cwd` and `parentSession`.
192
- 2. Update the field-count description (4 → 2 per-call fields).
193
- 3. Mark Step 3 as complete in the Phase 15 roadmap.
194
-
195
- ## Test Impact Analysis
196
-
197
- 1. No new test surfaces are needed — this is a pure mechanical refactoring (moving constructor parameters).
198
- The existing runner and manager test suites fully cover the behavior.
199
- 2. No existing tests become redundant — all tests exercise the same interactions, just with deps flowing through a different path.
200
- 3. Existing `agent-manager.test.ts` tests remain as-is in coverage scope.
201
- They verify `AgentManager` behavior (spawning, queueing, abort, etc.) independent of runner deps.
202
- 4. Existing `agent-runner.test.ts` and `concrete-agent-runner.test.ts` tests remain.
203
- They verify `runAgent()` and `ConcreteAgentRunner` behavior.
204
- Call-site patterns change but assertions stay the same.
205
-
206
- ## TDD Order
207
-
208
- 1. **Add `RunnerDeps` interface and update `runAgent()` parameter** — define `RunnerDeps`, change `runAgent()`'s last param from `io` to `deps`, update internal references.
209
- Update `agent-runner.test.ts` and `agent-runner-extension-tools.test.ts` call sites.
210
- Commit: `refactor: add RunnerDeps and update runAgent parameter (#231)`
211
-
212
- 2. **Update `ConcreteAgentRunner` to accept `RunnerDeps`** — change constructor from `RunnerIO` to `RunnerDeps`, update `.run()` to pass `this.deps`.
213
- Update `concrete-agent-runner.test.ts`.
214
- Add `createRunnerDeps()` helper to `test/helpers/runner-io.ts`.
215
- Commit: `refactor: ConcreteAgentRunner accepts RunnerDeps (#231)`
216
-
217
- 3. **Remove `exec` and `registry` from `RunContext`** — shrink the interface to 2 fields, update doc comment.
218
- Strip `exec`/`registry` from `context:` in all runner test call sites.
219
- Run `pnpm run check` to verify no stale references.
220
- Commit: `refactor: remove exec and registry from RunContext (#231)`
221
-
222
- 4. **Remove `exec` and `registry` from `AgentManager`** — remove from `AgentManagerOptions`, remove class fields, remove relay in `startAgent()`, clean up imports.
223
- Update `agent-manager.test.ts` factory.
224
- Commit: `refactor: remove relay deps from AgentManager (#231)`
225
-
226
- 5. **Update wiring in `index.ts`** — move `exec` and `registry` from `AgentManager` construction to `ConcreteAgentRunner` construction.
227
- Commit: `refactor: wire exec and registry to ConcreteAgentRunner (#231)`
228
-
229
- 6. **Update architecture docs** — update `RunContext` description and field counts, mark Step 3 complete.
230
- Commit: `docs: update architecture for runner self-contained (#231)`
231
-
232
- ## Risks and Mitigations
233
-
234
- 1. **Test churn** — ~20 `runAgent()` call sites change their last parameter pattern.
235
- Mitigation: mechanical find-and-replace; assertions stay identical.
236
- 2. **Step ordering** — Steps 3 and 4 both remove `exec`/`registry` from different types.
237
- If done in the wrong order, intermediate commits may not type-check.
238
- Mitigation: Step 1–2 add the new path (`deps`), Step 3 removes from `RunContext` (runner side), Step 4 removes from `AgentManager` (manager side), Step 5 wires them together.
239
- Each commit is independently valid.
240
- 3. **Import cleanup** — removing `exec`/`registry` from `AgentManager` may leave unused imports (`ShellExec`, `AgentTypeRegistry`).
241
- Mitigation: grep for other usages before removing; `pnpm run check` catches unused imports.
242
-
243
- ## Open Questions
244
-
245
- - None — the issue scope is narrow and the design is straightforward.