@gotgenes/pi-subagents 18.0.0 → 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 (257) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +22 -31
  3. package/docs/architecture/architecture.md +157 -204
  4. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  5. package/package.json +5 -6
  6. package/src/lifecycle/subagent.ts +5 -9
  7. package/src/observation/notification.ts +23 -10
  8. package/src/observation/subagent-events-observer.ts +1 -3
  9. package/src/tools/get-result-report.ts +60 -0
  10. package/src/tools/get-result-tool.ts +25 -40
  11. package/src/types.ts +1 -1
  12. package/src/ui/session-navigator.ts +0 -2
  13. package/.prettierignore +0 -5
  14. package/AGENTS.md +0 -7
  15. package/docs/plans/0048-implement-subagents-api.md +0 -304
  16. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  17. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  18. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  19. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  20. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  21. package/docs/plans/0057-structured-debug-logging.md +0 -154
  22. package/docs/plans/0061-session-format-transcript.md +0 -284
  23. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  24. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  25. package/docs/plans/0070-extract-event-handlers.md +0 -306
  26. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  27. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  28. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  29. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  30. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  31. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  32. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  33. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  34. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  35. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  36. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  37. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  38. package/docs/plans/0109-extract-settings-manager.md +0 -276
  39. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  40. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  41. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  42. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  43. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  44. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  45. package/docs/plans/0116-type-housekeeping.md +0 -351
  46. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  47. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  48. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  49. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  50. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  51. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  52. package/docs/plans/0135-extract-display-helpers.md +0 -182
  53. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  54. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  55. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  56. package/docs/plans/0146-narrow-ui-context.md +0 -319
  57. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  58. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  59. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  60. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  61. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  62. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  63. package/docs/plans/0167-narrow-runner-io.md +0 -150
  64. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  65. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  66. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  67. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  68. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  69. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  70. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  71. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  72. package/docs/plans/0192-define-session-context-interface.md +0 -107
  73. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  74. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  75. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  76. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  77. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  78. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  79. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  80. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  81. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  82. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  83. package/docs/plans/0216-decompose-start-agent.md +0 -255
  84. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  85. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  86. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  87. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  88. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  89. package/docs/plans/0229-agent-born-complete.md +0 -564
  90. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  91. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  92. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  93. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  94. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  95. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  96. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  97. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  98. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  99. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  100. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  101. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  102. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  103. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  104. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  105. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  106. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  107. package/docs/plans/0373-extract-subagent-state.md +0 -250
  108. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  109. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  110. package/docs/plans/0376-extract-manager-observer.md +0 -232
  111. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  112. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  113. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  114. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  115. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  116. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  117. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  118. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  119. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  120. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  121. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  122. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  123. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  124. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  125. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  126. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  127. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  128. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  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/retro/0048-implement-subagents-api.md +0 -44
  137. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  138. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  139. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  140. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  141. package/docs/retro/0057-structured-debug-logging.md +0 -77
  142. package/docs/retro/0061-session-format-transcript.md +0 -41
  143. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  144. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  145. package/docs/retro/0070-extract-event-handlers.md +0 -49
  146. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  147. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  148. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  149. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  150. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  151. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  152. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  153. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  154. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  155. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  156. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  157. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  158. package/docs/retro/0109-extract-settings-manager.md +0 -55
  159. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  160. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  161. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  162. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  163. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  164. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  165. package/docs/retro/0116-type-housekeeping.md +0 -42
  166. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  167. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  168. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  169. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  170. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  171. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  172. package/docs/retro/0135-extract-display-helpers.md +0 -38
  173. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  174. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  175. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  176. package/docs/retro/0146-narrow-ui-context.md +0 -70
  177. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  178. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  179. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  180. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  181. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  182. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  183. package/docs/retro/0167-narrow-runner-io.md +0 -63
  184. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  185. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  186. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  187. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  188. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  189. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  190. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  191. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  192. package/docs/retro/0192-define-session-context-interface.md +0 -59
  193. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  194. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  195. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  196. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  197. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  198. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  199. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  200. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  201. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  202. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  203. package/docs/retro/0216-decompose-start-agent.md +0 -80
  204. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  205. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  206. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  207. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  208. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  209. package/docs/retro/0229-agent-born-complete.md +0 -89
  210. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  211. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  212. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  213. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  214. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  215. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  216. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  217. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  218. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  219. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  220. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  221. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  222. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  223. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  224. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  225. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  226. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  227. package/docs/retro/0373-extract-subagent-state.md +0 -94
  228. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  229. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  230. package/docs/retro/0376-extract-manager-observer.md +0 -81
  231. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  232. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  233. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  234. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  235. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  236. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  237. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  238. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  239. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  240. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  241. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  242. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  243. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  244. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  245. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  246. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  247. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -45
  248. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  249. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  250. package/docs/retro/0445-native-session-navigation.md +0 -122
  251. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  252. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  253. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  254. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  255. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  256. package/src/observation/notification-state.ts +0 -27
  257. 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.