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
@@ -51,6 +51,14 @@ const OptionSchema = Type.Object({
51
51
  description: Type.String({ description: "One short sentence explaining impact/tradeoff if selected" }),
52
52
  });
53
53
 
54
+ const ShowWhenSchema = Type.Object({
55
+ questionId: Type.String({ description: "Earlier question id this question depends on" }),
56
+ selectedAnyOf: Type.Array(Type.String(), {
57
+ description: "Show this question only when the earlier question selection includes one of these labels",
58
+ minItems: 1,
59
+ }),
60
+ });
61
+
54
62
  const QuestionSchema = Type.Object({
55
63
  id: Type.String({ description: "Stable identifier for mapping answers (snake_case)" }),
56
64
  header: Type.String({ description: "Short header label shown in the UI (12 or fewer chars)" }),
@@ -65,6 +73,7 @@ const QuestionSchema = Type.Object({
65
73
  "If true, the user can select multiple options using SPACE to toggle and ENTER to confirm. No 'None of the above' option is added. Default: false.",
66
74
  }),
67
75
  ),
76
+ showWhen: Type.Optional(ShowWhenSchema),
68
77
  });
69
78
 
70
79
  const AskUserQuestionsParams = Type.Object({
@@ -88,6 +97,40 @@ function errorResult(
88
97
  }
89
98
 
90
99
  /** Convert the shared RoundResult into the JSON the LLM expects. */
100
+ function selectedLabels(answer: { answers: string[] } | undefined): string[] {
101
+ if (!answer) return [];
102
+ return answer.answers.filter((item) => typeof item === "string" && !item.startsWith("user_note:"));
103
+ }
104
+
105
+ function shouldAskQuestion(
106
+ question: Question,
107
+ answers: Record<string, { answers: string[] }>,
108
+ ): boolean {
109
+ if (!question.showWhen) return true;
110
+ const controlling = answers[question.showWhen.questionId];
111
+ if (!controlling) return false;
112
+ const selected = selectedLabels(controlling);
113
+ return selected.some((label) => question.showWhen?.selectedAnyOf.includes(label));
114
+ }
115
+
116
+ function selectedRoundLabels(answer: { selected: string | string[]; notes: string } | undefined): string[] {
117
+ if (!answer) return [];
118
+ return (Array.isArray(answer.selected) ? answer.selected : [answer.selected]).filter(
119
+ (item): item is string => typeof item === "string" && item.length > 0,
120
+ );
121
+ }
122
+
123
+ function shouldRenderRoundQuestion(
124
+ question: Question,
125
+ answers: RoundResult["answers"],
126
+ ): boolean {
127
+ if (!question.showWhen) return true;
128
+ const controlling = answers[question.showWhen.questionId];
129
+ if (!controlling) return false;
130
+ const selected = selectedRoundLabels(controlling);
131
+ return selected.some((label) => question.showWhen?.selectedAnyOf.includes(label));
132
+ }
133
+
91
134
  function formatForLLM(result: RoundResult): string {
92
135
  const answers: Record<string, { answers: string[] }> = {};
93
136
  for (const [id, answer] of Object.entries(result.answers)) {
@@ -118,6 +161,7 @@ export default function AskUserQuestions(pi: ExtensionAPI) {
118
161
  "Keep questions to 1 when possible; never exceed 3.",
119
162
  "For single-select: each question must have 2-3 options. Put the recommended option first with '(Recommended)' suffix. Do not include an 'Other' or 'None of the above' option - the client adds one automatically.",
120
163
  "For multi-select: set allowMultiple: true. The user can pick any number of options. No 'None of the above' is added.",
164
+ "For conditional follow-ups, use showWhen.questionId and showWhen.selectedAnyOf on a later question so it only appears after the matching earlier answer.",
121
165
  ],
122
166
  parameters: AskUserQuestionsParams,
123
167
 
@@ -136,6 +180,45 @@ export default function AskUserQuestions(pi: ExtensionAPI) {
136
180
  }
137
181
  }
138
182
 
183
+ for (let i = 0; i < params.questions.length; i++) {
184
+ const q = params.questions[i];
185
+ if (!q.showWhen) continue;
186
+ const controllerIndex = params.questions.findIndex((candidate) => candidate.id === q.showWhen?.questionId);
187
+ if (controllerIndex < 0 || controllerIndex >= i) {
188
+ return errorResult(
189
+ `Error: question "${q.id}" has invalid showWhen.questionId "${q.showWhen.questionId}" (must reference an earlier question id)`,
190
+ params.questions,
191
+ );
192
+ }
193
+ if (!q.showWhen.selectedAnyOf || q.showWhen.selectedAnyOf.length === 0) {
194
+ return errorResult(
195
+ `Error: question "${q.id}" showWhen.selectedAnyOf must include at least one option label`,
196
+ params.questions,
197
+ );
198
+ }
199
+ const controller = params.questions[controllerIndex];
200
+ const validLabels = [
201
+ ...controller.options.map((option) => option.label),
202
+ ...(!controller.allowMultiple ? [OTHER_OPTION_LABEL] : []),
203
+ ];
204
+ const invalidLabels = q.showWhen.selectedAnyOf.filter((label) => !validLabels.includes(label));
205
+ if (invalidLabels.length > 0) {
206
+ return errorResult(
207
+ `Error: question "${q.id}" showWhen.selectedAnyOf contains unknown option labels: ${invalidLabels.join(", ")}`,
208
+ params.questions,
209
+ );
210
+ }
211
+ }
212
+
213
+ // Subagents have no human to respond — fail fast and tell the model to decide autonomously.
214
+ if (process.argv.includes('--subagent-name')) {
215
+ return errorResult(
216
+ "Error: ask_user_questions cannot be used inside a subagent. " +
217
+ "There is no human available. Make a reasonable autonomous decision and proceed.",
218
+ params.questions,
219
+ );
220
+ }
221
+
139
222
  if (!ctx.hasUI) {
140
223
  return errorResult("Error: UI not available (non-interactive mode)", params.questions);
141
224
  }
@@ -149,6 +232,9 @@ export default function AskUserQuestions(pi: ExtensionAPI) {
149
232
  if (!result) {
150
233
  const answers: Record<string, { answers: string[] }> = {};
151
234
  for (const q of params.questions) {
235
+ if (!shouldAskQuestion(q, answers)) {
236
+ continue;
237
+ }
152
238
  const options = q.options.map((o) => o.label);
153
239
  if (!q.allowMultiple) {
154
240
  options.push(OTHER_OPTION_LABEL);
@@ -262,6 +348,7 @@ export default function AskUserQuestions(pi: ExtensionAPI) {
262
348
  lines.push(theme.fg("dim", details.channel));
263
349
  if (details.response) {
264
350
  for (const q of questions) {
351
+ if (!shouldAskQuestion(q, details.response.answers)) continue;
265
352
  const answer = details.response.answers[q.id];
266
353
  if (!answer) {
267
354
  lines.push(`${theme.fg("accent", q.header)}: ${theme.fg("dim", "(no answer)")}`);
@@ -284,6 +371,7 @@ export default function AskUserQuestions(pi: ExtensionAPI) {
284
371
 
285
372
  const lines: string[] = [];
286
373
  for (const q of details.questions) {
374
+ if (!shouldRenderRoundQuestion(q, details.response.answers)) continue;
287
375
  const answer = (details.response as RoundResult).answers[q.id];
288
376
  if (!answer) {
289
377
  lines.push(`${theme.fg("accent", q.header)}: ${theme.fg("dim", "(no answer)")}`);
@@ -44,22 +44,12 @@ export function registerBgShellTool(pi: ExtensionAPI, state: BgShellSharedState)
44
44
  "group_status (health of a process group), highlights (significant output lines only).",
45
45
 
46
46
  promptGuidelines: [
47
- "Use bg_shell to start long-running processes (servers, watchers, builds) that should not block the agent.",
48
- "After starting a server, use 'wait_for_ready' to efficiently block until it's listening avoids polling loops entirely.",
49
- "Use 'digest' instead of 'output' when you just need status — it returns a structured ~30-token summary instead of ~2000 tokens of raw output.",
50
- "Use 'highlights' to see only significant output (errors, URLs, results) typically 5-15 lines instead of hundreds.",
51
- "Use 'output' only when you need raw lines for debugging — add filter:'error|warning' to narrow results.",
52
- "The 'output' action returns only new output since the last check (incremental). Repeated calls are cheap on context.",
53
- "Set type:'server' and ready_port:3000 for dev servers so readiness detection is automatic.",
54
- "Set group:'my-stack' on related processes to manage them together with 'group_status'.",
55
- "Use 'run' to execute a command on a persistent shell session and block until it completes — returns structured output + exit code. Shell state (env vars, cwd, virtualenvs) persists across runs.",
56
- "Use 'send_and_wait' for interactive CLIs: send input and wait for expected output pattern.",
57
- "Use 'env' to check the current working directory and active environment variables of a shell session — useful after cd, source, or export commands.",
58
- "Background processes are session-scoped by default: a new session reaps them unless you set persist_across_sessions:true.",
59
- "Use 'restart' to kill and relaunch with the same config — preserves restart count.",
60
- "Background processes are auto-classified (server/build/test/watcher) based on the command.",
61
- "Process crashes and errors are automatically surfaced as alerts at the start of your next turn — you don't need to poll.",
62
- "To create a persistent shell session: bg_shell start with type:'shell'. The session stays alive for interactive use with 'send', 'send_and_wait', or 'run'.",
47
+ "Use bg_shell for long-running processes (servers, watchers, builds). Set type:'server' and ready_port for dev servers.",
48
+ "Use 'digest' for status (~30 tokens), 'highlights' for key output, 'output' only for raw debugging. All are incremental.",
49
+ "Use 'run' to execute a command on a persistent shell (state persists). Use 'send_and_wait' for interactive CLIs.",
50
+ "Use 'wait_for_ready' after starting a serverblocks until the port/pattern matches.",
51
+ "Use group:'name' to manage related processes together; 'group_status' for health.",
52
+ "Crashes are auto-surfaced next turn. Use persist_across_sessions:true to survive session restarts.",
63
53
  ],
64
54
 
65
55
  parameters: Type.Object({
@@ -84,9 +84,10 @@ export function registerCodegenTools(pi: ExtensionAPI, deps: ToolDeps): void {
84
84
  break;
85
85
  }
86
86
 
87
- case "browser_click_ref": {
87
+ case "browser_ref": {
88
88
  // Refs are session-specific — add comment
89
- testLines.push(` // browser_click_ref: ${entry.paramsSummary} replace with stable selector`);
89
+ const refAction = params.action ?? "click";
90
+ testLines.push(` // browser_ref (${refAction}): ${entry.paramsSummary} — replace with stable selector`);
90
91
  actionCount++;
91
92
  break;
92
93
  }
@@ -101,9 +102,8 @@ export function registerCodegenTools(pi: ExtensionAPI, deps: ToolDeps): void {
101
102
  break;
102
103
  }
103
104
 
104
- case "browser_fill_ref": {
105
- testLines.push(` // browser_fill_ref: ${entry.paramsSummary} replace with stable selector`);
106
- actionCount++;
105
+ case "browser_fill_ref_handled": {
106
+ // Already handled by browser_ref above
107
107
  break;
108
108
  }
109
109
 
@@ -17,216 +17,138 @@ export function registerNavigationTools(pi: ExtensionAPI, deps: ToolDeps): void
17
17
  name: "browser_navigate",
18
18
  label: "Browser Navigate",
19
19
  description:
20
- "Open the browser (if not already open) and navigate to a URL. Waits for network idle. Returns page title and current URL. Use ONLY for visually verifying locally-running web apps (e.g. http://localhost:3000). Do NOT use for documentation sites, GitHub, search results, or any external URL — use web_search instead. Screenshots are only captured when the `screenshot` parameter is set to true.",
20
+ "Navigate to a URL, go back/forward in history, or reload the page. " +
21
+ "Use ONLY for locally-running web apps (e.g. http://localhost:3000). " +
22
+ "Do NOT use for documentation sites, GitHub, or external URLs — use web_search instead.",
21
23
  parameters: Type.Object({
22
- url: Type.String({ description: "URL to navigate to, e.g. http://localhost:3000" }),
24
+ action: Type.Union([
25
+ Type.Literal("goto"),
26
+ Type.Literal("go_back"),
27
+ Type.Literal("go_forward"),
28
+ Type.Literal("reload"),
29
+ ], { description: "'goto' — navigate to url (default), 'go_back'/'go_forward' — history, 'reload' — refresh page" }),
30
+ url: Type.Optional(Type.String({ description: "URL to navigate to (required for goto action)." })),
23
31
  screenshot: Type.Optional(Type.Boolean({ description: "Capture and return a screenshot (default: false)", default: false })),
24
32
  }),
25
33
 
26
34
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
27
- let actionId: number | null = null;
28
- let beforeState: CompactPageState | null = null;
29
- try {
30
- const { page: p } = await deps.ensureBrowser();
31
- beforeState = await deps.captureCompactPageState(p, { includeBodyText: true });
32
- actionId = deps.beginTrackedAction("browser_navigate", params, beforeState.url).id;
33
- await p.goto(params.url, { waitUntil: "domcontentloaded", timeout: 30000 });
34
- await p.waitForLoadState("networkidle", { timeout: 5000 }).catch(() => { /* networkidle timeout — non-fatal, page may still be usable */ });
35
- await new Promise(resolve => setTimeout(resolve, 300));
36
-
37
- const title = await p.title();
38
- const url = p.url();
39
- const viewport = p.viewportSize();
40
- const vpText = viewport ? `${viewport.width}x${viewport.height}` : "unknown";
41
- const afterState = await deps.captureCompactPageState(p, { includeBodyText: true });
42
- const summary = deps.formatCompactStateSummary(afterState);
43
- const jsErrors = deps.getRecentErrors(p.url());
44
- const diff = diffCompactStates(beforeState, afterState);
45
- setLastActionBeforeState(beforeState);
46
- setLastActionAfterState(afterState);
47
- deps.finishTrackedAction(actionId, {
48
- status: "success",
49
- afterUrl: afterState.url,
50
- warningSummary: jsErrors.trim() || undefined,
51
- diffSummary: diff.summary,
52
- changed: diff.changed,
53
- beforeState,
54
- afterState,
55
- });
56
-
57
- let screenshotContent: any[] = [];
58
- if (params.screenshot) {
59
- try {
60
- let buf = await p.screenshot({ type: "jpeg", quality: 80, scale: "css" });
61
- buf = await deps.constrainScreenshot(p, buf, "image/jpeg", 80);
62
- screenshotContent = [{ type: "image", data: buf.toString("base64"), mimeType: "image/jpeg" }];
63
- } catch { /* non-fatal — screenshot is optional, navigation result is still valid */ }
64
- }
65
-
66
- return {
67
- content: [
68
- { type: "text", text: `Navigated to: ${url}\nTitle: ${title}\nViewport: ${vpText}\nAction: ${actionId}${jsErrors}\n\nDiff:\n${deps.formatDiffText(diff)}\n\nPage summary:\n${summary}` },
69
- ...screenshotContent,
70
- ],
71
- details: { title, url, status: "loaded", viewport: vpText, actionId, diff },
72
- };
73
- } catch (err: any) {
74
- if (actionId !== null) {
75
- deps.finishTrackedAction(actionId, { status: "error", afterUrl: deps.getActivePageOrNull()?.url() ?? "", error: err.message, beforeState: beforeState ?? undefined });
76
- }
77
- const errorShot = await deps.captureErrorScreenshot(deps.getActivePageOrNull());
78
- const content: any[] = [{ type: "text", text: `Navigation failed: ${err.message}` }];
79
- if (errorShot) {
80
- content.push({ type: "image", data: errorShot.data, mimeType: errorShot.mimeType });
81
- }
82
- return {
83
- content,
84
- details: { status: "error", error: err.message, actionId },
85
- isError: true,
86
- };
87
- }
88
- },
89
- });
90
-
91
- // -------------------------------------------------------------------------
92
- // browser_go_back
93
- // -------------------------------------------------------------------------
94
- pi.registerTool({
95
- name: "browser_go_back",
96
- label: "Browser Go Back",
97
- description: "Navigate back in browser history. Returns a compact page summary after navigation.",
98
- parameters: Type.Object({}),
99
-
100
- async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
101
- try {
102
- const { page: p } = await deps.ensureBrowser();
103
- const response = await p.goBack({ waitUntil: "domcontentloaded", timeout: 10000 });
104
-
105
- if (!response) {
106
- return {
107
- content: [{ type: "text", text: "No previous page in history." }],
108
- details: {},
109
- isError: true,
110
- };
111
- }
112
-
113
- await p.waitForLoadState("networkidle", { timeout: 5000 }).catch(() => { /* networkidle timeout — non-fatal, page may still be usable */ });
114
-
115
- const title = await p.title();
116
- const url = p.url();
117
- const summary = await deps.postActionSummary(p);
118
- const jsErrors = deps.getRecentErrors(p.url());
119
-
120
- return {
121
- content: [{ type: "text", text: `Navigated back to: ${url}\nTitle: ${title}${jsErrors}\n\nPage summary:\n${summary}` }],
122
- details: { title, url },
123
- };
124
- } catch (err: any) {
125
- const errorShot = await deps.captureErrorScreenshot(deps.getActivePageOrNull());
126
- const content: any[] = [{ type: "text", text: `Go back failed: ${err.message}` }];
127
- if (errorShot) {
128
- content.push({ type: "image", data: errorShot.data, mimeType: errorShot.mimeType });
129
- }
130
- return { content, details: { error: err.message }, isError: true };
131
- }
132
- },
133
- });
134
-
135
- // -------------------------------------------------------------------------
136
- // browser_go_forward
137
- // -------------------------------------------------------------------------
138
- pi.registerTool({
139
- name: "browser_go_forward",
140
- label: "Browser Go Forward",
141
- description: "Navigate forward in browser history. Returns a compact page summary after navigation.",
142
- parameters: Type.Object({}),
35
+ const action = params.action ?? "goto";
143
36
 
144
- async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
145
37
  try {
146
- const { page: p } = await deps.ensureBrowser();
147
- const response = await p.goForward({ waitUntil: "domcontentloaded", timeout: 10000 });
148
-
149
- if (!response) {
150
- return {
151
- content: [{ type: "text", text: "No forward page in history." }],
152
- details: {},
153
- isError: true,
154
- };
38
+ if (action === "goto") {
39
+ return await gotoAction(params);
40
+ } else if (action === "go_back") {
41
+ return await goBackForward("back");
42
+ } else if (action === "go_forward") {
43
+ return await goBackForward("forward");
44
+ } else {
45
+ return await reloadAction();
155
46
  }
156
-
157
- await p.waitForLoadState("networkidle", { timeout: 5000 }).catch(() => { /* networkidle timeout — non-fatal, page may still be usable */ });
158
-
159
- const title = await p.title();
160
- const url = p.url();
161
- const summary = await deps.postActionSummary(p);
162
- const jsErrors = deps.getRecentErrors(p.url());
163
-
164
- return {
165
- content: [{ type: "text", text: `Navigated forward to: ${url}\nTitle: ${title}${jsErrors}\n\nPage summary:\n${summary}` }],
166
- details: { title, url },
167
- };
168
47
  } catch (err: any) {
169
48
  const errorShot = await deps.captureErrorScreenshot(deps.getActivePageOrNull());
170
- const content: any[] = [{ type: "text", text: `Go forward failed: ${err.message}` }];
171
- if (errorShot) {
172
- content.push({ type: "image", data: errorShot.data, mimeType: errorShot.mimeType });
173
- }
49
+ const content: any[] = [{ type: "text" as const, text: `Navigation '${action}' failed: ${err.message}` }];
50
+ if (errorShot) content.push({ type: "image" as const, data: errorShot.data, mimeType: errorShot.mimeType });
174
51
  return { content, details: { error: err.message }, isError: true };
175
52
  }
176
53
  },
177
54
  });
178
55
 
179
- // -------------------------------------------------------------------------
180
- // browser_reload
181
- // -------------------------------------------------------------------------
182
- pi.registerTool({
183
- name: "browser_reload",
184
- label: "Browser Reload",
185
- description: "Reload the current page. Returns a screenshot, compact page summary, and page metadata (same shape as browser_navigate).",
186
- parameters: Type.Object({}),
187
-
188
- async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
56
+ // ── action implementations ──
57
+
58
+ async function gotoAction(params: { url?: string; screenshot?: boolean }) {
59
+ if (!params.url) {
60
+ return { content: [{ type: "text" as const, text: "Goto requires a 'url' parameter." }], details: { error: "missing_url" }, isError: true };
61
+ }
62
+ let actionId: number | null = null;
63
+ let beforeState: CompactPageState | null = null;
64
+ const { page: p } = await deps.ensureBrowser();
65
+ beforeState = await deps.captureCompactPageState(p, { includeBodyText: true });
66
+ actionId = deps.beginTrackedAction("browser_navigate", params, beforeState.url).id;
67
+ await p.goto(params.url, { waitUntil: "domcontentloaded", timeout: 30000 });
68
+ await p.waitForLoadState("networkidle", { timeout: 5000 }).catch(() => { /* non-fatal */ });
69
+ await new Promise(resolve => setTimeout(resolve, 300));
70
+
71
+ const title = await p.title();
72
+ const url = p.url();
73
+ const viewport = p.viewportSize();
74
+ const vpText = viewport ? `${viewport.width}x${viewport.height}` : "unknown";
75
+ const afterState = await deps.captureCompactPageState(p, { includeBodyText: true });
76
+ const summary = deps.formatCompactStateSummary(afterState);
77
+ const jsErrors = deps.getRecentErrors(p.url());
78
+ const diff = diffCompactStates(beforeState, afterState);
79
+ setLastActionBeforeState(beforeState);
80
+ setLastActionAfterState(afterState);
81
+ deps.finishTrackedAction(actionId, {
82
+ status: "success", afterUrl: afterState.url, warningSummary: jsErrors.trim() || undefined,
83
+ diffSummary: diff.summary, changed: diff.changed, beforeState, afterState,
84
+ });
85
+
86
+ let screenshotContent: any[] = [];
87
+ if (params.screenshot) {
189
88
  try {
190
- const { page: p } = await deps.ensureBrowser();
191
- await p.reload({ waitUntil: "domcontentloaded", timeout: 30000 });
192
- await p.waitForLoadState("networkidle", { timeout: 5000 }).catch(() => { /* networkidle timeout — non-fatal, page may still be usable */ });
193
-
194
- const title = await p.title();
195
- const url = p.url();
196
- const viewport = p.viewportSize();
197
- const vpText = viewport ? `${viewport.width}x${viewport.height}` : "unknown";
198
- const summary = await deps.postActionSummary(p);
199
- const jsErrors = deps.getRecentErrors(p.url());
200
-
201
- let screenshotContent: any[] = [];
202
- try {
203
- let buf = await p.screenshot({ type: "jpeg", quality: 80, scale: "css" });
204
- buf = await deps.constrainScreenshot(p, buf, "image/jpeg", 80);
205
- screenshotContent = [{
206
- type: "image",
207
- data: buf.toString("base64"),
208
- mimeType: "image/jpeg",
209
- }];
210
- } catch { /* non-fatal — screenshot is optional, reload result is still valid */ }
211
-
212
- return {
213
- content: [
214
- {
215
- type: "text",
216
- text: `Reloaded: ${url}\nTitle: ${title}\nViewport: ${vpText}${jsErrors}\n\nPage summary:\n${summary}`,
217
- },
218
- ...screenshotContent,
219
- ],
220
- details: { title, url, viewport: vpText },
221
- };
222
- } catch (err: any) {
223
- const errorShot = await deps.captureErrorScreenshot(deps.getActivePageOrNull());
224
- const content: any[] = [{ type: "text", text: `Reload failed: ${err.message}` }];
225
- if (errorShot) {
226
- content.push({ type: "image", data: errorShot.data, mimeType: errorShot.mimeType });
227
- }
228
- return { content, details: { error: err.message }, isError: true };
229
- }
230
- },
231
- });
89
+ let buf = await p.screenshot({ type: "jpeg", quality: 80, scale: "css" });
90
+ buf = await deps.constrainScreenshot(p, buf, "image/jpeg", 80);
91
+ screenshotContent = [{ type: "image" as const, data: buf.toString("base64"), mimeType: "image/jpeg" }];
92
+ } catch { /* non-fatal */ }
93
+ }
94
+
95
+ return {
96
+ content: [
97
+ { type: "text" as const, text: `Navigated to: ${url}\nTitle: ${title}\nViewport: ${vpText}\nAction: ${actionId}${jsErrors}\n\nDiff:\n${deps.formatDiffText(diff)}\n\nPage summary:\n${summary}` },
98
+ ...screenshotContent,
99
+ ],
100
+ details: { title, url, status: "loaded", viewport: vpText, actionId, diff },
101
+ };
102
+ }
103
+
104
+ async function goBackForward(direction: "back" | "forward") {
105
+ const { page: p } = await deps.ensureBrowser();
106
+ const response = direction === "back"
107
+ ? await p.goBack({ waitUntil: "domcontentloaded", timeout: 10000 })
108
+ : await p.goForward({ waitUntil: "domcontentloaded", timeout: 10000 });
109
+
110
+ if (!response) {
111
+ return {
112
+ content: [{ type: "text" as const, text: `No ${direction} page in history.` }],
113
+ details: {},
114
+ isError: true,
115
+ };
116
+ }
117
+
118
+ await p.waitForLoadState("networkidle", { timeout: 5000 }).catch(() => { /* non-fatal */ });
119
+ const title = await p.title();
120
+ const url = p.url();
121
+ const summary = await deps.postActionSummary(p);
122
+ const jsErrors = deps.getRecentErrors(p.url());
123
+
124
+ return {
125
+ content: [{ type: "text" as const, text: `Navigated ${direction} to: ${url}\nTitle: ${title}${jsErrors}\n\nPage summary:\n${summary}` }],
126
+ details: { title, url },
127
+ };
128
+ }
129
+
130
+ async function reloadAction() {
131
+ const { page: p } = await deps.ensureBrowser();
132
+ await p.reload({ waitUntil: "domcontentloaded", timeout: 30000 });
133
+ await p.waitForLoadState("networkidle", { timeout: 5000 }).catch(() => { /* non-fatal */ });
134
+
135
+ const title = await p.title();
136
+ const url = p.url();
137
+ const viewport = p.viewportSize();
138
+ const vpText = viewport ? `${viewport.width}x${viewport.height}` : "unknown";
139
+ const summary = await deps.postActionSummary(p);
140
+ const jsErrors = deps.getRecentErrors(p.url());
141
+
142
+ let screenshotContent: any[] = [];
143
+ try {
144
+ let buf = await p.screenshot({ type: "jpeg", quality: 80, scale: "css" });
145
+ buf = await deps.constrainScreenshot(p, buf, "image/jpeg", 80);
146
+ screenshotContent = [{ type: "image" as const, data: buf.toString("base64"), mimeType: "image/jpeg" }];
147
+ } catch { /* non-fatal */ }
148
+
149
+ return {
150
+ content: [{ type: "text" as const, text: `Reloaded: ${url}\nTitle: ${title}\nViewport: ${vpText}${jsErrors}\n\nPage summary:\n${summary}` }, ...screenshotContent],
151
+ details: { title, url, viewport: vpText },
152
+ };
153
+ }
232
154
  }