@gotgenes/pi-subagents 18.0.1 → 18.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/docs/architecture/architecture.md +165 -203
  3. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  4. package/package.json +6 -7
  5. package/src/config/default-agents.ts +3 -0
  6. package/src/lifecycle/subagent.ts +25 -14
  7. package/src/observation/notification.ts +23 -10
  8. package/src/observation/subagent-events-observer.ts +1 -3
  9. package/src/runtime.ts +2 -1
  10. package/src/service/service-adapter.ts +20 -17
  11. package/src/session/model-resolver.ts +31 -30
  12. package/src/tools/agent-tool.ts +17 -13
  13. package/src/tools/get-result-report.ts +60 -0
  14. package/src/tools/get-result-tool.ts +25 -40
  15. package/src/tools/helpers.ts +21 -5
  16. package/src/tools/spawn-config.ts +5 -5
  17. package/src/tools/steer-tool.ts +37 -27
  18. package/src/types.ts +4 -1
  19. package/src/ui/session-navigator.ts +0 -2
  20. package/.prettierignore +0 -5
  21. package/AGENTS.md +0 -7
  22. package/docs/plans/0048-implement-subagents-api.md +0 -304
  23. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  24. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  25. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  26. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  27. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  28. package/docs/plans/0057-structured-debug-logging.md +0 -154
  29. package/docs/plans/0061-session-format-transcript.md +0 -284
  30. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  31. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  32. package/docs/plans/0070-extract-event-handlers.md +0 -306
  33. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  34. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  35. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  36. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  37. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  38. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  39. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  40. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  41. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  42. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  43. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  44. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  45. package/docs/plans/0109-extract-settings-manager.md +0 -276
  46. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  47. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  48. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  49. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  50. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  51. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  52. package/docs/plans/0116-type-housekeeping.md +0 -351
  53. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  54. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  55. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  56. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  57. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  58. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  59. package/docs/plans/0135-extract-display-helpers.md +0 -182
  60. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  61. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  62. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  63. package/docs/plans/0146-narrow-ui-context.md +0 -319
  64. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  65. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  66. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  67. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  68. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  69. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  70. package/docs/plans/0167-narrow-runner-io.md +0 -150
  71. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  72. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  73. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  74. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  75. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  76. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  77. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  78. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  79. package/docs/plans/0192-define-session-context-interface.md +0 -107
  80. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  81. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  82. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  83. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  84. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  85. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  86. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  87. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  88. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  89. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  90. package/docs/plans/0216-decompose-start-agent.md +0 -255
  91. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  92. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  93. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  94. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  95. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  96. package/docs/plans/0229-agent-born-complete.md +0 -564
  97. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  98. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  99. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  100. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  101. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  102. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  103. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  104. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  105. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  106. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  107. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  108. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  109. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  110. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  111. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  112. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  113. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  114. package/docs/plans/0373-extract-subagent-state.md +0 -250
  115. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  116. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  117. package/docs/plans/0376-extract-manager-observer.md +0 -232
  118. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  119. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  120. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  121. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  122. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  123. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  124. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  125. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  126. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  127. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  128. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  129. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  130. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  131. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  132. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  133. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  134. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  135. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  136. package/docs/plans/0443-consolidate-remaining-test-clone-families.md +0 -268
  137. package/docs/plans/0444-shrink-widget-to-background-agents.md +0 -156
  138. package/docs/plans/0445-native-session-navigation.md +0 -365
  139. package/docs/plans/0446-spike-session-navigation-entry-criteria.md +0 -171
  140. package/docs/plans/0447-extract-subagents-settings-command.md +0 -220
  141. package/docs/plans/0448-block-disabled-agent-spawn.md +0 -207
  142. package/docs/plans/0462-navigation-renderer-tui-components.md +0 -338
  143. package/docs/plans/0463-file-snapshot-source-evicted-agents.md +0 -306
  144. package/docs/plans/0470-refresh-readme-command-surface.md +0 -159
  145. package/docs/retro/0048-implement-subagents-api.md +0 -44
  146. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  147. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  148. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  149. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  150. package/docs/retro/0057-structured-debug-logging.md +0 -77
  151. package/docs/retro/0061-session-format-transcript.md +0 -41
  152. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  153. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  154. package/docs/retro/0070-extract-event-handlers.md +0 -49
  155. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  156. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  157. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  158. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  159. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  160. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  161. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  162. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  163. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  164. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  165. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  166. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  167. package/docs/retro/0109-extract-settings-manager.md +0 -55
  168. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  169. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  170. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  171. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  172. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  173. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  174. package/docs/retro/0116-type-housekeeping.md +0 -42
  175. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  176. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  177. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  178. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  179. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  180. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  181. package/docs/retro/0135-extract-display-helpers.md +0 -38
  182. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  183. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  184. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  185. package/docs/retro/0146-narrow-ui-context.md +0 -70
  186. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  187. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  188. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  189. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  190. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  191. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  192. package/docs/retro/0167-narrow-runner-io.md +0 -63
  193. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  194. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  195. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  196. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  197. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  198. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  199. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  200. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  201. package/docs/retro/0192-define-session-context-interface.md +0 -59
  202. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  203. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  204. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  205. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  206. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  207. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  208. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  209. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  210. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  211. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  212. package/docs/retro/0216-decompose-start-agent.md +0 -80
  213. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  214. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  215. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  216. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  217. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  218. package/docs/retro/0229-agent-born-complete.md +0 -89
  219. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  220. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  221. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  222. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  223. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  224. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  225. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  226. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  227. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  228. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  229. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  230. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  231. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  232. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  233. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  234. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  235. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  236. package/docs/retro/0373-extract-subagent-state.md +0 -94
  237. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  238. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  239. package/docs/retro/0376-extract-manager-observer.md +0 -81
  240. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  241. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  242. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  243. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  244. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  245. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  246. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  247. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  248. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  249. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  250. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  251. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  252. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  253. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  254. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  255. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  256. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -97
  257. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  258. package/docs/retro/0443-consolidate-remaining-test-clone-families.md +0 -28
  259. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  260. package/docs/retro/0445-native-session-navigation.md +0 -122
  261. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  262. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  263. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  264. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  265. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  266. package/docs/retro/0470-refresh-readme-command-surface.md +0 -40
  267. package/src/observation/notification-state.ts +0 -27
  268. package/vitest.config.ts +0 -14
@@ -1,488 +0,0 @@
1
- ---
2
- issue: 99
3
- issue_title: "Replace live `ctx` capture with ParentSnapshot in AgentManager"
4
- ---
5
-
6
- # Replace live ctx capture with ParentSnapshot
7
-
8
- ## Problem Statement
9
-
10
- `AgentManager.spawn()` captures a live `ctx: ExtensionContext` reference into `SpawnArgs`, which is held in the concurrency queue until a slot opens.
11
- When the queued agent eventually dequeues, `runAgent()` reads from this live reference — `ctx.cwd`, `ctx.getSystemPrompt()`, `ctx.model`, `ctx.modelRegistry` — all of which may have changed since the agent was queued (model switch, cwd change, session restart).
12
-
13
- Additionally, `inheritContext` calls `ctx.sessionManager.getBranch()` at run time, forking the conversation as it exists at dequeue rather than at the point the user asked for the agent.
14
-
15
- `pi: ExtensionAPI` is also stored in `SpawnArgs` but is only relayed to `runner.run()`, which only uses it for `detectEnv()` — a classic parameter-relay smell.
16
-
17
- ## Goals
18
-
19
- - Replace `ctx: ExtensionContext` in `SpawnArgs` with a `ParentSnapshot` data object captured once at spawn time.
20
- - Remove `pi: ExtensionAPI` from `SpawnArgs` by injecting a narrow `ShellExec` callback into `AgentManager` at construction.
21
- - Update `AgentRunner.run()` to accept `ParentSnapshot` instead of `ctx`.
22
- - Narrow `detectEnv()` to accept `ShellExec` instead of the full `ExtensionAPI`.
23
- - Simplify test mocks — plain data snapshots replace SDK mock objects.
24
-
25
- ## Non-Goals
26
-
27
- - Session-event observation / callback-threading removal (architecture.md Step 3, #100) — separate issue, depends on this one.
28
- - Cleaning up `runtime.currentCtx` — it holds a live `{ pi, ctx }` for the service-adapter but is always "current" (set on `session_start`, cleared on `session_shutdown`); staleness is not a risk there.
29
- Simplifying it is a natural follow-up but out of scope.
30
- - Changes to `SpawnOptions` or the public `SubagentsService` API — both are unchanged.
31
-
32
- ## Background
33
-
34
- ### Relevant modules
35
-
36
- | Module | Role in this change |
37
- | ------------------------ | --------------------------------------------------------------------------------------- |
38
- | `src/agent-manager.ts` | Owns `SpawnArgs`, `spawn()`, `startAgent()` — primary target |
39
- | `src/agent-runner.ts` | `runAgent()` consumes `ctx` + `pi`; `AgentRunner` interface defines `run()` signature |
40
- | `src/env.ts` | `detectEnv()` accepts `ExtensionAPI` — narrowing to `ShellExec` |
41
- | `src/context.ts` | `buildParentContext()` reads `ctx.sessionManager.getBranch()` — called at snapshot time |
42
- | `src/session-config.ts` | `AssemblerContext` is already a narrow interface; `runAgent` builds it from ctx today |
43
- | `src/types.ts` | Will host `ParentSnapshot` and `ShellExec` type definitions |
44
- | `src/service-adapter.ts` | `AgentManagerLike.spawn` — narrow interface that mirrors `AgentManager.spawn()` |
45
- | `src/index.ts` | Wires `pi.exec` into `AgentManager`; wraps `spawn`/`spawnAndWait` for tools |
46
- | `src/ui/agent-menu.ts` | `AgentMenuManagerDeps.spawnAndWait` — narrow interface referencing `pi` parameter |
47
-
48
- ### Code-style constraints
49
-
50
- - **Parameter relay** (code-design skill): `pi` threads through `spawn` → `SpawnArgs` → `startAgent` → `RunOptions` → `runAgent` → `detectEnv`.
51
- The intermediaries (`spawn`, `startAgent`) never read `pi`.
52
- Fix: inject exec at the `AgentManager` level.
53
- - **Dependency width** (code-design skill): `runAgent` receives `ctx: ExtensionContext` but only reads 4 fields plus `sessionManager.getBranch()`.
54
- Fix: `ParentSnapshot` — a narrow interface with exactly the fields consumed.
55
- - **Law of Demeter** (design-review): `ctx.sessionManager.getBranch()` is a reach-through that forces tests to mock a nested `sessionManager` object.
56
- Fix: snapshot pre-computes `parentContext` as a string.
57
-
58
- ### Prerequisite status
59
-
60
- - Issue #98 (AgentRecord state machine) — **done**.
61
- `AgentRecord` is a class with encapsulated transition methods.
62
- - Issue #102 (shared test record factory) — **done**.
63
- All test record construction goes through `createTestRecord()`.
64
-
65
- ## Design Overview
66
-
67
- ### ParentSnapshot interface
68
-
69
- A plain data object capturing everything `runAgent()` reads from `ctx`:
70
-
71
- ```typescript
72
- export interface ParentSnapshot {
73
- /** Parent working directory. */
74
- cwd: string;
75
- /** Parent's effective system prompt (for append-mode agents). */
76
- systemPrompt: string;
77
- /** Parent's current model instance (fallback when agent config has no model). */
78
- model: unknown;
79
- /** Model registry for resolving config.model strings and creating sessions. */
80
- modelRegistry: {
81
- find(provider: string, modelId: string): unknown;
82
- getAvailable?(): Array<{ provider: string; id: string }>;
83
- };
84
- /** Pre-built parent conversation text (when inheritContext was requested). */
85
- parentContext?: string;
86
- }
87
- ```
88
-
89
- The `modelRegistry` field is a reference capture, not a data copy — it's a registry object with methods.
90
- This is acceptable because the registry is structurally stable within a session (models don't change at runtime).
91
- The key staleness risks (`cwd`, `systemPrompt`, `model`, conversation state) are all captured as values.
92
-
93
- ### ShellExec type
94
-
95
- A narrow callback type replacing `ExtensionAPI` in `detectEnv`:
96
-
97
- ```typescript
98
- export type ShellExec = (
99
- command: string,
100
- args: string[],
101
- options?: { cwd?: string; timeout?: number },
102
- ) => Promise<{ stdout: string; stderr: string; code: number }>;
103
- ```
104
-
105
- This matches the shape of `pi.exec()` but carries no SDK dependency.
106
-
107
- ### Snapshot built in spawn()
108
-
109
- `spawn()` keeps receiving `ctx` as a parameter (so callers don't need to know about `ParentSnapshot`).
110
- Internally, it immediately snapshots `ctx` and stores the snapshot in `SpawnArgs` — never the live `ctx` reference.
111
-
112
- ```typescript
113
- spawn(ctx: ExtensionContext, type: SubagentType, prompt: string, options: SpawnOptions): string {
114
- const snapshot: ParentSnapshot = buildParentSnapshot(ctx, options.inheritContext);
115
- const args: SpawnArgs = { snapshot, type, prompt, options };
116
- // ...
117
- }
118
- ```
119
-
120
- The `pi` parameter is removed from `spawn()`.
121
- The `exec` function is injected into `AgentManager` at construction time via `AgentManagerOptions`, since `pi.exec` is a stable capability (same function reference for the extension's lifetime).
122
-
123
- ### buildParentSnapshot helper
124
-
125
- A new `src/parent-snapshot.ts` module with a single exported function:
126
-
127
- ```typescript
128
- export function buildParentSnapshot(
129
- ctx: ExtensionContext,
130
- inheritContext?: boolean,
131
- ): ParentSnapshot {
132
- return {
133
- cwd: ctx.cwd,
134
- systemPrompt: ctx.getSystemPrompt(),
135
- model: ctx.model,
136
- modelRegistry: ctx.modelRegistry,
137
- parentContext: inheritContext ? buildParentContext(ctx) : undefined,
138
- };
139
- }
140
- ```
141
-
142
- This is the only place that touches `ExtensionContext` to build a snapshot.
143
- It calls `buildParentContext(ctx)` (from `context.ts`) which reads `ctx.sessionManager.getBranch()` — capturing the conversation at spawn time, not dequeue time.
144
-
145
- ### SpawnArgs changes
146
-
147
- ```typescript
148
- // Before
149
- interface SpawnArgs {
150
- pi: ExtensionAPI;
151
- ctx: ExtensionContext;
152
- type: SubagentType;
153
- prompt: string;
154
- options: SpawnOptions;
155
- }
156
-
157
- // After
158
- interface SpawnArgs {
159
- snapshot: ParentSnapshot;
160
- type: SubagentType;
161
- prompt: string;
162
- options: SpawnOptions;
163
- }
164
- ```
165
-
166
- ### AgentRunner.run() signature change
167
-
168
- ```typescript
169
- // Before
170
- run(ctx: ExtensionContext, type: SubagentType, prompt: string, options: RunOptions): Promise<RunResult>;
171
-
172
- // After
173
- run(snapshot: ParentSnapshot, type: SubagentType, prompt: string, options: RunOptions): Promise<RunResult>;
174
- ```
175
-
176
- ### RunOptions changes
177
-
178
- ```typescript
179
- // Removed fields:
180
- // pi: ExtensionAPI → replaced by exec: ShellExec
181
- // inheritContext?: boolean → handled by snapshot.parentContext
182
-
183
- // Added field:
184
- exec: ShellExec;
185
- ```
186
-
187
- ### runAgent flow changes
188
-
189
- Inside `runAgent()`, all `ctx.*` reads become `snapshot.*` reads:
190
-
191
- ```typescript
192
- // Before
193
- const effectiveCwd = options.cwd ?? ctx.cwd;
194
- const env = await detectEnv(options.pi, effectiveCwd);
195
- const cfg = assembleSessionConfig(type, {
196
- cwd: ctx.cwd,
197
- parentSystemPrompt: ctx.getSystemPrompt(),
198
- parentModel: ctx.model,
199
- modelRegistry: ctx.modelRegistry,
200
- }, ...);
201
-
202
- // After
203
- const effectiveCwd = options.cwd ?? snapshot.cwd;
204
- const env = await detectEnv(options.exec, effectiveCwd);
205
- const cfg = assembleSessionConfig(type, {
206
- cwd: snapshot.cwd,
207
- parentSystemPrompt: snapshot.systemPrompt,
208
- parentModel: snapshot.model,
209
- modelRegistry: snapshot.modelRegistry,
210
- }, ...);
211
- ```
212
-
213
- The `inheritContext` block changes:
214
-
215
- ```typescript
216
- // Before
217
- if (options.inheritContext) {
218
- const parentContext = buildParentContext(ctx);
219
- if (parentContext) effectivePrompt = parentContext + prompt;
220
- }
221
-
222
- // After
223
- if (snapshot.parentContext) {
224
- effectivePrompt = snapshot.parentContext + prompt;
225
- }
226
- ```
227
-
228
- The `createAgentSession()` call changes:
229
-
230
- ```typescript
231
- // Before: modelRegistry: ctx.modelRegistry
232
- // After: modelRegistry: snapshot.modelRegistry as ModelRegistry
233
- ```
234
-
235
- ### AgentManager constructor change
236
-
237
- ```typescript
238
- export interface AgentManagerOptions {
239
- runner: AgentRunner;
240
- worktrees: WorktreeManager;
241
- exec: ShellExec; // NEW — injected from pi.exec
242
- maxConcurrent?: number;
243
- getRunConfig?: () => RunConfig;
244
- onStart?: OnAgentStart;
245
- onComplete?: OnAgentComplete;
246
- onCompact?: OnAgentCompact;
247
- }
248
- ```
249
-
250
- `startAgent()` uses `this.exec` when building `RunOptions`:
251
-
252
- ```typescript
253
- const promise = this.runner.run(snapshot, type, prompt, {
254
- exec: this.exec,
255
- model: options.model,
256
- // ... (no more pi field)
257
- });
258
- ```
259
-
260
- ### Caller updates
261
-
262
- `index.ts`:
263
-
264
- ```typescript
265
- // Before
266
- const manager = new AgentManager({
267
- runner: { run: runAgent, resume: resumeAgent },
268
- worktrees: new GitWorktreeManager(process.cwd()),
269
- // ...
270
- });
271
-
272
- // After — adds exec
273
- const manager = new AgentManager({
274
- runner: { run: runAgent, resume: resumeAgent },
275
- worktrees: new GitWorktreeManager(process.cwd()),
276
- exec: (cmd, args, opts) => pi.exec(cmd, args, opts),
277
- // ...
278
- });
279
- ```
280
-
281
- Spawn wrappers simplify — no more `pi` injection:
282
-
283
- ```typescript
284
- // Before
285
- spawn: (ctx, type, prompt, opts) => manager.spawn(pi, ctx, type, prompt, opts),
286
- // After
287
- spawn: (ctx, type, prompt, opts) => manager.spawn(ctx, type, prompt, opts),
288
- ```
289
-
290
- `service-adapter.ts`:
291
-
292
- ```typescript
293
- // AgentManagerLike.spawn — drops pi parameter
294
- spawn(ctx: unknown, type: string, prompt: string, options: unknown): string;
295
- ```
296
-
297
- `ui/agent-menu.ts` — `spawnAndWait` drops `pi` parameter:
298
-
299
- ```typescript
300
- // Before
301
- spawnAndWait: (pi: ExtensionAPI | null, ctx: ExtensionContext, ...) => Promise<AgentRecord>;
302
- // After
303
- spawnAndWait: (ctx: ExtensionContext, ...) => Promise<AgentRecord>;
304
- ```
305
-
306
- ## Module-Level Changes
307
-
308
- ### New files
309
-
310
- 1. `src/parent-snapshot.ts` — `buildParentSnapshot()` function (cycle 3).
311
- 2. `test/parent-snapshot.test.ts` — unit tests for the builder (cycle 3).
312
-
313
- ### Changed files (source)
314
-
315
- Phase 1 (pi elimination):
316
-
317
- 1. `src/types.ts` — add `ShellExec` type (cycle 1); add `ParentSnapshot` interface (cycle 3).
318
- 2. `src/env.ts` — `detectEnv()` accepts `ShellExec` instead of `ExtensionAPI` (cycle 1).
319
- 3. `src/agent-runner.ts` — `RunOptions` replaces `pi` with `exec` (cycle 1); `runAgent()` first param changes to `ParentSnapshot`, removes `inheritContext` from `RunOptions` (cycle 4).
320
- 4. `src/agent-manager.ts` — `AgentManagerOptions` adds `exec`, `startAgent` uses `this.exec` (cycle 1); `spawn`/`spawnAndWait` drop `pi`, `SpawnArgs` drops `pi` (cycle 2); `spawn` builds snapshot, `SpawnArgs` replaces `ctx` with `snapshot` (cycle 5).
321
- 5. `src/index.ts` — pass `exec` to `AgentManager` constructor (cycle 1); simplify `spawn`/`spawnAndWait` wrappers (cycle 2).
322
- 6. `src/service-adapter.ts` — `AgentManagerLike.spawn` drops `pi` (cycle 2).
323
- 7. `src/ui/agent-menu.ts` — `AgentMenuManagerDeps.spawnAndWait` drops `pi` (cycle 2).
324
-
325
- ### Changed files (tests)
326
-
327
- 1. `test/env.test.ts` — `mockPi()` → `mockExec()` (cycle 1).
328
- 2. `test/agent-runner.test.ts` — `{ pi }` → `{ exec: vi.fn() }` (cycle 1); `ctx` mock → `ParentSnapshot` (cycle 4).
329
- 3. `test/agent-runner-extension-tools.test.ts` — same as agent-runner (cycles 1, 4).
330
- 4. `test/agent-manager.test.ts` — add `exec` to `createManager()` (cycle 1); remove `mockPi` from 27 `spawn()` calls (cycle 2); mock `buildParentSnapshot` (cycle 5).
331
- 5. `test/service-adapter.test.ts` — `AgentManagerLike.spawn` mock drops `pi` (cycle 2).
332
- 6. `test/ui/agent-menu.test.ts` — `spawnAndWait` mock drops `pi` (cycle 2).
333
-
334
- ### Unchanged files
335
-
336
- - `src/service.ts` — public API unchanged.
337
- - `src/context.ts` — `buildParentContext()` keeps its current signature; called by `buildParentSnapshot()`.
338
- - `src/session-config.ts` — `AssemblerContext` unchanged; `runAgent` builds it from snapshot fields.
339
- - `src/agent-record.ts` — unrelated.
340
- - `src/tools/agent-tool.ts` — already uses narrow `deps.manager` interface without `pi`; the `(ctx, type, prompt, opts)` shape is unchanged.
341
- - All other source and test files.
342
-
343
- ## Test Impact Analysis
344
-
345
- ### New tests enabled by the extraction
346
-
347
- - `buildParentSnapshot()` tested in isolation — verifies field mapping and `inheritContext` pre-computation.
348
- Previously impossible: the snapshot was implicit (live ctx was passed through).
349
- - `detectEnv()` tests become SDK-free — a plain `ShellExec` stub replaces the `ExtensionAPI` mock.
350
-
351
- ### Existing tests that simplify
352
-
353
- - `agent-runner.test.ts` — the `ctx` mock loses its methods (`getSystemPrompt: vi.fn()`, `sessionManager.getBranch: vi.fn()`) and becomes a plain data object.
354
- The `pi` mock (`{} as any`) is replaced by `exec: vi.fn()`.
355
- Every `runAgent(ctx, ...)` call is a mechanical replacement.
356
- - `agent-manager.test.ts` — `mockPi` is eliminated entirely (28 spawn calls).
357
- The `createManager()` helper gains an `exec` field.
358
- - `env.test.ts` — `mockPi()` factory simplifies to a `vi.fn()` returning exec results.
359
-
360
- ### Existing tests that stay as-is
361
-
362
- All `agent-manager.test.ts` tests that verify lifecycle (spawn, complete, abort, resume, queue drain, worktree) remain unchanged in intent — they verify the wiring between `AgentManager` and the record/runner.
363
- Only the mock construction and `spawn()` call sites change mechanically.
364
-
365
- ## TDD Order
366
-
367
- The cycles are organized into two phases following the Kent Beck principle "make the change that makes the change easy."
368
- Phase 1 (cycles 1–2) eliminates the `pi` parameter relay — an orthogonal concern that would otherwise cascade through every snapshot cycle.
369
- Phase 2 (cycles 3–5) introduces `ParentSnapshot`, landing on clean ground.
370
-
371
- ### Phase 1: Eliminate pi relay
372
-
373
- #### Cycle 1: ShellExec type + exec injection into runner path
374
-
375
- Test surface: `test/env.test.ts`, `test/agent-runner.test.ts`, `test/agent-runner-extension-tools.test.ts`, `test/agent-manager.test.ts` (updated).
376
-
377
- This cycle replaces `pi: ExtensionAPI` with `exec: ShellExec` in the runner path (leaf → middle → top), and injects `exec` into `AgentManager`.
378
- `SpawnArgs` still carries `pi` (unused) — removed in cycle 2.
379
-
380
- Changes:
381
-
382
- - `src/types.ts`: add `ShellExec` type.
383
- - `src/env.ts`: `detectEnv()` accepts `ShellExec` instead of `ExtensionAPI`.
384
- - `src/agent-runner.ts`: `RunOptions` replaces `pi: ExtensionAPI` with `exec: ShellExec`; `runAgent()` uses `options.exec` for `detectEnv()`; remove `ExtensionAPI` import.
385
- - `src/agent-manager.ts`: `AgentManagerOptions` adds `exec: ShellExec`; `startAgent()` passes `exec: this.exec` in `RunOptions` instead of `pi` from `SpawnArgs`; stop destructuring `pi` from `SpawnArgs` (it stays in the type for one cycle).
386
- - `src/index.ts`: pass `exec: (cmd, args, opts) => pi.exec(cmd, args, opts)` to `AgentManager` constructor.
387
- - `test/env.test.ts`: `mockPi()` → `mockExec()` returning a `ShellExec` stub.
388
- - `test/agent-runner.test.ts`: `{ pi }` → `{ exec: vi.fn() }` in all `runAgent()` calls.
389
- - `test/agent-runner-extension-tools.test.ts`: same.
390
- - `test/agent-manager.test.ts`: add `exec: vi.fn()` to `createManager()` defaults.
391
- - Run full test suite + `pnpm run check`.
392
-
393
- Commit: `refactor: inject ShellExec into runner path, replacing ExtensionAPI (#99)`
394
-
395
- #### Cycle 2: Remove pi from spawn path and callers
396
-
397
- Test surface: `test/agent-manager.test.ts`, `test/service-adapter.test.ts`, `test/ui/agent-menu.test.ts` (updated).
398
-
399
- With `exec` already injected and `pi` unused in `startAgent()`, this cycle removes it from the remaining surfaces.
400
-
401
- Changes:
402
-
403
- - `src/agent-manager.ts`: `SpawnArgs` drops `pi`; `spawn()` drops `pi` parameter; `spawnAndWait()` drops `pi` parameter; remove `ExtensionAPI` import.
404
- - `src/service-adapter.ts`: `AgentManagerLike.spawn()` drops `pi` parameter; `createSubagentsService().spawn()` passes `session.ctx` only.
405
- - `src/ui/agent-menu.ts`: `AgentMenuManagerDeps.spawnAndWait()` drops `pi` parameter; call site drops `null` first arg.
406
- - `src/index.ts`: simplify `spawn` wrapper — `(ctx, type, prompt, opts) => manager.spawn(ctx, type, prompt, opts)`; simplify `spawnAndWait` wrappers similarly.
407
- - `test/agent-manager.test.ts`: remove `mockPi` constant; drop it from all 27 `spawn()` call sites.
408
- - `test/service-adapter.test.ts`: update `AgentManagerLike.spawn` mock.
409
- - `test/ui/agent-menu.test.ts`: update `spawnAndWait` mock.
410
- - Run full test suite + `pnpm run check`.
411
-
412
- Commit: `refactor: remove pi parameter from spawn path (#99)`
413
-
414
- ### Phase 2: ParentSnapshot
415
-
416
- #### Cycle 3: ParentSnapshot type and builder
417
-
418
- Test surface: `test/parent-snapshot.test.ts` (new file).
419
-
420
- Tests cover:
421
-
422
- - Snapshots `cwd`, `systemPrompt` (from `getSystemPrompt()`), `model`, `modelRegistry` from a mock `ctx`.
423
- - When `inheritContext` is true and conversation exists, `parentContext` is populated.
424
- - When `inheritContext` is false or undefined, `parentContext` is undefined.
425
- - When `inheritContext` is true but conversation is empty, `parentContext` is undefined.
426
-
427
- Changes:
428
-
429
- - Add `ParentSnapshot` interface to `src/types.ts`.
430
- - Create `src/parent-snapshot.ts` with `buildParentSnapshot()`.
431
- - Create `test/parent-snapshot.test.ts`.
432
-
433
- Commit: `feat: add ParentSnapshot type and builder (#99)`
434
-
435
- #### Cycle 4: runAgent and AgentRunner accept ParentSnapshot
436
-
437
- Test surface: `test/agent-runner.test.ts`, `test/agent-runner-extension-tools.test.ts` (updated).
438
-
439
- With `pi` already eliminated (phase 1), this cycle only changes the first parameter of `runAgent()` — no other `RunOptions` churn.
440
-
441
- Changes:
442
-
443
- - `src/agent-runner.ts`:
444
- - `runAgent()` first parameter: `ctx: ExtensionContext` → `snapshot: ParentSnapshot`.
445
- - `AgentRunner.run()` interface: first parameter changes to `snapshot: ParentSnapshot`.
446
- - Remove `inheritContext` from `RunOptions` (snapshot has `parentContext`).
447
- - `inheritContext` block → `if (snapshot.parentContext)`.
448
- - `assembleSessionConfig` and `createAgentSession` calls read from `snapshot.*` instead of `ctx.*`.
449
- - Remove `ExtensionContext` import; remove `buildParentContext` import (no longer called here).
450
- - `test/agent-runner.test.ts`: replace `ctx` mock with a plain `ParentSnapshot` object.
451
- - `test/agent-runner-extension-tools.test.ts`: same.
452
- - Run `pnpm run check` — catches `agent-manager.ts` type error in `startAgent()`'s `runner.run()` call (still passing `ctx`); addressed in cycle 5.
453
-
454
- Commit: `refactor: AgentRunner accepts ParentSnapshot instead of ExtensionContext (#99)`
455
-
456
- #### Cycle 5: AgentManager spawn builds snapshot
457
-
458
- Test surface: `test/agent-manager.test.ts` (updated).
459
-
460
- Changes:
461
-
462
- - `src/agent-manager.ts`:
463
- - `spawn()` calls `buildParentSnapshot(ctx, options.inheritContext)` and stores the snapshot in `SpawnArgs`.
464
- - `SpawnArgs`: replace `ctx: ExtensionContext` with `snapshot: ParentSnapshot`.
465
- - `startAgent()` destructures `snapshot` from `SpawnArgs`; passes it to `runner.run(snapshot, ...)`.
466
- - Remove `ExtensionContext` import.
467
- - `test/agent-manager.test.ts`: `vi.mock("../src/parent-snapshot.js")` to isolate `buildParentSnapshot`; `mockCtx` stays minimal (`{ cwd: "/tmp" } as any`) because `spawn()` delegates to the mocked builder.
468
- - Run full test suite + `pnpm run check`.
469
-
470
- Commit: `refactor: AgentManager captures ParentSnapshot at spawn time (#99)`
471
-
472
- ## Risks and Mitigations
473
-
474
- | Risk | Mitigation |
475
- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
476
- | `modelRegistry` is a reference capture, not a data copy — could theoretically go stale | Registries are structurally stable within a session; the staleness risk applies to `cwd`/`systemPrompt`/`model`/conversation, all of which are value-captured |
477
- | Large test files (~410 + ~686 lines) need mechanical updates | Phase 1 handles `pi` churn (27 spawn calls) separately from phase 2's `ctx` → `snapshot` change, so each cycle touches one concern per test file |
478
- | `spawn()` still receives `ctx` — caller could accidentally use it post-snapshot | `ctx` is a function parameter, not stored; `SpawnArgs` holds only the snapshot; the type system prevents re-storing ctx since `SpawnArgs.ctx` no longer exists |
479
- | `buildParentContext` still accepts `ExtensionContext` — mocking it requires a nested `sessionManager` mock | The mock only appears in `parent-snapshot.test.ts` (one place); a follow-up could narrow `buildParentContext` to accept just the branch data |
480
- | Phase 1 temporarily leaves `pi` in `SpawnArgs` (cycle 1) before removing it (cycle 2) | The field is unused after cycle 1 (`startAgent` uses `this.exec` instead); cycle 2 removes it in the next commit |
481
-
482
- ## Open Questions
483
-
484
- - Whether to narrow `buildParentContext()` to accept `getBranch()` data directly instead of `ExtensionContext`.
485
- This would eliminate the last `ExtensionContext` usage in the snapshot path but is a separate refactoring of `context.ts`.
486
- Deferred — the current `buildParentSnapshot` wrapper isolates the SDK dependency to one module.
487
- - Whether `runtime.currentCtx` should be simplified from `{ pi, ctx }` to just `ctx` now that `pi` is not passed to `spawn()`.
488
- Natural follow-up but out of scope — `currentCtx.pi` has no other consumers and removing it is a 3-line change.