lsd-pi 1.2.4 → 1.3.6

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 (357) hide show
  1. package/README.md +22 -16
  2. package/dist/app-paths.d.ts +4 -0
  3. package/dist/app-paths.js +4 -0
  4. package/dist/bedrock-auth.d.ts +4 -0
  5. package/dist/bedrock-auth.js +4 -0
  6. package/dist/bundled-extension-paths.d.ts +4 -0
  7. package/dist/bundled-extension-paths.js +4 -0
  8. package/dist/cli-theme.d.ts +2 -2
  9. package/dist/cli-theme.js +13 -14
  10. package/dist/cli.js +43 -3
  11. package/dist/codex-rotate-settings.d.ts +4 -0
  12. package/dist/codex-rotate-settings.js +4 -0
  13. package/dist/help-text.d.ts +4 -0
  14. package/dist/help-text.js +4 -0
  15. package/dist/lsd-brand.d.ts +4 -0
  16. package/dist/lsd-brand.js +4 -0
  17. package/dist/onboarding-llm.d.ts +5 -0
  18. package/dist/onboarding-llm.js +5 -0
  19. package/dist/project-sessions.d.ts +4 -0
  20. package/dist/project-sessions.js +4 -0
  21. package/dist/resources/agents/generic.md +1 -0
  22. package/dist/resources/agents/scout.md +8 -1
  23. package/dist/resources/agents/worker.md +1 -0
  24. package/dist/resources/extensions/ask-user-questions.js +70 -0
  25. package/dist/resources/extensions/bg-shell/bg-shell-tool.js +6 -16
  26. package/dist/resources/extensions/browser-tools/tools/codegen.js +5 -5
  27. package/dist/resources/extensions/browser-tools/tools/navigation.js +107 -178
  28. package/dist/resources/extensions/browser-tools/tools/network-mock.js +112 -167
  29. package/dist/resources/extensions/browser-tools/tools/pages.js +182 -234
  30. package/dist/resources/extensions/browser-tools/tools/refs.js +202 -461
  31. package/dist/resources/extensions/browser-tools/tools/session.js +176 -323
  32. package/dist/resources/extensions/browser-tools/tools/state-persistence.js +91 -154
  33. package/dist/resources/extensions/browser-tools/utils.js +1 -1
  34. package/dist/resources/extensions/mac-tools/index.js +19 -34
  35. package/dist/resources/extensions/memory/index.js +20 -2
  36. package/dist/resources/extensions/shared/interview-ui.js +103 -20
  37. package/dist/resources/extensions/slash-commands/extension-manifest.json +2 -2
  38. package/dist/resources/extensions/slash-commands/fast.js +73 -0
  39. package/dist/resources/extensions/slash-commands/index.js +2 -0
  40. package/dist/resources/extensions/slash-commands/plan.js +54 -28
  41. package/dist/resources/extensions/slash-commands/tools.js +40 -4
  42. package/dist/resources/extensions/subagent/agent-switcher-component.js +208 -0
  43. package/dist/resources/extensions/subagent/agent-switcher-model.js +107 -0
  44. package/dist/resources/extensions/subagent/background-job-manager.js +24 -6
  45. package/dist/resources/extensions/subagent/background-runner.js +4 -0
  46. package/dist/resources/extensions/subagent/in-process-runner.js +387 -0
  47. package/dist/resources/extensions/subagent/index.js +715 -370
  48. package/dist/resources/extensions/subagent/launch-helpers.js +19 -5
  49. package/dist/resources/extensions/subagent/legacy-runner.js +503 -0
  50. package/dist/resources/extensions/voice/index.js +96 -36
  51. package/dist/resources/extensions/voice/push-to-talk.js +26 -0
  52. package/dist/shared-paths.d.ts +4 -0
  53. package/dist/shared-paths.js +4 -0
  54. package/dist/shared-preferences.d.ts +4 -0
  55. package/dist/shared-preferences.js +4 -0
  56. package/dist/startup-model-validation.d.ts +1 -1
  57. package/dist/startup-timings.d.ts +4 -0
  58. package/dist/startup-timings.js +4 -0
  59. package/dist/update-check.d.ts +4 -0
  60. package/dist/update-check.js +4 -0
  61. package/dist/update-cmd.d.ts +4 -0
  62. package/dist/update-cmd.js +4 -0
  63. package/dist/welcome-screen.js +4 -4
  64. package/dist/wizard.d.ts +4 -0
  65. package/dist/wizard.js +4 -0
  66. package/package.json +1 -1
  67. package/packages/pi-agent-core/dist/agent.d.ts +28 -0
  68. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  69. package/packages/pi-agent-core/dist/agent.js +105 -5
  70. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  71. package/packages/pi-agent-core/dist/types.d.ts +13 -2
  72. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  73. package/packages/pi-agent-core/dist/types.js.map +1 -1
  74. package/packages/pi-agent-core/src/agent.ts +142 -6
  75. package/packages/pi-agent-core/src/types.ts +12 -3
  76. package/packages/pi-ai/dist/adaptive/classifier.d.ts +29 -0
  77. package/packages/pi-ai/dist/adaptive/classifier.d.ts.map +1 -0
  78. package/packages/pi-ai/dist/adaptive/classifier.js +72 -0
  79. package/packages/pi-ai/dist/adaptive/classifier.js.map +1 -0
  80. package/packages/pi-ai/dist/adaptive/classifier.test.d.ts +2 -0
  81. package/packages/pi-ai/dist/adaptive/classifier.test.d.ts.map +1 -0
  82. package/packages/pi-ai/dist/adaptive/classifier.test.js +32 -0
  83. package/packages/pi-ai/dist/adaptive/classifier.test.js.map +1 -0
  84. package/packages/pi-ai/dist/index.d.ts +1 -0
  85. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  86. package/packages/pi-ai/dist/index.js +1 -0
  87. package/packages/pi-ai/dist/index.js.map +1 -1
  88. package/packages/pi-ai/dist/providers/amazon-bedrock.js +0 -2
  89. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  90. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  91. package/packages/pi-ai/dist/providers/anthropic-shared.js +0 -2
  92. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  93. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts +1 -1
  94. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  95. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  96. package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
  97. package/packages/pi-ai/dist/providers/google-gemini-cli.js +0 -4
  98. package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
  99. package/packages/pi-ai/dist/providers/google-vertex.js +0 -5
  100. package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
  101. package/packages/pi-ai/dist/providers/google.js +0 -5
  102. package/packages/pi-ai/dist/providers/google.js.map +1 -1
  103. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +35 -2
  104. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  105. package/packages/pi-ai/dist/providers/openai-codex-responses.js +32 -6
  106. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  107. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +127 -16
  108. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -1
  109. package/packages/pi-ai/dist/providers/openai-completions.d.ts +1 -1
  110. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  111. package/packages/pi-ai/dist/providers/openai-completions.js +0 -1
  112. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  113. package/packages/pi-ai/dist/providers/openai-responses.d.ts +9 -2
  114. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  115. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts +2 -0
  116. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts.map +1 -0
  117. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js +67 -0
  118. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js.map +1 -0
  119. package/packages/pi-ai/dist/providers/openai-responses.js +21 -3
  120. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  121. package/packages/pi-ai/dist/providers/openai-shared.d.ts +0 -1
  122. package/packages/pi-ai/dist/providers/openai-shared.d.ts.map +1 -1
  123. package/packages/pi-ai/dist/providers/openai-shared.js +0 -4
  124. package/packages/pi-ai/dist/providers/openai-shared.js.map +1 -1
  125. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  126. package/packages/pi-ai/dist/providers/simple-options.js +2 -1
  127. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  128. package/packages/pi-ai/dist/types.d.ts +6 -2
  129. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  130. package/packages/pi-ai/dist/types.js.map +1 -1
  131. package/packages/pi-ai/src/adaptive/classifier.test.ts +38 -0
  132. package/packages/pi-ai/src/adaptive/classifier.ts +107 -0
  133. package/packages/pi-ai/src/index.ts +1 -0
  134. package/packages/pi-ai/src/providers/amazon-bedrock.ts +0 -2
  135. package/packages/pi-ai/src/providers/anthropic-shared.ts +0 -2
  136. package/packages/pi-ai/src/providers/azure-openai-responses.ts +1 -1
  137. package/packages/pi-ai/src/providers/google-gemini-cli.ts +0 -4
  138. package/packages/pi-ai/src/providers/google-vertex.ts +0 -5
  139. package/packages/pi-ai/src/providers/google.ts +0 -5
  140. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +143 -20
  141. package/packages/pi-ai/src/providers/openai-codex-responses.ts +48 -7
  142. package/packages/pi-ai/src/providers/openai-completions.ts +1 -2
  143. package/packages/pi-ai/src/providers/openai-responses.fast-mode.test.ts +73 -0
  144. package/packages/pi-ai/src/providers/openai-responses.ts +27 -4
  145. package/packages/pi-ai/src/providers/openai-shared.ts +0 -3
  146. package/packages/pi-ai/src/providers/simple-options.ts +2 -1
  147. package/packages/pi-ai/src/types.ts +6 -2
  148. package/packages/pi-coding-agent/dist/cli/args.js +2 -2
  149. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +7 -2
  151. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/agent-session.js +53 -20
  153. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
  155. package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
  157. package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/core/lsp/lsp.md +3 -1
  159. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/sdk.js +36 -8
  161. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/sdk.test.js +37 -0
  163. package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +8 -0
  165. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  166. package/packages/pi-coding-agent/dist/core/session-manager.js +4 -0
  167. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  168. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +18 -7
  169. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts +2 -0
  171. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts.map +1 -0
  172. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js +35 -0
  173. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js.map +1 -0
  174. package/packages/pi-coding-agent/dist/core/settings-manager.js +32 -2
  175. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  176. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  177. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  178. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  180. package/packages/pi-coding-agent/dist/core/slash-commands.js +2 -1
  181. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/system-prompt.js +12 -3
  184. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/tool-priority.d.ts +4 -0
  186. package/packages/pi-coding-agent/dist/core/tool-priority.d.ts.map +1 -0
  187. package/packages/pi-coding-agent/dist/core/tool-priority.js +18 -0
  188. package/packages/pi-coding-agent/dist/core/tool-priority.js.map +1 -0
  189. package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts +2 -0
  190. package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts.map +1 -0
  191. package/packages/pi-coding-agent/dist/core/tool-priority.test.js +27 -0
  192. package/packages/pi-coding-agent/dist/core/tool-priority.test.js.map +1 -0
  193. package/packages/pi-coding-agent/dist/core/tools/grep.js +1 -1
  194. package/packages/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
  196. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/tools/index.js +2 -0
  198. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
  199. package/packages/pi-coding-agent/dist/core/tools/pty.d.ts +10 -1
  200. package/packages/pi-coding-agent/dist/core/tools/pty.d.ts.map +1 -1
  201. package/packages/pi-coding-agent/dist/core/tools/pty.js +29 -3
  202. package/packages/pi-coding-agent/dist/core/tools/pty.js.map +1 -1
  203. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts +2 -0
  204. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts.map +1 -0
  205. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js +26 -0
  206. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js.map +1 -0
  207. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts +45 -0
  208. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts.map +1 -0
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js +314 -0
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js.map +1 -0
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts +2 -0
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts.map +1 -0
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js +122 -0
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js.map +1 -0
  215. package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js +1 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js.map +1 -1
  217. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +2 -0
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +19 -2
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +11 -2
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +49 -6
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.js +1 -2
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +18 -4
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  232. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts +13 -0
  233. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts.map +1 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js +49 -0
  235. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js.map +1 -0
  236. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts +2 -0
  237. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts.map +1 -0
  238. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js +197 -0
  239. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js.map +1 -0
  240. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  241. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +106 -0
  242. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  243. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +7 -0
  245. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  246. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +3 -0
  247. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  248. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  249. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  250. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  251. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +88 -2
  252. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  253. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  254. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +41 -0
  255. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  256. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +2 -2
  257. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  258. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -6
  259. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  260. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +1 -1
  261. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  262. package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
  263. package/packages/pi-coding-agent/dist/modes/print-mode.js +6 -0
  264. package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
  265. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  266. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +20 -0
  267. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  268. package/packages/pi-coding-agent/dist/tests/path-display.test.js +15 -0
  269. package/packages/pi-coding-agent/dist/tests/path-display.test.js.map +1 -1
  270. package/packages/pi-coding-agent/package.json +1 -1
  271. package/packages/pi-coding-agent/src/cli/args.ts +2 -2
  272. package/packages/pi-coding-agent/src/core/agent-session.ts +58 -21
  273. package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
  274. package/packages/pi-coding-agent/src/core/lsp/lsp.md +3 -1
  275. package/packages/pi-coding-agent/src/core/sdk.test.ts +45 -0
  276. package/packages/pi-coding-agent/src/core/sdk.ts +39 -8
  277. package/packages/pi-coding-agent/src/core/session-manager.ts +12 -0
  278. package/packages/pi-coding-agent/src/core/settings-manager.fast-mode.test.ts +46 -0
  279. package/packages/pi-coding-agent/src/core/settings-manager.ts +50 -9
  280. package/packages/pi-coding-agent/src/core/skills.ts +4 -1
  281. package/packages/pi-coding-agent/src/core/slash-commands.ts +2 -1
  282. package/packages/pi-coding-agent/src/core/system-prompt.ts +14 -3
  283. package/packages/pi-coding-agent/src/core/tool-priority.test.ts +30 -0
  284. package/packages/pi-coding-agent/src/core/tool-priority.ts +17 -0
  285. package/packages/pi-coding-agent/src/core/tools/grep.ts +1 -1
  286. package/packages/pi-coding-agent/src/core/tools/index.ts +3 -0
  287. package/packages/pi-coding-agent/src/core/tools/pty.ts +45 -6
  288. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +31 -0
  289. package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.test.ts +172 -0
  290. package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.ts +402 -0
  291. package/packages/pi-coding-agent/src/modes/interactive/components/embedded-terminal.ts +1 -1
  292. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +18 -2
  293. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +63 -9
  294. package/packages/pi-coding-agent/src/modes/interactive/components/thinking-selector.ts +1 -2
  295. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1154 -1136
  296. package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +64 -0
  297. package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +228 -0
  298. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +494 -389
  299. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +7 -0
  300. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +3 -0
  301. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +103 -3
  302. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +60 -1
  303. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +11 -7
  304. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +1 -1
  305. package/packages/pi-coding-agent/src/modes/print-mode.ts +6 -0
  306. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +29 -0
  307. package/packages/pi-coding-agent/src/tests/path-display.test.ts +17 -0
  308. package/packages/pi-tui/dist/components/loader.d.ts +5 -2
  309. package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
  310. package/packages/pi-tui/dist/components/loader.js +33 -3
  311. package/packages/pi-tui/dist/components/loader.js.map +1 -1
  312. package/packages/pi-tui/src/components/loader.ts +31 -3
  313. package/packages/rpc-client/src/index.ts +1 -1
  314. package/packages/rpc-client/src/rpc-client.ts +29 -0
  315. package/packages/rpc-client/src/rpc-types.ts +1 -1
  316. package/pkg/dist/modes/interactive/theme/theme.d.ts +2 -2
  317. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  318. package/pkg/dist/modes/interactive/theme/theme.js +10 -6
  319. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  320. package/pkg/dist/modes/interactive/theme/themes.js +1 -1
  321. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  322. package/pkg/package.json +1 -1
  323. package/src/resources/agents/generic.md +1 -0
  324. package/src/resources/agents/scout.md +8 -1
  325. package/src/resources/agents/worker.md +1 -0
  326. package/src/resources/extensions/ask-user-questions.ts +88 -0
  327. package/src/resources/extensions/bg-shell/bg-shell-tool.ts +6 -16
  328. package/src/resources/extensions/browser-tools/tools/codegen.ts +5 -5
  329. package/src/resources/extensions/browser-tools/tools/navigation.ts +118 -196
  330. package/src/resources/extensions/browser-tools/tools/network-mock.ts +114 -205
  331. package/src/resources/extensions/browser-tools/tools/pages.ts +183 -237
  332. package/src/resources/extensions/browser-tools/tools/refs.ts +193 -507
  333. package/src/resources/extensions/browser-tools/tools/session.ts +182 -321
  334. package/src/resources/extensions/browser-tools/tools/state-persistence.ts +94 -172
  335. package/src/resources/extensions/browser-tools/utils.ts +1 -1
  336. package/src/resources/extensions/mac-tools/index.ts +19 -34
  337. package/src/resources/extensions/memory/index.ts +22 -2
  338. package/src/resources/extensions/shared/interview-ui.ts +108 -15
  339. package/src/resources/extensions/shared/tests/ask-user-freetext.test.ts +61 -0
  340. package/src/resources/extensions/shared/tests/custom-ui-fallbacks.test.ts +46 -0
  341. package/src/resources/extensions/slash-commands/extension-manifest.json +2 -2
  342. package/src/resources/extensions/slash-commands/fast.ts +89 -0
  343. package/src/resources/extensions/slash-commands/index.ts +2 -0
  344. package/src/resources/extensions/slash-commands/plan.ts +59 -30
  345. package/src/resources/extensions/slash-commands/tools.ts +43 -4
  346. package/src/resources/extensions/subagent/agent-switcher-component.ts +228 -0
  347. package/src/resources/extensions/subagent/agent-switcher-model.ts +160 -0
  348. package/src/resources/extensions/subagent/background-job-manager.ts +57 -6
  349. package/src/resources/extensions/subagent/background-runner.ts +8 -0
  350. package/src/resources/extensions/subagent/background-types.ts +4 -0
  351. package/src/resources/extensions/subagent/in-process-runner.ts +534 -0
  352. package/src/resources/extensions/subagent/index.ts +998 -493
  353. package/src/resources/extensions/subagent/launch-helpers.ts +15 -4
  354. package/src/resources/extensions/subagent/legacy-runner.ts +607 -0
  355. package/src/resources/extensions/voice/index.ts +308 -238
  356. package/src/resources/extensions/voice/push-to-talk.ts +42 -0
  357. package/src/resources/extensions/voice/tests/push-to-talk.test.ts +109 -0
@@ -48,6 +48,32 @@ function getBalancedToolNames(activeToolNames: string[]): string[] {
48
48
  ];
49
49
  }
50
50
 
51
+ const STANDARD_TOOLS = [
52
+ // Core
53
+ "read", "bash", "edit", "write", "lsp", "grep", "find", "ls",
54
+ // Background
55
+ "bg_shell",
56
+ // Search
57
+ "web_search", "fetch_page",
58
+ // Docs
59
+ "resolve_library", "get_library_docs",
60
+ // Agent
61
+ "subagent", "await_subagent", "Skill",
62
+ // User interaction
63
+ "ask_user_questions", "secure_env_collect",
64
+ // Browser (essential)
65
+ "browser_navigate", "browser_click", "browser_type", "browser_screenshot",
66
+ "browser_scroll", "browser_key_press", "browser_evaluate",
67
+ "browser_find", "browser_wait_for", "browser_close",
68
+ "browser_assert", "browser_batch",
69
+ // Tool management
70
+ "tool_search", "tool_enable",
71
+ ];
72
+
73
+ function getStandardToolNames(): string[] {
74
+ return [...STANDARD_TOOLS];
75
+ }
76
+
51
77
  function getFullToolNames(pi: ExtensionAPI): string[] {
52
78
  return pi.getAllTools().map((tool) => tool.name).filter((name): name is string => Boolean(name));
53
79
  }
@@ -180,7 +206,8 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
180
206
  currentActive.length > 0 ? currentActive.join(", ") : "(none)",
181
207
  "",
182
208
  "Usage:",
183
- " /tools balanced Switch to the balanced tool profile",
209
+ " /tools balanced Switch to the balanced tool profile (12 tools)",
210
+ " /tools standard Switch to the standard tool profile (~32 tools)",
184
211
  " /tools full Switch to the full tool profile (all available tools)",
185
212
  " /tools on Alias for /tools full",
186
213
  " /tools off Alias for /tools balanced",
@@ -196,7 +223,19 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
196
223
  pi.setActiveTools(nextActive);
197
224
  pi.sendMessage({
198
225
  customType: "tools:mode",
199
- content: `Balanced tool profile active: ${pi.getActiveTools().join(", ")}`,
226
+ content: `Balanced tool profile active (${pi.getActiveTools().length} tools): ${pi.getActiveTools().join(", ")}`,
227
+ display: true,
228
+ });
229
+ return;
230
+ }
231
+
232
+ if (input === "standard") {
233
+ settings.setToolProfile("standard");
234
+ const nextActive = getStandardToolNames();
235
+ pi.setActiveTools(nextActive);
236
+ pi.sendMessage({
237
+ customType: "tools:mode",
238
+ content: `Standard tool profile active (${pi.getActiveTools().length} tools): ${pi.getActiveTools().join(", ")}`,
200
239
  display: true,
201
240
  });
202
241
  return;
@@ -208,7 +247,7 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
208
247
  pi.setActiveTools(nextActive);
209
248
  pi.sendMessage({
210
249
  customType: "tools:mode",
211
- content: `Full tool profile active: ${pi.getActiveTools().join(", ")}`,
250
+ content: `Full tool profile active (${pi.getActiveTools().length} tools): ${pi.getActiveTools().join(", ")}`,
212
251
  display: true,
213
252
  });
214
253
  return;
@@ -216,7 +255,7 @@ export default function toolSearchExtension(pi: ExtensionAPI) {
216
255
 
217
256
  pi.sendMessage({
218
257
  customType: "tools:help",
219
- content: `Unknown /tools subcommand: ${input}\n\nTry /tools, /tools balanced, /tools full, /tools on, or /tools off.`,
258
+ content: `Unknown /tools subcommand: ${input}\n\nTry /tools, /tools balanced, /tools standard, /tools full, /tools on, or /tools off.`,
220
259
  display: true,
221
260
  });
222
261
  },
@@ -0,0 +1,228 @@
1
+ import type { ExtensionCommandContext, Theme } from "@gsd/pi-coding-agent";
2
+ import { Key, matchesKey, truncateToWidth, visibleWidth } from "@gsd/pi-tui";
3
+ import type { AgentSwitchTarget } from "./agent-switcher-model.js";
4
+
5
+ const MAX_VISIBLE_ROWS = 10;
6
+
7
+ function stateIcon(theme: Theme, target: AgentSwitchTarget): string {
8
+ if (target.kind === "parent") return theme.fg("accent", "●");
9
+ if (target.state === "running") return theme.fg("warning", "▶");
10
+ if (target.state === "failed") return theme.fg("error", "✗");
11
+ return theme.fg("success", "✓");
12
+ }
13
+
14
+ function stateBadge(theme: Theme, target: AgentSwitchTarget): string {
15
+ if (target.kind === "parent") return theme.fg("muted", "parent");
16
+ if (target.state === "running") return theme.fg("warning", "running");
17
+ if (target.state === "failed") return theme.fg("error", "failed");
18
+ return theme.fg("success", "completed");
19
+ }
20
+
21
+ function plainLabel(target: AgentSwitchTarget): string {
22
+ if (target.kind === "parent") {
23
+ const currentSuffix = target.isCurrent ? " (current)" : "";
24
+ return `● parent — main session${currentSuffix}`;
25
+ }
26
+ const icon = target.state === "running" ? "▶" : target.state === "failed" ? "✗" : "✓";
27
+ const currentSuffix = target.isCurrent ? " (current)" : "";
28
+ return `${icon} ${target.agentName} — ${target.taskPreview}${currentSuffix}`;
29
+ }
30
+
31
+ class AgentSwitcherComponent {
32
+ private readonly tui: { requestRender: () => void };
33
+ private readonly theme: Theme;
34
+ private readonly targets: AgentSwitchTarget[];
35
+ private readonly done: (result: AgentSwitchTarget | undefined) => void;
36
+ private selectedIndex: number;
37
+ private statusLine: string | undefined;
38
+ private cachedWidth?: number;
39
+ private cachedLines?: string[];
40
+
41
+ constructor(
42
+ tui: { requestRender: () => void },
43
+ theme: Theme,
44
+ targets: AgentSwitchTarget[],
45
+ done: (result: AgentSwitchTarget | undefined) => void,
46
+ ) {
47
+ this.tui = tui;
48
+ this.theme = theme;
49
+ this.targets = targets;
50
+ this.done = done;
51
+ this.selectedIndex = this.getInitialSelectionIndex();
52
+ }
53
+
54
+ private getInitialSelectionIndex(): number {
55
+ const firstSwitchable = this.targets.findIndex((target) => target.selectionAction !== "blocked");
56
+ return firstSwitchable >= 0 ? firstSwitchable : 0;
57
+ }
58
+
59
+ private move(delta: number): void {
60
+ if (this.targets.length === 0) return;
61
+ const next = Math.max(0, Math.min(this.targets.length - 1, this.selectedIndex + delta));
62
+ if (next === this.selectedIndex) return;
63
+ this.selectedIndex = next;
64
+ this.statusLine = undefined;
65
+ this.invalidate();
66
+ this.tui.requestRender();
67
+ }
68
+
69
+ handleInput(data: string): void {
70
+ if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c"))) {
71
+ this.done(undefined);
72
+ return;
73
+ }
74
+ if (matchesKey(data, Key.up)) {
75
+ this.move(-1);
76
+ return;
77
+ }
78
+ if (matchesKey(data, Key.down)) {
79
+ this.move(1);
80
+ return;
81
+ }
82
+ if (matchesKey(data, Key.pageUp)) {
83
+ this.move(-MAX_VISIBLE_ROWS);
84
+ return;
85
+ }
86
+ if (matchesKey(data, Key.pageDown)) {
87
+ this.move(MAX_VISIBLE_ROWS);
88
+ return;
89
+ }
90
+ if (matchesKey(data, Key.enter) || matchesKey(data, Key.space)) {
91
+ const selected = this.targets[this.selectedIndex];
92
+ if (!selected) {
93
+ this.done(undefined);
94
+ return;
95
+ }
96
+ if (selected.selectionAction === "blocked") {
97
+ this.statusLine = selected.blockedReason ?? "This row cannot be selected yet.";
98
+ this.invalidate();
99
+ this.tui.requestRender();
100
+ return;
101
+ }
102
+ this.done(selected);
103
+ }
104
+ }
105
+
106
+ private getVisibleWindow(): { start: number; end: number } {
107
+ if (this.targets.length <= MAX_VISIBLE_ROWS) return { start: 0, end: this.targets.length };
108
+ const half = Math.floor(MAX_VISIBLE_ROWS / 2);
109
+ let start = Math.max(0, this.selectedIndex - half);
110
+ let end = Math.min(this.targets.length, start + MAX_VISIBLE_ROWS);
111
+ if (end - start < MAX_VISIBLE_ROWS) {
112
+ start = Math.max(0, end - MAX_VISIBLE_ROWS);
113
+ }
114
+ return { start, end };
115
+ }
116
+
117
+ private renderRow(target: AgentSwitchTarget, isSelected: boolean, width: number): string {
118
+ const cursor = isSelected ? this.theme.fg("accent", "▸") : " ";
119
+ const icon = stateIcon(this.theme, target);
120
+ const baseLabel = target.kind === "parent"
121
+ ? "parent — main session"
122
+ : `${target.agentName} — ${target.taskPreview}`;
123
+ const currentTag = target.isCurrent ? this.theme.fg("accent", "(current)") : "";
124
+ const badge = stateBadge(this.theme, target);
125
+ const right = [currentTag, badge].filter(Boolean).join(" ");
126
+
127
+ const leftRaw = `${cursor} ${icon} ${baseLabel}`;
128
+ const rightWidth = right ? visibleWidth(right) + 1 : 0;
129
+ const maxLeft = Math.max(10, width - 4 - rightWidth);
130
+ const left = truncateToWidth(leftRaw, maxLeft, "…");
131
+ const spacing = Math.max(1, width - 4 - visibleWidth(left) - visibleWidth(right));
132
+ const composed = `${left}${" ".repeat(spacing)}${right}`;
133
+
134
+ if (!isSelected) {
135
+ if (target.selectionAction === "blocked") return this.theme.fg("dim", composed);
136
+ return composed;
137
+ }
138
+ return this.theme.bold(composed);
139
+ }
140
+
141
+ private box(inner: string[], width: number): string[] {
142
+ const bdr = (s: string) => this.theme.fg("borderMuted", s);
143
+ const iw = width - 4;
144
+ const lines: string[] = [];
145
+ lines.push(bdr("╭" + "─".repeat(width - 2) + "╮"));
146
+ for (const line of inner) {
147
+ const truncated = truncateToWidth(line, iw, "…");
148
+ const pad = Math.max(0, iw - visibleWidth(truncated));
149
+ lines.push(bdr("│") + " " + truncated + " ".repeat(pad) + " " + bdr("│"));
150
+ }
151
+ lines.push(bdr("╰" + "─".repeat(width - 2) + "╯"));
152
+ return lines;
153
+ }
154
+
155
+ render(width: number): string[] {
156
+ if (this.cachedLines && this.cachedWidth === width) return this.cachedLines;
157
+ const inner: string[] = [];
158
+ inner.push(this.theme.bold(this.theme.fg("accent", "Agent session switcher")));
159
+ inner.push(this.theme.fg("dim", "Parent + tracked subagents"));
160
+ inner.push("");
161
+
162
+ if (this.targets.length === 0) {
163
+ inner.push(this.theme.fg("dim", "No switch targets available."));
164
+ } else {
165
+ const { start, end } = this.getVisibleWindow();
166
+ for (let i = start; i < end; i++) {
167
+ inner.push(this.renderRow(this.targets[i]!, i === this.selectedIndex, width));
168
+ }
169
+ if (start > 0 || end < this.targets.length) {
170
+ inner.push("");
171
+ inner.push(this.theme.fg("dim", `${this.selectedIndex + 1}/${this.targets.length}`));
172
+ }
173
+ }
174
+
175
+ inner.push("");
176
+ const actionHint = this.targets[this.selectedIndex]?.selectionAction === "attach_live"
177
+ ? "Enter attach live"
178
+ : "Enter switch";
179
+ inner.push(this.theme.fg("dim", `${actionHint} · Esc cancel`));
180
+ if (this.statusLine) {
181
+ inner.push(this.theme.fg("warning", this.statusLine));
182
+ }
183
+
184
+ this.cachedLines = this.box(inner, width);
185
+ this.cachedWidth = width;
186
+ return this.cachedLines;
187
+ }
188
+
189
+ invalidate(): void {
190
+ this.cachedWidth = undefined;
191
+ this.cachedLines = undefined;
192
+ }
193
+ }
194
+
195
+ export async function showAgentSwitcher(
196
+ ctx: ExtensionCommandContext,
197
+ targets: AgentSwitchTarget[],
198
+ ): Promise<AgentSwitchTarget | undefined> {
199
+ const result = await ctx.ui.custom<AgentSwitchTarget | undefined>(
200
+ (tui, theme, _kb, done) => new AgentSwitcherComponent(tui, theme, targets, done),
201
+ {
202
+ overlay: true,
203
+ overlayOptions: {
204
+ anchor: "center",
205
+ width: "65%",
206
+ minWidth: 60,
207
+ maxHeight: "70%",
208
+ },
209
+ },
210
+ );
211
+
212
+ if (result !== undefined && result !== null) {
213
+ return result;
214
+ }
215
+
216
+ if (targets.length === 0) return undefined;
217
+ const labels = targets.map((target, index) => `${index + 1}. ${plainLabel(target)}`);
218
+ const selected = await ctx.ui.select("Switch agent session", labels);
219
+ if (!selected || Array.isArray(selected)) return undefined;
220
+ const selectedIndex = labels.indexOf(selected);
221
+ if (selectedIndex < 0) return undefined;
222
+ const target = targets[selectedIndex]!;
223
+ if (target.selectionAction === "blocked") {
224
+ ctx.ui.notify(target.blockedReason ?? "That target is not selectable yet.", "warning");
225
+ return undefined;
226
+ }
227
+ return target;
228
+ }
@@ -0,0 +1,160 @@
1
+ export type AgentSwitchTargetKind = "parent" | "subagent";
2
+ export type AgentSwitchTargetState = "running" | "completed" | "failed";
3
+ export type AgentSwitchTargetSelectionAction = "switch_saved" | "attach_live" | "blocked";
4
+
5
+ export interface AgentSwitchTarget {
6
+ id: string;
7
+ kind: AgentSwitchTargetKind;
8
+ sessionFile: string;
9
+ parentSessionFile?: string;
10
+ agentName: string;
11
+ taskPreview: string;
12
+ state: AgentSwitchTargetState;
13
+ isCurrent: boolean;
14
+ isLiveAttachCapable: boolean;
15
+ selectionAction: AgentSwitchTargetSelectionAction;
16
+ blockedReason?: string;
17
+ runningJobId?: string;
18
+ sortTime: number;
19
+ }
20
+
21
+ export interface AgentSwitchSessionLinkRecord {
22
+ id: string;
23
+ agentName: string;
24
+ task: string;
25
+ parentSessionFile: string;
26
+ subagentSessionFile: string;
27
+ updatedAt: number;
28
+ state: AgentSwitchTargetState;
29
+ }
30
+
31
+ export interface AgentSwitchRunningJobRecord {
32
+ id: string;
33
+ agentName: string;
34
+ task: string;
35
+ startedAt: number;
36
+ parentSessionFile?: string;
37
+ sessionFile?: string;
38
+ cwd?: string;
39
+ }
40
+
41
+ export interface BuildAgentSwitchTargetsInput {
42
+ currentSessionFile: string;
43
+ rootParentSessionFile: string;
44
+ currentCwd?: string;
45
+ trackedLinks: AgentSwitchSessionLinkRecord[];
46
+ runningJobs: AgentSwitchRunningJobRecord[];
47
+ }
48
+
49
+ function summarizeTask(task: string, max = 72): string {
50
+ const trimmed = task.replace(/\s+/g, " ").trim();
51
+ if (!trimmed) return "(no task)";
52
+ return trimmed.length > max ? `${trimmed.slice(0, max)}…` : trimmed;
53
+ }
54
+
55
+ function stateRank(state: AgentSwitchTargetState): number {
56
+ if (state === "running") return 0;
57
+ if (state === "completed") return 1;
58
+ return 2;
59
+ }
60
+
61
+ function shouldIncludeRunningJob(
62
+ job: AgentSwitchRunningJobRecord,
63
+ rootParentSessionFile: string,
64
+ currentCwd?: string,
65
+ ): boolean {
66
+ if (job.parentSessionFile) return job.parentSessionFile === rootParentSessionFile;
67
+ if (currentCwd && job.cwd) return currentCwd === job.cwd;
68
+ return false;
69
+ }
70
+
71
+ export function buildAgentSwitchTargets(input: BuildAgentSwitchTargetsInput): AgentSwitchTarget[] {
72
+ const targetsBySessionFile = new Map<string, AgentSwitchTarget>();
73
+
74
+ for (const link of input.trackedLinks) {
75
+ const canAttachLive = link.state === "running";
76
+ targetsBySessionFile.set(link.subagentSessionFile, {
77
+ id: link.id,
78
+ kind: "subagent",
79
+ sessionFile: link.subagentSessionFile,
80
+ parentSessionFile: link.parentSessionFile,
81
+ agentName: link.agentName,
82
+ taskPreview: summarizeTask(link.task),
83
+ state: link.state,
84
+ isCurrent: link.subagentSessionFile === input.currentSessionFile,
85
+ isLiveAttachCapable: canAttachLive,
86
+ selectionAction: link.state === "running" ? (canAttachLive ? "attach_live" : "blocked") : "switch_saved",
87
+ blockedReason: link.state === "running" && !canAttachLive
88
+ ? "Live attach is not implemented yet for running subagents."
89
+ : undefined,
90
+ sortTime: link.updatedAt,
91
+ });
92
+ }
93
+
94
+ const runningExtras: AgentSwitchTarget[] = [];
95
+ for (const job of input.runningJobs) {
96
+ if (!shouldIncludeRunningJob(job, input.rootParentSessionFile, input.currentCwd)) continue;
97
+
98
+ if (job.sessionFile && targetsBySessionFile.has(job.sessionFile)) {
99
+ const existing = targetsBySessionFile.get(job.sessionFile)!;
100
+ existing.state = "running";
101
+ existing.isLiveAttachCapable = true;
102
+ existing.selectionAction = "attach_live";
103
+ existing.blockedReason = undefined;
104
+ existing.runningJobId = job.id;
105
+ existing.sortTime = Math.max(existing.sortTime, job.startedAt);
106
+ continue;
107
+ }
108
+
109
+ const canAttachLive = Boolean(job.sessionFile);
110
+ const syntheticSessionFile = job.sessionFile ?? `${input.rootParentSessionFile}#${job.id}`;
111
+ runningExtras.push({
112
+ id: job.id,
113
+ kind: "subagent",
114
+ sessionFile: syntheticSessionFile,
115
+ parentSessionFile: job.parentSessionFile ?? input.rootParentSessionFile,
116
+ agentName: job.agentName,
117
+ taskPreview: summarizeTask(job.task),
118
+ state: "running",
119
+ isCurrent: Boolean(job.sessionFile && job.sessionFile === input.currentSessionFile),
120
+ isLiveAttachCapable: canAttachLive,
121
+ selectionAction: canAttachLive ? "attach_live" : "blocked",
122
+ blockedReason: canAttachLive ? undefined : "Live attach is not implemented yet for running subagents.",
123
+ runningJobId: job.id,
124
+ sortTime: job.startedAt,
125
+ });
126
+ }
127
+
128
+ const parentTarget: AgentSwitchTarget = {
129
+ id: "parent",
130
+ kind: "parent",
131
+ sessionFile: input.rootParentSessionFile,
132
+ agentName: "parent",
133
+ taskPreview: "Main session",
134
+ state: "completed",
135
+ isCurrent: input.currentSessionFile === input.rootParentSessionFile,
136
+ isLiveAttachCapable: false,
137
+ selectionAction: input.currentSessionFile === input.rootParentSessionFile ? "blocked" : "switch_saved",
138
+ blockedReason: input.currentSessionFile === input.rootParentSessionFile
139
+ ? "You are already in the parent/main session."
140
+ : undefined,
141
+ sortTime: Number.MAX_SAFE_INTEGER,
142
+ };
143
+
144
+ const targets = [
145
+ parentTarget,
146
+ ...targetsBySessionFile.values(),
147
+ ...runningExtras,
148
+ ];
149
+
150
+ targets.sort((a, b) => {
151
+ if (a.isCurrent !== b.isCurrent) return a.isCurrent ? -1 : 1;
152
+ if (a.kind !== b.kind) return a.kind === "parent" ? -1 : 1;
153
+ const stateDiff = stateRank(a.state) - stateRank(b.state);
154
+ if (stateDiff !== 0) return stateDiff;
155
+ if (a.sortTime !== b.sortTime) return b.sortTime - a.sortTime;
156
+ return a.agentName.localeCompare(b.agentName);
157
+ });
158
+
159
+ return targets;
160
+ }
@@ -16,6 +16,26 @@ import type {
16
16
 
17
17
  export type { BackgroundSubagentJob, BackgroundJobStatus };
18
18
 
19
+ type BackgroundJobResult = {
20
+ summary: string;
21
+ stderr: string;
22
+ exitCode: number;
23
+ model?: string;
24
+ sessionFile?: string;
25
+ parentSessionFile?: string;
26
+ };
27
+
28
+ type BackgroundJobMetadata = {
29
+ parentSessionFile?: string;
30
+ sessionFile?: string;
31
+ model?: string;
32
+ };
33
+
34
+ type InProcessHandleLike = {
35
+ abort: () => void;
36
+ dispose: () => void;
37
+ };
38
+
19
39
  export class BackgroundJobManager {
20
40
  private jobs = new Map<string, BackgroundSubagentJob>();
21
41
  private evictionTimers = new Map<string, ReturnType<typeof setTimeout>>();
@@ -44,10 +64,11 @@ export class BackgroundJobManager {
44
64
  agentName: string,
45
65
  task: string,
46
66
  cwd: string,
47
- runFn: (signal: AbortSignal) => Promise<{ summary: string; stderr: string; exitCode: number; model?: string }>,
67
+ runFn: (signal: AbortSignal) => Promise<BackgroundJobResult>,
68
+ metadata?: BackgroundJobMetadata,
48
69
  ): string {
49
70
  const abortController = new AbortController();
50
- return this.attachJob(agentName, task, cwd, abortController, runFn(abortController.signal));
71
+ return this.attachJob(agentName, task, cwd, abortController, runFn(abortController.signal), metadata);
51
72
  }
52
73
 
53
74
  /**
@@ -58,9 +79,33 @@ export class BackgroundJobManager {
58
79
  task: string,
59
80
  cwd: string,
60
81
  abortController: AbortController,
61
- resultPromise: Promise<{ summary: string; stderr: string; exitCode: number; model?: string }>,
82
+ resultPromise: Promise<BackgroundJobResult>,
83
+ metadata?: BackgroundJobMetadata,
84
+ ): string {
85
+ return this.attachJob(agentName, task, cwd, abortController, resultPromise, metadata);
86
+ }
87
+
88
+ /**
89
+ * Adopt an in-process subagent handle into background tracking.
90
+ */
91
+ adoptHandle(
92
+ agentName: string,
93
+ task: string,
94
+ cwd: string,
95
+ handle: InProcessHandleLike,
96
+ resultPromise: Promise<BackgroundJobResult>,
97
+ metadata?: BackgroundJobMetadata,
62
98
  ): string {
63
- return this.attachJob(agentName, task, cwd, abortController, resultPromise);
99
+ const abortController = new AbortController();
100
+ const onAbort = () => handle.abort();
101
+ abortController.signal.addEventListener("abort", onAbort, { once: true });
102
+
103
+ const wrapped = resultPromise.finally(() => {
104
+ abortController.signal.removeEventListener("abort", onAbort);
105
+ handle.dispose();
106
+ });
107
+
108
+ return this.attachJob(agentName, task, cwd, abortController, wrapped, metadata);
64
109
  }
65
110
 
66
111
  /**
@@ -121,7 +166,8 @@ export class BackgroundJobManager {
121
166
  task: string,
122
167
  cwd: string,
123
168
  abortController: AbortController,
124
- resultPromise: Promise<{ summary: string; stderr: string; exitCode: number; model?: string }>,
169
+ resultPromise: Promise<BackgroundJobResult>,
170
+ metadata?: BackgroundJobMetadata,
125
171
  ): string {
126
172
  const running = this.getRunningJobs();
127
173
  if (running.length >= this.maxRunning) {
@@ -148,18 +194,23 @@ export class BackgroundJobManager {
148
194
  cwd,
149
195
  status: "running",
150
196
  startedAt: Date.now(),
197
+ model: metadata?.model,
198
+ sessionFile: metadata?.sessionFile,
199
+ parentSessionFile: metadata?.parentSessionFile,
151
200
  abortController,
152
201
  promise: undefined as unknown as Promise<void>,
153
202
  };
154
203
 
155
204
  job.promise = resultPromise
156
- .then(({ summary, stderr, exitCode, model }) => {
205
+ .then(({ summary, stderr, exitCode, model, sessionFile, parentSessionFile }) => {
157
206
  job.status = exitCode === 0 ? "completed" : "failed";
158
207
  job.completedAt = Date.now();
159
208
  job.resultSummary = summary;
160
209
  job.stderr = stderr;
161
210
  job.exitCode = exitCode;
162
211
  job.model = model;
212
+ job.sessionFile = sessionFile;
213
+ job.parentSessionFile = parentSessionFile;
163
214
  this.scheduleEviction(id);
164
215
  this.deliverResult(job);
165
216
  })
@@ -13,6 +13,7 @@ import type { AgentConfig } from "./agents.js";
13
13
  export interface BackgroundLaunchContext {
14
14
  defaultCwd: string;
15
15
  model?: { provider: string; id: string };
16
+ parentSessionFile?: string;
16
17
  }
17
18
 
18
19
  /**
@@ -38,6 +39,8 @@ export function runSubagentInBackground(
38
39
  finalOutput: string;
39
40
  stderr: string;
40
41
  model?: string;
42
+ sessionFile?: string;
43
+ parentSessionFile?: string;
41
44
  }>,
42
45
  ): string {
43
46
  const effectiveCwd = cwd ?? ctx.defaultCwd;
@@ -61,8 +64,13 @@ export function runSubagentInBackground(
61
64
  stderr: result.stderr,
62
65
  exitCode: result.exitCode,
63
66
  model: result.model,
67
+ sessionFile: result.sessionFile,
68
+ parentSessionFile: result.parentSessionFile,
64
69
  };
65
70
  },
71
+ {
72
+ parentSessionFile: ctx.parentSessionFile,
73
+ },
66
74
  );
67
75
 
68
76
  return jobId;
@@ -31,6 +31,10 @@ export interface BackgroundSubagentJob {
31
31
  stderr?: string;
32
32
  /** Model used by the subagent (if known) */
33
33
  model?: string;
34
+ /** Session file created by the subagent run (if sessions are enabled) */
35
+ sessionFile?: string;
36
+ /** Parent session file this subagent was launched from (if known) */
37
+ parentSessionFile?: string;
34
38
  /**
35
39
  * Set by await_job-style consumers when they've already consumed the result.
36
40
  * Suppresses the follow-up delivery.