@gotgenes/pi-subagents 18.0.0 → 18.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +22 -31
  3. package/docs/architecture/architecture.md +157 -204
  4. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  5. package/package.json +5 -6
  6. package/src/lifecycle/subagent.ts +5 -9
  7. package/src/observation/notification.ts +23 -10
  8. package/src/observation/subagent-events-observer.ts +1 -3
  9. package/src/tools/get-result-report.ts +60 -0
  10. package/src/tools/get-result-tool.ts +25 -40
  11. package/src/types.ts +1 -1
  12. package/src/ui/session-navigator.ts +0 -2
  13. package/.prettierignore +0 -5
  14. package/AGENTS.md +0 -7
  15. package/docs/plans/0048-implement-subagents-api.md +0 -304
  16. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  17. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  18. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  19. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  20. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  21. package/docs/plans/0057-structured-debug-logging.md +0 -154
  22. package/docs/plans/0061-session-format-transcript.md +0 -284
  23. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  24. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  25. package/docs/plans/0070-extract-event-handlers.md +0 -306
  26. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  27. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  28. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  29. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  30. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  31. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  32. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  33. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  34. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  35. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  36. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  37. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  38. package/docs/plans/0109-extract-settings-manager.md +0 -276
  39. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  40. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  41. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  42. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  43. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  44. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  45. package/docs/plans/0116-type-housekeeping.md +0 -351
  46. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  47. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  48. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  49. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  50. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  51. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  52. package/docs/plans/0135-extract-display-helpers.md +0 -182
  53. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  54. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  55. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  56. package/docs/plans/0146-narrow-ui-context.md +0 -319
  57. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  58. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  59. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  60. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  61. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  62. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  63. package/docs/plans/0167-narrow-runner-io.md +0 -150
  64. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  65. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  66. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  67. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  68. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  69. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  70. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  71. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  72. package/docs/plans/0192-define-session-context-interface.md +0 -107
  73. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  74. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  75. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  76. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  77. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  78. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  79. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  80. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  81. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  82. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  83. package/docs/plans/0216-decompose-start-agent.md +0 -255
  84. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  85. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  86. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  87. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  88. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  89. package/docs/plans/0229-agent-born-complete.md +0 -564
  90. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  91. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  92. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  93. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  94. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  95. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  96. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  97. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  98. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  99. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  100. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  101. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  102. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  103. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  104. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  105. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  106. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  107. package/docs/plans/0373-extract-subagent-state.md +0 -250
  108. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  109. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  110. package/docs/plans/0376-extract-manager-observer.md +0 -232
  111. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  112. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  113. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  114. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  115. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  116. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  117. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  118. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  119. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  120. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  121. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  122. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  123. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  124. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  125. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  126. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  127. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  128. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  129. package/docs/plans/0444-shrink-widget-to-background-agents.md +0 -156
  130. package/docs/plans/0445-native-session-navigation.md +0 -365
  131. package/docs/plans/0446-spike-session-navigation-entry-criteria.md +0 -171
  132. package/docs/plans/0447-extract-subagents-settings-command.md +0 -220
  133. package/docs/plans/0448-block-disabled-agent-spawn.md +0 -207
  134. package/docs/plans/0462-navigation-renderer-tui-components.md +0 -338
  135. package/docs/plans/0463-file-snapshot-source-evicted-agents.md +0 -306
  136. package/docs/retro/0048-implement-subagents-api.md +0 -44
  137. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  138. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  139. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  140. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  141. package/docs/retro/0057-structured-debug-logging.md +0 -77
  142. package/docs/retro/0061-session-format-transcript.md +0 -41
  143. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  144. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  145. package/docs/retro/0070-extract-event-handlers.md +0 -49
  146. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  147. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  148. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  149. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  150. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  151. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  152. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  153. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  154. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  155. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  156. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  157. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  158. package/docs/retro/0109-extract-settings-manager.md +0 -55
  159. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  160. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  161. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  162. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  163. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  164. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  165. package/docs/retro/0116-type-housekeeping.md +0 -42
  166. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  167. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  168. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  169. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  170. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  171. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  172. package/docs/retro/0135-extract-display-helpers.md +0 -38
  173. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  174. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  175. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  176. package/docs/retro/0146-narrow-ui-context.md +0 -70
  177. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  178. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  179. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  180. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  181. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  182. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  183. package/docs/retro/0167-narrow-runner-io.md +0 -63
  184. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  185. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  186. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  187. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  188. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  189. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  190. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  191. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  192. package/docs/retro/0192-define-session-context-interface.md +0 -59
  193. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  194. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  195. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  196. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  197. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  198. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  199. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  200. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  201. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  202. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  203. package/docs/retro/0216-decompose-start-agent.md +0 -80
  204. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  205. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  206. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  207. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  208. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  209. package/docs/retro/0229-agent-born-complete.md +0 -89
  210. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  211. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  212. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  213. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  214. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  215. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  216. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  217. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  218. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  219. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  220. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  221. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  222. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  223. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  224. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  225. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  226. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  227. package/docs/retro/0373-extract-subagent-state.md +0 -94
  228. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  229. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  230. package/docs/retro/0376-extract-manager-observer.md +0 -81
  231. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  232. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  233. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  234. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  235. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  236. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  237. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  238. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  239. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  240. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  241. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  242. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  243. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  244. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  245. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  246. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  247. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -45
  248. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  249. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  250. package/docs/retro/0445-native-session-navigation.md +0 -122
  251. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  252. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  253. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  254. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  255. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  256. package/src/observation/notification-state.ts +0 -27
  257. package/vitest.config.ts +0 -14
@@ -1,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.