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
@@ -143,6 +143,7 @@ test("AgentSession switches supported Anthropic models to adaptive when the sett
143
143
  const settingsManager = SettingsManager.inMemory();
144
144
  settingsManager.setDefaultModelAndProvider("openai", "gpt-5.4");
145
145
  settingsManager.setDefaultThinkingLevel("high");
146
+ settingsManager.setClientAdaptiveByDefault(false);
146
147
  settingsManager.setAnthropicAdaptiveByDefault(true);
147
148
  const sessionManager = SessionManager.inMemory(tempDir);
148
149
  const resourceLoader = new DefaultResourceLoader({
@@ -180,3 +181,47 @@ test("AgentSession switches supported Anthropic models to adaptive when the sett
180
181
  assert.equal(session.thinkingLevel, "adaptive");
181
182
  session.dispose();
182
183
  });
184
+
185
+ test("createAgentSession defaults to adaptive for reasoning models when clientAdaptiveByDefault is enabled", async () => {
186
+ const tempDir = join(
187
+ process.cwd(),
188
+ ".tmp-tests",
189
+ `sdk-client-adaptive-${Date.now()}-${Math.random().toString(36).slice(2)}`,
190
+ );
191
+ mkdirSync(tempDir, { recursive: true });
192
+ tempDirs.push(tempDir);
193
+
194
+ const authStorage = AuthStorage.inMemory({
195
+ openai: { type: "api_key", key: "test-openai-key" },
196
+ });
197
+ const modelRegistry = new ModelRegistry(authStorage, join(tempDir, "models.json"));
198
+ const settingsManager = SettingsManager.inMemory();
199
+ settingsManager.setDefaultModelAndProvider("openai", "gpt-5.4");
200
+ settingsManager.setDefaultThinkingLevel("high");
201
+ settingsManager.setClientAdaptiveByDefault(true);
202
+ const sessionManager = SessionManager.inMemory(tempDir);
203
+ const resourceLoader = new DefaultResourceLoader({
204
+ cwd: tempDir,
205
+ agentDir: tempDir,
206
+ settingsManager,
207
+ noExtensions: true,
208
+ noSkills: true,
209
+ noPromptTemplates: true,
210
+ noThemes: true,
211
+ });
212
+
213
+ const { session } = await createAgentSession({
214
+ cwd: tempDir,
215
+ agentDir: tempDir,
216
+ authStorage,
217
+ modelRegistry,
218
+ settingsManager,
219
+ sessionManager,
220
+ resourceLoader,
221
+ });
222
+
223
+ assert.equal(session.model?.provider, "openai");
224
+ assert.equal(session.model?.id, "gpt-5.4");
225
+ assert.equal(session.thinkingLevel, "adaptive");
226
+ session.dispose();
227
+ });
@@ -144,11 +144,15 @@ function shouldPreferAdaptiveThinkingByDefault(
144
144
  model: Model<any> | undefined,
145
145
  settingsManager: SettingsManager,
146
146
  ): boolean {
147
- return !!model &&
148
- model.provider === "anthropic" &&
149
- model.reasoning === true &&
150
- settingsManager.getAnthropicAdaptiveByDefault() &&
151
- supportsAdaptiveThinking(model.id);
147
+ if (!model || model.reasoning !== true) {
148
+ return false;
149
+ }
150
+
151
+ if (model.provider === "anthropic" && supportsAdaptiveThinking(model.id)) {
152
+ return settingsManager.getClientAdaptiveByDefault() || settingsManager.getAnthropicAdaptiveByDefault();
153
+ }
154
+
155
+ return settingsManager.getClientAdaptiveByDefault() && !!settingsManager.getAdaptiveClassifierModel();
152
156
  }
153
157
 
154
158
  /**
@@ -278,9 +282,23 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
278
282
  const balancedToolNames = editMode === "hashline"
279
283
  ? ["hashline_read", "bash", "hashline_edit", "write", "lsp", "bg_shell", "tool_search", "tool_enable", "Skill", "subagent", "await_subagent", "ask_user_questions"]
280
284
  : ["read", "bash", "edit", "write", "lsp", "bg_shell", "tool_search", "tool_enable", "Skill", "subagent", "await_subagent", "ask_user_questions"];
285
+ const standardToolNames = [
286
+ "read", "bash", "edit", "write", "lsp", "grep", "find", "ls",
287
+ "bg_shell",
288
+ "web_search", "fetch_page",
289
+ "resolve_library", "get_library_docs",
290
+ "subagent", "await_subagent", "Skill",
291
+ "ask_user_questions", "secure_env_collect",
292
+ "browser_navigate", "browser_click", "browser_type", "browser_screenshot",
293
+ "browser_scroll", "browser_key_press", "browser_evaluate",
294
+ "browser_find", "browser_wait_for", "browser_close",
295
+ "browser_assert", "browser_batch",
296
+ "tool_search", "tool_enable",
297
+ ];
298
+ const profileToolNames = toolProfile === "standard" ? standardToolNames : balancedToolNames;
281
299
  const defaultActiveToolNames: string[] = toolProfile === "full"
282
300
  ? []
283
- : balancedToolNames;
301
+ : profileToolNames;
284
302
  const initialActiveToolNames: string[] = options.tools
285
303
  ? options.tools.map((t) => t.name).filter((n): n is string => typeof n === "string" && n in allTools)
286
304
  : defaultActiveToolNames;
@@ -326,7 +344,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
326
344
 
327
345
  const extensionRunnerRef: { current?: ExtensionRunner } = {};
328
346
 
329
- agent = new Agent({
347
+ // Resolve the adaptive classifier model for LLM-based adaptive thinking classification.
348
+ const adaptiveClassifierModelRef = settingsManager.getAdaptiveClassifierModel();
349
+ let adaptiveClassifierModel: Model<any> | undefined;
350
+ if (adaptiveClassifierModelRef) {
351
+ const parts = adaptiveClassifierModelRef.split("/");
352
+ if (parts.length === 2) {
353
+ adaptiveClassifierModel = modelRegistry.find(parts[0]!, parts[1]!) ?? undefined;
354
+ }
355
+ }
356
+
357
+ const agentOptions: ConstructorParameters<typeof Agent>[0] & { getFastMode?: () => boolean } = {
330
358
  initialState: {
331
359
  systemPrompt: "",
332
360
  model,
@@ -341,6 +369,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
341
369
  }
342
370
  return runner.emitBeforeProviderRequest(payload, currentModel);
343
371
  },
372
+ getFastMode: () => settingsManager.getFastMode(),
344
373
  sessionId: sessionManager.getSessionId(),
345
374
  transformContext: async (messages) => {
346
375
  const runner = extensionRunnerRef.current;
@@ -353,6 +382,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
353
382
  thinkingBudgets: settingsManager.getThinkingBudgets(),
354
383
  maxRetryDelayMs: settingsManager.getRetrySettings().maxDelayMs,
355
384
  externalToolExecution: (m) => modelRegistry.getProviderAuthMode(m.provider) === "externalCli",
385
+ adaptiveClassifierModel,
356
386
  getApiKey: async (provider) => {
357
387
  // Use the provider argument from the in-flight request;
358
388
  // agent.state.model may already be switched mid-turn.
@@ -422,7 +452,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
422
452
  `Set an API key environment variable or run '/login ${resolvedProvider}'.`,
423
453
  );
424
454
  },
425
- });
455
+ };
456
+ agent = new Agent(agentOptions);
426
457
 
427
458
  // Restore messages if session has existing data
428
459
  if (hasExistingSession) {
@@ -59,10 +59,18 @@ export interface SessionHeader {
59
59
  timestamp: string;
60
60
  cwd: string;
61
61
  parentSession?: string;
62
+ subagentName?: string;
63
+ subagentTask?: string;
64
+ subagentSystemPrompt?: string;
65
+ subagentTools?: string[];
62
66
  }
63
67
 
64
68
  export interface NewSessionOptions {
65
69
  parentSession?: string;
70
+ subagentName?: string;
71
+ subagentTask?: string;
72
+ subagentSystemPrompt?: string;
73
+ subagentTools?: string[];
66
74
  }
67
75
 
68
76
  export interface SessionEntryBase {
@@ -915,6 +923,10 @@ export class SessionManager {
915
923
  timestamp,
916
924
  cwd: this.cwd,
917
925
  parentSession: options?.parentSession,
926
+ subagentName: options?.subagentName,
927
+ subagentTask: options?.subagentTask,
928
+ subagentSystemPrompt: options?.subagentSystemPrompt,
929
+ subagentTools: options?.subagentTools,
918
930
  };
919
931
  this.fileEntries = [header];
920
932
  this.sessionEntries = [];
@@ -0,0 +1,46 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { describe, it } from "node:test";
6
+
7
+ import { SettingsManager } from "./settings-manager.js";
8
+
9
+ describe("SettingsManager fastMode", () => {
10
+ it("defaults fastMode to false", () => {
11
+ const manager = SettingsManager.inMemory();
12
+ assert.equal(manager.getFastMode(), false);
13
+ });
14
+
15
+ it("persists fastMode changes to settings.json", async () => {
16
+ const root = mkdtempSync(join(tmpdir(), "pi-fast-mode-settings-"));
17
+ const cwd = join(root, "project");
18
+ const agentDir = join(root, "agent");
19
+
20
+ try {
21
+ const manager = SettingsManager.create(cwd, agentDir);
22
+ assert.equal(manager.getFastMode(), false);
23
+
24
+ manager.setFastMode(true);
25
+ await manager.flush();
26
+
27
+ const rawEnabled = JSON.parse(readFileSync(join(agentDir, "settings.json"), "utf8")) as {
28
+ fastMode?: boolean;
29
+ };
30
+ assert.equal(rawEnabled.fastMode, true);
31
+
32
+ const reloaded = SettingsManager.create(cwd, agentDir);
33
+ assert.equal(reloaded.getFastMode(), true);
34
+
35
+ reloaded.setFastMode(false);
36
+ await reloaded.flush();
37
+
38
+ const rawDisabled = JSON.parse(readFileSync(join(agentDir, "settings.json"), "utf8")) as {
39
+ fastMode?: boolean;
40
+ };
41
+ assert.equal(rawDisabled.fastMode, false);
42
+ } finally {
43
+ rmSync(root, { recursive: true, force: true });
44
+ }
45
+ });
46
+ });
@@ -41,7 +41,6 @@ export interface ImageSettings {
41
41
  }
42
42
 
43
43
  export interface ThinkingBudgetsSettings {
44
- minimal?: number;
45
44
  low?: number;
46
45
  medium?: number;
47
46
  high?: number;
@@ -131,8 +130,10 @@ export interface Settings {
131
130
  autoSwitchPlanModel?: boolean; // default: false — enable opusplan-style model switching (auto-switch to reasoning model on entry, new-session option on approval)
132
131
  permissionMode?: "danger-full-access" | "accept-on-edit" | "auto" | "plan";
133
132
  classifierModel?: string;
134
- defaultThinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive";
133
+ adaptiveClassifierModel?: string;
134
+ defaultThinkingLevel?: "off" | "low" | "medium" | "high" | "xhigh" | "adaptive";
135
135
  anthropicAdaptiveByDefault?: boolean; // default: false — prefer adaptive thinking when using supported Anthropic models
136
+ clientAdaptiveByDefault?: boolean; // default: true — prefer adaptive thinking for any reasoning-capable model
136
137
  transport?: TransportSetting; // default: "sse"
137
138
  steeringMode?: "all" | "one-at-a-time";
138
139
  followUpMode?: "all" | "one-at-a-time";
@@ -153,11 +154,12 @@ export interface Settings {
153
154
  themes?: string[]; // Array of local theme file paths or directories
154
155
  enableSkillCommands?: boolean; // default: true - register skills as /skill:name commands
155
156
  toolSearch?: boolean; // legacy boolean toggle from deprecated minimal profile; retained for migration only
156
- toolProfile?: "balanced" | "full"; // default: "balanced"
157
+ toolProfile?: "balanced" | "standard" | "full"; // default: "balanced"
157
158
  terminal?: TerminalSettings;
158
159
  images?: ImageSettings;
159
160
  enabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag)
160
161
  codexRotate?: boolean; // Enable the bundled codex-rotate extension (default: false)
162
+ fastMode?: boolean; // Enable OpenAI/Codex fast tier routing (priority service tier where supported)
161
163
  cacheTimer?: boolean; // Show elapsed time since last response in the footer (default: true)
162
164
  pinLastPrompt?: boolean; // Pin last sent prompt above the editor as a reminder (default: false)
163
165
  doubleEscapeAction?: "fork" | "tree" | "none"; // Action for double-escape with empty editor (default: "tree")
@@ -185,6 +187,7 @@ export interface Settings {
185
187
  editorScheme?: "auto" | "vscode" | "cursor" | "zed" | "jetbrains" | "sublime" | "file"; // URI scheme for Cmd+click file links (default: "auto")
186
188
  autoDream?: boolean; // default: false — enable automatic memory consolidation (dream) after sessions
187
189
  autoMemory?: boolean; // default: false — enable automatic memory extraction from session transcripts
190
+ notificationSound?: boolean; // default: false — play terminal bell when agent finishes responding
188
191
  telegramLiveRelayAutoConnect?: boolean; // default: false — auto-run /lsd telegram connect on startup
189
192
  }
190
193
 
@@ -752,6 +755,10 @@ export class SettingsManager {
752
755
  return this.settings.classifierModel;
753
756
  }
754
757
 
758
+ getAdaptiveClassifierModel(): string | undefined {
759
+ return this.settings.adaptiveClassifierModel;
760
+ }
761
+
755
762
  setDefaultProvider(provider: string): void {
756
763
  this.setScopedSetting("defaultProvider", provider);
757
764
  }
@@ -839,6 +846,16 @@ export class SettingsManager {
839
846
  this.setGlobalSetting("classifierModel", modelRef);
840
847
  }
841
848
 
849
+ setAdaptiveClassifierModel(modelRef: string | undefined): void {
850
+ if (modelRef === undefined) {
851
+ delete this.globalSettings.adaptiveClassifierModel;
852
+ this.markModified("adaptiveClassifierModel");
853
+ this.save();
854
+ return;
855
+ }
856
+ this.setGlobalSetting("adaptiveClassifierModel", modelRef);
857
+ }
858
+
842
859
  setDefaultModelAndProvider(provider: string, modelId: string): void {
843
860
  if (this.hasProjectSettings()) {
844
861
  this.projectSettings.defaultProvider = provider;
@@ -893,11 +910,11 @@ export class SettingsManager {
893
910
  this.setGlobalSetting("themeAccent", accent);
894
911
  }
895
912
 
896
- getDefaultThinkingLevel(): "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive" | undefined {
913
+ getDefaultThinkingLevel(): "off" | "low" | "medium" | "high" | "xhigh" | "adaptive" | undefined {
897
914
  return this.settings.defaultThinkingLevel;
898
915
  }
899
916
 
900
- setDefaultThinkingLevel(level: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive"): void {
917
+ setDefaultThinkingLevel(level: "off" | "low" | "medium" | "high" | "xhigh" | "adaptive"): void {
901
918
  this.setGlobalSetting("defaultThinkingLevel", level);
902
919
  }
903
920
 
@@ -909,6 +926,14 @@ export class SettingsManager {
909
926
  this.setGlobalSetting("anthropicAdaptiveByDefault", enabled);
910
927
  }
911
928
 
929
+ getClientAdaptiveByDefault(): boolean {
930
+ return this.settings.clientAdaptiveByDefault ?? true;
931
+ }
932
+
933
+ setClientAdaptiveByDefault(enabled: boolean): void {
934
+ this.setGlobalSetting("clientAdaptiveByDefault", enabled);
935
+ }
936
+
912
937
  getTransport(): TransportSetting {
913
938
  return this.settings.transport ?? "sse";
914
939
  }
@@ -1094,15 +1119,15 @@ export class SettingsManager {
1094
1119
  this.setGlobalSetting("enableSkillCommands", enabled);
1095
1120
  }
1096
1121
 
1097
- getToolProfile(): "balanced" | "full" {
1122
+ getToolProfile(): "balanced" | "standard" | "full" {
1098
1123
  const profile = this.settings.toolProfile;
1099
- if (profile === "balanced" || profile === "full") return profile;
1124
+ if (profile === "balanced" || profile === "standard" || profile === "full") return profile;
1100
1125
  // Migrate legacy minimal/toolSearch settings to balanced.
1101
1126
  if (this.settings.toolSearch !== undefined) return "balanced";
1102
- return "full";
1127
+ return "standard";
1103
1128
  }
1104
1129
 
1105
- setToolProfile(profile: "balanced" | "full"): void {
1130
+ setToolProfile(profile: "balanced" | "standard" | "full"): void {
1106
1131
  this.setGlobalSetting("toolProfile", profile);
1107
1132
  // Keep legacy field in sync with deprecated minimal mode removal.
1108
1133
  this.setGlobalSetting("toolSearch", false);
@@ -1164,6 +1189,14 @@ export class SettingsManager {
1164
1189
  this.setGlobalSetting("codexRotate", enabled);
1165
1190
  }
1166
1191
 
1192
+ getFastMode(): boolean {
1193
+ return this.settings.fastMode ?? false;
1194
+ }
1195
+
1196
+ setFastMode(enabled: boolean): void {
1197
+ this.setGlobalSetting("fastMode", enabled);
1198
+ }
1199
+
1167
1200
  getCacheTimer(): boolean {
1168
1201
  return this.settings.cacheTimer ?? true;
1169
1202
  }
@@ -1422,6 +1455,14 @@ export class SettingsManager {
1422
1455
  this.setGlobalSetting("autoMemory", enabled);
1423
1456
  }
1424
1457
 
1458
+ getNotificationSound(): boolean {
1459
+ return this.settings.notificationSound ?? false;
1460
+ }
1461
+
1462
+ setNotificationSound(enabled: boolean): void {
1463
+ this.setGlobalSetting("notificationSound", enabled);
1464
+ }
1465
+
1425
1466
  getTelegramLiveRelayAutoConnect(): boolean {
1426
1467
  return this.settings.telegramLiveRelayAutoConnect ?? false;
1427
1468
  }
@@ -332,9 +332,12 @@ export function formatSkillsForPrompt(skills: Skill[]): string {
332
332
  ];
333
333
 
334
334
  for (const skill of visibleSkills) {
335
+ const cappedDescription = skill.description.length > 200
336
+ ? skill.description.slice(0, 197) + "..."
337
+ : skill.description;
335
338
  lines.push(" <skill>");
336
339
  lines.push(` <name>${escapeXml(skill.name)}</name>`);
337
- lines.push(` <description>${escapeXml(skill.description)}</description>`);
340
+ lines.push(` <description>${escapeXml(cappedDescription)}</description>`);
338
341
  lines.push(` <location>${escapeXml(skill.filePath)}</location>`);
339
342
  lines.push(" </skill>");
340
343
  }
@@ -27,6 +27,7 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
27
27
  { name: "session", description: "Show session info and stats" },
28
28
  { name: "changelog", description: "Show changelog entries" },
29
29
  { name: "hotkeys", description: "Show all keyboard shortcuts" },
30
+ { name: "btw", description: "Ask a side question without interrupting the current task" },
30
31
  { name: "fork", description: "Create a new fork from a previous message" },
31
32
  { name: "tree", description: "Navigate session tree (switch branches)" },
32
33
  { name: "provider", description: "Manage provider configuration" },
@@ -40,7 +41,7 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
40
41
  { name: "compact", description: "Manually compact the session context" },
41
42
  { name: "resume", description: "Resume a different session" },
42
43
  { name: "reload", description: "Reload extensions, skills, prompts, and themes" },
43
- { name: "thinking", description: "Set thinking level (off/minimal/low/medium/high/xhigh)" },
44
+ { name: "thinking", description: "Set thinking level (off/low/medium/high/xhigh)" },
44
45
  { name: "edit-mode", description: "Toggle edit mode (standard/hashline)" },
45
46
  { name: "terminal", description: "Run a shell command directly (e.g. /terminal ping -c3 1.1.1.1)" },
46
47
  { name: "quit", description: "Quit pi" },
@@ -149,14 +149,22 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
149
149
  const hasLsp = tools.includes("lsp");
150
150
 
151
151
  // Priority-ordered compact guidelines
152
- addGuideline("Be concise. Prefer short, direct answers over preamble.");
152
+ addGuideline("Terse output. All technical substance stays. Only fluff dies.");
153
+ addGuideline("Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging (might be worth/could consider).");
154
+ addGuideline("Fragments OK. Short synonyms (big not extensive, fix not implement a solution for). Pattern: [thing] [action] [reason]. [next step].");
155
+ addGuideline("Never announce what you're about to do — just do it. Never summarize what you just did unless the user asks.");
156
+ addGuideline("Code blocks, error messages, file paths, and technical terms stay exact. Only compress prose.");
157
+ addGuideline("For security warnings, irreversible actions, or multi-step sequences where fragments risk misread — use full clear sentences. Resume terse after.");
153
158
  addGuideline("For conceptual, product, or UX questions, answer first; inspect code only if implementation detail is needed.");
154
159
 
155
160
  const hasSubagent = tools.includes("subagent");
156
161
 
157
162
  if (hasLsp) {
158
163
  addGuideline(
159
- "Code navigation in typed codebases: use lsp for symbols (definition, references, implementation, hover, diagnostics, rename, format). Use grep/find/ls for text patterns, filenames, and non-code files.",
164
+ "Code navigation in typed codebases: use lsp first for symbols (definition, references, implementation, type info, hover, callers, diagnostics, rename, format). Do not use grep/find as a substitute for symbol navigation when LSP is available. Use grep/find/ls for raw text patterns, filenames, and non-code files. If LSP is unavailable or failing, say so briefly and then fall back to grep/read.",
165
+ );
166
+ addGuideline(
167
+ "Before choosing a code navigation tool, classify intent: symbol lookup (definition/reference/implementation/type/caller/rename) → lsp; raw text or filename search → grep/find.",
160
168
  );
161
169
  } else {
162
170
  addGuideline("Use grep/find/ls for code search and file exploration (faster than bash, respects .gitignore)");
@@ -219,6 +227,9 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
219
227
  }
220
228
 
221
229
  const guidelines = guidelinesList.map((g) => `- ${g}`).join("\n");
230
+ const lspDecisionTable = hasLsp
231
+ ? `\n\nTool selection — code navigation quick map:\n\n| Intent | Use | Avoid |\n| --- | --- | --- |\n| Find where a symbol is defined | \`lsp definition\` | \`grep\` |\n| Find all usages of a symbol | \`lsp references\` | \`grep\` |\n| Find implementations of an interface/type | \`lsp implementation\` | \`grep\` |\n| Get type info/docs for a symbol | \`lsp hover\` | raw text search |\n| Find callers of a function | \`lsp incoming_calls\` | \`grep\` |\n| Search literal text patterns (TODO, logs, strings) | \`grep\` | \`lsp\` |\n| Search filenames/paths | \`find\` / \`ls\` | \`lsp\` |\n| Rename a symbol across codebase | \`lsp rename\` | find-and-replace |\n| Check type errors after edits | \`lsp diagnostics\` | ad-hoc grep |\n| Format a file | \`lsp format\` | external formatter guesswork |`
232
+ : "";
222
233
 
223
234
  const piDocsBlock = existsSync(readmePath)
224
235
  ? `\n\nPi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):
@@ -236,7 +247,7 @@ ${toolsList}
236
247
  In addition to the tools above, you may have access to other custom tools depending on the project.
237
248
 
238
249
  Guidelines:
239
- ${guidelines}${piDocsBlock}`;
250
+ ${guidelines}${lspDecisionTable}${piDocsBlock}`;
240
251
 
241
252
  if (appendSection) {
242
253
  prompt += appendSection;
@@ -0,0 +1,30 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ import { getToolPriority, shouldCollapse } from "./tool-priority.js";
5
+
6
+ describe("tool priority", () => {
7
+ it("keeps edit and write always visible", () => {
8
+ assert.equal(getToolPriority("edit"), "always");
9
+ assert.equal(getToolPriority("write"), "always");
10
+ assert.equal(shouldCollapse("edit", false), false);
11
+ assert.equal(shouldCollapse("write", true), false);
12
+ });
13
+
14
+ it("collapses bash and bg_shell only on success", () => {
15
+ assert.equal(getToolPriority("bash"), "on-error");
16
+ assert.equal(getToolPriority("bg_shell"), "on-error");
17
+ assert.equal(shouldCollapse("bash", false), true);
18
+ assert.equal(shouldCollapse("bash", true), false);
19
+ assert.equal(shouldCollapse("bg_shell", false), true);
20
+ assert.equal(shouldCollapse("bg_shell", true), false);
21
+ });
22
+
23
+ it("collapses read, grep, and unknown tools", () => {
24
+ assert.equal(getToolPriority("read"), "collapse");
25
+ assert.equal(getToolPriority("grep"), "collapse");
26
+ assert.equal(getToolPriority("unknown-tool"), "collapse");
27
+ assert.equal(shouldCollapse("read", false), true);
28
+ assert.equal(shouldCollapse("unknown-tool", true), true);
29
+ });
30
+ });
@@ -0,0 +1,17 @@
1
+ export type ToolPriority = "always" | "on-error" | "collapse";
2
+
3
+ const ALWAYS_VISIBLE = new Set(["edit", "write"]);
4
+ const ON_ERROR = new Set(["bash", "bg_shell"]);
5
+
6
+ export function getToolPriority(toolName: string): ToolPriority {
7
+ if (ALWAYS_VISIBLE.has(toolName)) return "always";
8
+ if (ON_ERROR.has(toolName)) return "on-error";
9
+ return "collapse";
10
+ }
11
+
12
+ export function shouldCollapse(toolName: string, isError: boolean): boolean {
13
+ const priority = getToolPriority(toolName);
14
+ if (priority === "always") return false;
15
+ if (priority === "on-error") return !isError;
16
+ return true;
17
+ }
@@ -66,7 +66,7 @@ export function createGrepTool(cwd: string, options?: GrepToolOptions): AgentToo
66
66
  return {
67
67
  name: "grep",
68
68
  label: "grep",
69
- description: `Search file contents for a text pattern. Returns matching lines with file paths and line numbers. Respects .gitignore. Output is truncated to ${DEFAULT_LIMIT} matches or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Long lines are truncated to ${GREP_MAX_LINE_LENGTH} chars. For finding symbol definitions, references, type info, or callers in code, prefer the \`lsp\` tool instead it resolves semantically.`,
69
+ description: `Search file contents for a text pattern. Returns matching lines with file paths and line numbers. Respects .gitignore. Output is truncated to ${DEFAULT_LIMIT} matches or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Long lines are truncated to ${GREP_MAX_LINE_LENGTH} chars. Use this for raw text search, not semantic code navigation. For symbol definitions, references, type info, callers, implementations, or rename targets in code, use the \`lsp\` tool instead when available.`,
70
70
  parameters: grepSchema,
71
71
  execute: async (
72
72
  _toolCallId: string,
@@ -148,6 +148,9 @@ export const readOnlyTools: Tool[] = [readTool, grepTool, findTool, lsTool];
148
148
 
149
149
  const ptyBuiltinTools = createPtyTools(process.cwd());
150
150
 
151
+ /** Write raw input to an active agent PTY session. Used by the UI for user-controlled focus. */
152
+ export const writeToPtySession: (sessionId: string, input: string) => void = ptyBuiltinTools.writeToSession;
153
+
151
154
  // All available tools (using process.cwd())
152
155
  export const allTools = {
153
156
  read: readTool,
@@ -81,7 +81,7 @@ export interface PtyToolDetails {
81
81
  }
82
82
 
83
83
  class PtySessionManager {
84
- private sessions = new Map<string, ManagedPtySession>();
84
+ readonly sessions = new Map<string, ManagedPtySession>();
85
85
 
86
86
  constructor(private cwd: string) {}
87
87
 
@@ -196,17 +196,34 @@ class PtySessionManager {
196
196
 
197
197
  async wait(
198
198
  sessionId: string,
199
- options?: { text?: string; view?: "viewport" | "buffer"; timeoutMs?: number; stableMs?: number },
199
+ options?: { text?: string; view?: "viewport" | "buffer"; timeoutMs?: number; stableMs?: number; signal?: AbortSignal; onUpdate?: (result: { content: Array<{ type: string; text: string }>; details: any }) => void },
200
200
  ): Promise<{ session: ManagedPtySession; text: string }> {
201
201
  const timeoutMs = Math.max(50, options?.timeoutMs ?? 30000);
202
202
  const stableMs = Math.max(50, options?.stableMs ?? 800);
203
203
  const view = options?.view ?? "viewport";
204
+ const signal = options?.signal;
205
+ const onUpdate = options?.onUpdate;
204
206
  const start = Date.now();
205
207
  let lastSnapshot = "";
206
208
  let stableSince = Date.now();
209
+ let lastUpdateText = "";
207
210
 
208
211
  while (Date.now() - start < timeoutMs) {
212
+ if (signal?.aborted) {
213
+ throw new Error(`pty_wait cancelled`);
214
+ }
209
215
  const { text } = await this.read(sessionId, view);
216
+
217
+ // Push live screen text updates to UI
218
+ if (onUpdate && text !== lastUpdateText) {
219
+ lastUpdateText = text;
220
+ const session = this.get(sessionId);
221
+ onUpdate({
222
+ content: [{ type: "text", text: buildReadText(session, view, text) }],
223
+ details: { sessionId, pid: session.pty.handle.pid, completed: session.completed, cancelled: session.cancelled, exitCode: session.exitCode, view, screenText: text },
224
+ });
225
+ }
226
+
210
227
  if (options?.text) {
211
228
  if (text.includes(options.text)) {
212
229
  return { session: this.get(sessionId), text };
@@ -222,7 +239,10 @@ class PtySessionManager {
222
239
  return { session: this.get(sessionId), text };
223
240
  }
224
241
  }
225
- await new Promise((resolve) => setTimeout(resolve, 100));
242
+ await new Promise<void>((resolve) => {
243
+ const t = setTimeout(resolve, 100);
244
+ signal?.addEventListener("abort", () => { clearTimeout(t); resolve(); }, { once: true });
245
+ });
226
246
  }
227
247
 
228
248
  await this.read(sessionId, view);
@@ -249,7 +269,17 @@ function buildReadText(session: ManagedPtySession, view: "viewport" | "buffer",
249
269
  return text ? `${header}\n${text}` : `${header}\n(no visible text)`;
250
270
  }
251
271
 
252
- export function createPtyTools(cwd: string): Record<string, AgentTool<any, PtyToolDetails>> {
272
+ export interface PtyToolsResult {
273
+ pty_start: AgentTool<any, PtyToolDetails>;
274
+ pty_send: AgentTool<any, PtyToolDetails>;
275
+ pty_read: AgentTool<any, PtyToolDetails>;
276
+ pty_wait: AgentTool<any, PtyToolDetails>;
277
+ pty_resize: AgentTool<any, PtyToolDetails>;
278
+ pty_kill: AgentTool<any, PtyToolDetails>;
279
+ writeToSession: (sessionId: string, input: string) => void;
280
+ }
281
+
282
+ export function createPtyTools(cwd: string): PtyToolsResult {
253
283
  const manager = new PtySessionManager(cwd);
254
284
 
255
285
  const ptyStartTool: AgentTool<typeof ptyStartSchema, PtyToolDetails> = {
@@ -302,9 +332,9 @@ export function createPtyTools(cwd: string): Record<string, AgentTool<any, PtyTo
302
332
  label: "pty_wait",
303
333
  description: "Wait until text appears in a PTY session or until the PTY screen stops changing. Useful between pty_send calls.",
304
334
  parameters: ptyWaitSchema,
305
- execute: async (_toolCallId, { sessionId, text, view, timeoutMs, stableMs }) => {
335
+ execute: async (_toolCallId, { sessionId, text, view, timeoutMs, stableMs }, signal, onUpdate) => {
306
336
  const effectiveView = view ?? "viewport";
307
- const result = await manager.wait(sessionId, { text, view: effectiveView, timeoutMs, stableMs });
337
+ const result = await manager.wait(sessionId, { text, view: effectiveView, timeoutMs, stableMs, signal, onUpdate: onUpdate as ((result: { content: Array<{ type: string; text: string }>; details: any }) => void) | undefined });
308
338
  return {
309
339
  content: [{ type: "text", text: buildReadText(result.session, effectiveView, result.text) }],
310
340
  details: { sessionId, pid: result.session.pty.handle.pid, completed: result.session.completed, cancelled: result.session.cancelled, exitCode: result.session.exitCode, view: effectiveView, screenText: result.text },
@@ -341,6 +371,14 @@ export function createPtyTools(cwd: string): Record<string, AgentTool<any, PtyTo
341
371
  },
342
372
  };
343
373
 
374
+ /** Write raw input to an active PTY session by ID. Used by the UI when the user focuses and types into an agent PTY terminal. */
375
+ function writeToSession(sessionId: string, input: string): void {
376
+ const session = manager.sessions.get(sessionId);
377
+ if (session && !session.completed && session.pty.handle.isActive()) {
378
+ session.pty.handle.write(input);
379
+ }
380
+ }
381
+
344
382
  return {
345
383
  pty_start: ptyStartTool,
346
384
  pty_send: ptySendTool,
@@ -348,6 +386,7 @@ export function createPtyTools(cwd: string): Record<string, AgentTool<any, PtyTo
348
386
  pty_wait: ptyWaitTool,
349
387
  pty_resize: ptyResizeTool,
350
388
  pty_kill: ptyKillTool,
389
+ writeToSession,
351
390
  };
352
391
  }
353
392
 
@@ -0,0 +1,31 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import stripAnsi from "strip-ansi";
4
+
5
+ import { ToolSummaryLine } from "../tool-summary-line.js";
6
+ import { initTheme } from "../../theme/theme.js";
7
+
8
+ initTheme("dark");
9
+
10
+ describe("ToolSummaryLine", () => {
11
+ it("aggregates repeated tools with tool-row style formatting", () => {
12
+ const summary = new ToolSummaryLine();
13
+ summary.addTool("read", 600);
14
+ summary.addTool("lsp", 250);
15
+ summary.addTool("read", 150);
16
+
17
+ const rendered = stripAnsi(summary.render(160).join("\n"));
18
+ assert.match(rendered, /^ ● collapsed tools /);
19
+ assert.ok(rendered.includes("read ×2 · lsp · 1.0s"));
20
+ assert.equal(rendered.includes("⎯"), false);
21
+ });
22
+
23
+ it("renders nothing when empty or hidden", () => {
24
+ const summary = new ToolSummaryLine();
25
+ assert.deepEqual(summary.render(80), []);
26
+
27
+ summary.addTool("grep", 100);
28
+ summary.setHidden(true);
29
+ assert.deepEqual(summary.render(80), []);
30
+ });
31
+ });