lsd-pi 1.3.2 → 1.3.7

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 (205) hide show
  1. package/dist/cli.js +2 -1
  2. package/dist/lsd-settings-manager.d.ts +2 -0
  3. package/dist/lsd-settings-manager.js +5 -0
  4. package/dist/resource-loader.js +33 -3
  5. package/dist/resources/extensions/browser-tools/tools/codegen.js +5 -5
  6. package/dist/resources/extensions/browser-tools/tools/navigation.js +107 -178
  7. package/dist/resources/extensions/browser-tools/tools/network-mock.js +112 -167
  8. package/dist/resources/extensions/browser-tools/tools/pages.js +182 -234
  9. package/dist/resources/extensions/browser-tools/tools/refs.js +202 -461
  10. package/dist/resources/extensions/browser-tools/tools/session.js +176 -323
  11. package/dist/resources/extensions/browser-tools/tools/state-persistence.js +91 -154
  12. package/dist/resources/extensions/browser-tools/utils.js +1 -1
  13. package/dist/resources/extensions/cache-timer/index.js +3 -2
  14. package/dist/resources/extensions/slash-commands/extension-manifest.json +2 -2
  15. package/dist/resources/extensions/slash-commands/fast.js +73 -0
  16. package/dist/resources/extensions/slash-commands/index.js +2 -0
  17. package/dist/resources/extensions/slash-commands/plan.js +37 -12
  18. package/dist/resources/extensions/subagent/background-job-manager.js +13 -0
  19. package/dist/resources/extensions/subagent/in-process-runner.js +387 -0
  20. package/dist/resources/extensions/subagent/index.js +278 -626
  21. package/dist/resources/extensions/subagent/legacy-runner.js +503 -0
  22. package/dist/resources/extensions/voice/index.js +96 -36
  23. package/dist/resources/extensions/voice/push-to-talk.js +26 -0
  24. package/dist/welcome-screen.js +2 -2
  25. package/package.json +1 -1
  26. package/packages/pi-agent-core/dist/agent.d.ts +19 -0
  27. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  28. package/packages/pi-agent-core/dist/agent.js +16 -0
  29. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  30. package/packages/pi-agent-core/src/agent.ts +32 -2
  31. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +34 -1
  32. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  33. package/packages/pi-ai/dist/providers/openai-codex-responses.js +32 -4
  34. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  35. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +127 -16
  36. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -1
  37. package/packages/pi-ai/dist/providers/openai-responses.d.ts +8 -1
  38. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  39. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts +2 -0
  40. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts.map +1 -0
  41. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js +67 -0
  42. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js.map +1 -0
  43. package/packages/pi-ai/dist/providers/openai-responses.js +21 -3
  44. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  45. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  46. package/packages/pi-ai/dist/providers/simple-options.js +2 -0
  47. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  48. package/packages/pi-ai/dist/types.d.ts +5 -0
  49. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  50. package/packages/pi-ai/dist/types.js.map +1 -1
  51. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +143 -20
  52. package/packages/pi-ai/src/providers/openai-codex-responses.ts +47 -4
  53. package/packages/pi-ai/src/providers/openai-responses.fast-mode.test.ts +73 -0
  54. package/packages/pi-ai/src/providers/openai-responses.ts +26 -3
  55. package/packages/pi-ai/src/providers/simple-options.ts +2 -0
  56. package/packages/pi-ai/src/types.ts +5 -0
  57. package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
  58. package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
  59. package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
  60. package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
  61. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  62. package/packages/pi-coding-agent/dist/core/sdk.js +4 -2
  63. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  64. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts +2 -0
  65. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts.map +1 -0
  66. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js +35 -0
  67. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js.map +1 -0
  68. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +12 -0
  69. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  70. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts +2 -0
  71. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts.map +1 -0
  72. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js +35 -0
  73. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js.map +1 -0
  74. package/packages/pi-coding-agent/dist/core/settings-manager.js +24 -0
  75. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  76. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  77. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  78. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  79. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  80. package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -1
  81. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  82. package/packages/pi-coding-agent/dist/core/tool-priority.d.ts +4 -0
  83. package/packages/pi-coding-agent/dist/core/tool-priority.d.ts.map +1 -0
  84. package/packages/pi-coding-agent/dist/core/tool-priority.js +18 -0
  85. package/packages/pi-coding-agent/dist/core/tool-priority.js.map +1 -0
  86. package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts +2 -0
  87. package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts.map +1 -0
  88. package/packages/pi-coding-agent/dist/core/tool-priority.test.js +27 -0
  89. package/packages/pi-coding-agent/dist/core/tool-priority.test.js.map +1 -0
  90. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +5 -0
  91. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
  92. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +21 -0
  93. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +16 -1
  95. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  96. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts +2 -0
  97. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts.map +1 -0
  98. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js +34 -0
  99. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js.map +1 -0
  100. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts +45 -0
  101. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts.map +1 -0
  102. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js +314 -0
  103. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js.map +1 -0
  104. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts +2 -0
  105. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts.map +1 -0
  106. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js +122 -0
  107. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js.map +1 -0
  108. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +7 -5
  109. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +86 -28
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +4 -0
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +23 -10
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +8 -0
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +52 -6
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +19 -0
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +127 -14
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts +14 -0
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts.map +1 -0
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js +93 -0
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js.map +1 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts +2 -0
  129. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts.map +1 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js +328 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js.map +1 -0
  132. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +123 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +7 -0
  139. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +4 -0
  141. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +9 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +103 -23
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +41 -0
  149. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +4 -4
  151. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  152. package/packages/pi-coding-agent/package.json +1 -1
  153. package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
  154. package/packages/pi-coding-agent/src/core/sdk.ts +4 -2
  155. package/packages/pi-coding-agent/src/core/settings-manager.collapse-tool-calls.test.ts +46 -0
  156. package/packages/pi-coding-agent/src/core/settings-manager.fast-mode.test.ts +46 -0
  157. package/packages/pi-coding-agent/src/core/settings-manager.ts +36 -0
  158. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  159. package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -1
  160. package/packages/pi-coding-agent/src/core/tool-priority.test.ts +30 -0
  161. package/packages/pi-coding-agent/src/core/tool-priority.ts +17 -0
  162. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +20 -0
  163. package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +26 -0
  164. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +41 -0
  165. package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.test.ts +172 -0
  166. package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.ts +402 -0
  167. package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +105 -28
  168. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +21 -6
  169. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +63 -6
  170. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1262 -1138
  171. package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +120 -0
  172. package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +396 -0
  173. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +530 -398
  174. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -1
  175. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +7 -0
  176. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +4 -0
  177. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +109 -23
  178. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +60 -1
  179. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +4 -4
  180. package/packages/pi-tui/dist/components/editor.js +3 -3
  181. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  182. package/packages/pi-tui/src/components/editor.ts +3 -3
  183. package/pkg/dist/modes/interactive/theme/themes.js +4 -4
  184. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  185. package/pkg/package.json +1 -1
  186. package/src/resources/extensions/browser-tools/tools/codegen.ts +5 -5
  187. package/src/resources/extensions/browser-tools/tools/navigation.ts +118 -196
  188. package/src/resources/extensions/browser-tools/tools/network-mock.ts +114 -205
  189. package/src/resources/extensions/browser-tools/tools/pages.ts +183 -237
  190. package/src/resources/extensions/browser-tools/tools/refs.ts +193 -507
  191. package/src/resources/extensions/browser-tools/tools/session.ts +182 -321
  192. package/src/resources/extensions/browser-tools/tools/state-persistence.ts +94 -172
  193. package/src/resources/extensions/browser-tools/utils.ts +1 -1
  194. package/src/resources/extensions/cache-timer/index.ts +3 -2
  195. package/src/resources/extensions/slash-commands/extension-manifest.json +2 -2
  196. package/src/resources/extensions/slash-commands/fast.ts +89 -0
  197. package/src/resources/extensions/slash-commands/index.ts +2 -0
  198. package/src/resources/extensions/slash-commands/plan.ts +42 -12
  199. package/src/resources/extensions/subagent/background-job-manager.ts +28 -0
  200. package/src/resources/extensions/subagent/in-process-runner.ts +534 -0
  201. package/src/resources/extensions/subagent/index.ts +489 -799
  202. package/src/resources/extensions/subagent/legacy-runner.ts +607 -0
  203. package/src/resources/extensions/voice/index.ts +308 -238
  204. package/src/resources/extensions/voice/push-to-talk.ts +42 -0
  205. package/src/resources/extensions/voice/tests/push-to-talk.test.ts +109 -0
@@ -53,7 +53,7 @@ export function createExtensionUIContext(host: any): ExtensionUIContext {
53
53
  }
54
54
  return result;
55
55
  },
56
- getToolsExpanded: () => host.toolOutputExpanded,
56
+ getToolsExpanded: () => host.collapsedToolCallsExpanded || host.toolOutputExpanded,
57
57
  setToolsExpanded: (expanded) => host.setToolsExpanded(expanded),
58
58
  };
59
59
  }
@@ -54,6 +54,13 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
54
54
  }
55
55
 
56
56
  if (host.session.isStreaming) {
57
+ if (text === "/btw" || text.startsWith("/btw ")) {
58
+ const handled = await dispatchSlashCommand(text, host.getSlashCommandContext());
59
+ if (handled) {
60
+ host.editor.setText("");
61
+ return;
62
+ }
63
+ }
57
64
  host.editor.addToHistory?.(text);
58
65
  host.editor.setText("");
59
66
  host.recordLastSentPrompt?.(text);
@@ -11,8 +11,12 @@ export interface InteractiveModeStateHost {
11
11
  chatContainer: any;
12
12
  settingsManager: any;
13
13
  pendingTools: Map<string, any>;
14
+ collapsedToolSummaryLine?: any;
15
+ collapsedToolCallsExpanded: boolean;
14
16
  toolOutputExpanded: boolean;
15
17
  hideThinkingBlock: boolean;
18
+ notificationSoundEnabled: boolean;
19
+ playNotificationSoundOnAgentEnd: boolean;
16
20
  isBashMode: boolean;
17
21
  onInputCallback?: (text: string) => void;
18
22
  isInitialized: boolean;
@@ -99,6 +99,7 @@ import { SessionSelectorComponent } from "./components/session-selector.js";
99
99
  import { SettingsSelectorComponent } from "./components/settings-selector.js";
100
100
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
101
101
  import { ToolExecutionComponent } from "./components/tool-execution.js";
102
+ import { ToolSummaryLine } from "./components/tool-summary-line.js";
102
103
  import { TreeSelectorComponent } from "./components/tree-selector.js";
103
104
  import { UserMessageComponent } from "./components/user-message.js";
104
105
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
@@ -320,14 +321,20 @@ export class InteractiveMode {
320
321
 
321
322
  // Tool execution tracking: toolCallId -> component
322
323
  private pendingTools = new Map<string, ToolExecutionComponent>();
324
+ private collapsedToolSummaryLine: ToolSummaryLine | undefined = undefined;
323
325
  private agentPtyComponents = new Map<string, EmbeddedTerminalComponent>();
324
326
 
325
327
  // Tool output expansion state
328
+ private collapsedToolCallsExpanded = false;
326
329
  private toolOutputExpanded = false;
327
330
 
328
331
  // Thinking block visibility state
329
332
  private hideThinkingBlock = false;
330
333
 
334
+ // Notification sound (terminal bell on completed agent response)
335
+ notificationSoundEnabled = false;
336
+ playNotificationSoundOnAgentEnd = false;
337
+
331
338
  // Pin last prompt feature
332
339
  private pinLastPromptEnabled = false;
333
340
  private lastSentPromptText: string | undefined = undefined;
@@ -435,6 +442,11 @@ export class InteractiveMode {
435
442
  // Load pin last prompt setting
436
443
  this.pinLastPromptEnabled = this.settingsManager.getPinLastPrompt();
437
444
 
445
+ // Load notification sound setting
446
+ this.notificationSoundEnabled = this.settingsManager.getNotificationSound();
447
+ this.footer.setNotificationSoundEnabled(this.notificationSoundEnabled);
448
+ this.footer.setVerboseFooterEnabled(this.settingsManager.getVerboseFooter());
449
+
438
450
  // Register themes from resource loader and initialize
439
451
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
440
452
  initTheme(this.settingsManager.getTheme(), true, this.settingsManager.getThemeAccent());
@@ -2327,6 +2339,7 @@ export class InteractiveMode {
2327
2339
  this.defaultEditor.onAction("cyclePermissionMode", () => this.cyclePermissionMode());
2328
2340
  this.defaultEditor.onAction("showHotkeys", () => showHotkeys(this.getSlashCommandContext()));
2329
2341
  this.defaultEditor.onAction("terminalFocus", () => this.toggleEmbeddedTerminalFocus());
2342
+ this.defaultEditor.onAction("toggleNotificationSound", () => this.toggleNotificationSound());
2330
2343
 
2331
2344
  // Global debug handler on TUI (works regardless of focus)
2332
2345
  this.ui.onDebug = () => this.handleDebugCommand();
@@ -2347,6 +2360,7 @@ export class InteractiveMode {
2347
2360
  if (wasBashMode !== this.isBashMode) {
2348
2361
  this.updateEditorBorderColor();
2349
2362
  }
2363
+ this.updateEditorExpandHint();
2350
2364
  };
2351
2365
 
2352
2366
  // Handle clipboard image paste (triggered on Ctrl+V)
@@ -3035,6 +3049,14 @@ export class InteractiveMode {
3035
3049
  }
3036
3050
  }
3037
3051
 
3052
+ private toggleNotificationSound(): void {
3053
+ this.notificationSoundEnabled = !this.notificationSoundEnabled;
3054
+ this.settingsManager.setNotificationSound(this.notificationSoundEnabled);
3055
+ this.footer.setNotificationSoundEnabled(this.notificationSoundEnabled);
3056
+ this.ui.requestRender();
3057
+ this.showStatus(`Notification sound: ${this.notificationSoundEnabled ? "enabled" : "disabled"}`);
3058
+ }
3059
+
3038
3060
  private async cycleModel(direction: "forward" | "backward"): Promise<void> {
3039
3061
  try {
3040
3062
  const result = await this.session.cycleModel(direction);
@@ -3053,57 +3075,92 @@ export class InteractiveMode {
3053
3075
  }
3054
3076
  }
3055
3077
 
3078
+ private hasCollapsedToolCallSummaries(): boolean {
3079
+ return this.chatContainer.children.some((child) => child instanceof ToolSummaryLine);
3080
+ }
3081
+
3082
+ private shouldUseCollapsedToolCallIntermediateState(): boolean {
3083
+ return this.settingsManager.getCollapseToolCalls() && this.hasCollapsedToolCallSummaries();
3084
+ }
3085
+
3056
3086
  private toggleToolOutputExpansion(): void {
3057
- this.setToolsExpanded(!this.toolOutputExpanded);
3087
+ if (this.toolOutputExpanded) {
3088
+ this.setToolOutputExpansionState(false, false);
3089
+ return;
3090
+ }
3091
+ if (this.shouldUseCollapsedToolCallIntermediateState() && !this.collapsedToolCallsExpanded) {
3092
+ this.setToolOutputExpansionState(true, false);
3093
+ return;
3094
+ }
3095
+ this.setToolOutputExpansionState(true, true);
3058
3096
  }
3059
3097
 
3060
3098
  private setToolsExpanded(expanded: boolean): void {
3061
- this.toolOutputExpanded = expanded;
3099
+ this.setToolOutputExpansionState(expanded, expanded);
3100
+ }
3101
+
3102
+ private setToolOutputExpansionState(collapsedToolCallsExpanded: boolean, toolOutputExpanded: boolean): void {
3103
+ this.collapsedToolCallsExpanded = collapsedToolCallsExpanded || toolOutputExpanded;
3104
+ this.toolOutputExpanded = toolOutputExpanded;
3105
+ const collapseToolCalls = this.settingsManager.getCollapseToolCalls();
3106
+ const showCollapsedToolCalls = collapseToolCalls && this.collapsedToolCallsExpanded;
3062
3107
  for (const child of this.chatContainer.children) {
3063
3108
  if (isExpandable(child)) {
3064
- child.setExpanded(expanded);
3109
+ child.setExpanded(toolOutputExpanded);
3110
+ }
3111
+ if (child instanceof ToolExecutionComponent) {
3112
+ child.setHidden(!showCollapsedToolCalls && child.shouldHideWhenCollapsed(collapseToolCalls));
3113
+ }
3114
+ if (child instanceof ToolSummaryLine) {
3115
+ child.setHidden(!collapseToolCalls || showCollapsedToolCalls);
3065
3116
  }
3066
3117
  }
3067
3118
  if (this.bashComponent) {
3068
- this.bashComponent.setExpanded(expanded);
3119
+ this.bashComponent.setExpanded(toolOutputExpanded);
3069
3120
  }
3070
3121
  for (const component of this.pendingBashComponents) {
3071
- component.setExpanded(expanded);
3122
+ component.setExpanded(toolOutputExpanded);
3072
3123
  }
3073
3124
  this.updateEditorExpandHint();
3074
3125
  this.ui.requestRender();
3075
3126
  }
3076
3127
 
3077
- /** Append/remove the "ctrl+o to expand" hint in the editor bottom border. */
3128
+ /** Append/remove right-aligned expand and streaming hints in editor bottom border. */
3078
3129
  updateEditorExpandHint(): void {
3079
3130
  const expandKey = appKey(this.keybindings, "expandTools");
3080
3131
  const collapseHint = `${theme.fg("dim", expandKey)}${theme.fg("muted", " collapse")}`;
3081
- const expandHint = `${theme.fg("dim", expandKey)}${theme.fg("muted", " : verbose")}`;
3082
- // The base hint set during agent_start
3083
- const enterKey = theme.fg("dim", "↵");
3084
- const followUpKey = theme.fg("dim", appKey(this.keybindings, "followUp"));
3085
- const steerLabel = theme.fg("muted", " steer");
3086
- const queueLabel = theme.fg("muted", " queue");
3087
- const baseHint = `${enterKey}${steerLabel} ${followUpKey}${queueLabel}`;
3132
+ const expandToolCallsHint = `${theme.fg("dim", expandKey)}${theme.fg("muted", " : uncollapsed tools")}`;
3133
+ const expandVerboseHint = `${theme.fg("dim", expandKey)}${theme.fg("muted", " : verbose")}`;
3134
+ const editorHasText = this.defaultEditor.getText().trim().length > 0;
3135
+ const streamingHints = editorHasText
3136
+ ? `${theme.fg("dim", "↵")}${theme.fg("muted", " steer")} ${theme.fg("dim", appKey(this.keybindings, "followUp"))}${theme.fg("muted", " queue")}`
3137
+ : "";
3088
3138
 
3089
3139
  // Check if there are any expandable tool outputs in the chat
3090
3140
  const hasToolOutputs =
3091
3141
  this.chatContainer.children.some(isExpandable) ||
3092
3142
  !!this.bashComponent ||
3093
3143
  this.pendingBashComponents.length > 0;
3144
+ const canExpandCollapsedToolCalls = this.shouldUseCollapsedToolCallIntermediateState();
3094
3145
 
3095
- const activeHint = this.toolOutputExpanded ? collapseHint : expandHint;
3146
+ let activeHint = expandVerboseHint;
3147
+ if (this.toolOutputExpanded) {
3148
+ activeHint = collapseHint;
3149
+ } else if (this.collapsedToolCallsExpanded && canExpandCollapsedToolCalls) {
3150
+ activeHint = expandVerboseHint;
3151
+ } else if (canExpandCollapsedToolCalls) {
3152
+ activeHint = expandToolCallsHint;
3153
+ }
3096
3154
 
3097
- if (this.loadingAnimation) {
3098
- // Agent is running - always show expand/collapse hint when there are tool outputs
3099
- this.defaultEditor.bottomHint = hasToolOutputs
3100
- ? `${baseHint} ${activeHint}`
3101
- : baseHint;
3102
- } else if (hasToolOutputs) {
3103
- // Idle - show expand/collapse hint so user knows ctrl+o works
3104
- this.defaultEditor.bottomHint = activeHint;
3155
+ const rightHints: string[] = [];
3156
+ if (this.loadingAnimation && streamingHints) {
3157
+ rightHints.push(streamingHints);
3158
+ }
3159
+ if (hasToolOutputs) {
3160
+ rightHints.push(activeHint);
3105
3161
  }
3106
- // If no tool outputs and idle, leave bottomHint as-is (cleared by agent_end)
3162
+
3163
+ this.defaultEditor.bottomHint = rightHints.join(" ");
3107
3164
  }
3108
3165
 
3109
3166
  private toggleThinkingBlockVisibility(): void {
@@ -3454,7 +3511,9 @@ export class InteractiveMode {
3454
3511
  enableSkillCommands: this.settingsManager.getEnableSkillCommands(),
3455
3512
  toolProfile: this.settingsManager.getToolProfile(),
3456
3513
  codexRotate: this.settingsManager.getCodexRotate(),
3514
+ fastMode: this.settingsManager.getFastMode(),
3457
3515
  cacheTimer: this.settingsManager.getCacheTimer(),
3516
+ verboseFooter: this.settingsManager.getVerboseFooter(),
3458
3517
  pinLastPrompt: this.settingsManager.getPinLastPrompt(),
3459
3518
  steeringMode: this.session.steeringMode,
3460
3519
  followUpMode: this.session.followUpMode,
@@ -3479,10 +3538,12 @@ export class InteractiveMode {
3479
3538
  clearOnShrink: this.settingsManager.getClearOnShrink(),
3480
3539
  timestampFormat: this.settingsManager.getTimestampFormat(),
3481
3540
  toolOutputMode: this.settingsManager.getToolOutputMode(),
3541
+ collapseToolCalls: this.settingsManager.getCollapseToolCalls(),
3482
3542
  rtk: this.settingsManager.getRtk(),
3483
3543
  editorScheme: this.settingsManager.getEditorScheme(),
3484
3544
  autoDream: this.settingsManager.getAutoDream(),
3485
3545
  autoMemory: this.settingsManager.getAutoMemory(),
3546
+ notificationSound: this.notificationSoundEnabled,
3486
3547
  telegramLiveRelayAutoConnect: this.settingsManager.getTelegramLiveRelayAutoConnect(),
3487
3548
  sandboxEnabled: this.settingsManager.getSandboxSettings().enabled ?? true,
3488
3549
  sandboxNetworkMode: this.settingsManager.getSandboxSettings().networkMode
@@ -3653,10 +3714,20 @@ export class InteractiveMode {
3653
3714
  this.settingsManager.setCodexRotate(enabled);
3654
3715
  this.showStatus(`Codex rotate: ${enabled ? "enabled" : "disabled"} (restart required)`);
3655
3716
  },
3717
+ onFastModeChange: (enabled) => {
3718
+ this.settingsManager.setFastMode(enabled);
3719
+ this.showStatus(`Fast mode: ${enabled ? "enabled" : "disabled"}`);
3720
+ },
3656
3721
  onCacheTimerChange: (enabled) => {
3657
3722
  this.settingsManager.setCacheTimer(enabled);
3658
3723
  this.showStatus(`Cache timer: ${enabled ? "enabled" : "disabled"}`);
3659
3724
  },
3725
+ onVerboseFooterChange: (enabled) => {
3726
+ this.settingsManager.setVerboseFooter(enabled);
3727
+ this.footer.setVerboseFooterEnabled(enabled);
3728
+ this.ui.requestRender();
3729
+ this.showStatus(`Verbose footer: ${enabled ? "enabled" : "disabled"}`);
3730
+ },
3660
3731
  onPinLastPromptChange: (enabled) => {
3661
3732
  this.settingsManager.setPinLastPrompt(enabled);
3662
3733
  this.pinLastPromptEnabled = enabled;
@@ -3675,6 +3746,13 @@ export class InteractiveMode {
3675
3746
  this.settingsManager.setAutoMemory(enabled);
3676
3747
  this.showStatus(`Auto memory: ${enabled ? "enabled" : "disabled"}`);
3677
3748
  },
3749
+ onNotificationSoundChange: (enabled) => {
3750
+ this.notificationSoundEnabled = enabled;
3751
+ this.settingsManager.setNotificationSound(enabled);
3752
+ this.footer.setNotificationSoundEnabled(enabled);
3753
+ this.ui.requestRender();
3754
+ this.showStatus(`Notification sound: ${enabled ? "enabled" : "disabled"}`);
3755
+ },
3678
3756
  onTelegramLiveRelayAutoConnectChange: (enabled) => {
3679
3757
  this.settingsManager.setTelegramLiveRelayAutoConnect(enabled);
3680
3758
  this.showStatus(`Telegram autoconnect: ${enabled ? "enabled" : "disabled"}`);
@@ -3789,6 +3867,14 @@ export class InteractiveMode {
3789
3867
  this.settingsManager.setEditorScheme(scheme);
3790
3868
  this.showStatus(`Editor link scheme: ${scheme}`);
3791
3869
  },
3870
+ onCollapseToolCallsChange: (enabled) => {
3871
+ this.settingsManager.setCollapseToolCalls(enabled);
3872
+ this.setToolOutputExpansionState(enabled && (this.collapsedToolCallsExpanded || this.toolOutputExpanded), this.toolOutputExpanded);
3873
+ if (!enabled) {
3874
+ this.collapsedToolSummaryLine = undefined;
3875
+ }
3876
+ this.showStatus(`Collapse tool calls: ${enabled ? "enabled" : "disabled"}`);
3877
+ },
3792
3878
  onToolOutputModeChange: (mode) => {
3793
3879
  this.settingsManager.setToolOutputMode(mode);
3794
3880
  for (const child of this.chatContainer.children) {
@@ -17,6 +17,9 @@ import type {
17
17
  EditorComponent,
18
18
  MarkdownTheme,
19
19
  } from "@gsd/pi-tui";
20
+ import {
21
+ streamSimple,
22
+ } from "@gsd/pi-ai";
20
23
  import {
21
24
  type Component,
22
25
  Container,
@@ -32,17 +35,19 @@ import type { AgentSession } from "../../core/agent-session.js";
32
35
  import type { AppAction, KeybindingsManager } from "../../core/keybindings.js";
33
36
  import type { SessionManager } from "../../core/session-manager.js";
34
37
  import type { SettingsManager } from "../../core/settings-manager.js";
38
+ import { convertToLlm } from "../../core/messages.js";
35
39
  import { copyToClipboard } from "../../utils/clipboard.js";
36
40
  import { getChangelogPath, parseChangelog } from "../../utils/changelog.js";
37
41
  import { ArminComponent } from "./components/armin.js";
38
42
  import { BorderedLoader } from "./components/bordered-loader.js";
43
+ import { BtwOverlayComponent } from "./components/btw-overlay.js";
39
44
  import { DynamicBorder } from "./components/dynamic-border.js";
40
45
  import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
41
46
  import { appKey, editorKey, formatKeyForDisplay } from "./components/keybinding-hints.js";
42
47
  import { SelectSubmenu, THINKING_DESCRIPTIONS } from "./components/settings-selector.js";
43
48
  import { theme } from "./theme/theme.js";
44
49
 
45
- import type { TUI } from "@gsd/pi-tui";
50
+ import type { OverlayHandle, TUI } from "@gsd/pi-tui";
46
51
 
47
52
  // ---------------------------------------------------------------------------
48
53
  // Context interface — the subset of InteractiveMode needed by slash commands
@@ -173,6 +178,15 @@ export async function dispatchSlashCommand(
173
178
  showHotkeys(ctx);
174
179
  return true;
175
180
  }
181
+ if (text === "/btw" || text.startsWith("/btw ")) {
182
+ const question = text.startsWith("/btw ") ? text.slice(5).trim() : "";
183
+ if (!question) {
184
+ ctx.showWarning("Usage: /btw <question>");
185
+ return true;
186
+ }
187
+ await handleBtwCommand(question, ctx);
188
+ return true;
189
+ }
176
190
  if (text === "/fork") {
177
191
  ctx.showUserMessageSelector();
178
192
  return true;
@@ -704,6 +718,7 @@ export function showHotkeys(ctx: SlashCommandContext): void {
704
718
  const externalEditor = getAppKeyDisplay(ctx.keybindings, "externalEditor");
705
719
  const followUp = getAppKeyDisplay(ctx.keybindings, "followUp");
706
720
  const dequeue = getAppKeyDisplay(ctx.keybindings, "dequeue");
721
+ const toggleNotificationSound = getAppKeyDisplay(ctx.keybindings, "toggleNotificationSound");
707
722
 
708
723
  let hotkeys = `
709
724
  **Navigation**
@@ -746,6 +761,7 @@ export function showHotkeys(ctx: SlashCommandContext): void {
746
761
  | \`${externalEditor}\` | Edit message in external editor |
747
762
  | \`${followUp}\` | Queue follow-up message |
748
763
  | \`${dequeue}\` | Restore queued messages |
764
+ | \`${toggleNotificationSound}\` | Toggle notification sound |
749
765
  | \`Ctrl+V\` | Paste image from clipboard |
750
766
  | \`/\` | Slash commands |
751
767
  | \`!\` | Run bash command |
@@ -779,6 +795,49 @@ export function showHotkeys(ctx: SlashCommandContext): void {
779
795
  ctx.requestRender();
780
796
  }
781
797
 
798
+ async function handleBtwCommand(question: string, ctx: SlashCommandContext): Promise<void> {
799
+ const model = ctx.session.model;
800
+ if (!model) {
801
+ ctx.showWarning("No model selected. Cannot use /btw.");
802
+ return;
803
+ }
804
+
805
+ if (!ctx.session.modelRegistry.isProviderRequestReady(model.provider)) {
806
+ ctx.showWarning(`No API key available for provider: ${model.provider}`);
807
+ return;
808
+ }
809
+
810
+ const systemPrompt = ctx.session.systemPrompt;
811
+ const messages = convertToLlm(ctx.session.messages);
812
+ const sessionId = ctx.session.sessionId;
813
+ const apiKey = await ctx.session.modelRegistry.getApiKey(model, sessionId);
814
+ let handle: OverlayHandle | undefined;
815
+
816
+ const overlay = new BtwOverlayComponent(
817
+ question,
818
+ model,
819
+ systemPrompt,
820
+ messages,
821
+ ctx.getMarkdownThemeWithSettings(),
822
+ ctx.ui,
823
+ () => {
824
+ handle?.hide();
825
+ },
826
+ () => {
827
+ ctx.requestRender();
828
+ },
829
+ streamSimple,
830
+ { apiKey, sessionId },
831
+ );
832
+
833
+ handle = ctx.ui.showOverlay(overlay, {
834
+ width: "80%",
835
+ maxHeight: "70%",
836
+ anchor: "center",
837
+ margin: { top: 2, bottom: 2, left: 4, right: 4 },
838
+ });
839
+ }
840
+
782
841
  async function handleCompactCommand(customInstructions: string | undefined, ctx: SlashCommandContext): Promise<void> {
783
842
  const entries = ctx.sessionManager.getEntries();
784
843
  const messageCount = entries.filter((e) => e.type === "message").length;
@@ -78,8 +78,8 @@ const dark: ThemeJson = {
78
78
  mdHr: "gray",
79
79
  mdListBullet: "accent",
80
80
 
81
- toolDiffAdded: "green",
82
- toolDiffRemoved: "red",
81
+ toolDiffAdded: "#4ade80",
82
+ toolDiffRemoved: "#fb7185",
83
83
  toolDiffContext: "gray",
84
84
 
85
85
  syntaxComment: "#6A9955",
@@ -174,8 +174,8 @@ const light: ThemeJson = {
174
174
  mdHr: "mediumGray",
175
175
  mdListBullet: "green",
176
176
 
177
- toolDiffAdded: "green",
178
- toolDiffRemoved: "red",
177
+ toolDiffAdded: "#15803d",
178
+ toolDiffRemoved: "#b91c1c",
179
179
  toolDiffContext: "mediumGray",
180
180
 
181
181
  syntaxComment: "#008000",
@@ -343,15 +343,15 @@ export class Editor {
343
343
  result.push(this.borderColor(indicator + "─".repeat(Math.max(0, remaining))));
344
344
  }
345
345
  else if (this.bottomHint) {
346
- // Embed hint right-aligned in the bottom border: ─────── hint ─
346
+ // Embed hint at far right in bottom border: ─────────────── hint ─
347
347
  // Apply borderColor only to the dashes so the hint's own styling is preserved.
348
348
  const hintVisible = visibleWidth(this.bottomHint);
349
349
  const minDashes = 1;
350
350
  const separatorWidth = 1; // single space on each side of hint
351
351
  const totalFixed = hintVisible + separatorWidth * 2 + minDashes * 2;
352
352
  if (width >= totalFixed) {
353
- const leftDashes = Math.floor((width - hintVisible - separatorWidth * 2) * 0.75);
354
- const rightDashes = Math.max(minDashes, width - hintVisible - separatorWidth * 2 - leftDashes);
353
+ const rightDashes = minDashes;
354
+ const leftDashes = Math.max(minDashes, width - hintVisible - separatorWidth * 2 - rightDashes);
355
355
  const line = this.borderColor("─".repeat(leftDashes)) +
356
356
  " " +
357
357
  this.bottomHint +