@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,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.