@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,322 +0,0 @@
1
- ---
2
- issue: 108
3
- issue_title: "refactor(pi-subagents): extract AgentTypeRegistry class from module-scoped state"
4
- ---
5
-
6
- # Extract AgentTypeRegistry class
7
-
8
- ## Problem Statement
9
-
10
- `agent-types.ts` manages a module-scoped `Map<string, AgentConfig>` mutated by `registerAgents()` and read by 12+ call sites across 6 files.
11
- This is global mutable state hidden behind free functions — tests must call `registerAgents(new Map())` in `beforeEach` to reset it, and `reloadCustomAgents` is threaded as a callback through `AgentToolDeps` and `AgentMenuDeps` because there is no object to own the reload.
12
-
13
- ## Goals
14
-
15
- - Wrap the module-scoped `agents` Map and its free functions into an injectable `AgentTypeRegistry` class.
16
- - Replace the `reloadCustomAgents` callback (threaded through 2 dependency bags) with `registry.reload()`.
17
- - Move `DEFAULT_AGENT_NAMES` from `types.ts` to the registry (it is a constant, not a type).
18
- - Enable test isolation without module resets — each test creates its own registry instance.
19
- - Use lift-and-shift: introduce the class alongside the free functions, migrate consumers incrementally, then remove the free functions.
20
-
21
- ## Non-Goals
22
-
23
- - `SettingsManager` extraction (#109) — separate Phase 7 step.
24
- - `AgentActivityTracker` extraction (#110) — separate Phase 7 step.
25
- - Splitting `AgentRecord` lifecycle state (#111) — separate Phase 7 step.
26
- - Narrowing `AgentConfig` (21 fields) — tracked in the architecture doc but out of scope.
27
- - Moving `BUILTIN_TOOL_NAMES` — it is a constant with no Map dependency, stays as a module export.
28
-
29
- ## Background
30
-
31
- ### Architecture reference
32
-
33
- Phase 7, Step A1 in `docs/architecture/architecture.md`.
34
- Steps A1–A3 are independent and can proceed in any order.
35
- This plan addresses A1 only.
36
-
37
- ### Relevant modules
38
-
39
- | Module | Role | agent-types dependency |
40
- | --------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
41
- | `agent-types.ts` | Module-scoped `agents` Map + 11 free functions | Owns the state |
42
- | `default-agents.ts` | `DEFAULT_AGENTS` Map constant | Read by `registerAgents` |
43
- | `custom-agents.ts` | `loadCustomAgents()` → disk scan | Imports `BUILTIN_TOOL_NAMES` |
44
- | `session-config.ts` | `assembleSessionConfig()` | Imports `resolveAgentConfig`, `getToolNamesForType`, `getMemoryToolNames`, `getReadOnlyMemoryToolNames` |
45
- | `agent-runner.ts` | `runAgent()` → calls `assembleSessionConfig` | No direct import (transitive via session-config) |
46
- | `agent-manager.ts` | `AgentManager` → calls runner | No direct import |
47
- | `tools/agent-tool.ts` | Agent tool definition | Imports `resolveAgentConfig`, `resolveType`; receives `reloadCustomAgents` via `AgentToolDeps` |
48
- | `ui/agent-menu.ts` | `/agents` command handler | Imports `BUILTIN_TOOL_NAMES`, `getAllTypes`, `resolveAgentConfig`, `resolveType`; receives `reloadCustomAgents` via `AgentMenuDeps` |
49
- | `ui/agent-widget.ts` | Widget + `getDisplayName` / `getPromptModeLabel` helpers | Imports `resolveAgentConfig` |
50
- | `ui/conversation-viewer.ts` | Live conversation overlay | Imports `getDisplayName`, `getPromptModeLabel` from `agent-widget.ts` |
51
- | `tools/get-result-tool.ts` | `get_subagent_result` tool | Imports `getDisplayName` from `agent-widget.ts` |
52
- | `index.ts` | Extension entry point, wiring | Imports `registerAgents`, `getAvailableTypes`, `getDefaultAgentNames`, `getUserAgentNames`, `resolveAgentConfig`; defines `reloadCustomAgents` closure |
53
-
54
- ### Test files affected
55
-
56
- | Test file | Current agent-types coupling | Change needed |
57
- | -------------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
58
- | `agent-types.test.ts` (333 lines) | Tests free functions directly, calls `registerAgents()` in `beforeEach` | Add class tests; eventually migrate free-function tests to class tests |
59
- | `session-config.test.ts` (601 lines) | `vi.mock("../src/agent-types.js")` | Pass mock registry param, remove module mock |
60
- | `agent-runner.test.ts` (302 lines) | `vi.mock("../src/agent-types.js")` | Provide mock registry in `RunOptions`, remove module mock |
61
- | `agent-runner-extension-tools.test.ts` (307 lines) | `vi.mock("../src/agent-types.js")` | Same as `agent-runner.test.ts` |
62
- | `tools/agent-tool.test.ts` (240 lines) | Mocks `reloadCustomAgents` in deps | Replace with `registry` in deps |
63
- | `ui/agent-menu.test.ts` (184 lines) | `vi.mock("../../src/agent-types.js")`, mocks `reloadCustomAgents` in deps | Replace with `registry` in deps, remove module mock |
64
- | `agent-widget.test.ts` (26 lines) | Minimal | Pass registry to widget constructor |
65
- | `agent-manager.test.ts` (712 lines) | No `agent-types.js` mock | Add registry to constructor options |
66
-
67
- ### Constraints from AGENTS.md / code-design skill
68
-
69
- - **ISP:** `session-config.ts` should accept a narrow interface (only `resolveAgentConfig` + `getToolNamesForType`), not the full registry class.
70
- - **DIP:** Accept collaborators as parameters; keep IO at the edges.
71
- - **Pi SDK boundaries:** Pure helpers must not import Pi SDK types.
72
- - **Lift-and-shift:** Never plan a single step that rewrites an entire large test file at once.
73
-
74
- ## Design Overview
75
-
76
- ### `AgentTypeRegistry` class
77
-
78
- ```typescript
79
- export class AgentTypeRegistry {
80
- private agents = new Map<string, AgentConfig>();
81
-
82
- constructor(private loadUserAgents: () => Map<string, AgentConfig>) {
83
- this.reload();
84
- }
85
-
86
- /** Re-scan custom agents from disk and merge with defaults. */
87
- reload(): void { /* clear + merge DEFAULT_AGENTS + loadUserAgents() */ }
88
-
89
- resolveAgentConfig(type: string): AgentConfig { /* ... */ }
90
- resolveType(name: string): string | undefined { /* ... */ }
91
- getAvailableTypes(): string[] { /* ... */ }
92
- getAllTypes(): string[] { /* ... */ }
93
- getDefaultAgentNames(): string[] { /* ... */ }
94
- getUserAgentNames(): string[] { /* ... */ }
95
- isValidType(type: string): boolean { /* ... */ }
96
- getToolNamesForType(type: string): string[] { /* ... */ }
97
-
98
- static readonly DEFAULT_AGENT_NAMES = ["general-purpose", "Explore", "Plan"] as const;
99
- }
100
- ```
101
-
102
- The constructor accepts a `loadUserAgents` callback (typically `() => loadCustomAgents(process.cwd())`).
103
- This keeps `process.cwd()` at the edge (in `index.ts`) and makes tests trivial — they pass `() => new Map()` or a fixture.
104
-
105
- ### What stays as free functions
106
-
107
- - **`BUILTIN_TOOL_NAMES`** — constant array, no Map dependency.
108
- Stays as a module-level export in `agent-types.ts`.
109
- - **`getMemoryToolNames` / `getReadOnlyMemoryToolNames`** — pure functions over constant arrays, no Map access.
110
- Stay as module-level exports.
111
-
112
- ### Narrow interface for `session-config.ts`
113
-
114
- ```typescript
115
- /** Narrow registry interface for session-config (ISP). */
116
- export interface AgentConfigLookup {
117
- resolveAgentConfig(type: string): AgentConfig;
118
- getToolNamesForType(type: string): string[];
119
- }
120
- ```
121
-
122
- `assembleSessionConfig` gains a `registry: AgentConfigLookup` parameter.
123
- Tests construct a plain object satisfying this interface — no class instantiation needed.
124
-
125
- ### Threading through the call chain
126
-
127
- `session-config.ts` ← called by `agent-runner.ts` ← called by `AgentManager`:
128
-
129
- 1. `assembleSessionConfig(type, ctx, options, env, registry)` — new param.
130
- 2. `RunOptions` gains `registry: AgentConfigLookup`.
131
- 3. `AgentManagerOptions` gains `registry: AgentTypeRegistry`.
132
- 4. `AgentManager` stores the registry and passes it to the runner via `RunOptions`.
133
-
134
- ### Replacing `reloadCustomAgents` callback
135
-
136
- Before:
137
-
138
- ```typescript
139
- // index.ts
140
- const reloadCustomAgents = () => {
141
- const userAgents = loadCustomAgents(process.cwd());
142
- registerAgents(userAgents);
143
- };
144
-
145
- // AgentToolDeps / AgentMenuDeps
146
- reloadCustomAgents: () => void;
147
- ```
148
-
149
- After:
150
-
151
- ```typescript
152
- // index.ts
153
- const registry = new AgentTypeRegistry(() => loadCustomAgents(process.cwd()));
154
-
155
- // AgentToolDeps / AgentMenuDeps
156
- registry: AgentTypeRegistry;
157
-
158
- // Callers use:
159
- deps.registry.reload();
160
- ```
161
-
162
- ### Display helpers (`getDisplayName`, `getPromptModeLabel`)
163
-
164
- These functions in `agent-widget.ts` call `resolveAgentConfig(type)`.
165
- After the extraction, they accept the registry (or a `resolveAgentConfig` callback) as a parameter.
166
- The `AgentWidget` constructor gains a `registry` parameter and passes it to these helpers internally.
167
- External callers (`conversation-viewer.ts`, `get-result-tool.ts`, etc.) pass the registry they already hold via their deps.
168
-
169
- ## Module-Level Changes
170
-
171
- ### New
172
-
173
- No new files.
174
-
175
- ### Modified
176
-
177
- 1. **`src/agent-types.ts`**
178
- - Add `AgentTypeRegistry` class with all instance methods.
179
- - Add `AgentConfigLookup` interface.
180
- - Keep free functions temporarily (delegation shim during migration).
181
- - Final step: remove free functions, module-scoped `agents` Map, and `registerAgents`.
182
-
183
- 2. **`src/types.ts`**
184
- - Remove `DEFAULT_AGENT_NAMES` constant (moved to `AgentTypeRegistry.DEFAULT_AGENT_NAMES`).
185
-
186
- 3. **`src/session-config.ts`**
187
- - `assembleSessionConfig` gains a `registry: AgentConfigLookup` parameter.
188
- - Remove imports of `resolveAgentConfig`, `getToolNamesForType` from `agent-types.ts`.
189
-
190
- 4. **`src/agent-runner.ts`**
191
- - `RunOptions` gains `registry: AgentConfigLookup`.
192
- - `runAgent` passes `options.registry` to `assembleSessionConfig`.
193
-
194
- 5. **`src/agent-manager.ts`**
195
- - `AgentManagerOptions` gains `registry: AgentTypeRegistry`.
196
- - `AgentManager` stores `this.registry` and passes it into `RunOptions` when calling `runner.run`.
197
-
198
- 6. **`src/tools/agent-tool.ts`**
199
- - `AgentToolDeps`: add `registry: AgentTypeRegistry`, remove `reloadCustomAgents`.
200
- - Replace `resolveType(...)` / `resolveAgentConfig(...)` imports with `deps.registry.resolveType(...)` / `deps.registry.resolveAgentConfig(...)`.
201
- - Replace `deps.reloadCustomAgents()` with `deps.registry.reload()`.
202
-
203
- 7. **`src/ui/agent-menu.ts`**
204
- - `AgentMenuDeps`: add `registry: AgentTypeRegistry`, remove `reloadCustomAgents`.
205
- - Replace `getAllTypes()`/`resolveAgentConfig()`/`resolveType()` imports with `deps.registry.*` calls.
206
- - `BUILTIN_TOOL_NAMES` import stays (it is a constant, not a method).
207
- - Replace `deps.reloadCustomAgents()` with `deps.registry.reload()`.
208
-
209
- 8. **`src/ui/agent-widget.ts`**
210
- - `AgentWidget` constructor gains a `registry: AgentTypeRegistry` (or narrow interface) parameter.
211
- - `getDisplayName(type, registry)` / `getPromptModeLabel(type, registry)` gain a registry parameter.
212
- - Internal render methods use `this.registry`.
213
-
214
- 9. **`src/ui/conversation-viewer.ts`**
215
- - `ConversationViewer` constructor gains a registry parameter.
216
- - Passes it to `getDisplayName` / `getPromptModeLabel` calls.
217
-
218
- 10. **`src/tools/get-result-tool.ts`**
219
- - `GetResultToolDeps` (or equivalent) gains registry.
220
- - Passes it to `getDisplayName` calls.
221
-
222
- 11. **`src/index.ts`**
223
- - Construct `AgentTypeRegistry` with `() => loadCustomAgents(process.cwd())`.
224
- - Pass registry to `AgentManager`, agent-tool deps, menu deps, widget, get-result-tool deps.
225
- - Remove `reloadCustomAgents` closure.
226
- - Remove free-function imports (`registerAgents`, `getDefaultAgentNames`, `getUserAgentNames`, `getAvailableTypes`, `resolveAgentConfig`).
227
- - Use `registry.*` methods directly for `buildTypeListText`.
228
-
229
- ### Removed
230
-
231
- - Free functions from `agent-types.ts`: `registerAgents`, `resolveType`, `resolveAgentConfig`, `getAvailableTypes`, `getAllTypes`, `getDefaultAgentNames`, `getUserAgentNames`, `isValidType`, `getToolNamesForType` (final cleanup step).
232
- - Module-scoped `agents` Map and `resolveKey` helper.
233
- - `DEFAULT_AGENT_NAMES` from `types.ts`.
234
- - `reloadCustomAgents` field from `AgentToolDeps` and `AgentMenuDeps`.
235
-
236
- ## Test Impact Analysis
237
-
238
- ### New tests enabled
239
-
240
- - **Isolation without module resets:** Each test creates its own `AgentTypeRegistry` with a fixture callback, eliminating cross-test state leakage and the `registerAgents(new Map())` ceremony.
241
- - **Reload behavior:** Tests can verify `registry.reload()` picks up new agents without touching module state.
242
-
243
- ### Existing tests that become redundant
244
-
245
- - The existing free-function tests in `agent-types.test.ts` become redundant once the class tests cover the same behavior.
246
- They can be removed in the final cleanup step.
247
-
248
- ### Existing tests that must stay
249
-
250
- - `session-config.test.ts` — tests `assembleSessionConfig` behavior; mock setup changes from `vi.mock("agent-types.js")` to passing a mock `AgentConfigLookup` object.
251
- - `agent-runner.test.ts`, `agent-runner-extension-tools.test.ts` — test runner behavior; mock setup changes from `vi.mock("agent-types.js")` to providing mock registry in `RunOptions`.
252
- - `tools/agent-tool.test.ts` — tests tool handler; deps mock changes from `reloadCustomAgents: vi.fn()` to `registry: mockRegistry`.
253
- - `ui/agent-menu.test.ts` — tests menu handler; deps mock changes similarly.
254
- - `agent-manager.test.ts` — must add a mock registry to constructor options.
255
-
256
- ## TDD Order
257
-
258
- 1. **Create `AgentTypeRegistry` class** — Add class to `agent-types.ts` alongside existing free functions.
259
- Add `AgentConfigLookup` interface.
260
- Test all methods in a new `describe('AgentTypeRegistry')` block in `agent-types.test.ts`: construction, `reload()`, `resolveAgentConfig`, `resolveType`, `getAvailableTypes`, `getAllTypes`, `getDefaultAgentNames`, `getUserAgentNames`, `isValidType`, `getToolNamesForType`.
261
- - Test surface: `agent-types.test.ts` — new describe block
262
- - Commit: `feat(pi-subagents): add AgentTypeRegistry class (#108)`
263
-
264
- 2. **Inject through the config-assembly chain** — `assembleSessionConfig` gains `registry: AgentConfigLookup` param.
265
- `RunOptions` gains `registry: AgentConfigLookup`.
266
- `AgentManagerOptions` gains `registry: AgentTypeRegistry`.
267
- Construct registry in `index.ts` and pass through `AgentManager` → `runAgent` → `assembleSessionConfig`.
268
- Update `session-config.test.ts` (replace `vi.mock("agent-types.js")` with mock `AgentConfigLookup` object), `agent-runner.test.ts` and `agent-runner-extension-tools.test.ts` (provide mock registry in `RunOptions`, remove `vi.mock`), `agent-manager.test.ts` (add registry to constructor options).
269
- - Test surface: `session-config.test.ts`, `agent-runner.test.ts`, `agent-runner-extension-tools.test.ts`, `agent-manager.test.ts`
270
- - Commit: `refactor(pi-subagents): inject registry through config-assembly chain (#108)`
271
-
272
- 3. **Inject into agent tool** — Add `registry: AgentTypeRegistry` to `AgentToolDeps`, remove `reloadCustomAgents`.
273
- Replace `resolveType` / `resolveAgentConfig` module imports with `deps.registry.*` calls.
274
- Replace `deps.reloadCustomAgents()` with `deps.registry.reload()`.
275
- Update `index.ts` agent-tool deps and `tools/agent-tool.test.ts`.
276
- - Test surface: `tools/agent-tool.test.ts`
277
- - Commit: `refactor(pi-subagents): inject registry into agent tool (#108)`
278
-
279
- 4. **Inject into agent menu** — Add `registry: AgentTypeRegistry` to `AgentMenuDeps`, remove `reloadCustomAgents`.
280
- Replace `getAllTypes` / `resolveAgentConfig` / `resolveType` module imports with `deps.registry.*` calls.
281
- Replace `deps.reloadCustomAgents()` with `deps.registry.reload()`.
282
- Update `index.ts` menu deps and `ui/agent-menu.test.ts`.
283
- - Test surface: `ui/agent-menu.test.ts`
284
- - Commit: `refactor(pi-subagents): inject registry into agent menu (#108)`
285
-
286
- 5. **Inject into agent widget and display helpers** — `AgentWidget` constructor gains `registry`.
287
- `getDisplayName(type, registry)` and `getPromptModeLabel(type, registry)` gain registry parameters.
288
- `ConversationViewer` constructor gains registry.
289
- `GetResultToolDeps` gains registry for `getDisplayName` calls.
290
- Update `index.ts` wiring, `agent-widget.test.ts`, and any test files that call these helpers.
291
- - Test surface: `agent-widget.test.ts`, related callers
292
- - Commit: `refactor(pi-subagents): inject registry into agent widget (#108)`
293
-
294
- 6. **Move `DEFAULT_AGENT_NAMES` to registry** — Add static `DEFAULT_AGENT_NAMES` property on `AgentTypeRegistry`.
295
- Remove the constant from `types.ts`.
296
- Grep confirms no import consumers exist — the constant is defined but unused.
297
- - Test surface: `agent-types.test.ts` — add assertion for static property
298
- - Commit: `refactor(pi-subagents): move DEFAULT_AGENT_NAMES to registry (#108)`
299
-
300
- 7. **Remove free-function exports** — Delete `registerAgents`, `resolveType`, `resolveAgentConfig`, `getAvailableTypes`, `getAllTypes`, `getDefaultAgentNames`, `getUserAgentNames`, `isValidType`, `getToolNamesForType`, the module-scoped `agents` Map, and `resolveKey` helper from `agent-types.ts`.
301
- Remove the free-function tests from `agent-types.test.ts` (now covered by class tests).
302
- Remove any remaining free-function imports from `index.ts`.
303
- Verify with `pnpm run check` that no dangling references remain.
304
- - Test surface: `agent-types.test.ts` — remove old describe block
305
- - Commit: `refactor(pi-subagents): remove free-function exports from agent-types (#108)`
306
-
307
- ## Risks and Mitigations
308
-
309
- | Risk | Impact | Mitigation |
310
- | --------------------------------------------------------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------- |
311
- | Large blast radius — 11 source files, 8 test files | Merge conflicts if other PRs land concurrently | Lift-and-shift: free functions keep working until final removal; each step is a valid commit |
312
- | `vi.mock("agent-types.js")` removal in runner tests changes behavior | Tests may expose latent bugs in session-config | Mock `AgentConfigLookup` with the same values the current `vi.mock` provides |
313
- | Display helpers (`getDisplayName`, `getPromptModeLabel`) thread registry through many callers | Signature churn in UI layer | These callers already have a deps bag or constructor params — registry fits naturally |
314
- | `agent-types.test.ts` (333 lines) needs migration from free-function tests to class tests | Large test rewrite in step 7 | Step 1 creates class tests first; step 7 only deletes the now-redundant free-function tests |
315
- | `AgentRunner` interface change (`RunOptions` gains `registry`) | Breaks callers that construct `RunOptions` | Only `agent-manager.ts` constructs `RunOptions`; single-site change |
316
-
317
- ## Open Questions
318
-
319
- - **`getDisplayName` / `getPromptModeLabel` placement:** These are thin display helpers that wrap `resolveAgentConfig`.
320
- The plan proposes adding a registry parameter.
321
- An alternative is to make them methods on the registry itself (e.g., `registry.getDisplayName(type)`), trading purity for convenience.
322
- Decide during implementation based on how natural the call sites feel.
@@ -1,276 +0,0 @@
1
- ---
2
- issue: 109
3
- issue_title: "refactor(pi-subagents): extract SettingsManager class"
4
- ---
5
-
6
- # Extract SettingsManager class
7
-
8
- ## Problem Statement
9
-
10
- The settings read/write/persist cycle is spread across free functions in `settings.ts` (`loadSettings`, `saveSettings`, `applySettings`, `applyAndEmitLoaded`, `saveAndEmitChanged`), a `SettingsAppliers` callback interface, and 6 settings-related fields in `AgentMenuDeps`.
11
- The in-memory values live on two different objects (`SubagentRuntime` for `defaultMaxTurns`/`graceTurns`, `AgentManager` for `maxConcurrent`).
12
- This is mutable state plus the methods that read and write it — a class waiting to happen.
13
-
14
- ## Goals
15
-
16
- - Encapsulate the settings concern into a single testable `SettingsManager` class.
17
- - Own all three in-memory settings values (`defaultMaxTurns`, `graceTurns`, `maxConcurrent`).
18
- - Absorb the `SettingsAppliers` interface and the composite functions `applyAndEmitLoaded`, `saveAndEmitChanged`.
19
- - Collapse the 6 settings-related fields in `AgentMenuDeps` to a single `settings` collaborator (13 → 8 fields).
20
- - Replace `getDefaultMaxTurns` in `AgentToolDeps` with a narrow settings accessor.
21
- - Move `maxConcurrent` ownership from `AgentManager` to `SettingsManager`; `AgentManager` reads via injected function.
22
- - Keep pure helpers (`sanitize`, `loadSettings`, `saveSettings`, `persistToastFor`) as private/internal implementation details.
23
- - This is a non-breaking refactoring change — no public API surface changes.
24
-
25
- ## Non-Goals
26
-
27
- - Changing the persistence format (`subagents.json`) or the global-vs-project merge strategy.
28
- - Extracting `AgentActivityTracker` (#110) — that is the next step in Phase 7.
29
- - Changing the `SubagentsService` public API (`service.ts`).
30
- - Touching `RunConfig` — it stays as-is; `SettingsManager` naturally satisfies it.
31
-
32
- ## Background
33
-
34
- ### Current module map
35
-
36
- | Module | Settings concern |
37
- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
38
- | `settings.ts` | Free functions: `loadSettings`, `saveSettings`, `applySettings`, `applyAndEmitLoaded`, `saveAndEmitChanged`, `persistToastFor`. Types: `SubagentsSettings`, `SettingsAppliers`, `SettingsEmit`. |
39
- | `runtime.ts` | `SubagentRuntime.defaultMaxTurns` and `.graceTurns` — mutable fields read by `AgentManager` via `getRunConfig`. |
40
- | `agent-manager.ts` | `private maxConcurrent` — used for queue decisions (`spawn`, `drainQueue`). Exposed via `get/setMaxConcurrent`. |
41
- | `index.ts` | Wires callbacks: constructs `SettingsAppliers` closures, calls `applyAndEmitLoaded` at startup, builds 6 callback fields for `AgentMenuDeps`. |
42
- | `ui/agent-menu.ts` | `AgentMenuDeps` has 6 settings fields: `getDefaultMaxTurns`, `setDefaultMaxTurns`, `getGraceTurns`, `setGraceTurns`, `snapshotSettings`, `saveSettings`. `AgentMenuManager` has `getMaxConcurrent`, `setMaxConcurrent`. |
43
- | `tools/agent-tool.ts` | `AgentToolDeps.getDefaultMaxTurns` — reads the runtime default for the Agent tool. |
44
-
45
- ### Architecture reference
46
-
47
- Phase 7, Step A2 in `docs/architecture/architecture.md`.
48
- Predecessor A1 (`AgentTypeRegistry`, #108) is complete.
49
-
50
- ### Applicable constraints (from AGENTS.md / code-design)
51
-
52
- - One concern per file — the class consolidates what is currently scattered.
53
- - Prefer explicit configuration over hidden behavior.
54
- - Dependency inversion — consumers accept narrow interfaces, not the concrete class.
55
- - No output arguments — the current `SettingsAppliers` callback pattern writes into external state; the class owns the state directly.
56
- - ES2024 target; pnpm only.
57
-
58
- ## Design Overview
59
-
60
- ### SettingsManager class
61
-
62
- ```typescript
63
- export class SettingsManager {
64
- // Private fields with built-in defaults
65
- private _defaultMaxTurns: number | undefined = undefined;
66
- private _graceTurns: number = 5;
67
- private _maxConcurrent: number = 4; // DEFAULT_MAX_CONCURRENT
68
-
69
- private readonly emit: SettingsEmit;
70
- private readonly cwd: string;
71
-
72
- constructor(deps: { emit: SettingsEmit; cwd: string });
73
-
74
- // ── Property accessors with normalization ──
75
- get defaultMaxTurns(): number | undefined;
76
- set defaultMaxTurns(n: number | undefined); // 0 or undefined → undefined; else max(1, n)
77
-
78
- get graceTurns(): number;
79
- set graceTurns(n: number); // max(1, n)
80
-
81
- get maxConcurrent(): number;
82
- set maxConcurrent(n: number); // max(1, n)
83
-
84
- // ── Lifecycle methods ──
85
-
86
- /** Load merged settings (global + project), apply to in-memory, emit settings_loaded. */
87
- load(): SubagentsSettings;
88
-
89
- /** Snapshot current values for persistence (defaultMaxTurns uses 0 for unlimited). */
90
- snapshot(): { maxConcurrent: number; defaultMaxTurns: number; graceTurns: number };
91
-
92
- /** Persist snapshot, emit settings_changed, return toast. */
93
- saveAndNotify(successMsg: string): { message: string; level: "info" | "warning" };
94
- }
95
- ```
96
-
97
- The setter for `defaultMaxTurns` inlines the `normalizeMaxTurns` logic (`0 → undefined`, else `Math.max(1, n)`) to avoid a new dependency from `settings.ts` → `agent-runner.ts`.
98
- The `normalizeMaxTurns` export in `agent-runner.ts` stays for per-invocation normalization in the Agent tool.
99
-
100
- ### maxConcurrent ownership transfer
101
-
102
- `AgentManager` currently owns `maxConcurrent` and calls `this.drainQueue()` in `setMaxConcurrent`.
103
- After the change:
104
-
105
- 1. `SettingsManager` owns the value.
106
- 2. `AgentManager` accepts a `getMaxConcurrent: () => number` function (injected via `AgentManagerOptions`).
107
- All internal reads (`spawn` queue check, `drainQueue` loop) use `this.getMaxConcurrent()` instead of `this.maxConcurrent`.
108
- 3. `AgentManager.setMaxConcurrent(n)` is replaced with `notifyConcurrencyChanged()` — it only drains the queue; the value has already been set on `SettingsManager` by the caller.
109
- 4. `AgentMenuManager` loses `getMaxConcurrent` and `setMaxConcurrent`; gains `notifyConcurrencyChanged`.
110
- The menu reads `settings.maxConcurrent` directly for display.
111
-
112
- ### Consumer interface narrowing
113
-
114
- Each consumer gets the narrowest type it needs:
115
-
116
- | Consumer | Interface |
117
- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
118
- | `AgentMenuDeps.settings` | `{ maxConcurrent: number; defaultMaxTurns: number \| undefined; graceTurns: number; saveAndNotify(msg: string): { message: string; level: "info" \| "warning" } }` |
119
- | `AgentToolDeps.settings` | `{ readonly defaultMaxTurns: number \| undefined }` |
120
- | `AgentManagerOptions.getMaxConcurrent` | `() => number` (function, not object) |
121
- | `AgentManagerOptions.getRunConfig` | Unchanged — `SettingsManager` satisfies `RunConfig` structurally. |
122
-
123
- ### RunConfig compatibility
124
-
125
- `SettingsManager` has `defaultMaxTurns` and `graceTurns` as readable properties, so it structurally satisfies the existing `RunConfig` interface.
126
- In `index.ts`, the `getRunConfig` option becomes `() => settings` (returning the `SettingsManager` instance directly).
127
-
128
- ### SubagentRuntime cleanup
129
-
130
- `SubagentRuntime.defaultMaxTurns` and `.graceTurns` are removed.
131
- These fields exist solely for settings; after the extraction, `SubagentRuntime` only retains session state and widget delegation.
132
-
133
- ## Module-Level Changes
134
-
135
- ### `src/settings.ts`
136
-
137
- - **Add** `SettingsManager` class (constructor, 3 property accessors, `load`, `snapshot`, `saveAndNotify`).
138
- - **Keep** `SubagentsSettings`, `SettingsEmit`, `loadSettings`, `saveSettings`, `sanitize`, `persistToastFor` as internal helpers (some may become unexported).
139
- - **Remove** `SettingsAppliers` interface.
140
- - **Remove** `applySettings`, `applyAndEmitLoaded`, `saveAndEmitChanged` functions.
141
-
142
- ### `src/runtime.ts`
143
-
144
- - **Remove** `defaultMaxTurns` and `graceTurns` fields from `SubagentRuntime`.
145
- - **Remove** `RunConfig` interface (no longer needed — consumers read from `SettingsManager` directly).
146
-
147
- ### `src/agent-manager.ts`
148
-
149
- - **Change** `AgentManagerOptions`: remove `maxConcurrent?: number`; add `getMaxConcurrent?: () => number`.
150
- - **Change** constructor: store `getMaxConcurrent` function instead of a value.
151
- - **Replace** `private maxConcurrent: number` with `private readonly getMaxConcurrent: () => number`.
152
- - **Replace** `setMaxConcurrent(n)` with `notifyConcurrencyChanged()` (public, just calls `drainQueue()`).
153
- - **Keep** `getRunConfig` — wiring changes in `index.ts` to point at settings.
154
-
155
- ### `src/ui/agent-menu.ts`
156
-
157
- - **Remove** from `AgentMenuDeps`: `getDefaultMaxTurns`, `setDefaultMaxTurns`, `getGraceTurns`, `setGraceTurns`, `snapshotSettings`, `saveSettings`.
158
- - **Add** to `AgentMenuDeps`: `settings` with the narrow inline interface.
159
- - **Remove** from `AgentMenuManager`: `getMaxConcurrent`, `setMaxConcurrent`.
160
- - **Add** to `AgentMenuManager`: `notifyConcurrencyChanged: () => void`.
161
- - **Update** `showSettings`: read from `deps.settings`, write to `deps.settings`, call `deps.manager.notifyConcurrencyChanged()` after concurrency change.
162
- - **Update** `notifyApplied`: call `deps.settings.saveAndNotify(msg)`.
163
-
164
- ### `src/tools/agent-tool.ts`
165
-
166
- - **Remove** `getDefaultMaxTurns` from `AgentToolDeps`.
167
- - **Add** `settings: { readonly defaultMaxTurns: number | undefined }` to `AgentToolDeps`.
168
- - **Update** usage: `deps.getDefaultMaxTurns()` → `deps.settings.defaultMaxTurns`.
169
-
170
- ### `src/index.ts`
171
-
172
- - **Create** `SettingsManager` before `AgentManager`; call `.load()`.
173
- - **Pass** `getMaxConcurrent: () => settings.maxConcurrent` to `AgentManager`.
174
- - **Pass** `getRunConfig: () => settings` to `AgentManager`.
175
- - **Pass** `settings` to `AgentMenuDeps` and `AgentToolDeps`.
176
- - **Remove** the ad-hoc `applyAndEmitLoaded` call and the 6 callback fields.
177
- - **Remove** `runtime.defaultMaxTurns` and `runtime.graceTurns` references.
178
-
179
- ## Test Impact Analysis
180
-
181
- ### New unit tests enabled
182
-
183
- - **Integrated settings lifecycle**: construct → `load()` → mutate → `saveAndNotify()` → verify snapshot and events, all on a single object.
184
- Previously impossible because state was scattered across free functions, callbacks, and two separate objects.
185
- - **Normalization in setters**: direct tests for `set defaultMaxTurns(0) → undefined`, `set graceTurns(0) → 1`, `set maxConcurrent(0) → 1`.
186
- Previously tested only indirectly through `applySettings` + callback mocks.
187
- - **Snapshot consistency**: verify that `snapshot()` reflects the current in-memory state after mutations.
188
-
189
- ### Existing tests that become redundant
190
-
191
- - `applySettings` tests — the SettingsAppliers callback pattern is removed; normalization logic moves into the class setters.
192
- - `applyAndEmitLoaded` tests — absorbed into `SettingsManager.load()` tests.
193
- - `saveAndEmitChanged` tests — absorbed into `SettingsManager.saveAndNotify()` tests.
194
-
195
- ### Existing tests that must stay
196
-
197
- - All `loadSettings` / `saveSettings` / sanitizer tests — these test the I/O + validation layer, which remains as internal helpers.
198
- - `persistToastFor` tests — pure function, still used internally by `saveAndNotify`.
199
- - `agent-menu.test.ts` settings tests — still needed; mock shape changes from 6 fields to a settings object.
200
- - `agent-tool.test.ts` — mock shape changes from `getDefaultMaxTurns` function to `settings` object.
201
- - `agent-manager.test.ts` — mock shape changes from `maxConcurrent` number to `getMaxConcurrent` function.
202
-
203
- ## TDD Order
204
-
205
- ### Cycle 1: SettingsManager — constructor, defaults, get/set normalization
206
-
207
- 1. Red: test constructor produces correct defaults (`defaultMaxTurns: undefined`, `graceTurns: 5`, `maxConcurrent: 4`).
208
- Test setter normalization: `defaultMaxTurns = 0 → undefined`, `graceTurns = 0 → 1`, `maxConcurrent = 0 → 1`, `defaultMaxTurns = 10 → 10`.
209
- 2. Green: implement `SettingsManager` class with private fields, constructor, and property accessors.
210
- 3. Commit: `feat: add SettingsManager class with get/set normalization`
211
-
212
- ### Cycle 2: SettingsManager — load, snapshot, saveAndNotify, events
213
-
214
- 1. Red: test `load()` reads merged settings from disk, applies to in-memory values, emits `subagents:settings_loaded`.
215
- Test `snapshot()` returns current values with `defaultMaxTurns ?? 0`.
216
- Test `saveAndNotify()` persists to disk, emits `subagents:settings_changed`, returns toast.
217
- Test save failure returns warning-level toast.
218
- 2. Green: implement `load()`, `snapshot()`, `saveAndNotify()` using existing `loadSettings`, `saveSettings`, `persistToastFor`.
219
- 3. Commit: `feat: SettingsManager load, save, snapshot, and lifecycle events`
220
-
221
- ### Cycle 3: Narrow AgentMenuDeps — collapse 6 fields to settings
222
-
223
- 1. Red: update `makeDeps` in `agent-menu.test.ts` — replace 6 settings fields with a `settings` mock object; replace `getMaxConcurrent`/`setMaxConcurrent` on manager with `notifyConcurrencyChanged`.
224
- All existing menu tests fail due to mock shape change.
225
- 2. Green: update `AgentMenuDeps` and `AgentMenuManager` interfaces; update `showSettings` and `notifyApplied` to use `deps.settings`.
226
- 3. Run `pnpm run check` to verify types.
227
- 4. Commit: `refactor: collapse settings fields in AgentMenuDeps to SettingsManager`
228
-
229
- ### Cycle 4: Narrow AgentToolDeps — replace getDefaultMaxTurns
230
-
231
- 1. Red: update `makeDeps` in `agent-tool.test.ts` — replace `getDefaultMaxTurns` with `settings: { defaultMaxTurns: undefined }`.
232
- 2. Green: update `AgentToolDeps` interface; update `createAgentTool` to read `deps.settings.defaultMaxTurns`.
233
- 3. Run `pnpm run check`.
234
- 4. Commit: `refactor: replace getDefaultMaxTurns with settings in AgentToolDeps`
235
-
236
- ### Cycle 5: Move maxConcurrent from AgentManager to SettingsManager
237
-
238
- 1. Red: update `agent-manager.test.ts` — replace `maxConcurrent` option with `getMaxConcurrent` function; replace `setMaxConcurrent` calls with `notifyConcurrencyChanged`.
239
- 2. Green: update `AgentManagerOptions` (replace `maxConcurrent?: number` with `getMaxConcurrent?: () => number`), update constructor, replace `private maxConcurrent` with `private readonly getMaxConcurrent`, rename `setMaxConcurrent` → `notifyConcurrencyChanged`.
240
- 3. Run `pnpm run check`.
241
- 4. Commit: `refactor: AgentManager reads maxConcurrent from SettingsManager`
242
-
243
- ### Cycle 6: Wire SettingsManager in index.ts
244
-
245
- 1. Update `index.ts`: create `SettingsManager` before `AgentManager`; call `.load()`; pass to all consumers; remove `applyAndEmitLoaded` call and ad-hoc callback closures.
246
- 2. Run full test suite.
247
- 3. Commit: `refactor: wire SettingsManager in extension init`
248
-
249
- ### Cycle 7: Remove SubagentRuntime settings fields
250
-
251
- 1. Remove `defaultMaxTurns` and `graceTurns` from `SubagentRuntime`.
252
- 2. Remove `RunConfig` interface from `runtime.ts` (import from `settings.ts` if still needed, or inline).
253
- 3. Run `pnpm run check`.
254
- 4. Commit: `refactor: remove settings fields from SubagentRuntime`
255
-
256
- ### Cycle 8: Remove superseded free functions and types
257
-
258
- 1. Remove `SettingsAppliers`, `applySettings`, `applyAndEmitLoaded`, `saveAndEmitChanged` from `settings.ts`.
259
- 2. Remove corresponding test sections from `settings.test.ts`.
260
- 3. Make `loadSettings`, `saveSettings` unexported if no external consumers remain (keep exported if tests import them directly for the sanitizer/IO tests).
261
- 4. Run full test suite.
262
- 5. Commit: `refactor: remove superseded settings free functions and SettingsAppliers`
263
-
264
- ## Risks and Mitigations
265
-
266
- | Risk | Mitigation |
267
- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
268
- | `maxConcurrent` ownership transfer breaks queue drain timing | `notifyConcurrencyChanged()` preserves the drain-on-change behavior; `AgentManager` reads via function on every queue decision, so the value is always current. |
269
- | Large mock updates in agent-menu.test.ts cause merge conflicts | Lift-and-shift: cycles 1–2 add the new class without touching existing code; cycles 3–5 migrate one consumer at a time. |
270
- | `normalizeMaxTurns` logic duplicated between setter and agent-runner.ts | The setter inlines trivial normalization (`0 → undefined`, else `max(1, n)`); `normalizeMaxTurns` stays in `agent-runner.ts` for per-invocation use. Both are simple enough that duplication is cheaper than a shared dependency. |
271
- | `RunConfig` removal from `runtime.ts` breaks imports | Grep all `RunConfig` imports before removing; move the type to `settings.ts` or inline at the use site if needed. |
272
-
273
- ## Open Questions
274
-
275
- - Should `loadSettings` and `saveSettings` remain exported (for the standalone sanitizer/IO tests) or become private to the module?
276
- Defer until cycle 8 — check whether any test imports them directly.