@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,247 +0,0 @@
1
- ---
2
- issue: 80
3
- issue_title: "refactor: consolidate getConfig / getAgentConfig into a single resolution path"
4
- ---
5
-
6
- # Consolidate agent config lookup into resolveAgentConfig
7
-
8
- ## Problem Statement
9
-
10
- `agent-types.ts` exports two overlapping lookup functions:
11
-
12
- - `getConfig(type)` — returns a narrow shape (`displayName`, `description`, `builtinToolNames`, `extensions`, `skills`, `promptMode`) with a guaranteed-non-null fallback chain (unknown → general-purpose → absolute fallback).
13
- - `getAgentConfig(type)` — returns the full `AgentConfig | undefined`.
14
-
15
- Every field `getConfig()` returns also exists on `AgentConfig`.
16
- Callers that need both must call both and keep them in sync:
17
-
18
- ```typescript
19
- const config = getConfig(type);
20
- const agentConfig = getAgentConfig(type);
21
- ```
22
-
23
- This showed up concretely in `session-config.ts` (extracted in #71), where the assembler calls both, then handles the `agentConfig === undefined` fallback separately — duplicating the same fallback chain that `getConfig()` already handles internally.
24
- Test mocks must also set up both `mockGetConfig` and `mockGetAgentConfig` with compatible values, which is error-prone.
25
-
26
- ## Goals
27
-
28
- - Add a single `resolveAgentConfig(type): AgentConfig` function that returns a guaranteed-non-null `AgentConfig`, handling the fallback chain internally (unknown → general-purpose → absolute fallback).
29
- - Migrate all callers of `getConfig()` and `getAgentConfig()` to `resolveAgentConfig()`.
30
- - Remove `getConfig()` and `getAgentConfig()`.
31
- - Simplify test mocks from two compatible stubs to one.
32
-
33
- This is a pure internal refactor — no behavior change, no public API impact (the package's `exports` only expose `service.ts`).
34
-
35
- ## Non-Goals
36
-
37
- - Changing the `AgentConfig` type shape.
38
- - Refactoring `getToolNamesForType()` (it has its own lookup logic and is a separate concern).
39
- - Modifying the public API surface (`service.ts`).
40
- - Changing the fallback semantics (the chain stays: type → general-purpose → absolute fallback).
41
-
42
- ## Background
43
-
44
- ### Relevant modules
45
-
46
- | Module | Role |
47
- | --------------------- | --------------------------------------------------------------------------------------------------------------------- |
48
- | `agent-types.ts` | Unified agent type registry — owns `getConfig`, `getAgentConfig`, `resolveType`, `getToolNamesForType`, `isValidType` |
49
- | `session-config.ts` | Pure configuration assembler — calls both `getConfig` + `getAgentConfig` |
50
- | `ui/agent-widget.ts` | Widget rendering — calls `getConfig` via `getDisplayName()` and `getPromptModeLabel()` helpers |
51
- | `index.ts` | Extension entry point — calls `getAgentConfig` (3 sites) for type listing and model label |
52
- | `tools/agent-tool.ts` | Agent tool handler — calls `getAgentConfig` (1 site) after `resolveType()` |
53
- | `ui/agent-menu.ts` | Agent menu UI — calls `getAgentConfig` (5 sites) for listing, detail, and existence checks |
54
-
55
- ### Current caller inventory
56
-
57
- Source files that import `getConfig` or `getAgentConfig`:
58
-
59
- | File | `getConfig` calls | `getAgentConfig` calls | Notes |
60
- | --------------------- | ----------------- | ---------------------- | --------------------------------------------- |
61
- | `session-config.ts` | 1 | 1 | Primary motivation; dual-call pattern |
62
- | `ui/agent-widget.ts` | 2 (via helpers) | 0 | `getDisplayName()`, `getPromptModeLabel()` |
63
- | `index.ts` | 0 | 3 | Iterates known names; `?.` is defensive |
64
- | `tools/agent-tool.ts` | 0 | 1 | After `resolveType()` — guaranteed to exist |
65
- | `ui/agent-menu.ts` | 0 | 5 | 3 iterate known names; 2 are existence guards |
66
-
67
- Test files that mock `agent-types.js` with both functions:
68
-
69
- | Test file | Mocks `getConfig` | Mocks `getAgentConfig` |
70
- | -------------------------------------- | ----------------- | ---------------------- |
71
- | `session-config.test.ts` | ✓ | ✓ |
72
- | `agent-runner.test.ts` | ✓ | ✓ |
73
- | `agent-runner-extension-tools.test.ts` | ✓ | ✓ |
74
-
75
- ### Caller migration notes
76
-
77
- - **`session-config.ts`**: Replace both calls with one `resolveAgentConfig(type)`.
78
- Read `extensions` and `skills` directly from the returned `AgentConfig` instead of the narrow shape.
79
- The prompt-building fallback (`agentConfig` null → use `DEFAULT_AGENTS.get("general-purpose")`) collapses into the single call since `resolveAgentConfig` already handles the fallback.
80
- - **`ui/agent-widget.ts`**: `getDisplayName()` becomes `resolveAgentConfig(type).displayName ?? resolveAgentConfig(type).name` (or cache the result).
81
- `getPromptModeLabel()` reads `.promptMode` from the resolved config.
82
- - **`index.ts`**: All 3 call sites iterate names from `getDefaultAgentNames()` or `getUserAgentNames()` — configs are guaranteed to exist.
83
- Direct replacement with `resolveAgentConfig()`.
84
- - **`tools/agent-tool.ts`**: Called after `resolveType()` — config guaranteed.
85
- Direct replacement.
86
- - **`ui/agent-menu.ts`**: 3 of 5 calls iterate `getAllTypes()` — configs guaranteed.
87
- The 2 defensive existence guards (lines 187, 248) switch to `resolveType(name) != null` checks, then use `resolveAgentConfig()` for the config.
88
-
89
- ### Constraints from AGENTS.md
90
-
91
- - Keep modules focused and composable (one concern per file).
92
- - Prefer explicit configuration over hidden behavior.
93
- - Use `vi.hoisted()` for module-level mocks, `.mockClear()` when the factory provides a default.
94
- - Run `pnpm run check` after interface changes.
95
-
96
- ## Design Overview
97
-
98
- ### `resolveAgentConfig` semantics
99
-
100
- ```typescript
101
- export function resolveAgentConfig(type: string): AgentConfig
102
- ```
103
-
104
- 1. Case-insensitive key lookup via `resolveKey(type)`.
105
- 2. If found and `enabled !== false`, return the `AgentConfig`.
106
- 3. Fallback to `general-purpose` (if present and enabled).
107
- 4. Absolute fallback: a synthetic `AgentConfig` with safe defaults (same values as today's `getConfig` absolute fallback, plus the missing `AgentConfig` fields like `name`, `systemPrompt`).
108
-
109
- The function is pure (reads from the module-level `agents` map) and deterministic.
110
-
111
- ### Absolute fallback shape
112
-
113
- The absolute fallback is a complete `AgentConfig` synthesized inline, matching the current `getConfig` absolute fallback values plus required `AgentConfig` fields:
114
-
115
- ```typescript
116
- {
117
- name: type,
118
- displayName: "Agent",
119
- description: "General-purpose agent for complex, multi-step tasks",
120
- builtinToolNames: BUILTIN_TOOL_NAMES,
121
- extensions: true,
122
- skills: true,
123
- systemPrompt: "",
124
- promptMode: "append",
125
- }
126
- ```
127
-
128
- ### session-config.ts simplification
129
-
130
- Before (two calls, two variables, null-check branching):
131
-
132
- ```typescript
133
- const config = getConfig(type);
134
- const agentConfig = getAgentConfig(type);
135
- // ...
136
- const extensions = options.isolated ? false : config.extensions;
137
- const skills = options.isolated ? false : config.skills;
138
- // ...
139
- if (agentConfig) {
140
- systemPrompt = buildAgentPrompt(agentConfig, ...);
141
- } else {
142
- const fallback = DEFAULT_AGENTS.get("general-purpose");
143
- systemPrompt = buildAgentPrompt({ ...fallback, name: type }, ...);
144
- }
145
- ```
146
-
147
- After (one call, no null checks):
148
-
149
- ```typescript
150
- const agentConfig = resolveAgentConfig(type);
151
- // ...
152
- const extensions = options.isolated ? false : agentConfig.extensions;
153
- const skills = options.isolated ? false : agentConfig.skills;
154
- // ...
155
- systemPrompt = buildAgentPrompt(agentConfig, ...);
156
- ```
157
-
158
- The `DEFAULT_AGENTS` import in `session-config.ts` becomes unnecessary.
159
-
160
- ## Module-Level Changes
161
-
162
- ### `src/agent-types.ts`
163
-
164
- - **Add** `resolveAgentConfig(type: string): AgentConfig` — guaranteed-non-null lookup with fallback chain.
165
- - **Remove** `getConfig(type)` — replaced by `resolveAgentConfig`.
166
- - **Remove** `getAgentConfig(type)` — replaced by `resolveAgentConfig`.
167
-
168
- ### `src/session-config.ts`
169
-
170
- - **Change** imports: replace `getConfig`, `getAgentConfig` with `resolveAgentConfig`.
171
- - **Remove** `DEFAULT_AGENTS` import (no longer needed for the unknown-type fallback prompt path).
172
- - **Simplify** `assembleSessionConfig()`: one `resolveAgentConfig(type)` call replaces two; remove the `if (agentConfig)` / `else` branch for prompt building.
173
-
174
- ### `src/ui/agent-widget.ts`
175
-
176
- - **Change** import: replace `getConfig` with `resolveAgentConfig`.
177
- - **Update** `getDisplayName()`: `const config = resolveAgentConfig(type); return config.displayName ?? config.name;`.
178
- - **Update** `getPromptModeLabel()`: read `promptMode` from resolved config.
179
-
180
- ### `src/index.ts`
181
-
182
- - **Change** import: replace `getAgentConfig` with `resolveAgentConfig`.
183
- - **Update** 3 call sites: direct replacement (all iterate known names).
184
-
185
- ### `src/tools/agent-tool.ts`
186
-
187
- - **Change** import: replace `getAgentConfig` with `resolveAgentConfig`.
188
- - **Update** 1 call site: direct replacement (type already resolved via `resolveType()`).
189
-
190
- ### `src/ui/agent-menu.ts`
191
-
192
- - **Change** import: replace `getAgentConfig` with `resolveAgentConfig`, add `resolveType` if not already imported.
193
- - **Update** 5 call sites: 3 are direct replacements; 2 existence guards switch to `resolveType(name) != null` before calling `resolveAgentConfig()`.
194
-
195
- ### Test files
196
-
197
- - **`test/agent-types.test.ts`**: Add tests for `resolveAgentConfig`; remove tests for `getConfig` and `getAgentConfig`.
198
- - **`test/session-config.test.ts`**: Replace `mockGetConfig` + `mockGetAgentConfig` with a single `mockResolveAgentConfig`; remove all `mockGetConfig` usages.
199
- - **`test/agent-runner.test.ts`**: Update `vi.mock("../src/agent-types.js")` factory: replace `getConfig` + `getAgentConfig` with `resolveAgentConfig`.
200
- - **`test/agent-runner-extension-tools.test.ts`**: Same mock update as above.
201
-
202
- ## Test Impact Analysis
203
-
204
- 1. **New unit tests enabled**: `resolveAgentConfig` gets focused tests for the fallback chain (unknown → general-purpose → absolute fallback), case-insensitive lookup, and disabled-type fallback.
205
- These replace the scattered `getConfig` fallback tests in `agent-types.test.ts`.
206
- 2. **Redundant tests**: `getConfig`-specific tests (return shape, fallback, extension allowlist) become redundant since `resolveAgentConfig` returns the full `AgentConfig` directly.
207
- Remove them.
208
- 3. **Tests that must stay**: `agent-types.test.ts` tests for `registerAgents`, `resolveType`, `isValidType`, `getAvailableTypes`, `getAllTypes`, `getToolNamesForType`, `getMemoryToolNames`, `getReadOnlyMemoryToolNames` are unaffected.
209
- `session-config.test.ts` tests all stay — they test assembler behavior, just with a simpler mock setup.
210
-
211
- ## TDD Order
212
-
213
- 1. **Add `resolveAgentConfig` with tests** — add the function to `agent-types.ts` alongside existing functions.
214
- Tests: known type returns config; unknown type falls back to general-purpose; disabled type falls back; absolute fallback when general-purpose is missing; case-insensitive lookup.
215
- Commit: `feat: add resolveAgentConfig with guaranteed-non-null fallback chain`.
216
-
217
- 2. **Migrate `session-config.ts` and its tests** — replace `getConfig` + `getAgentConfig` imports with `resolveAgentConfig`; remove `DEFAULT_AGENTS` import; simplify the assembler body (one call, no null-check branch).
218
- Update `session-config.test.ts`: replace `mockGetConfig` + `mockGetAgentConfig` with single `mockResolveAgentConfig`; update all test setups.
219
- Commit: `refactor: migrate session-config to resolveAgentConfig`.
220
-
221
- 3. **Migrate `agent-widget.ts`** — update `getDisplayName()` and `getPromptModeLabel()` to use `resolveAgentConfig`.
222
- Commit: `refactor: migrate agent-widget to resolveAgentConfig`.
223
-
224
- 4. **Migrate remaining source callers** — update `index.ts` (3 sites), `tools/agent-tool.ts` (1 site), `ui/agent-menu.ts` (5 sites, including 2 existence-guard rewrites).
225
- Commit: `refactor: migrate remaining callers to resolveAgentConfig`.
226
-
227
- 5. **Update transitive test mocks** — update `agent-runner.test.ts` and `agent-runner-extension-tools.test.ts` mock factories to export `resolveAgentConfig` instead of `getConfig` + `getAgentConfig`.
228
- Commit: `test: update agent-runner test mocks for resolveAgentConfig`.
229
-
230
- 6. **Remove `getConfig` and `getAgentConfig`** — delete both functions from `agent-types.ts`; remove their tests from `agent-types.test.ts`.
231
- Run `pnpm run check` to verify no remaining references.
232
- Commit: `refactor: remove getConfig and getAgentConfig`.
233
-
234
- ## Risks and Mitigations
235
-
236
- | Risk | Mitigation |
237
- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
238
- | Overlooked caller of removed functions | Grep all `src/` and `test/` files for `getConfig` and `getAgentConfig` before the removal step; `pnpm run check` catches any remaining imports. |
239
- | `agent-menu.ts` existence guards behave differently with `resolveType()` | `resolveType()` is already the canonical existence check used by `agent-tool.ts`; the guard semantics are identical. |
240
- | Absolute fallback `AgentConfig` shape drift | The absolute fallback is a single inline literal — any future `AgentConfig` field additions will cause a type error at the definition site. |
241
- | Test mock compatibility during migration | Lift-and-shift: `resolveAgentConfig` coexists with old functions until all callers are migrated; each step leaves tests green. |
242
-
243
- ## Open Questions
244
-
245
- - `getToolNamesForType()` does its own lookup with similar fallback logic.
246
- It could be simplified to delegate to `resolveAgentConfig()`, but that is out of scope for this issue.
247
- Consider a follow-up if the duplication becomes a maintenance concern.
@@ -1,142 +0,0 @@
1
- ---
2
- issue: 84
3
- issue_title: "refactor: extract GitWorktreeManager class from worktree.ts free functions"
4
- ---
5
-
6
- # Extract GitWorktreeManager class
7
-
8
- ## Problem Statement
9
-
10
- `worktree.ts` exports three free functions — `createWorktree(cwd, id)`, `cleanupWorktree(cwd, wt, desc)`, and `pruneWorktrees(cwd)` — that all operate on git worktrees relative to a repository root.
11
- Every caller threads `cwd` as the first argument.
12
- These functions form a cohesive unit with natural shared state (the repo root), but today there is no object to capture it.
13
-
14
- Issue #72 (dependency-inject `AgentManager`'s collaborators) needs a `WorktreeManager` interface to inject.
15
- Extracting the class first keeps #72 a clean DI + constructor refactor instead of mixing object extraction with dependency injection.
16
-
17
- ## Goals
18
-
19
- - Define a `WorktreeManager` interface in `worktree.ts` with three methods: `create(id)`, `cleanup(wt, desc)`, `prune()` — no `cwd` parameter.
20
- - Add a `GitWorktreeManager` class that captures `cwd` at construction and delegates to the existing free functions.
21
- - Export both the interface and the class.
22
- - Existing free functions stay exported and unchanged.
23
- - No behavior change.
24
-
25
- ## Non-Goals
26
-
27
- - Changing `AgentManager` to use the new class (that is #72).
28
- - Refactoring the internal implementation of `createWorktree`, `cleanupWorktree`, or `pruneWorktrees`.
29
- - Publishing `WorktreeManager` as a cross-extension API via `Symbol.for()` — it is internal to this package.
30
-
31
- ## Background
32
-
33
- ### Prerequisites
34
-
35
- None — this is the first step in the #72 dependency chain.
36
-
37
- ### Relevant modules
38
-
39
- | Module | Role |
40
- | ----------------------- | ---------------------------------------------------------- |
41
- | `src/worktree.ts` | Free functions for git worktree create/cleanup/prune |
42
- | `test/worktree.test.ts` | Integration tests that create real git repos and worktrees |
43
- | `src/agent-manager.ts` | Only consumer of the three free functions (6 call sites) |
44
-
45
- ### Constraints from AGENTS.md / code-style
46
-
47
- - Keep modules focused and composable (one concern per file).
48
- - When a shared interface references a collaborator, use a narrow interface type — not the concrete class.
49
- - Business logic should be pure functions wherever possible — keep IO at the edges.
50
-
51
- The free functions are IO (they shell out to `git`), so wrapping them in a class that captures `cwd` is the right level of abstraction — the class is a thin adapter, not business logic.
52
-
53
- ## Design Overview
54
-
55
- ### Interface and class
56
-
57
- Added at the bottom of `worktree.ts`, after the existing free functions:
58
-
59
- ```typescript
60
- export interface WorktreeManager {
61
- create(id: string): WorktreeInfo | undefined;
62
- cleanup(wt: WorktreeInfo, description: string): WorktreeCleanupResult;
63
- prune(): void;
64
- }
65
-
66
- export class GitWorktreeManager implements WorktreeManager {
67
- constructor(private readonly cwd: string) {}
68
-
69
- create(id: string): WorktreeInfo | undefined {
70
- return createWorktree(this.cwd, id);
71
- }
72
-
73
- cleanup(wt: WorktreeInfo, description: string): WorktreeCleanupResult {
74
- return cleanupWorktree(this.cwd, wt, description);
75
- }
76
-
77
- prune(): void {
78
- pruneWorktrees(this.cwd);
79
- }
80
- }
81
- ```
82
-
83
- ### Why the interface lives in `worktree.ts`
84
-
85
- The `WorktreeManager` interface references `WorktreeInfo` and `WorktreeCleanupResult`, which are already defined in `worktree.ts`.
86
- Co-locating avoids a separate file for a 5-line interface.
87
- This matches the #72 plan's expectation: `import type { WorktreeManager } from "./worktree.js"`.
88
-
89
- ### No changes to callers
90
-
91
- `agent-manager.ts` continues importing and calling the free functions directly.
92
- Issue #72 handles the migration to the injected `WorktreeManager`.
93
-
94
- ## Module-Level Changes
95
-
96
- ### `src/worktree.ts` (modified)
97
-
98
- - Add `WorktreeManager` interface (3 methods).
99
- - Add `GitWorktreeManager` class implementing the interface.
100
- - Existing free functions, types, and imports unchanged.
101
-
102
- ### `test/worktree.test.ts` (modified)
103
-
104
- - Add a new `describe("GitWorktreeManager")` block with tests for the class.
105
- - Existing free-function tests stay as-is.
106
-
107
- ## Test Impact Analysis
108
-
109
- ### New unit tests enabled
110
-
111
- 1. `GitWorktreeManager.create` — delegates to `createWorktree` with the captured `cwd`.
112
- 2. `GitWorktreeManager.cleanup` — delegates to `cleanupWorktree` with the captured `cwd`.
113
- 3. `GitWorktreeManager.prune` — delegates to `pruneWorktrees` with the captured `cwd`.
114
-
115
- These are thin delegation tests that verify the class wires `cwd` correctly.
116
- They reuse the same real-git-repo test infrastructure already in `worktree.test.ts`.
117
-
118
- ### Existing tests
119
-
120
- All existing tests in `worktree.test.ts` stay unchanged — they test the free functions directly.
121
-
122
- ## TDD Order
123
-
124
- 1. **RED:** Add `describe("GitWorktreeManager")` with tests for `create`, `cleanup`, and `prune`.
125
- Tests import `GitWorktreeManager` which does not exist yet — compilation fails.
126
- Commit: `test: add GitWorktreeManager tests`
127
-
128
- 2. **GREEN:** Add `WorktreeManager` interface and `GitWorktreeManager` class to `worktree.ts`.
129
- All tests pass.
130
- Run `pnpm run check` to verify types.
131
- Commit: `feat: extract WorktreeManager interface and GitWorktreeManager class (#84)`
132
-
133
- ## Risks and Mitigations
134
-
135
- | Risk | Mitigation |
136
- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
137
- | Class delegation tests are slow because they create real git repos | The existing test suite already does this; the new tests reuse the same `initGitRepo()` helper and add ~3 test cases to an already-fast suite. |
138
- | Future callers bypass the interface and use `GitWorktreeManager` directly | The #72 plan types `AgentManager`'s constructor parameter as `WorktreeManager` (the interface), not the class. Code review enforces this. |
139
-
140
- ## Open Questions
141
-
142
- None — the interface shape is specified by the issue and matches the #72 plan exactly.
@@ -1,240 +0,0 @@
1
- ---
2
- issue: 87
3
- issue_title: "refactor: evolve SubagentRuntime from data bag to object with methods"
4
- ---
5
-
6
- # Evolve SubagentRuntime from data bag to object with methods
7
-
8
- ## Problem Statement
9
-
10
- `SubagentRuntime` (introduced in #69) consolidates all mutable extension state into one object, but it remains a plain data bag — an interface with public mutable fields and no methods.
11
- This causes two structural smells in `index.ts`:
12
-
13
- 1. **Output arguments** — handlers write raw fields on the runtime instead of calling methods:
14
- `runtime.currentCtx = { pi, ctx }` and `runtime.currentCtx = undefined`.
15
- 2. **Law of Demeter violations** — 8 occurrences of `runtime.widget!.method()` across 4 call sites in `index.ts`, where callers reach through the runtime to talk to the widget with unsafe `!` non-null assertions.
16
-
17
- Issue #70 (extract event handlers) explicitly lists this issue as a prerequisite.
18
- Without these methods, extracted handlers would just move the output-argument and LoD smells from `index.ts` into handler classes.
19
-
20
- ## Goals
21
-
22
- - Convert `SubagentRuntime` from an interface + factory to a class with methods.
23
- - Add session-context methods: `setSessionContext(pi, ctx)` and `clearSessionContext()`.
24
- - Add widget delegation methods that absorb the `runtime.widget!` reach-throughs: `setUICtx()`, `onTurnStart()`, `markFinished()`, `updateWidget()`, `ensureTimer()`.
25
- - Update all 10 call sites in `index.ts` to use the new methods — eliminate raw `currentCtx` field writes and `widget!` assertions.
26
- - No behavior change; pure structural refactor.
27
-
28
- ## Non-Goals
29
-
30
- - Extracting event handlers into separate files (that is #70).
31
- - Adding methods for `defaultMaxTurns` / `graceTurns` writes — those field writes remain as-is; the issue scope covers `currentCtx` and `widget!` only.
32
- - Changing the `SubagentsService` interface.
33
- - Making `widget` private — `index.ts` still assigns `runtime.widget = new AgentWidget(...)` after construction.
34
- - Adding new features.
35
-
36
- ## Background
37
-
38
- ### Prior art
39
-
40
- `pi-permission-system`'s `PermissionSession` is a class with lifecycle methods (`refreshConfig`, `resetForNewSession`, `shutdown`).
41
- Lifecycle handlers in `src/handlers/lifecycle.ts` call those methods instead of writing fields.
42
- This issue brings `SubagentRuntime` to the same level.
43
-
44
- ### Current runtime shape
45
-
46
- `src/runtime.ts` exports a `SubagentRuntime` interface and a `createSubagentRuntime()` factory:
47
-
48
- ```typescript
49
- export interface SubagentRuntime {
50
- defaultMaxTurns: number | undefined;
51
- graceTurns: number;
52
- currentCtx: { pi: unknown; ctx: unknown } | undefined;
53
- readonly agentActivity: Map<string, AgentActivity>;
54
- widget: AgentWidget | null;
55
- }
56
- ```
57
-
58
- ### Call sites to migrate
59
-
60
- Two `currentCtx` writes (output arguments):
61
-
62
- | Line | Current | After |
63
- | ---- | ---------------------------------- | ------------------------------------ |
64
- | 126 | `runtime.currentCtx = { pi, ctx }` | `runtime.setSessionContext(pi, ctx)` |
65
- | 138 | `runtime.currentCtx = undefined` | `runtime.clearSessionContext()` |
66
-
67
- Eight `widget!` reach-throughs (LoD violations):
68
-
69
- | Line | Current | After |
70
- | ---- | ------------------------------------------- | ----------------------------------- |
71
- | 60 | `runtime.widget!.markFinished(id)` | `runtime.markFinished(id)` |
72
- | 61 | `runtime.widget!.update()` | `runtime.updateWidget()` |
73
- | 149 | `runtime.widget!.setUICtx(ctx.ui as UICtx)` | `runtime.setUICtx(ctx.ui as UICtx)` |
74
- | 150 | `runtime.widget!.onTurnStart()` | `runtime.onTurnStart()` |
75
- | 204 | `runtime.widget!.setUICtx(ctx as UICtx)` | `runtime.setUICtx(ctx as UICtx)` |
76
- | 205 | `runtime.widget!.ensureTimer()` | `runtime.ensureTimer()` |
77
- | 206 | `runtime.widget!.update()` | `runtime.updateWidget()` |
78
- | 207 | `runtime.widget!.markFinished(id)` | `runtime.markFinished(id)` |
79
-
80
- ### Dependency chain
81
-
82
- Issue #69 (SubagentRuntime) is closed/implemented.
83
- This issue (#87) is a prerequisite for #70 (extract event handlers).
84
- The #70 plan defines narrow handler interfaces (`LifecycleRuntime`, `ToolStartRuntime`) that the runtime class satisfies structurally.
85
-
86
- ### Relevant constraints from AGENTS.md / code-style skill
87
-
88
- - Keep modules focused and composable (one concern per file).
89
- - Do not pass a shared dependency bag to functions that only use a subset — define narrow interfaces per consumer.
90
- - Do not write back into a received dependency bag (output arguments).
91
- - Do not reach through an injected collaborator to talk to a stranger (Law of Demeter).
92
- - When multiple callers perform the same reach-through, the missing abstraction is a method on the intermediate object that delegates internally.
93
-
94
- ## Design Overview
95
-
96
- ### Class conversion
97
-
98
- Convert `SubagentRuntime` from an interface to a class.
99
- Public fields stay as-is — callers that read `runtime.defaultMaxTurns`, `runtime.currentCtx`, `runtime.agentActivity`, etc. continue to work.
100
- The `createSubagentRuntime()` factory becomes a thin alias returning `new SubagentRuntime()`, preserving backward compatibility for `index.ts` and existing tests during the transition.
101
-
102
- ```typescript
103
- export class SubagentRuntime {
104
- defaultMaxTurns: number | undefined = undefined;
105
- graceTurns: number = 5;
106
- currentCtx: { pi: unknown; ctx: unknown } | undefined = undefined;
107
- readonly agentActivity: Map<string, AgentActivity> = new Map();
108
- widget: AgentWidget | null = null;
109
-
110
- setSessionContext(pi: unknown, ctx: unknown): void {
111
- this.currentCtx = { pi, ctx };
112
- }
113
-
114
- clearSessionContext(): void {
115
- this.currentCtx = undefined;
116
- }
117
-
118
- setUICtx(ctx: UICtx): void {
119
- this.widget?.setUICtx(ctx);
120
- }
121
-
122
- onTurnStart(): void {
123
- this.widget?.onTurnStart();
124
- }
125
-
126
- markFinished(id: string): void {
127
- this.widget?.markFinished(id);
128
- }
129
-
130
- updateWidget(): void {
131
- this.widget?.update();
132
- }
133
-
134
- ensureTimer(): void {
135
- this.widget?.ensureTimer();
136
- }
137
- }
138
- ```
139
-
140
- ### Widget delegation null safety
141
-
142
- Current code uses `runtime.widget!.method()` — an unsafe non-null assertion that would throw if widget were null.
143
- The delegation methods use optional chaining (`this.widget?.method()`), which silently no-ops when widget is null.
144
- This is safe and intentional: widget is always assigned before any agent can complete, so the null path is unreachable in practice, but the delegation removes the assertion smell.
145
- The #70 plan explicitly expects this behavior: "Widget null safety: after #87, the runtime's delegation methods handle null internally."
146
-
147
- ### UICtx type import
148
-
149
- `runtime.ts` already imports `AgentActivity` and `AgentWidget` from `ui/agent-widget.ts`.
150
- Adding `UICtx` to the same type import is consistent with the existing dependency.
151
- `UICtx` is a lean local interface (two method signatures), not a Pi SDK type.
152
-
153
- ### What stays unchanged
154
-
155
- - `RunConfig` interface — remains as-is.
156
- - `defaultMaxTurns` / `graceTurns` field writes from settings appliers — out of scope per non-goals.
157
- - `runtime.currentCtx` reads via getter callbacks (`getCtx: () => runtime.currentCtx`) — reads are not output arguments.
158
- - `runtime.widget = new AgentWidget(...)` assignment — `widget` stays public.
159
- - `agentActivity` map usage across notification, tool, and menu deps — unchanged.
160
-
161
- ## Module-Level Changes
162
-
163
- ### `src/runtime.ts` (modified)
164
-
165
- - Convert `SubagentRuntime` from `export interface` to `export class` with field initializers.
166
- - Add `setSessionContext(pi, ctx)` and `clearSessionContext()` methods.
167
- - Add `setUICtx(ctx)`, `onTurnStart()`, `markFinished(id)`, `updateWidget()`, `ensureTimer()` delegation methods.
168
- - Add `UICtx` to the existing type import from `./ui/agent-widget.js`.
169
- - Keep `createSubagentRuntime()` as `() => new SubagentRuntime()` for backward compat.
170
- - Keep `RunConfig` interface unchanged.
171
-
172
- ### `src/index.ts` (modified)
173
-
174
- - Replace `runtime.currentCtx = { pi, ctx }` with `runtime.setSessionContext(pi, ctx)` (line 126).
175
- - Replace `runtime.currentCtx = undefined` with `runtime.clearSessionContext()` (line 138).
176
- - Replace all 8 `runtime.widget!.method()` reach-throughs with `runtime.method()` delegation calls (lines 60, 61, 149, 150, 204–207).
177
- - No import changes needed — `runtime` is already imported via the factory.
178
-
179
- ### `test/runtime.test.ts` (modified)
180
-
181
- - Add tests for `setSessionContext` / `clearSessionContext` methods.
182
- - Add tests for each widget delegation method using duck-typed widget stubs.
183
- - Add test verifying delegation methods no-op when widget is null.
184
- - Existing tests remain — factory defaults, field mutability, instance isolation, and widget assignment all still apply.
185
-
186
- ## Test Impact Analysis
187
-
188
- ### New unit tests enabled
189
-
190
- 1. `test/runtime.test.ts` additions — `setSessionContext` sets `currentCtx` correctly; `clearSessionContext` resets it to `undefined`.
191
- 2. `test/runtime.test.ts` additions — Each widget delegation method forwards to the widget's corresponding method; all delegation methods silently no-op when widget is null.
192
-
193
- ### Existing tests that become redundant
194
-
195
- None.
196
- The existing `runtime.test.ts` tests cover factory defaults, field mutability, and instance isolation — all still valid with the class conversion.
197
- The "fields are independently mutable" test exercises direct field writes, which remain supported.
198
-
199
- ### Existing tests that stay as-is
200
-
201
- - `test/runtime.test.ts` — All 5 existing tests pass unchanged (the class satisfies the same structural contract as the previous interface-based object).
202
- - `test/print-mode.test.ts` — Calls `session_shutdown` via the extension's handler map; transparent to runtime internals.
203
- - All other test files — No dependency on `SubagentRuntime` fields or methods.
204
-
205
- ## TDD Order
206
-
207
- 1. **Convert `SubagentRuntime` to a class; add session-context methods.**
208
- Convert the interface to a class with field initializers matching current defaults.
209
- Add `setSessionContext(pi, ctx)` and `clearSessionContext()` methods.
210
- Update `createSubagentRuntime()` to return `new SubagentRuntime()`.
211
- Add tests in `runtime.test.ts`: `setSessionContext` sets `currentCtx`; `clearSessionContext` resets to `undefined`; round-trip set→clear.
212
- Run existing tests to verify no regressions.
213
- Commit: `feat: add session-context methods to SubagentRuntime`
214
-
215
- 2. **Add widget delegation methods; add tests.**
216
- Add `setUICtx(ctx)`, `onTurnStart()`, `markFinished(id)`, `updateWidget()`, `ensureTimer()` methods to the class.
217
- Add `UICtx` to the type import from `./ui/agent-widget.js`.
218
- Add tests in `runtime.test.ts`: each delegation method forwards to the widget stub; all methods no-op when widget is null.
219
- Commit: `feat: add widget delegation methods to SubagentRuntime`
220
-
221
- 3. **Migrate all call sites in `index.ts` to use the new methods.**
222
- Replace the 2 `currentCtx` writes with `setSessionContext` / `clearSessionContext`.
223
- Replace the 8 `widget!` reach-throughs with delegation methods.
224
- Run full test suite and `pnpm run check`.
225
- Commit: `refactor: use SubagentRuntime methods in extension factory (#87)`
226
-
227
- ## Risks and Mitigations
228
-
229
- | Risk | Mitigation |
230
- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
231
- | Class conversion breaks code that constructs `SubagentRuntime` as a plain object | Only `createSubagentRuntime()` constructs runtime instances (in `index.ts` and tests). The factory is updated to return `new SubagentRuntime()`. No code constructs a `{ ... } as SubagentRuntime` literal. |
232
- | Widget delegation silently swallows errors when widget is null (changes behavior from throw to no-op) | The null path is unreachable in practice — widget is always assigned before any agent completes. The silent no-op is strictly safer than the `!` assertion. The #70 plan explicitly expects this behavior. |
233
- | Adding `UICtx` import to `runtime.ts` increases coupling to the widget module | `runtime.ts` already imports `AgentActivity` and `AgentWidget` from the same module. `UICtx` is a lean 2-method interface, not a Pi SDK type. Coupling is minimal and consistent. |
234
- | Remaining output-argument writes (`defaultMaxTurns`, `graceTurns`) are left unaddressed | Explicitly out of scope per the issue's acceptance criteria. Can be addressed in a follow-up if the pattern becomes painful. |
235
-
236
- ## Open Questions
237
-
238
- - Should `createSubagentRuntime()` be removed in favor of `new SubagentRuntime()` directly?
239
- The factory adds no value over a no-arg constructor, but removing it widens the blast radius without benefit.
240
- Defer — remove it as part of #70 or a future cleanup if it feels redundant.