@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,154 +0,0 @@
1
- ---
2
- issue: 57
3
- issue_title: "feat: structured debug logging for silenced catch blocks"
4
- ---
5
-
6
- # Structured debug logging for silenced catch blocks
7
-
8
- ## Problem Statement
9
-
10
- The codebase contains ~20 `catch { /* ignore */ }` blocks spread across `agent-manager.ts`, `worktree.ts`, `output-file.ts`, `skill-loader.ts`, `custom-agents.ts`, `memory.ts`, `env.ts`, and `notification.ts`.
11
- Each block correctly suppresses a non-essential side-effect failure, but in development the silence makes it impossible to diagnose what is actually failing inside those paths.
12
-
13
- ## Goals
14
-
15
- - Add a `debugLog(context, err)` utility in `src/debug.ts` gated on
16
- `PI_SUBAGENTS_DEBUG=1`.
17
- - Thread `debugLog` into every silenced `catch` block listed in the issue scope.
18
- - Leave production behavior **completely unchanged** — no new log output unless
19
- the env var is explicitly set.
20
- - Provide a `debug.test.ts` unit test covering the on/off branching.
21
-
22
- ## Non-Goals
23
-
24
- - `usage.ts` catch blocks — they return `0`/`null` on failure and are already
25
- recoverable, not truly "silent drops"; they are out of scope.
26
- - `settings.ts` catch block — it returns `false` on failure, already surfaced
27
- to the caller; out of scope.
28
- - Log rotation, structured JSON output, or stderr vs. stdout routing.
29
- - Wiring debug output to the Pi UI; `console.warn` to stderr is sufficient.
30
-
31
- ## Background
32
-
33
- The `AGENTS.md` rule "prefer explicit configuration over hidden behavior" aligns with gating noise behind an opt-in env var rather than always-on logging.
34
- The code-style skill notes that business logic should remain pure — `debug.ts` must not import the Pi SDK; it only reads `process.env`.
35
-
36
- The `DEBUG` constant is evaluated once at module import time (top-level `const`).
37
- That matches how similar opt-in env vars work in Node.js tooling and keeps the hot path to a single boolean branch.
38
-
39
- ### Catch blocks in scope
40
-
41
- | File | Line(s) | Context label(s) |
42
- | ------------------ | ------------------------------------ | ------------------------------------------------------- |
43
- | `env.ts` | 15, 23 | `"git rev-parse"`, `"git branch"` |
44
- | `skill-loader.ts` | 74 | `"readdirSync skill root"` |
45
- | `custom-agents.ts` | 37, 47 | `"readdirSync agents dir"`, `"readFileSync agent file"` |
46
- | `memory.ts` | 33, 47 | `"lstatSync"`, `"readFileSync"` |
47
- | `output-file.ts` | 83 | `"write JSONL chunk"` |
48
- | `worktree.ts` | 40, 56, 110, 130, 132, 147, 151, 161 | `"git rev-parse"`, `"git worktree add"`, etc. |
49
- | `agent-manager.ts` | 233, 249, 266, 275, 480 | `"outputCleanup"`, `"onComplete callback"`, etc. |
50
- | `notification.ts` | 145 | `"notification render"` |
51
-
52
- ## Design Overview
53
-
54
- ### `src/debug.ts`
55
-
56
- ```typescript
57
- export const DEBUG = process.env.PI_SUBAGENTS_DEBUG === "1";
58
-
59
- export function debugLog(context: string, err: unknown): void {
60
- if (DEBUG) console.warn(`[pi-subagents:debug] ${context}:`, err);
61
- }
62
- ```
63
-
64
- The module-level `DEBUG` constant is the canonical source.
65
- `debugLog` is a pure side-effect function: no return value, no SDK dependency, no coupling to Pi types.
66
-
67
- ### Threading pattern
68
-
69
- Every silenced `catch` block is updated from:
70
-
71
- ```typescript
72
- } catch { /* ignore */ }
73
- ```
74
-
75
- to:
76
-
77
- ```typescript
78
- } catch (err) { debugLog("<context label>", err); }
79
- ```
80
-
81
- When the block already had a named error (`catch (err)`), only the `debugLog` call is added; the existing comment, if descriptive, is removed in favour of the context string.
82
-
83
- ### Context label convention
84
-
85
- Labels read as `"<verb> <noun>"`, lower-case, mirroring the surrounding function name when unambiguous (e.g., `"removeWorktree"`, `"outputCleanup"`).
86
-
87
- ## Module-Level Changes
88
-
89
- | File | Change |
90
- | ---------------------- | ---------------------------------------------------------------------------- |
91
- | `src/debug.ts` | **New.** Exports `DEBUG` constant and `debugLog` function. |
92
- | `test/debug.test.ts` | **New.** Unit tests for `debugLog` on/off behaviour. |
93
- | `src/env.ts` | Add `debugLog` import; name the two bare `catch` errors; call `debugLog`. |
94
- | `src/skill-loader.ts` | Add `debugLog` import; name the bare `catch` error; call `debugLog`. |
95
- | `src/custom-agents.ts` | Add `debugLog` import; name the two bare `catch` errors; call `debugLog`. |
96
- | `src/memory.ts` | Add `debugLog` import; name the two bare `catch` errors; call `debugLog`. |
97
- | `src/output-file.ts` | Add `debugLog` import; name the bare `catch` error; call `debugLog`. |
98
- | `src/worktree.ts` | Add `debugLog` import; name the eight bare `catch` errors; call `debugLog`. |
99
- | `src/agent-manager.ts` | Add `debugLog` import; name/update the five `catch` blocks; call `debugLog`. |
100
- | `src/notification.ts` | Add `debugLog` import; name the bare `catch` error; call `debugLog`. |
101
-
102
- ## Test Impact Analysis
103
-
104
- 1. **New tests enabled** — `debug.test.ts` can directly unit-test `debugLog` in isolation.
105
- Asserting `console.warn` is/isn't called based on the env var is now trivial.
106
- Previously this path was entirely untested.
107
-
108
- 2. **Existing tests become redundant** — None.
109
- The catch blocks were never exercised by existing tests (they were silent drops), so no existing test coverage needs to be removed.
110
-
111
- 3. **Tests that must stay as-is** — All existing tests remain valid.
112
- The threading adds a call inside each catch but does not change control flow, return values, or thrown exceptions; no test expectations change.
113
-
114
- ## TDD Order
115
-
116
- 1. **`src/debug.ts` + `test/debug.test.ts`** Test surface: `debugLog` calls `console.warn` when `PI_SUBAGENTS_DEBUG=1`; does nothing when unset or `"0"`.
117
- Use `vi.spyOn(console, 'warn')` + `vi.stubEnv('PI_SUBAGENTS_DEBUG', '1')` + `vi.resetModules()` + dynamic import to exercise both branches.
118
- Suggested commit: `feat: add debugLog utility gated on PI_SUBAGENTS_DEBUG (#57)`
119
-
120
- 2. **Thread into `env.ts`** Test surface: `env.test.ts` — existing tests still pass (no new assertions required because the catch paths are tested implicitly by the non-git-dir test already covering the swallowed failures).
121
- Suggested commit: `feat: thread debugLog into env catch blocks (#57)`
122
-
123
- 3. **Thread into `skill-loader.ts`** Test surface: `skill-loader.test.ts` — existing tests still pass.
124
- The readdirSync-error catch is exercised when the skill root does not exist (covered by existing "not found" test paths).
125
- Suggested commit: `feat: thread debugLog into skill-loader catch block (#57)`
126
-
127
- 4. **Thread into `custom-agents.ts` and `memory.ts`** These two files have structurally identical filesystem-error catch blocks and can land in a single step.
128
- Test surface: `custom-agents.test.ts`, `memory.test.ts` — existing tests still pass.
129
- Suggested commit: `feat: thread debugLog into custom-agents and memory catch blocks (#57)`
130
-
131
- 5. **Thread into `output-file.ts`** Test surface: `output-file.test.ts` — existing tests still pass.
132
- Suggested commit: `feat: thread debugLog into output-file catch block (#57)`
133
-
134
- 6. **Thread into `worktree.ts`** Test surface: `worktree.test.ts` — existing tests still pass.
135
- Eight catch sites; name each with its enclosing function for label clarity.
136
- Suggested commit: `feat: thread debugLog into worktree catch blocks (#57)`
137
-
138
- 7. **Thread into `agent-manager.ts` and `notification.ts`** Test surface: `agent-manager.test.ts`, `notification.test.ts` — existing tests still pass.
139
- Suggested commit: `feat: thread debugLog into agent-manager and notification catch blocks (#57)`
140
-
141
- ## Risks and Mitigations
142
-
143
- | Risk | Mitigation |
144
- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
145
- | `DEBUG` is a module-level constant — `vi.stubEnv` alone won't flip it mid-test | Use `vi.resetModules()` + dynamic `import()` inside a test that sets the env var before re-importing; the test skill documents this pattern |
146
- | Adding `(err)` to a bare `catch` in TypeScript is type-safe (`unknown`) but a `noImplicitAny`-adjacent change | `tsconfig` already uses `"useUnknownInCatchVariables": true` (default in strict mode); no cast needed |
147
- | Verbose worktree.ts threading (8 sites) could miss one | The TDD step runs `pnpm vitest run worktree` and `pnpm run check` before committing |
148
-
149
- ## Open Questions
150
-
151
- - Should a future issue expose `debugLog` as part of the public `service.ts` API so consumer extensions can share the same debug flag?
152
- Deferred — out of scope for this change; no consumer currently needs it.
153
- - Should `PI_SUBAGENTS_DEBUG` be documented in the package `README.md`?
154
- Likely yes, but deferred to a follow-up doc PR.
@@ -1,284 +0,0 @@
1
- ---
2
- issue: 61
3
- issue_title: "feat: port subagent transcript logging to Pi's official JSONL session format"
4
- ---
5
-
6
- # Port subagent transcripts to Pi's official session format
7
-
8
- ## Problem Statement
9
-
10
- Subagent conversation transcripts are written as JSONL by `output-file.ts`, but use a bespoke flat format (`{ isSidechain, agentId, type, message, timestamp, cwd }`) that does not conform to Pi's official session file format.
11
- Pi's `SessionManager` writes tree-structured JSONL with a session header, UUIDv7 entry IDs, `parentId` tree structure, and typed entry discriminants (`"message"`, `"compaction"`, etc.).
12
- The bespoke format cannot be loaded by Pi's session tooling (session selector, export-html, resume) and requires manual `jq` inspection for debugging.
13
-
14
- ## Goals
15
-
16
- - Subagent transcripts written in Pi's official JSONL session format via `SessionManager`.
17
- - Transcripts discoverable via the parent session path (nested under a parent-session-relative directory).
18
- - Parent session linkage via the `parentSession` header field.
19
- - Delete `output-file.ts` — the SDK's `SessionManager` handles all JSONL writing natively.
20
- - Existing debugging use case preserved (full turn-by-turn history on disk).
21
-
22
- ## Non-Goals
23
-
24
- - Making subagent sessions resumable via Pi's `/resume` command (future work; requires session selection UX changes).
25
- - Cross-extension parent-session resolution (issue #22 — separate track).
26
- - Changing the `SessionManager` usage for the child `AgentSession` itself (the child session already uses `SessionManager.inMemory()`; we replace it with a persisted one).
27
-
28
- ## Background
29
-
30
- ### Current flow
31
-
32
- 1. `agent-tool.ts` calls `createOutputFilePath(cwd, agentId, parentSessionId)` → creates `/tmp/pi-subagents-<uid>/<encoded-cwd>/<parentSessionId>/tasks/<agentId>.output`.
33
- 2. `writeInitialEntry(path, agentId, prompt, cwd)` writes the first user message in the bespoke format.
34
- 3. `streamToOutputFile(session, path, agentId, cwd)` subscribes to `turn_end` events and appends bespoke JSONL entries.
35
- 4. `agent-manager.ts` calls the cleanup function on completion/error to do a final flush and unsubscribe.
36
-
37
- ### Pi's SessionManager API
38
-
39
- `SessionManager` from `@earendil-works/pi-coding-agent` provides:
40
-
41
- - `SessionManager.create(cwd, sessionDir?)` — creates a persisted session file in the given directory.
42
- - `newSession({ parentSession? })` — writes a `SessionHeader` with `parentSession` linking.
43
- - `appendMessage(message)` — writes a `SessionMessageEntry` with auto-generated UUIDv7 `id` and `parentId` (tree structure).
44
- - `appendCompaction(...)`, `appendCustomEntry(...)` — first-class support for all entry types.
45
- - `getSessionFile()` — returns the path to the JSONL file on disk.
46
-
47
- The child `AgentSession` (created by `createAgentSession`) accepts a `sessionManager` option.
48
- Currently set to `SessionManager.inMemory(cwd)`, switching to `SessionManager.create(cwd, sessionDir)` makes the SDK write official-format JSONL automatically — no manual streaming code needed.
49
-
50
- ### Reference implementations
51
-
52
- nicobailon/pi-subagents places subagent sessions under `<parent-session-dir>/<parent-session-basename>/<runId>/`.
53
- This keeps them discoverable via the parent session path without cluttering the main session list.
54
- Our approach follows the same pattern: derive a `sessionDir` from the parent session file.
55
-
56
- ### Constraints
57
-
58
- - `agent-runner.ts` already imports `SessionManager` for the `inMemory()` call — switching to `create()` adds no new dependency.
59
- - `ctx.sessionManager` is `ReadonlySessionManager` and provides `getSessionId()`, `getSessionFile()`, and `getSessionDir()`.
60
- - The `code-style` skill requires keeping IO at the edges and not hiding dependencies.
61
- The session directory derivation should be a pure function that receives the parent session file path.
62
-
63
- ## Design Overview
64
-
65
- ### Core change: persisted SessionManager in agent-runner
66
-
67
- Replace `SessionManager.inMemory(cwd)` with `SessionManager.create(cwd, sessionDir)` inside `runAgent()`.
68
- The `sessionDir` is derived from the parent's session file path, and the `parentSession` option links the child to the parent.
69
-
70
- The key insight is that the SDK's `createAgentSession` already writes all messages through the `SessionManager` it receives.
71
- By switching from in-memory to persisted, every message the child agent produces is automatically written in official JSONL format — no manual subscription or streaming code required.
72
-
73
- ### Session directory derivation
74
-
75
- ```typescript
76
- /**
77
- * Derive the session directory for a subagent from the parent session file.
78
- * Layout: <parent-dir>/<parent-basename>/tasks/
79
- *
80
- * Example:
81
- * parent: ~/.pi/agent/sessions/--home-user-project--/2026-05-20T12-00-00Z_.jsonl
82
- * result: ~/.pi/agent/sessions/--home-user-project--/2026-05-20T12-00-00Z_/tasks/
83
- *
84
- * Falls back to a temp directory when the parent session is not persisted.
85
- */
86
- function deriveSubagentSessionDir(
87
- parentSessionFile: string | undefined,
88
- cwd: string,
89
- ): string;
90
- ```
91
-
92
- ### Data flow (after)
93
-
94
- 1. `agent-tool.ts` passes `parentSessionFile` (from `ctx.sessionManager.getSessionFile()`) and `parentSessionId` to `agent-manager.ts` via `SpawnOptions`.
95
- 2. `agent-manager.ts` threads them to `agent-runner.ts` via `RunOptions`.
96
- 3. `agent-runner.ts` calls `deriveSubagentSessionDir(parentSessionFile, cwd)` and creates `SessionManager.create(cwd, sessionDir)` with `{ parentSession: parentSessionId }`.
97
- 4. The `createAgentSession` SDK call receives this persisted `SessionManager`.
98
- 5. All messages are written to disk automatically by the SDK.
99
- 6. `agent-runner.ts` returns the session file path in `RunResult` (via `sessionManager.getSessionFile()`).
100
- 7. `agent-tool.ts` stores the path on `AgentRecord.outputFile` for display in notifications/UI.
101
-
102
- ### What gets deleted
103
-
104
- - `output-file.ts` — entirely replaced by the persisted `SessionManager`.
105
- - `output-file.test.ts` — the `encodeCwd` tests are no longer needed (Pi's `SessionManager` handles directory encoding internally).
106
- - `streamToOutputFile`, `writeInitialEntry`, `createOutputFilePath` imports from `agent-tool.ts`.
107
- - `outputCleanup` field from `AgentRecord` (no manual cleanup needed; the `SessionManager` is append-only).
108
- - The `onSessionCreated` wrapper in `agent-tool.ts` that wires output-file streaming.
109
-
110
- ### What changes
111
-
112
- - `RunOptions` gains `parentSessionFile?: string` and `parentSessionId?: string`.
113
- - `RunResult` gains `sessionFile?: string` (path to the persisted session JSONL).
114
- - `SpawnOptions` gains `parentSessionFile?: string` and `parentSessionId?: string`.
115
- - `AgentRecord.outputCleanup` is removed (no manual flush/unsubscribe lifecycle).
116
- - `agent-manager.ts` removes the `outputCleanup` calls in completion/error paths.
117
- - `agent-tool.ts` sets `record.outputFile` from the `RunResult.sessionFile` instead of calling `createOutputFilePath`.
118
- - `notification.ts`, `renderer.ts`, `types.ts` — `outputFile` field semantics unchanged (still a path string); only the source changes.
119
-
120
- ### Edge cases
121
-
122
- 1. **Parent session not persisted** (e.g., API/headless mode where the parent uses `SessionManager.inMemory()`): `ctx.sessionManager.getSessionFile()` returns `undefined`.
123
- Fallback: use a temp directory under `/tmp/pi-subagents-<uid>/` (similar to current behavior) so transcripts are still written to disk.
124
- 2. **Worktree isolation**: `effectiveCwd` differs from `ctx.cwd`.
125
- The `SessionManager.create()` call uses `effectiveCwd` (same as current `inMemory` call), but the `sessionDir` is still derived from the parent session — the session header's `cwd` field correctly records the worktree path.
126
-
127
- ## Module-Level Changes
128
-
129
- ### New file: `src/session-dir.ts`
130
-
131
- Pure function `deriveSubagentSessionDir(parentSessionFile, cwd)`.
132
- Extracts the parent session basename, constructs `<parent-dir>/<parent-basename>/tasks/`.
133
- Falls back to a temp directory when `parentSessionFile` is undefined.
134
-
135
- ### Modified: `src/agent-runner.ts`
136
-
137
- - Import `deriveSubagentSessionDir` from `session-dir.ts`.
138
- - Add `parentSessionFile?: string` and `parentSessionId?: string` to `RunOptions`.
139
- - Add `sessionFile?: string` to `RunResult`.
140
- - Replace `SessionManager.inMemory(cfg.effectiveCwd)` with:
141
-
142
- ```typescript
143
- const sessionDir = deriveSubagentSessionDir(options.parentSessionFile, cfg.effectiveCwd);
144
- const sessionManager = SessionManager.create(cfg.effectiveCwd, sessionDir);
145
- sessionManager.newSession({ parentSession: options.parentSessionId });
146
- ```
147
-
148
- - After `session.prompt()`, capture `sessionManager.getSessionFile()` into `RunResult.sessionFile`.
149
-
150
- ### Modified: `src/agent-manager.ts`
151
-
152
- - Add `parentSessionFile?: string` and `parentSessionId?: string` to `SpawnOptions`.
153
- - Thread them to `RunOptions` in the `runner.run()` call.
154
- - Remove `outputCleanup` calls from the completion and error handlers.
155
-
156
- ### Modified: `src/tools/agent-tool.ts`
157
-
158
- - Remove import of `createOutputFilePath`, `streamToOutputFile`, `writeInitialEntry`.
159
- - Remove the `onSessionCreated` wrapper that wired output-file streaming.
160
- - Pass `parentSessionFile: ctx.sessionManager.getSessionFile()` and `parentSessionId: ctx.sessionManager.getSessionId()` in `SpawnOptions`.
161
- - After spawn, set `record.outputFile` from the returned session file path (available via `onSessionCreated` callback which gives access to the session's `sessionManager`).
162
-
163
- ### Modified: `src/types.ts`
164
-
165
- - Remove `outputCleanup?: () => void` from `AgentRecord`.
166
-
167
- ### Deleted: `src/output-file.ts`
168
-
169
- Entire file removed.
170
-
171
- ### Deleted: `test/output-file.test.ts`
172
-
173
- Entire file removed.
174
-
175
- ### Unchanged: `src/notification.ts`, `src/renderer.ts`
176
-
177
- These read `record.outputFile` (a path string) — the field still exists, just populated differently.
178
- The `NotificationDetails.outputFile` field is unchanged.
179
-
180
- ## Test Impact Analysis
181
-
182
- ### New tests enabled by extraction
183
-
184
- 1. `test/session-dir.test.ts` — unit tests for `deriveSubagentSessionDir`:
185
- - Parent session file present → derives correct nested path.
186
- - Parent session file undefined → falls back to temp directory.
187
- - Various path shapes (POSIX, Windows-like).
188
-
189
- 2. `test/agent-runner.test.ts` — updated tests verifying:
190
- - `SessionManager.create` is called (not `inMemory`) when `parentSessionFile` is provided.
191
- - `newSession({ parentSession })` is called with the parent session ID.
192
- - `RunResult.sessionFile` is populated from the session manager.
193
-
194
- ### Tests that become redundant
195
-
196
- - `test/output-file.test.ts` (`encodeCwd` tests) — the directory encoding is now handled by `deriveSubagentSessionDir` with a different layout.
197
- The `encodeCwd` function is deleted.
198
-
199
- ### Tests that stay as-is
200
-
201
- - All `agent-manager.test.ts` tests that mock the runner — they don't depend on `output-file` internals.
202
- - All `notification.test.ts` and `renderer.test.ts` tests — they read `record.outputFile` which remains a string.
203
- - The `onSessionCreated` callback tests in `agent-tool.test.ts` need updating to remove the output-file wiring expectations.
204
-
205
- ## TDD Order
206
-
207
- ### Step 1: Add `deriveSubagentSessionDir` with tests
208
-
209
- 1. Create `src/session-dir.ts` with the pure function.
210
- 2. Create `test/session-dir.test.ts` with tests for parent-present and fallback cases.
211
- 3. Commit: `feat: add deriveSubagentSessionDir for session directory derivation (#61)`
212
-
213
- ### Step 2: Thread parent session info through SpawnOptions and RunOptions
214
-
215
- 1. Add `parentSessionFile?: string` and `parentSessionId?: string` to `SpawnOptions` in `agent-manager.ts`.
216
- 2. Add `parentSessionFile?: string` and `parentSessionId?: string` to `RunOptions` in `agent-runner.ts`.
217
- 3. Add `sessionFile?: string` to `RunResult` in `agent-runner.ts`.
218
- 4. Thread the new fields from `SpawnOptions` → `RunOptions` in `agent-manager.ts`.
219
- 5. Update tests to verify the new fields are threaded.
220
- 6. Commit: `feat: thread parent session info through spawn and run options (#61)`
221
-
222
- ### Step 3: Switch agent-runner to persisted SessionManager
223
-
224
- 1. In `runAgent()`, replace `SessionManager.inMemory(cfg.effectiveCwd)` with the persisted variant using `deriveSubagentSessionDir`.
225
- 2. Call `sessionManager.newSession({ parentSession: options.parentSessionId })`.
226
- 3. Capture `sessionManager.getSessionFile()` into `RunResult.sessionFile`.
227
- 4. Update `agent-runner.test.ts` mocks to expect `SessionManager.create` instead of `SessionManager.inMemory`.
228
- 5. Commit: `feat: use persisted SessionManager for subagent sessions (#61)`
229
-
230
- ### Step 4: Remove output-file wiring from agent-tool and agent-manager
231
-
232
- 1. Remove the `onSessionCreated` output-file wrapper in `agent-tool.ts`.
233
- 2. Remove `createOutputFilePath`, `writeInitialEntry`, `streamToOutputFile` imports.
234
- 3. Set `record.outputFile` from the session manager's file path (via `onSessionCreated` callback).
235
- 4. Pass `parentSessionFile` and `parentSessionId` in spawn options.
236
- 5. Remove `outputCleanup` calls from `agent-manager.ts` completion/error handlers.
237
- 6. Remove `outputCleanup` from `AgentRecord` in `types.ts`.
238
- 7. Update agent-tool and agent-manager tests.
239
- 8. Commit: `feat: wire session file path through agent-tool, remove output-file streaming (#61)`
240
-
241
- ### Step 5: Delete output-file.ts and its tests
242
-
243
- 1. Delete `src/output-file.ts`.
244
- 2. Delete `test/output-file.test.ts`.
245
- 3. Run full test suite to verify no remaining references.
246
- 4. Commit: `feat!: remove bespoke output-file transcript format (#61)`
247
-
248
- ### Step 6: Documentation update
249
-
250
- 1. Update `docs/architecture/architecture.md` — remove `output-file.ts` from the module listing, add `session-dir.ts`, mark #61 as complete.
251
- 2. Commit: `docs: update architecture for session format migration (#61)`
252
-
253
- ## Risks and Mitigations
254
-
255
- ### Risk: SessionManager.create may fail in environments without a writable home directory
256
-
257
- The temp-directory fallback in `deriveSubagentSessionDir` handles this case.
258
- When the parent session file is unavailable (headless/API mode), we fall back to a temp directory just like the current implementation.
259
-
260
- ### Risk: Persisted sessions accumulate disk space over time
261
-
262
- Pi's session tooling already manages session storage.
263
- Subagent sessions nested under the parent session directory are cleaned up when the parent session is deleted.
264
- This is an improvement over the current `/tmp` location where files persist until system cleanup.
265
-
266
- ### Risk: Tests that mock SessionManager.inMemory need updating
267
-
268
- Step 3 explicitly plans for updating these mocks.
269
- The change is localized to `agent-runner.ts` and its direct test file.
270
-
271
- ### Risk: Breaking change for consumers that read outputFile paths
272
-
273
- The `outputFile` field changes from `/tmp/pi-subagents-<uid>/.../tasks/<agentId>.output` to `~/.pi/agent/sessions/.../<timestamp>_<uuid>.jsonl`.
274
- Consumers that parse the path (unlikely) would break, but the field is internal to the extension and only used for display.
275
- The format of the file content changes from bespoke JSONL to Pi's official format — this is the intentional breaking change.
276
-
277
- ## Open Questions
278
-
279
- 1. Should the plan add a `sessionDir` field to `AgentRecord` alongside `outputFile`, or is the session file path sufficient for all use cases?
280
- Deferred — start with `outputFile` pointing to the session file; add `sessionDir` if needed later.
281
- 2. Should foreground agents also get persisted sessions, or only background agents?
282
- The current `output-file` code only runs for background agents.
283
- Persisted sessions are valuable for both — the plan applies the change in `agent-runner.ts` which serves both paths.
284
- If this proves too noisy, a follow-up can gate it behind a setting.
@@ -1,215 +0,0 @@
1
- ---
2
- issue: 66
3
- issue_title: "refactor: replace `as any` casts in extracted tool/menu factories with proper SDK types"
4
- ---
5
-
6
- # Replace `as any` casts in factory dep interfaces with proper SDK types
7
-
8
- ## Problem Statement
9
-
10
- The decomposition in #54 introduced 14 `as any` casts at the wiring boundary in `src/index.ts`.
11
- These exist because the factory dep interfaces (`AgentToolManager`, `GetResultDeps`, `SteerToolDeps`, `AgentMenuDeps`, `NotificationDeps`) declare `unknown` or `object` for parameters that are actually well-typed SDK exports.
12
- Every cast papers over a real type that the SDK already exports.
13
- For comparison, `pi-permission-system` imports SDK types directly in its internal interfaces and has zero `as any` casts.
14
-
15
- ## Goals
16
-
17
- - Replace every `as any` cast in `src/index.ts` by typing the corresponding dep interface parameter with the actual SDK type.
18
- - Keep factory modules decoupled from `AgentManager` where possible — use shared types (`types.ts`) or SDK imports.
19
- - No runtime behavior change — this is purely a type-safety improvement.
20
- - Existing tests continue to pass without modification (factory `execute` methods use `ctx: any` or `ctx: unknown`, so test mocks are unaffected).
21
-
22
- ## Non-Goals
23
-
24
- - Fixing `as any` casts in `agent-runner.ts`, `conversation-viewer.ts`, `tools/helpers.ts`, or `tools/agent-tool.ts:550` — those access untyped SDK message internals, a different concern.
25
- - Removing the `ctx as UICtx` cast (line 196) — that is a named cast, not `as any`.
26
- - Removing `as any` from test files (e.g., `agent-menu.test.ts`'s `ctx as any`) — test mocks are intentionally narrow.
27
-
28
- ## Background
29
-
30
- ### Current cast inventory (`src/index.ts`)
31
-
32
- | Line | Cast | Root cause |
33
- | ---- | ----------------------------------------------- | --------------------------------------------------------------------------- |
34
- | 59 | `msg as any, opts as any` | `NotificationDeps.sendMessage(msg: unknown, opts: unknown)` |
35
- | 188 | `ctx as any, opts as any` | `AgentToolManager.spawn(ctx: unknown, …, opts: object)` |
36
- | 189 | `ctx as any, opts as any` | `AgentToolManager.spawnAndWait(ctx: unknown, …, opts: object)` |
37
- | 208 | `}) as any` | Cascading from above — `createAgentTool` return doesn't match `defineTool` |
38
- | 215 | `session as any` | `GetResultDeps.getConversation(session: unknown)` |
39
- | 223 | `session as any` | `SteerToolDeps.steerAgent(session: unknown, …)` |
40
- | 232 | `(piArg ?? pi) as any, ctx as any, opts as any` | `AgentMenuManager.spawnAndWait(pi: unknown, ctx: unknown, …, opts: object)` |
41
- | 242 | `registry as any` | `AgentMenuDeps.getModelLabel(…, registry?: unknown)` |
42
- | 271 | `ctx as any` | `MenuContext` not structurally compatible with `ExtensionCommandContext` |
43
-
44
- ### SDK types available
45
-
46
- All are exported from `@earendil-works/pi-coding-agent`:
47
-
48
- - `ExtensionContext` — tool execute context (has `ui`, `modelRegistry`, `cwd`, etc.)
49
- - `ExtensionCommandContext` — command handler context (extends `ExtensionContext`)
50
- - `ExtensionAPI` — the `pi` object passed to extensions
51
- - `AgentSession` — session handle (has `steer()`, `messages`, etc.)
52
-
53
- `ModelRegistry` is already defined locally in `src/model-resolver.ts` and matches the SDK shape.
54
- `SpawnOptions` is defined locally in `src/agent-manager.ts` (not currently exported).
55
-
56
- ### Relevant AGENTS.md constraints
57
-
58
- - "Avoid `any` unless absolutely necessary."
59
- - "Keep Pi SDK imports out of business-logic modules."
60
- Tool definitions, event handlers, and command handlers are SDK consumers — they may import SDK types directly.
61
- The restriction targets pure helpers, utilities, and domain modules.
62
- - "When writing event handlers that consume Pi SDK types, prefer lean local payload interfaces over full SDK event types."
63
-
64
- The factory modules (`tools/agent-tool.ts`, `tools/get-result-tool.ts`, `tools/steer-tool.ts`, `ui/agent-menu.ts`) are SDK consumers (they define tools and command handlers), so importing SDK types is acceptable.
65
- The notification module (`notification.ts`) is a pure helper — it should use narrow local types rather than SDK imports.
66
-
67
- ## Design Overview
68
-
69
- ### Strategy per interface
70
-
71
- 1. **`NotificationDeps.sendMessage`** — define narrow inline parameter types matching `ExtensionAPI.sendMessage`'s shape.
72
- No SDK import needed; the notification module stays SDK-independent.
73
-
74
- 2. **`AgentToolManager`** — import `ExtensionContext` for `ctx`, export + import `SpawnOptions` from `agent-manager.ts` for `opts`.
75
- The tool module is an SDK consumer, so SDK imports are acceptable.
76
-
77
- 3. **`AgentToolWidget.setUICtx`** — stays `ctx: unknown` (not `as any`; currently `ctx as UICtx`).
78
- Out of scope.
79
-
80
- 4. **`GetResultDeps.getConversation`** — import `AgentSession` for `session`.
81
-
82
- 5. **`SteerToolDeps.steerAgent`** — import `AgentSession` for `session`.
83
-
84
- 6. **`AgentMenuManager.spawnAndWait`** — import `ExtensionAPI` for `pi`, `ExtensionContext` for `ctx`, export + import `SpawnOptions` for `opts`.
85
-
86
- 7. **`AgentMenuDeps.getModelLabel`** — import `ModelRegistry` from `../model-resolver.js` for `registry`.
87
-
88
- 8. **`MenuContext` structural compatibility** — switch `MenuUI` from property syntax (strict function types) to method syntax (bivariant), and type `modelRegistry` as `ModelRegistry`.
89
- This makes `ExtensionCommandContext` structurally assignable to `MenuContext`, removing the cast without broadening the handler's dependency on the full SDK type.
90
- Tests continue using narrow mocks because the handler's parameter is still the narrow `MenuContext`.
91
-
92
- ### `SpawnOptions` export
93
-
94
- `SpawnOptions` in `agent-manager.ts` is currently a private interface.
95
- Exporting it as a named `type` export adds no runtime cost and avoids duplicating the 15-field type in each factory.
96
- Both `tools/agent-tool.ts` and `ui/agent-menu.ts` already import `AgentRecord` from `types.ts`, so an intra-package `import type` from `agent-manager.ts` follows the same pattern.
97
-
98
- ### Cascading cast resolution
99
-
100
- The `createAgentTool({…}) as any` cast on line 208 exists because TypeScript cannot verify the returned object satisfies `ToolDefinition` when inner types are `unknown`/`object`.
101
- Once the inner types are correct, the factory return type matches `ToolDefinition` naturally, and the outer `as any` cast resolves without further changes.
102
-
103
- ## Module-Level Changes
104
-
105
- ### `src/agent-manager.ts`
106
-
107
- - Export the existing `SpawnOptions` interface (add `export` keyword).
108
-
109
- ### `src/notification.ts`
110
-
111
- - Replace `sendMessage: (msg: unknown, opts: unknown) => void` with narrow inline types:
112
-
113
- ```typescript
114
- sendMessage: (
115
- msg: { customType: string; content: string; display?: boolean; details?: unknown },
116
- opts?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
117
- ) => void;
118
- ```
119
-
120
- ### `src/tools/agent-tool.ts`
121
-
122
- - Add `import type { ExtensionContext } from "@earendil-works/pi-coding-agent";`
123
- - Add `import type { SpawnOptions } from "../agent-manager.js";`
124
- - `AgentToolManager.spawn`: `ctx: unknown` → `ctx: ExtensionContext`, `opts: object` → `opts: SpawnOptions`
125
- - `AgentToolManager.spawnAndWait`: same changes, opts as `Omit<SpawnOptions, "isBackground">`
126
-
127
- ### `src/tools/get-result-tool.ts`
128
-
129
- - Add `import type { AgentSession } from "@earendil-works/pi-coding-agent";`
130
- - `GetResultDeps.getConversation`: `session: unknown` → `session: AgentSession`
131
-
132
- ### `src/tools/steer-tool.ts`
133
-
134
- - Add `import type { AgentSession } from "@earendil-works/pi-coding-agent";`
135
- - `SteerToolDeps.steerAgent`: `session: unknown` → `session: AgentSession`
136
-
137
- ### `src/ui/agent-menu.ts`
138
-
139
- - Add `import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";`
140
- - Add `import type { SpawnOptions } from "../agent-manager.js";`
141
- - Add `import type { ModelRegistry } from "../model-resolver.js";`
142
- - `AgentMenuManager.spawnAndWait`: `pi: unknown` → `pi: ExtensionAPI | null`, `ctx: unknown` → `ctx: ExtensionContext`, `opts: object` → `opts: Omit<SpawnOptions, "isBackground">`
143
- - `AgentMenuDeps.getModelLabel`: `registry?: unknown` → `registry?: ModelRegistry`
144
- - `MenuUI`: switch from property syntax to method syntax for structural compatibility with `ExtensionUIContext`.
145
- Update `notify` second parameter from `level: string` to `type?: string` to match SDK.
146
- - `MenuContext.modelRegistry`: `unknown` → `ModelRegistry`
147
-
148
- ### `src/index.ts`
149
-
150
- - Remove all 14 `as any` casts.
151
- - No new imports needed (types already used transitively).
152
-
153
- ## Test Impact Analysis
154
-
155
- 1. **No new unit tests needed** — this is a type-only refactoring with no behavioral change.
156
- 2. **No existing tests become redundant** — the tests exercise tool execution and menu behavior, not type signatures.
157
- 3. **Existing tests must stay as-is** — factory `execute` methods use `ctx: any` or `ctx: unknown`, so `makeCtx()` test helpers remain compatible.
158
- Test-side `as any` casts (e.g., `handler(ctx as any)` in `agent-menu.test.ts`) are out of scope.
159
-
160
- ## TDD Order
161
-
162
- Since this is a type-only change, each step is verified by `pnpm run check` (tsc) rather than new vitest tests.
163
- The full test suite (`pnpm vitest run`) is run at the end as a regression check.
164
-
165
- 1. **Export `SpawnOptions`** — add `export` to the existing `interface SpawnOptions` in `agent-manager.ts`.
166
- Verify: `pnpm run check`.
167
- Commit: `refactor: export SpawnOptions from agent-manager`
168
-
169
- 2. **Type `NotificationDeps`** — replace `(msg: unknown, opts: unknown)` with narrow inline types in `notification.ts`.
170
- Remove `msg as any, opts as any` casts (line 59) in `index.ts`.
171
- Verify: `pnpm run check`.
172
- Commit: `refactor: type NotificationDeps.sendMessage parameters (#66)`
173
-
174
- 3. **Type `AgentToolManager`** — import `ExtensionContext` and `SpawnOptions`, update `spawn` and `spawnAndWait` signatures in `agent-tool.ts`.
175
- Remove `ctx as any, opts as any` casts (lines 188–189) and the cascading `}) as any` cast (line 208) in `index.ts`.
176
- Verify: `pnpm run check`.
177
- Commit: `refactor: type AgentToolManager with ExtensionContext and SpawnOptions (#66)`
178
-
179
- 4. **Type `GetResultDeps`** — import `AgentSession`, update `getConversation` in `get-result-tool.ts`.
180
- Remove `session as any` cast (line 215) in `index.ts`.
181
- Verify: `pnpm run check`.
182
- Commit: `refactor: type GetResultDeps.getConversation with AgentSession (#66)`
183
-
184
- 5. **Type `SteerToolDeps`** — import `AgentSession`, update `steerAgent` in `steer-tool.ts`.
185
- Remove `session as any` cast (line 223) in `index.ts`.
186
- Verify: `pnpm run check`.
187
- Commit: `refactor: type SteerToolDeps.steerAgent with AgentSession (#66)`
188
-
189
- 6. **Type `AgentMenuManager` + `AgentMenuDeps`** — import `ExtensionAPI`, `ExtensionContext`, `SpawnOptions`, `ModelRegistry`.
190
- Update `spawnAndWait` and `getModelLabel` signatures in `agent-menu.ts`.
191
- Remove `(piArg ?? pi) as any, ctx as any, opts as any` (line 232) and `registry as any` (line 242) casts in `index.ts`.
192
- Verify: `pnpm run check`.
193
- Commit: `refactor: type AgentMenu interfaces with SDK types (#66)`
194
-
195
- 7. **Align `MenuContext` with `ExtensionCommandContext`** — switch `MenuUI` to method syntax, fix `notify` parameter, type `modelRegistry` as `ModelRegistry`.
196
- Remove `ctx as any` cast (line 271) in `index.ts`.
197
- Verify: `pnpm run check`.
198
- Commit: `refactor: align MenuContext for structural ExtensionCommandContext compat (#66)`
199
-
200
- 8. **Final verification** — run `pnpm vitest run` for the full test suite.
201
- Grep `src/index.ts` for remaining `as any` — expect zero.
202
- Commit: none (verification only).
203
-
204
- ## Risks and Mitigations
205
-
206
- | Risk | Mitigation |
207
- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
208
- | Exporting `SpawnOptions` from `agent-manager.ts` couples factory modules to the manager's type | The coupling is type-only (`import type`), adds no runtime dependency, and follows the existing pattern (`AgentRecord` in `types.ts` is already shared). |
209
- | `MenuUI` method-syntax change could break structural compatibility with test mocks | Test mocks use `vi.fn()` which is bivariant; method syntax on the `MenuUI` interface only affects how TypeScript checks assignability from `ExtensionUIContext`, not from mock objects. |
210
- | SDK type exports could change in a future Pi release | The SDK types used (`ExtensionContext`, `AgentSession`, `ExtensionAPI`) are stable public API. `ModelRegistry` is a local interface. |
211
- | Cascading `as any` on `createAgentTool` may not resolve automatically | If TypeScript still cannot infer the return type, add explicit `satisfies ToolDefinition<…>` or a return type annotation. Verified in step 3. |
212
-
213
- ## Open Questions
214
-
215
- - None — the issue's "Proposed change" section is unambiguous and all SDK types are confirmed available.