@gotgenes/pi-subagents 18.0.1 → 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 (260) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/docs/architecture/architecture.md +157 -204
  3. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  4. package/package.json +5 -6
  5. package/src/lifecycle/subagent.ts +5 -9
  6. package/src/observation/notification.ts +23 -10
  7. package/src/observation/subagent-events-observer.ts +1 -3
  8. package/src/tools/get-result-report.ts +60 -0
  9. package/src/tools/get-result-tool.ts +25 -40
  10. package/src/types.ts +1 -1
  11. package/src/ui/session-navigator.ts +0 -2
  12. package/.prettierignore +0 -5
  13. package/AGENTS.md +0 -7
  14. package/docs/plans/0048-implement-subagents-api.md +0 -304
  15. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  16. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  17. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  18. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  19. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  20. package/docs/plans/0057-structured-debug-logging.md +0 -154
  21. package/docs/plans/0061-session-format-transcript.md +0 -284
  22. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  23. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  24. package/docs/plans/0070-extract-event-handlers.md +0 -306
  25. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  26. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  27. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  28. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  29. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  30. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  31. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  32. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  33. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  34. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  35. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  36. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  37. package/docs/plans/0109-extract-settings-manager.md +0 -276
  38. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  39. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  40. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  41. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  42. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  43. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  44. package/docs/plans/0116-type-housekeeping.md +0 -351
  45. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  46. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  47. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  48. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  49. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  50. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  51. package/docs/plans/0135-extract-display-helpers.md +0 -182
  52. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  53. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  54. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  55. package/docs/plans/0146-narrow-ui-context.md +0 -319
  56. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  57. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  58. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  59. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  60. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  61. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  62. package/docs/plans/0167-narrow-runner-io.md +0 -150
  63. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  64. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  65. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  66. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  67. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  68. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  69. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  70. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  71. package/docs/plans/0192-define-session-context-interface.md +0 -107
  72. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  73. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  74. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  75. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  76. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  77. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  78. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  79. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  80. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  81. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  82. package/docs/plans/0216-decompose-start-agent.md +0 -255
  83. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  84. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  85. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  86. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  87. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  88. package/docs/plans/0229-agent-born-complete.md +0 -564
  89. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  90. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  91. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  92. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  93. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  94. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  95. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  96. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  97. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  98. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  99. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  100. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  101. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  102. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  103. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  104. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  105. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  106. package/docs/plans/0373-extract-subagent-state.md +0 -250
  107. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  108. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  109. package/docs/plans/0376-extract-manager-observer.md +0 -232
  110. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  111. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  112. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  113. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  114. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  115. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  116. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  117. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  118. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  119. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  120. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  121. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  122. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  123. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  124. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  125. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  126. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  127. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  128. package/docs/plans/0443-consolidate-remaining-test-clone-families.md +0 -268
  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/plans/0470-refresh-readme-command-surface.md +0 -159
  137. package/docs/retro/0048-implement-subagents-api.md +0 -44
  138. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  139. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  140. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  141. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  142. package/docs/retro/0057-structured-debug-logging.md +0 -77
  143. package/docs/retro/0061-session-format-transcript.md +0 -41
  144. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  145. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  146. package/docs/retro/0070-extract-event-handlers.md +0 -49
  147. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  148. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  149. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  150. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  151. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  152. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  153. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  154. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  155. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  156. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  157. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  158. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  159. package/docs/retro/0109-extract-settings-manager.md +0 -55
  160. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  161. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  162. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  163. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  164. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  165. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  166. package/docs/retro/0116-type-housekeeping.md +0 -42
  167. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  168. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  169. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  170. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  171. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  172. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  173. package/docs/retro/0135-extract-display-helpers.md +0 -38
  174. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  175. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  176. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  177. package/docs/retro/0146-narrow-ui-context.md +0 -70
  178. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  179. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  180. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  181. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  182. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  183. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  184. package/docs/retro/0167-narrow-runner-io.md +0 -63
  185. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  186. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  187. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  188. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  189. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  190. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  191. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  192. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  193. package/docs/retro/0192-define-session-context-interface.md +0 -59
  194. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  195. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  196. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  197. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  198. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  199. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  200. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  201. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  202. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  203. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  204. package/docs/retro/0216-decompose-start-agent.md +0 -80
  205. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  206. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  207. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  208. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  209. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  210. package/docs/retro/0229-agent-born-complete.md +0 -89
  211. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  212. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  213. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  214. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  215. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  216. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  217. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  218. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  219. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  220. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  221. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  222. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  223. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  224. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  225. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  226. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  227. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  228. package/docs/retro/0373-extract-subagent-state.md +0 -94
  229. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  230. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  231. package/docs/retro/0376-extract-manager-observer.md +0 -81
  232. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  233. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  234. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  235. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  236. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  237. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  238. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  239. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  240. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  241. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  242. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  243. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  244. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  245. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  246. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  247. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  248. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -97
  249. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  250. package/docs/retro/0443-consolidate-remaining-test-clone-families.md +0 -28
  251. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  252. package/docs/retro/0445-native-session-navigation.md +0 -122
  253. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  254. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  255. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  256. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  257. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  258. package/docs/retro/0470-refresh-readme-command-surface.md +0 -40
  259. package/src/observation/notification-state.ts +0 -27
  260. package/vitest.config.ts +0 -14
@@ -1,264 +0,0 @@
1
- ---
2
- issue: 423
3
- issue_title: "pi-subagents: make the agent widget self-drive from lifecycle events"
4
- ---
5
-
6
- # Make the agent widget self-drive from lifecycle events
7
-
8
- ## Problem Statement
9
-
10
- Today the dependency arrow points the wrong way: the core spawn tools reach into the widget to drive it.
11
- `foreground-runner.ts` calls `widget.ensureTimer()` (inside its `onSessionCreated` callback) and `widget.markFinished(fgId)` after completion; `background-spawner.ts` calls `widget.ensureTimer()` and `widget.update()` after spawn.
12
- For the widget to be a clean reactive consumer of the core — and for the spawn tools to shed their widget dependency — the widget must subscribe to lifecycle signals and manage its own update timer.
13
- This is the fourth step of the Phase 18 spine (widget/tool decoupling track), enabled by Step 3 ([#422]) removing the activity map and the second session subscription.
14
-
15
- ## Goals
16
-
17
- - Make `AgentWidget` a `SubagentManagerObserver`: start its 80 ms update timer in response to `onSubagentStarted` / `onSubagentCreated`, and re-render on `onSubagentCompleted` / `onSubagentCompacted`.
18
- - Wire the widget as a second lifecycle consumer alongside `SubagentEventsObserver` via a new `CompositeSubagentObserver` that fans out manager notifications — keeping `SubagentManager`'s single-observer contract closed for modification.
19
- - Remove all inbound widget calls from the spawn tools (`ensureTimer` / `update` / `markFinished`) and drop the `ForegroundWidgetDeps` / `BackgroundWidgetDeps` parameters from both spawners.
20
- - Narrow `AgentToolWidget` to the single method the tool still uses (`setUICtx`); UICtx capture stays in `ToolStartHandler` and `AgentTool`.
21
- - Delete the now-redundant `AgentWidget.markFinished` (its bookkeeping is already covered by `seedFinishedAgents`) and make `ensureTimer` private.
22
-
23
- This is **not** a breaking change.
24
- The widget, the spawn tools, the observer interface, and `AgentToolWidget` are all internal symbols — none appear in the public service surface (`src/service/service.ts`) or the settings entry (`src/layered-settings.ts`).
25
- Observable widget behavior is unchanged (the timer starts when an agent appears, renders, and self-clears when idle); commits use `refactor:` / `docs:`.
26
-
27
- ## Non-Goals
28
-
29
- - Dropping the `widget` constructor param from `AgentTool` entirely and the widget/map stubs from `createToolDeps` — that is Phase 18 Step 5 ([#424]).
30
- This step only narrows `AgentToolWidget` to `setUICtx`; the param stays.
31
- - Migrating the widget to subscribe to the public `pi.events` (`subagents:*`) broadcast channels instead of the internal observer.
32
- The event-bus surface is the substitutable-UI direction, but it depends on the public event-contract reconciliation in Step 6 ([#425]) and the UI-distribution decision in Step 8 ([#427]); using it now would front-run an unreconciled contract.
33
- Option A (the composite observer) keeps a later `pi.events` migration to a localized `index.ts` swap.
34
- - Touching `record-observer.ts`, `SubagentState`, the record getters, or the `SubagentEventsObserver` event/notification dispatch.
35
- - Changing the foreground streaming path: `runForeground`'s `onSessionCreated` keeps binding `recordRef` for the `onUpdate` stream.
36
-
37
- ## Background
38
-
39
- Relevant modules and their current relationships:
40
-
41
- - `src/ui/agent-widget.ts` — `AgentWidget` polls `manager.listAgents()` every 80 ms via a `setInterval` started by `ensureTimer()`.
42
- `update()` is idempotent: it self-seeds any finished agent into `finishedTurnAge` via `seedFinishedAgents()` (added in [#421] / [#422]), renders, and self-clears the timer through `clearWidget()` when no agents are active or lingering.
43
- `markFinished(id)` and `ensureTimer()` carry `fallow-ignore-next-line unused-class-member` because their only callers are the spawn tools (via narrow interfaces).
44
- - `src/lifecycle/subagent-manager.ts` — exposes a single `observer?: SubagentManagerObserver` slot (four methods: `onSubagentStarted`, `onSubagentCreated`, `onSubagentCompleted`, `onSubagentCompacted`).
45
- `buildObserver()` adapts it into a per-agent `SubagentLifecycleObserver`.
46
- `onSubagentStarted` fires for both foreground and background (on `record.start()`); `onSubagentCreated` fires only for background (in `spawn()`); `onSubagentCompleted` fires **only for background** (`onRunFinished` guards on `options.isBackground`).
47
- - `src/observation/subagent-events-observer.ts` — the sole current observer; dispatches `pi.events` lifecycle events, session-entry persistence, and completion notifications.
48
- - `src/tools/foreground-runner.ts` / `src/tools/background-spawner.ts` — drive the widget via `ForegroundWidgetDeps` / `BackgroundWidgetDeps`.
49
- - `src/tools/agent-tool.ts` — holds a 4-method `AgentToolWidget` (`setUICtx`, `ensureTimer`, `update`, `markFinished`), passes the widget into both spawners, and calls `this.widget.setUICtx(ctx.ui)`.
50
- - `src/index.ts` — constructs `observer` (the events observer), passes it to the manager, then constructs `widget` after the manager (the widget needs `manager.listAgents()`).
51
-
52
- Key behavioral facts that shape the design:
53
-
54
- - **Foreground completion is not observed.**
55
- The manager never calls `onSubagentCompleted` for foreground agents, so the widget cannot learn of foreground completion through the observer.
56
- It does not need to: `seedFinishedAgents()` already seeds any agent with `completedAt` on the next poll tick, so `markFinished` is fully redundant and is removed.
57
- - **The widget keeps its `manager` reference.**
58
- Rendering still reads `manager.listAgents()`; the observer only changes the *trigger* that starts the timer, not the data source.
59
- Full broadcast-plus-query decoupling is the Step 8 concern, deliberately out of scope here.
60
-
61
- AGENTS.md / package constraints that apply:
62
-
63
- - pi-subagents is a narrow core that is open for extension, closed for modification — dependency arrows point inward.
64
- A composite observer in the composition root (`index.ts`) keeps `SubagentManager` untouched, rather than teaching the core to fan out to N consumers.
65
- - Run `pnpm fallow dead-code` before pushing — privatizing `ensureTimer` and deleting `markFinished` change the dead-code surface.
66
-
67
- ## Design Overview
68
-
69
- ### CompositeSubagentObserver (new collaborator)
70
-
71
- A small fan-out observer lets the manager keep its single-observer contract while several independent consumers subscribe.
72
- It lives next to the existing observer in `src/observation/`.
73
-
74
- ```typescript
75
- // src/observation/composite-subagent-observer.ts
76
- export class CompositeSubagentObserver implements SubagentManagerObserver {
77
- private readonly delegates: SubagentManagerObserver[];
78
- constructor(delegates: SubagentManagerObserver[]) {
79
- this.delegates = [...delegates];
80
- }
81
- /** Register an additional observer (breaks the widget↔manager construction cycle). */
82
- add(observer: SubagentManagerObserver): void {
83
- this.delegates.push(observer);
84
- }
85
- onSubagentStarted(record: Subagent): void {
86
- this.forEach((o) => o.onSubagentStarted(record), "onSubagentStarted");
87
- }
88
- // …onSubagentCreated / onSubagentCompleted / onSubagentCompacted follow the same shape…
89
- private forEach(call: (o: SubagentManagerObserver) => void, label: string): void {
90
- for (const o of this.delegates) {
91
- try { call(o); } catch (err) { debugLog(`CompositeSubagentObserver.${label}`, err); }
92
- }
93
- }
94
- }
95
- ```
96
-
97
- Each delegate is isolated in a `try`/`catch` so a widget render throw cannot suppress event emission, and vice versa.
98
- The `add` method exists to break the construction cycle (see wiring below), not as speculative flexibility — `index.ts` is its only caller.
99
-
100
- ### Consumer call site (index.ts wiring)
101
-
102
- The widget needs the manager (for `listAgents()`) and the manager needs the observer, so the composite is constructed first, the widget is registered after construction:
103
-
104
- ```typescript
105
- const eventsObserver = new SubagentEventsObserver({ /* emit, appendEntry, notifications */ });
106
- const observer = new CompositeSubagentObserver([eventsObserver]);
107
- const manager = new SubagentManager({ /* …, */ observer /* , … */ });
108
- // …
109
- const widget = new AgentWidget(manager, registry);
110
- observer.add(widget); // the manager consults `observer` only at spawn time, after this runs
111
- ```
112
-
113
- The manager reads `this.observer` lazily (only at `spawn` / `spawnAndWait`), so registering the widget after construction is safe — no notification can fire before `observer.add(widget)` executes.
114
-
115
- ### AgentWidget as a reactive observer
116
-
117
- `AgentWidget implements SubagentManagerObserver`.
118
- It ignores the `record` argument (it re-reads `listAgents()`), reacting by intent rather than by payload:
119
-
120
- ```typescript
121
- // react to lifecycle, self-drive the timer
122
- onSubagentStarted(_record: Subagent): void { this.startLoop(); }
123
- onSubagentCreated(_record: Subagent): void { this.startLoop(); }
124
- onSubagentCompleted(_record: Subagent): void { this.update(); }
125
- onSubagentCompacted(_record: Subagent, _info: CompactionInfo): void { this.update(); }
126
-
127
- private startLoop(): void { this.ensureTimer(); this.update(); }
128
- ```
129
-
130
- `startLoop` (started / created) starts the timer and renders; completed / compacted only `update()` because the timer is already running by then (started fires at spawn, created fires before the queued run).
131
- `update()` self-seeds the finished agent and keeps the timer alive for the linger window, then `clearWidget()` stops it once idle — unchanged from today.
132
- `ensureTimer()` becomes `private` (now called from `startLoop`, so it loses its `fallow-ignore`); `markFinished` is deleted.
133
-
134
- This follows the `SubagentEventsObserver` precedent: a class that implements `SubagentManagerObserver` and is invoked polymorphically by the manager counts as used, so the new methods need no `fallow-ignore`.
135
-
136
- ### Extracted-from / upstream check
137
-
138
- This extraction introduces no Tell-Don't-Ask or output-argument violations: the widget reads `listAgents()` (a query it already owns) and mutates only its own `finishedTurnAge` / timer state.
139
- The composite only forwards calls; it holds no state beyond its delegate list and never reaches through a delegate.
140
-
141
- ## Module-Level Changes
142
-
143
- Source:
144
-
145
- - `src/observation/composite-subagent-observer.ts` — **new.**
146
- `CompositeSubagentObserver implements SubagentManagerObserver`; constructor takes a delegate array; `add()` appends; each method fans out through a private `forEach` with per-delegate `try`/`catch` + `debugLog`.
147
- - `src/ui/agent-widget.ts` — add `implements SubagentManagerObserver`; add `onSubagentStarted` / `onSubagentCreated` / `onSubagentCompleted` / `onSubagentCompacted` and the private `startLoop`; make `ensureTimer` `private` and drop its `fallow-ignore`; delete `markFinished` and its `fallow-ignore`; import `SubagentManagerObserver` and `CompactionInfo` types; reword the `seedFinishedAgents` doc comment so it no longer references the removed `markFinished` method.
148
- - `src/index.ts` — rename the events-observer local to `eventsObserver`; construct `const observer = new CompositeSubagentObserver([eventsObserver])` and pass it to the manager; add `observer.add(widget)` after widget construction; import `CompositeSubagentObserver`; update the widget-construction comment to note the post-construction observer registration.
149
- - `src/tools/foreground-runner.ts` — remove the `ForegroundWidgetDeps` interface and the `widget` parameter; remove `widget.ensureTimer()` from `onSessionCreated` (keep the `recordRef` binding) and the `fgId` variable + the post-completion `widget.markFinished(fgId)` block.
150
- - `src/tools/background-spawner.ts` — remove the `BackgroundWidgetDeps` interface and the `widget` parameter; remove the `widget.ensureTimer()` / `widget.update()` calls; trim the doc comment ("Owns: widget update and launch message formatting" → "Owns: launch message formatting").
151
- - `src/tools/agent-tool.ts` — narrow `AgentToolWidget` to `{ setUICtx(ctx: UICtx): void }`; reword its doc comment; drop the `this.widget` argument from the `spawnBackground` / `runForeground` calls; keep `this.widget.setUICtx(ctx.ui)` and the constructor param.
152
-
153
- Tests:
154
-
155
- - `test/observation/composite-subagent-observer.test.ts` — **new.**
156
- Fan-out to all delegates in registration order for each of the four methods; `add()` registers a late delegate; a throwing delegate does not suppress the others.
157
- - `test/ui/agent-widget.test.ts` — new `describe` for the observer methods: `onSubagentStarted` / `onSubagentCreated` start the timer and render (advancing fake timers keeps re-rendering); `onSubagentCompleted` / `onSubagentCompacted` trigger a render; the widget self-clears once idle.
158
- - `test/tools/foreground-runner.test.ts` — drop the `widget` arg from every `runForeground(...)` call; remove the "calls runtime.ensureTimer and runtime.markFinished after completion" test and the `spawnAndWaitRegistering` widget-binding framing (keep the streaming-`onUpdate` test).
159
- - `test/tools/background-spawner.test.ts` — drop the `widget` arg from every `spawnBackground(...)` call; remove the "calls runtime.ensureTimer and runtime.update after spawn" test.
160
- - `test/helpers/make-deps.ts` — narrow the `widget` fixture to `{ setUICtx: vi.fn() }`; reword the `AgentToolFixture.widget` doc comment (drop the `BackgroundWidgetDeps` / `ForegroundWidgetDeps` mention).
161
- - `test/helpers/make-deps.test.ts` — rewrite the "all widget methods are vi.fn stubs" test to assert only `setUICtx`; remove the `BackgroundWidgetDeps` / `ForegroundWidgetDeps` structural-compatibility tests and their imports.
162
-
163
- Docs:
164
-
165
- - `docs/architecture/architecture.md`:
166
- - File tree (around line 318) — add `composite-subagent-observer.ts` under `observation/` with a one-line description.
167
- - System diagram (around line 64) — add a `SubagentManager -.->|notifies| Widget` edge (the inverted, reactive direction) alongside the existing `Widget -.->|polls| SubagentManager`.
168
- - Widget prose (around line 363) — note the 80 ms poll loop is now *started by lifecycle notifications* rather than by spawn-tool calls.
169
- - Phase 18 roadmap Step 4 entry — mark ✅ with a `Landed:` bullet matching the Steps 1–3 format.
170
- - Step-dependency Mermaid — `S4` node gets the ✅ marker.
171
- - `.pi/skills/package-pi-subagents/SKILL.md`:
172
- - Domain table — Observation `5 → 6` modules (add `composite-subagent-observer.ts` to the list); header "seven domains (57 files)" → "(58 files)".
173
- - Module-dependency-flow block — note the widget is now a lifecycle observer fanned out through the composite alongside `subagent-events-observer`.
174
-
175
- Verify the exact current counts with `grep` before editing (the Step 3 sweep set Observation to 5 and the total to 57); the architecture health-metrics snapshot table (`7,751 (62 files)`) is the Phase 18 *findings* snapshot and is left as-is, per the Step 3 precedent.
176
-
177
- Historical docs under `docs/plans/`, `docs/retro/`, and `docs/architecture/history/` are records of past work and are **not** edited.
178
-
179
- ## Test Impact Analysis
180
-
181
- 1. **New tests enabled.**
182
- The composite observer becomes unit-testable in isolation (fan-out order, `add`, per-delegate fault isolation) — impossible before, since there was no fan-out unit.
183
- The widget's timer-start logic becomes directly testable through its observer methods, rather than only indirectly through the spawn tools.
184
- 2. **Tests that become redundant.**
185
- The spawner widget-driving assertions (`foreground-runner.test.ts` "calls … ensureTimer and … markFinished", `background-spawner.test.ts` "calls … ensureTimer and … update") test inbound calls that no longer exist — removed.
186
- `make-deps.test.ts`'s "all widget methods are vi.fn stubs" shrinks to `setUICtx`; the `BackgroundWidgetDeps` / `ForegroundWidgetDeps` structural-compat tests are removed with their interfaces.
187
- 3. **Tests that must stay.**
188
- The spawner result-text tests (launch message, queued/started, output-file, error), the foreground "calls onUpdate with streaming details while running" test (pins the surviving `recordRef` binding), the `AgentWidget.update` self-seed tests (the mechanism that replaces `markFinished`), and the `agent-tool` `setUICtx` test all exercise behavior that survives this step.
189
-
190
- ## Invariants at risk
191
-
192
- This step touches surfaces refactored by Phase 18 Steps 1–3.
193
- Relevant prior `Landed:` invariants and the tests that pin them:
194
-
195
- - "The foreground `observer.onSessionCreated` keeps `recordRef`/`fgId` binding" (Step 3 outcome).
196
- This step removes `fgId` (only ever used by the deleted `markFinished` call) but **keeps `recordRef`** for the streaming `onUpdate`.
197
- Pinned by `test/tools/foreground-runner.test.ts` "calls onUpdate with streaming details while running" — must stay green.
198
- - "The widget owns detection of completions via `seedFinishedAgents`" (Steps 1–2 outcome).
199
- Removing `markFinished` relies on this; pinned by `test/ui/agent-widget.test.ts` "AgentWidget.update self-seeds finished agents" and "does not advance the linger age on repeated update() without a turn".
200
- - "One session subscription per child; runtime holds zero UI state" (Step 3 outcome).
201
- Unchanged here — the widget subscribes to *manager lifecycle notifications*, not a second *session* subscription.
202
-
203
- No new test is required to protect these; the surviving streaming and self-seed tests plus the type checker cover them.
204
-
205
- ## TDD Order
206
-
207
- Non-breaking refactor; every commit keeps `pnpm run check` and the full suite green.
208
- Steps are ordered so the widget is wired as an observer *before* the spawn-tool calls are removed — no commit leaves the widget without a timer-start signal.
209
-
210
- 1. **Add `CompositeSubagentObserver` (pure addition).**
211
- Write `test/observation/composite-subagent-observer.test.ts` (red), then `src/observation/composite-subagent-observer.ts` (green): fan-out in registration order, `add()`, per-delegate fault isolation.
212
- Not yet wired.
213
- Run `pnpm run check`.
214
- Commit: `refactor: add CompositeSubagentObserver to fan out lifecycle notifications (#423)`.
215
-
216
- 2. **Subscribe the widget to lifecycle notifications.**
217
- Add the four observer methods + `implements SubagentManagerObserver` + `startLoop` to `AgentWidget` (red→green with the new widget observer tests); wire `index.ts` (rename `eventsObserver`, wrap in `CompositeSubagentObserver`, `observer.add(widget)`).
218
- The spawn tools still call the widget in this commit — the resulting double-drive is idempotent (`ensureTimer` uses `??=`; `update` and finished-seeding are idempotent), so behavior is unchanged.
219
- The widget's observer methods are now invoked polymorphically by the manager, so they need no `fallow-ignore`.
220
- Run `pnpm run check` and the full suite.
221
- Commit: `refactor: subscribe the widget to lifecycle notifications via a composite observer (#423)`.
222
-
223
- 3. **Stop driving the widget from the spawn tools (atomic removal).**
224
- Remove the `widget` parameter and `ForegroundWidgetDeps` / `BackgroundWidgetDeps` interfaces from both spawners; drop the `ensureTimer` / `update` / `markFinished` call sites (and the now-orphaned `fgId`); narrow `AgentToolWidget` to `setUICtx` and drop the `this.widget` args from the spawner calls in `AgentTool`; delete `AgentWidget.markFinished` and make `ensureTimer` private.
225
- Update the spawner tests (drop the `widget` arg, remove the two widget-driving tests), `make-deps.ts` (narrow the fixture), and `make-deps.test.ts` (rewrite the widget test, remove the structural-compat tests).
226
- Removing the exported interfaces and the `widget` params breaks every call site and test at the type level, so they land together.
227
- The widget keeps self-driving via the composite wired in Step 2, so there is no behavior gap.
228
- Run `pnpm run check`, the full suite, and `pnpm fallow dead-code`.
229
- Commit: `refactor: stop driving the widget from the spawn tools (#423)`.
230
-
231
- 4. **Update the architecture doc and package skill.**
232
- Apply the doc edits in Module-Level Changes (file tree, system-diagram edge, widget prose, Step 4 ✅ + `Landed:` bullet, `S4` Mermaid marker; SKILL.md Observation count `5 → 6` and total `57 → 58`, flow block).
233
- Verify any touched Mermaid renders (load the `mermaid` skill).
234
- Commit: `docs: mark Phase 18 Step 4 complete and record the widget self-drive (#423)`.
235
-
236
- ## Risks and Mitigations
237
-
238
- - **Behavior gap between commits.**
239
- Removing the spawn-tool calls before the widget is an observer would leave the widget with no timer-start signal.
240
- Mitigation: Step 2 wires the composite (widget self-drives, alongside the still-present spawn-tool calls) strictly before Step 3 removes those calls.
241
- - **Construction cycle (widget ↔ manager).**
242
- The widget needs the manager; the manager needs the observer; the observer includes the widget.
243
- Mitigation: construct the composite with `[eventsObserver]`, pass it to the manager, then `observer.add(widget)` after the widget is built — the manager consults the observer only at spawn time.
244
- - **Transient double-drive in Step 2.**
245
- Both the spawn tools and the observer drive the widget in that commit.
246
- Mitigation: every driven method is idempotent (`ensureTimer` `??=`; `update` re-renders; finished-seeding only seeds when absent), so the doubled calls are harmless.
247
- - **Dead-code surface change.**
248
- Privatizing `ensureTimer` and deleting `markFinished` alter what `fallow` sees.
249
- Mitigation: Step 3 runs `pnpm fallow dead-code`; the new observer methods are invoked via the interface (the `SubagentEventsObserver` precedent), so they are not flagged.
250
- - **Foreground completion is unobserved.**
251
- The manager does not fire `onSubagentCompleted` for foreground agents.
252
- Mitigation: `seedFinishedAgents` already covers foreground completion through polling — pinned by the existing self-seed tests — so removing `markFinished` is safe.
253
-
254
- ## Open Questions
255
-
256
- None blocking.
257
- Whether the widget should ultimately subscribe to the public `pi.events` (`subagents:*`) broadcast instead of the internal observer — the substitutable-UI direction — is deferred to the public-event-contract reconciliation ([#425]) and the UI-distribution decision ([#427]).
258
- Option A keeps that future migration to a localized `index.ts` swap.
259
-
260
- [#421]: https://github.com/gotgenes/pi-packages/issues/421
261
- [#422]: https://github.com/gotgenes/pi-packages/issues/422
262
- [#424]: https://github.com/gotgenes/pi-packages/issues/424
263
- [#425]: https://github.com/gotgenes/pi-packages/issues/425
264
- [#427]: https://github.com/gotgenes/pi-packages/issues/427
@@ -1,166 +0,0 @@
1
- ---
2
- issue: 424
3
- issue_title: "pi-subagents: drop the widget and activity-map dependencies from the subagent tool"
4
- ---
5
-
6
- # Drop the widget dependency from the subagent tool
7
-
8
- ## Problem Statement
9
-
10
- The LLM-facing `subagent` tool's real concern is dispatch, yet `AgentTool` still takes a `widget` constructor dependency and calls `this.widget.setUICtx(ctx.ui)` at the start of `execute`.
11
- Every `AgentTool` unit test has to stub the widget through `createToolDeps` — testability friction that marks the domain seam.
12
- Now that the widget self-drives its timer from lifecycle notifications (Phase 18 Step 4, [#423]) and `ToolStartHandler` already captures the UI context on every `tool_execution_start`, the tool no longer needs the widget at all.
13
-
14
- Note: the issue title and the roadmap entry for this step also mention an `agentActivity` / activity-map dependency, but that was already removed from `AgentTool` and the runtime in Phase 18 Step 3 ([#422]).
15
- The only remaining dependency to drop is `widget`.
16
- This plan corrects that stale wording where it appears in the roadmap.
17
-
18
- ## Goals
19
-
20
- - Remove the `widget` constructor parameter from `AgentTool`; the tool depends only on manager / runtime / settings / registry / agentDir.
21
- - Remove the now-redundant `this.widget.setUICtx(ctx.ui)` call from `AgentTool.execute` — UICtx capture stays in `ToolStartHandler`.
22
- - Delete the `AgentToolWidget` interface and the `UICtx` import that only that interface used.
23
- - Drop the `widget` field, its stub, and the `AgentToolWidget` import from the `createToolDeps` fixture.
24
- - Update the `index.ts` call site and all affected tests in the same commit (the constructor-signature change breaks them at typecheck time).
25
- - This change is **not breaking** — `AgentTool` is internal (the package's only public exports are the service and settings entries), and observable behavior is preserved because `ToolStartHandler` already captures UICtx before the tool executes.
26
-
27
- ## Non-Goals
28
-
29
- - No change to `ToolStartHandler`, `AgentWidget`, or the `UICtx` type itself (it stays in `src/ui/agent-widget.ts`, consumed by the widget and `ToolStartHandler`).
30
- - No change to `foreground-runner.ts` / `background-spawner.ts` (their widget driving was already removed in [#423]).
31
- - No work on Phase 18 Steps 6–8 ([#425], [#426], [#427]) — the public-event-contract reconciliation, test-clone consolidation, and UI-direction ADR are separate steps.
32
-
33
- ## Background
34
-
35
- Relevant modules and their current state:
36
-
37
- - `src/tools/agent-tool.ts` — `AgentTool` constructor signature is `(manager, runtime, widget, settings, registry, agentDir)`.
38
- `execute` opens with `this.widget.setUICtx(ctx.ui as UICtx)`.
39
- The file defines the narrow `AgentToolWidget` interface (`setUICtx` only, already narrowed in [#423]) and imports `UICtx` from `#src/ui/agent-widget` solely for that interface and the cast.
40
- - `src/handlers/tool-start.ts` — `ToolStartHandler.handleToolExecutionStart` already calls `this.widget.setUICtx(ctx.ui)` then `this.widget.onTurnStart()`.
41
- `tool_execution_start` fires before any tool's `execute`, so the widget already has the current UICtx by the time `AgentTool.execute` runs.
42
- This handler is wired in `index.ts` and is the canonical UICtx-capture site.
43
- - `src/index.ts:152` — the sole `new AgentTool(...)` call site, passing `widget` as the third argument.
44
- `widget` is still constructed and registered as a lifecycle observer (`observer.add(widget)`) and passed to `ToolStartHandler` — those usages stay.
45
- - `test/helpers/make-deps.ts` — `createToolDeps` builds an `AgentToolFixture` with a `widget` field stub (`{ setUICtx: vi.fn() }`) and imports `AgentToolWidget`.
46
- - `test/helpers/make-deps.test.ts` — has a `describe("widget defaults")` block asserting the stub.
47
- - `test/tools/agent-tool.test.ts` — `makeTool` passes `deps.widget`; the test `"sets UI context on runtime at start of execute"` asserts `deps.widget.setUICtx` was called.
48
-
49
- AGENTS.md constraint: pi-subagents is a minimal core whose dependency arrows point inward.
50
- Removing an outbound widget dependency from the LLM tool moves the package further toward "UI is a pure consumer of broadcast events," which is the stated precondition for the Phase 18 UI-direction decision.
51
-
52
- ## Design Overview
53
-
54
- This is a purely subtractive refactor — no new collaborator, no new interface, no moved behavior.
55
- The widget already receives its UICtx from `ToolStartHandler` on every turn's first tool execution, so the tool's own `setUICtx` call is redundant.
56
-
57
- `AgentTool.execute` after the change opens directly with the registry reload:
58
-
59
- ```typescript
60
- async execute(toolCallId, params, signal, onUpdate, ctx) {
61
- // Reload custom agents so new .pi/agents/*.md files are picked up without restart
62
- this.registry.reload();
63
- const config = resolveSpawnConfig(params, this.registry, this.runtime.getModelInfo(), this.settings);
64
- // ... unchanged
65
- }
66
- ```
67
-
68
- The `ctx` parameter is still required by the `defineTool` `execute` signature; the tool simply no longer reads `ctx.ui`.
69
-
70
- New constructor signature:
71
-
72
- ```typescript
73
- constructor(
74
- private readonly manager: AgentToolManager,
75
- private readonly runtime: AgentToolRuntime,
76
- private readonly settings: AgentToolSettings,
77
- private readonly registry: AgentTypeRegistry,
78
- private readonly agentDir: string,
79
- ) { /* unchanged body */ }
80
- ```
81
-
82
- Edge cases:
83
-
84
- - UICtx availability — `tool_execution_start` always precedes `execute`, so removing the tool's capture loses no coverage; the widget is registered as an observer and already has the UICtx when it renders.
85
- - No behavior depends on the tool calling `setUICtx` twice per turn; the second call (in the tool) was idempotent for the same `ctx.ui` and a no-op when unchanged.
86
-
87
- ## Module-Level Changes
88
-
89
- - `src/tools/agent-tool.ts`
90
- - Remove the `import { type UICtx } from "#src/ui/agent-widget";` line.
91
- - Remove the `AgentToolWidget` interface (and its doc comment).
92
- - Remove the `private readonly widget: AgentToolWidget` constructor parameter.
93
- - Remove the `this.widget.setUICtx(ctx.ui as UICtx);` statement and its comment from `execute`.
94
- - `src/index.ts`
95
- - Update line 152: `new AgentTool(manager, runtime, settings, registry, getAgentDir())` (drop `widget`).
96
- - Leave the `widget` construction, `observer.add(widget)`, and `new ToolStartHandler(widget)` wiring unchanged.
97
- - `test/helpers/make-deps.ts`
98
- - Drop `AgentToolWidget` from the imports from `#src/tools/agent-tool`.
99
- - Remove the `widget: AgentToolWidget` field from `AgentToolFixture` (and its doc comment).
100
- - Remove the `const widget: AgentToolWidget = { setUICtx: vi.fn() };` stub and the `widget,` entry in the returned object.
101
- - `test/helpers/make-deps.test.ts`
102
- - Remove the `describe("widget defaults", ...)` block.
103
- - `test/tools/agent-tool.test.ts`
104
- - Update `makeTool` to drop `deps.widget` from the `new AgentTool(...)` call.
105
- - Remove the `"sets UI context on runtime at start of execute"` test (UICtx capture is covered by `test/handlers/tool-start.test.ts`).
106
- - `docs/architecture/architecture.md`
107
- - Mark Phase 18 Step 5 complete (✅) with a `Landed:` bullet, mirroring the Step 4 entry.
108
- - Correct the stale `agentActivity` mention in the Step 5 description (line ~979) — only the `widget` param remains to drop.
109
- - Update the step dependency diagram node `S5` to the completed (✅) marker.
110
-
111
- Grep confirmation performed during planning: `AgentToolWidget` appears only in `src/tools/agent-tool.ts` and `test/helpers/make-deps.ts`; the `UICtx` type stays in use by `src/ui/agent-widget.ts`, `src/handlers/tool-start.ts` (via `unknown`), and `test/ui/agent-widget.test.ts`.
112
- No `package-*/SKILL.md` references `AgentToolWidget` or the tool's widget dependency.
113
-
114
- ## Test Impact Analysis
115
-
116
- 1. New tests enabled: none required — this is a subtractive refactor, not a new seam.
117
- The remaining `AgentTool` tests get simpler (no widget stub to thread).
118
- 2. Tests that become redundant:
119
- - `test/tools/agent-tool.test.ts` → `"sets UI context on runtime at start of execute"` — the tool no longer captures UICtx; `test/handlers/tool-start.test.ts` (`"calls setUICtx with the context's ui"`) already pins that behavior on its true owner.
120
- Remove it.
121
- - `test/helpers/make-deps.test.ts` → `describe("widget defaults")` — the fixture no longer has a widget field.
122
- Remove it.
123
- 3. Tests that must stay as-is:
124
- - `test/handlers/tool-start.test.ts` — genuinely exercises UICtx capture, now the sole owner of that responsibility.
125
- - `test/ui/agent-widget.test.ts` — exercises the widget's own `setUICtx`, unaffected.
126
- - All other `AgentTool` dispatch/resume/background/foreground tests — exercise the tool's real concern and only shed the unused widget argument.
127
-
128
- ## Invariants at risk
129
-
130
- Phase 18 Step 4 ([#423]) documented Outcome: "the widget is a reactive consumer; no inbound calls from core spawn tools."
131
- This step extends that to the LLM tool.
132
- The invariant that UICtx is captured exactly once per turn by `ToolStartHandler` is pinned by `test/handlers/tool-start.test.ts` (`"calls setUICtx with the context's ui"` and `"calls setUICtx before onTurnStart"`) — both stay green and require no new test.
133
- No earlier step's `Outcome:` is regressed: the widget's self-driving timer (Step 4) is independent of the tool's removed `setUICtx` call.
134
-
135
- ## TDD Order
136
-
137
- This is a behavior-preserving refactor; the green suite stays green.
138
- Because removing the constructor parameter breaks the `index.ts` call site, the `make-deps` fixture, and the `agent-tool` tests at typecheck time, all source and test edits land in one commit.
139
-
140
- 1. **Refactor: drop the widget dependency from `AgentTool`.**
141
- Edit `src/tools/agent-tool.ts` (remove the `UICtx` import, `AgentToolWidget` interface, `widget` param, and `setUICtx` call), `src/index.ts` (drop `widget` from the constructor call), `test/helpers/make-deps.ts` (drop the field, stub, and import), `test/helpers/make-deps.test.ts` (remove the widget-defaults block), and `test/tools/agent-tool.test.ts` (drop `deps.widget` from `makeTool`, remove the UICtx test).
142
- Run `pnpm run check` and the full package suite.
143
- Commit: `refactor: drop the widget dependency from the subagent tool (#424)`.
144
- 2. **Docs: mark Phase 18 Step 5 complete.**
145
- Update `docs/architecture/architecture.md` — Step 5 ✅ + `Landed:` bullet, correct the stale `agentActivity` wording, update the `S5` diagram node.
146
- Commit: `docs: mark Phase 18 Step 5 complete and drop the tool widget dep (#424)`.
147
-
148
- ## Risks and Mitigations
149
-
150
- - Risk: removing the tool's `setUICtx` loses UICtx for the widget in some path.
151
- Mitigation: `tool_execution_start` fires before every `execute`, and `ToolStartHandler` captures UICtx there; the existing handler tests pin this.
152
- Run the full suite, not just `agent-tool.test.ts`, since the change touches a shared fixture (`make-deps.ts`).
153
- - Risk: an orphaned import left behind after deletions (Biome `noUnusedImports` is warning-level, exit 0).
154
- Mitigation: re-read `agent-tool.ts` and `make-deps.ts` after editing; confirm `UICtx` and `AgentToolWidget` have no remaining references via grep.
155
- - Risk: the stale `agentActivity` wording is mistaken for live work to do.
156
- Mitigation: this plan explicitly records that `agentActivity` was already removed in [#422]; only `widget` remains.
157
-
158
- ## Open Questions
159
-
160
- None — the scope is fully determined by the established roadmap and the verified current code state.
161
-
162
- [#422]: https://github.com/gotgenes/pi-packages/issues/422
163
- [#423]: https://github.com/gotgenes/pi-packages/issues/423
164
- [#425]: https://github.com/gotgenes/pi-packages/issues/425
165
- [#426]: https://github.com/gotgenes/pi-packages/issues/426
166
- [#427]: https://github.com/gotgenes/pi-packages/issues/427
@@ -1,144 +0,0 @@
1
- ---
2
- issue: 425
3
- issue_title: "pi-subagents: reconcile the public SUBAGENT_EVENTS contract with emitted channels"
4
- ---
5
-
6
- # Reconcile the public `SUBAGENT_EVENTS` contract with emitted channels
7
-
8
- ## Problem Statement
9
-
10
- The public lifecycle-event contract has drifted out of sync with what the core actually broadcasts on `pi.events`.
11
- `SUBAGENT_EVENTS.ACTIVITY = "subagents:activity"` is declared in the service surface (`src/service/service.ts`) and the architecture doc's lifecycle-events table, but no module ever emits it — a vacant hook that the architecture's own "no vacant hooks" rule forbids.
12
- The vacancy hardened in Phase 18 Steps 1–5, which deleted the entire activity tier (`AgentActivityTracker`, `ui-observer`), so there is no streaming-progress source left to broadcast.
13
- In the other direction, four channels the core does emit — `subagents:failed`, `subagents:compacted`, `subagents:created` (all in `SubagentEventsObserver`), and `subagents:steered` (in `steer-tool.ts`) — are absent from the constant map.
14
- A consumer reading `SUBAGENT_EVENTS` therefore gets one channel that never fires and misses four that do.
15
-
16
- ## Goals
17
-
18
- - Remove the vacant `SUBAGENT_EVENTS.ACTIVITY` constant.
19
- This is a **breaking change** to the public surface: it deletes a key from the exported `SUBAGENT_EVENTS` map, so a consumer referencing `SUBAGENT_EVENTS.ACTIVITY` breaks at the type level on upgrade.
20
- Use `feat!:` with a `BREAKING CHANGE:` footer.
21
- - Add the four emitted agent-lifecycle channels to the constant map: `FAILED`, `COMPACTED`, `CREATED`, `STEERED`.
22
- - After the change, declared channels equal emitted agent-lifecycle channels — no vacant hook, no undeclared emission.
23
- - Update the lifecycle-events table in `docs/architecture/architecture.md` to match, and correct the stale `subagents:completed` payload shape while there.
24
- - Mark Phase 18 Step 6 complete in the architecture roadmap.
25
-
26
- ## Non-Goals
27
-
28
- - Re-introducing a streaming-progress (`activity`) event.
29
- The activity tier was deliberately deleted in Steps 1–3; resurrecting it is out of scope and was rejected during planning.
30
- - Adding the config-domain events (`subagents:settings_loaded`, `subagents:settings_changed`) or the child-session seam events (`subagents:child:*`) to `SUBAGENT_EVENTS`.
31
- Those belong to separate domains and already have their own constant homes (settings emitter, `child-lifecycle.ts`); `SUBAGENT_EVENTS` is the agent-lifecycle bus only.
32
- - The `subagents:record` session entry — it is an `appendEntry` (session persistence), not a `pi.events.emit`, so it is not a channel constant.
33
- - Changing any event payload shape or emission site.
34
- This issue reconciles the *constant map* with what is *already emitted*; emission logic is untouched.
35
- - Phase 18 Steps 7–8 (test-clone consolidation, UI reconsideration) — separate issues ([#426], [#427]).
36
-
37
- ## Background
38
-
39
- - `src/service/service.ts` declares `SUBAGENT_EVENTS` as an `as const` object — the public, cross-extension channel constants, re-exported through the `.` subpath entry and rolled into `dist/public.d.ts`.
40
- - `src/observation/subagent-events-observer.ts` emits `subagents:started`, `subagents:completed`, `subagents:failed`, `subagents:compacted`, and `subagents:created`.
41
- - `src/tools/steer-tool.ts` emits `subagents:steered` with payload `{ id, message }`.
42
- - The completed/failed payload is produced by `buildEventData(record)` in `src/observation/notification.ts`: `{ id, type, description, result, error, status, toolUses, durationMs, tokens? }`.
43
- The architecture doc's current table lists `subagents:completed` as `{ id, type, status, result?, error? }`, which is stale.
44
- - AGENTS.md / `package-pi-subagents` skill constraint: any change to the public surface must run `pnpm --filter @gotgenes/pi-subagents run verify:public-types` (a CI gate), and sibling packages consume this one from the published registry release, not a workspace symlink.
45
- - The "no vacant hooks" rule (ADR-0002, architecture doc §519) governs both directions here: admit a surface only when a real consumer/emitter exists.
46
-
47
- This is a value-only reconciliation of a shared constant: no new collaborator, no dependency-wiring change, no new parameter on any interface — so the `design-review` dependency-width / Law-of-Demeter checklist surfaces nothing actionable.
48
-
49
- ## Design Overview
50
-
51
- The reconciled constant map:
52
-
53
- ```typescript
54
- /** Event channel constants for pi.events subscriptions. */
55
- export const SUBAGENT_EVENTS = {
56
- STARTED: "subagents:started",
57
- COMPLETED: "subagents:completed",
58
- FAILED: "subagents:failed",
59
- COMPACTED: "subagents:compacted",
60
- CREATED: "subagents:created",
61
- STEERED: "subagents:steered",
62
- } as const;
63
- ```
64
-
65
- The declared set is now exactly the set of agent-lifecycle channels emitted by `SubagentEventsObserver` and `steer-tool.ts`.
66
-
67
- Updated lifecycle-events table (`architecture.md`):
68
-
69
- | Channel | Payload | When |
70
- | --------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------- |
71
- | `subagents:started` | `{ id, type, description }` | Agent begins running |
72
- | `subagents:completed` | `{ id, type, description, status, result?, error?, toolUses, durationMs, tokens? }` | Agent finishes successfully |
73
- | `subagents:failed` | same as `completed` (`buildEventData` shape) | Agent ends in `error`/`stopped`/`aborted` |
74
- | `subagents:compacted` | `{ id, type, description, reason, tokensBefore, compactionCount }` | Child session compacts |
75
- | `subagents:created` | `{ id, type, description, isBackground }` | Background agent created (pre-admission) |
76
- | `subagents:steered` | `{ id, message }` | Steering message delivered to a running agent |
77
-
78
- The `subagents:activity` row is removed.
79
-
80
- ### Edge cases
81
-
82
- - `as const` preservation: the literal-string value types must stay narrow, so the new keys keep the `as const` assertion.
83
- `verify:public-types` confirms the rolled `dist/public.d.ts` exposes the narrowed literal types.
84
- - No runtime emission changes: the four added constants name channels that are *already* fired, so subscribers wired to the string literals see no behavioral change — only the typed constant is now available.
85
-
86
- ## Module-Level Changes
87
-
88
- - `packages/pi-subagents/src/service/service.ts` — remove the `ACTIVITY` key; add `FAILED`, `COMPACTED`, `CREATED`, `STEERED` to `SUBAGENT_EVENTS`.
89
- - `packages/pi-subagents/test/service/service.test.ts` — update the `SUBAGENT_EVENTS` assertion: drop the `ACTIVITY` expectation, add expectations for the four new constants.
90
- - `packages/pi-subagents/docs/architecture/architecture.md` — replace the lifecycle-events table (remove the `activity` row, add `failed`/`compacted`/`created`/`steered`, correct the `completed` payload); mark Phase 18 Step 6 complete with a `✅ … — complete.` prefix and a `Landed:` bullet.
91
-
92
- Grep confirmation that no other live reference to the removed symbol exists:
93
-
94
- - `grep -rn "SUBAGENT_EVENTS.ACTIVITY\|subagents:activity"` across `src/`, `test/`, and `.pi/skills/package-*/SKILL.md`: the only live hits are `service.ts:100`, the `service.test.ts` assertion, and the `architecture.md` table — all three are updated here.
95
- Remaining `subagents:activity` matches live in historical/plan docs (`docs/plans/0048-*`, `docs/architecture/history/`, the structural-analysis finding #6 snapshot) and are intentionally left as historical record.
96
- - `docs/comparison-with-upstream.md` already lists the lifecycle bus as `created, started, completed, failed, steered, compacted` (no `activity`) — it matches the reconciled set and needs no edit.
97
- - The architecture structural-analysis finding #6 (the phase-start smell snapshot) is left as-is, consistent with how findings #1–5 remain present-tense snapshots while their steps are marked `✅`.
98
-
99
- ## Test Impact Analysis
100
-
101
- This is a constant-map reconciliation, not an extraction, so no new lower-level test surface opens up.
102
-
103
- 1. New tests enabled: none beyond the expanded `SUBAGENT_EVENTS` assertion — the change adds no new function or collaborator.
104
- 2. Tests becoming redundant: none.
105
- The emission-site tests in `test/observation/subagent-events-observer.test.ts` (which assert `subagents:failed`/`compacted`/`created` are emitted) and the steer-tool test stay — they pin the *emission*, while the service test pins the *declaration*.
106
- 3. Tests that must stay as-is: the observer and steer-tool emission tests — they genuinely exercise the channels the constants now name, and are the other half of the "declared == emitted" invariant.
107
-
108
- ## Invariants at risk
109
-
110
- This step touches the public-contract surface that Step 6 is itself responsible for; no earlier Phase 18 step (1–5) refactored `SUBAGENT_EVENTS`, so there is no prior `Outcome:`/`Landed:` invariant to regress.
111
- The invariant this step establishes — declared channels equal emitted agent-lifecycle channels — is pinned from both sides:
112
-
113
- - Declaration: the updated `test/service/service.test.ts` `SUBAGENT_EVENTS` assertion.
114
- - Emission: existing `test/observation/subagent-events-observer.test.ts` (`failed`/`compacted`/`created`) and the steer-tool test (`steered`).
115
-
116
- ## TDD Order
117
-
118
- 1. **Reconcile the constant map (red → green → commit).**
119
- Test surface: `test/service/service.test.ts` — rewrite the `SUBAGENT_EVENTS` assertion to expect `STARTED`, `COMPLETED`, `FAILED`, `COMPACTED`, `CREATED`, `STEERED` and to assert `ACTIVITY` is absent (`expect("ACTIVITY" in SUBAGENT_EVENTS).toBe(false)` or drop the import-time reference).
120
- This fails to compile/assert against the current map (red).
121
- Then edit `src/service/service.ts`: remove `ACTIVITY`, add the four new keys (green).
122
- Run `pnpm --filter @gotgenes/pi-subagents run check`, `pnpm --filter @gotgenes/pi-subagents run test`, and `pnpm --filter @gotgenes/pi-subagents run verify:public-types` (public-surface gate).
123
- Commit: `feat!: reconcile SUBAGENT_EVENTS with emitted channels (#425)` with a `BREAKING CHANGE:` footer noting the removal of `SUBAGENT_EVENTS.ACTIVITY` and the migration (subscribe to the emitted channel constants `FAILED`/`COMPACTED`/`CREATED`/`STEERED`; there is no replacement for `ACTIVITY` — the activity tier was removed in Phase 18).
124
- 2. **Update the architecture doc (commit, no test).**
125
- Replace the lifecycle-events table and correct the `completed` payload; mark Phase 18 Step 6 `✅ … — complete.` with a `Landed:` bullet.
126
- Run `pnpm --filter @gotgenes/pi-subagents run lint` (rumdl).
127
- Commit: `docs: reconcile lifecycle-events table with SUBAGENT_EVENTS (#425)`.
128
-
129
- ## Risks and Mitigations
130
-
131
- - **Risk:** the breaking removal of `ACTIVITY` surprises a consumer.
132
- **Mitigation:** the constant was never emitted, so no consumer could meaningfully act on it; the `BREAKING CHANGE:` footer documents the removal and the absence of a replacement, and release-please carries it to the CHANGELOG and the issue close comment.
133
- - **Risk:** the rolled `dist/public.d.ts` drifts from the source `as const` shape.
134
- **Mitigation:** `verify:public-types` packs the tarball and type-checks a throwaway consumer against both entries — run it in Step 1 before committing.
135
- - **Risk:** stale `subagents:activity` references linger in docs.
136
- **Mitigation:** the Module-Level Changes grep enumerates every live vs. historical hit; only the three live references are edited, historical snapshots are intentionally preserved.
137
-
138
- ## Open Questions
139
-
140
- None.
141
- The two design choices (remove vs. emit `ACTIVITY`; three vs. four added channels) were resolved during planning: remove `ACTIVITY`, and declare all four emitted agent-lifecycle channels (`failed`/`compacted`/`created`/`steered`).
142
-
143
- [#426]: https://github.com/gotgenes/pi-packages/issues/426
144
- [#427]: https://github.com/gotgenes/pi-packages/issues/427