@blackbox_ai/blackbox-cli 0.8.6 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/README.md +20 -0
  2. package/dist/assets/notification.mp3 +0 -0
  3. package/dist/package.json +2 -2
  4. package/dist/src/commands/configure/ConfigureUI.d.ts +1 -1
  5. package/dist/src/commands/configure/ConfigureUI.js +91 -22
  6. package/dist/src/commands/configure/ConfigureUI.js.map +1 -1
  7. package/dist/src/commands/configure.js +57 -16
  8. package/dist/src/commands/configure.js.map +1 -1
  9. package/dist/src/commands/shortcut.d.ts +10 -0
  10. package/dist/src/commands/shortcut.js +72 -0
  11. package/dist/src/commands/shortcut.js.map +1 -0
  12. package/dist/src/commands/voice.js +39 -34
  13. package/dist/src/commands/voice.js.map +1 -1
  14. package/dist/src/config/config.js +8 -5
  15. package/dist/src/config/config.js.map +1 -1
  16. package/dist/src/config/modelFetcher.d.ts +4 -0
  17. package/dist/src/config/modelFetcher.js +68 -22
  18. package/dist/src/config/modelFetcher.js.map +1 -1
  19. package/dist/src/config/settings.d.ts +2 -0
  20. package/dist/src/config/settings.js +48 -3
  21. package/dist/src/config/settings.js.map +1 -1
  22. package/dist/src/config/settingsSchema.d.ts +64 -6
  23. package/dist/src/config/settingsSchema.js +62 -6
  24. package/dist/src/config/settingsSchema.js.map +1 -1
  25. package/dist/src/gemini.js +31 -20
  26. package/dist/src/gemini.js.map +1 -1
  27. package/dist/src/generated/git-commit.d.ts +2 -2
  28. package/dist/src/generated/git-commit.js +2 -2
  29. package/dist/src/services/BuiltinCommandLoader.js +4 -2
  30. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  31. package/dist/src/services/agentExecutor.d.ts +57 -0
  32. package/dist/src/services/agentExecutor.js +149 -0
  33. package/dist/src/services/agentExecutor.js.map +1 -0
  34. package/dist/src/services/voiceRecordingService.d.ts +13 -1
  35. package/dist/src/services/voiceRecordingService.js +38 -5
  36. package/dist/src/services/voiceRecordingService.js.map +1 -1
  37. package/dist/src/ui/App.d.ts +1 -1
  38. package/dist/src/ui/App.js +292 -26
  39. package/dist/src/ui/App.js.map +1 -1
  40. package/dist/src/ui/colors.js +3 -0
  41. package/dist/src/ui/colors.js.map +1 -1
  42. package/dist/src/ui/commands/agentCommand.d.ts +7 -0
  43. package/dist/src/ui/commands/agentCommand.js +181 -0
  44. package/dist/src/ui/commands/agentCommand.js.map +1 -0
  45. package/dist/src/ui/commands/modelCommand.js +15 -2
  46. package/dist/src/ui/commands/modelCommand.js.map +1 -1
  47. package/dist/src/ui/commands/quitCommand.js +5 -0
  48. package/dist/src/ui/commands/quitCommand.js.map +1 -1
  49. package/dist/src/ui/commands/subAgentsCommand.d.ts +7 -0
  50. package/dist/src/ui/commands/subAgentsCommand.js +32 -0
  51. package/dist/src/ui/commands/subAgentsCommand.js.map +1 -0
  52. package/dist/src/ui/commands/types.d.ts +24 -3
  53. package/dist/src/ui/commands/types.js.map +1 -1
  54. package/dist/src/ui/commands/voiceCommand.js +70 -34
  55. package/dist/src/ui/commands/voiceCommand.js.map +1 -1
  56. package/dist/src/ui/commands/voiceCommand.test.d.ts +6 -0
  57. package/dist/src/ui/commands/voiceCommand.test.js +131 -0
  58. package/dist/src/ui/commands/voiceCommand.test.js.map +1 -0
  59. package/dist/src/ui/components/AgentModelSelector.d.ts +17 -0
  60. package/dist/src/ui/components/AgentModelSelector.js +41 -0
  61. package/dist/src/ui/components/AgentModelSelector.js.map +1 -0
  62. package/dist/src/ui/components/AgentSetDialog.d.ts +15 -0
  63. package/dist/src/ui/components/AgentSetDialog.js +52 -0
  64. package/dist/src/ui/components/AgentSetDialog.js.map +1 -0
  65. package/dist/src/ui/components/ApiKeyInputDialog.d.ts +17 -0
  66. package/dist/src/ui/components/ApiKeyInputDialog.js +44 -0
  67. package/dist/src/ui/components/ApiKeyInputDialog.js.map +1 -0
  68. package/dist/src/ui/components/AsciiArt.d.ts +3 -3
  69. package/dist/src/ui/components/AsciiArt.js +18 -18
  70. package/dist/src/ui/components/AsciiArt.js.map +1 -1
  71. package/dist/src/ui/components/AuthDialog.js +50 -7
  72. package/dist/src/ui/components/AuthDialog.js.map +1 -1
  73. package/dist/src/ui/components/Footer.d.ts +8 -0
  74. package/dist/src/ui/components/Footer.js +17 -4
  75. package/dist/src/ui/components/Footer.js.map +1 -1
  76. package/dist/src/ui/components/GenericProviderKeyPrompt.js +1 -1
  77. package/dist/src/ui/components/GenericProviderKeyPrompt.js.map +1 -1
  78. package/dist/src/ui/components/Header.js +6 -10
  79. package/dist/src/ui/components/Header.js.map +1 -1
  80. package/dist/src/ui/components/HistoryBrowserDialog.js +2 -1
  81. package/dist/src/ui/components/HistoryBrowserDialog.js.map +1 -1
  82. package/dist/src/ui/components/InputPrompt.js +3 -1
  83. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  84. package/dist/src/ui/components/MemoryUsageDisplay.js +41 -8
  85. package/dist/src/ui/components/MemoryUsageDisplay.js.map +1 -1
  86. package/dist/src/ui/components/SettingsDialog.js +4 -3
  87. package/dist/src/ui/components/SettingsDialog.js.map +1 -1
  88. package/dist/src/ui/components/StatsDisplay.js +1 -1
  89. package/dist/src/ui/components/StatsDisplay.js.map +1 -1
  90. package/dist/src/ui/components/SuggestionsDisplay.js +3 -2
  91. package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
  92. package/dist/src/ui/components/Tips.js +1 -1
  93. package/dist/src/ui/components/Tips.js.map +1 -1
  94. package/dist/src/ui/components/agents/SingleAgentDialog.d.ts +23 -0
  95. package/dist/src/ui/components/agents/SingleAgentDialog.js +222 -0
  96. package/dist/src/ui/components/agents/SingleAgentDialog.js.map +1 -0
  97. package/dist/src/ui/components/messages/DiffRenderer.js +1 -9
  98. package/dist/src/ui/components/messages/DiffRenderer.js.map +1 -1
  99. package/dist/src/ui/components/messages/ErrorMessage.js +2 -1
  100. package/dist/src/ui/components/messages/ErrorMessage.js.map +1 -1
  101. package/dist/src/ui/components/messages/InfoMessage.js +2 -1
  102. package/dist/src/ui/components/messages/InfoMessage.js.map +1 -1
  103. package/dist/src/ui/components/messages/ToolGroupMessage.js +4 -1
  104. package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
  105. package/dist/src/ui/components/messages/ToolMessage.js +4 -4
  106. package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
  107. package/dist/src/ui/components/messages/UserMessage.js +1 -2
  108. package/dist/src/ui/components/messages/UserMessage.js.map +1 -1
  109. package/dist/src/ui/components/multiagent/MultiAgentConfigDialog.js +1 -19
  110. package/dist/src/ui/components/multiagent/MultiAgentConfigDialog.js.map +1 -1
  111. package/dist/src/ui/components/shared/RadioButtonSelect.js +14 -4
  112. package/dist/src/ui/components/shared/RadioButtonSelect.js.map +1 -1
  113. package/dist/src/ui/contexts/SessionContext.d.ts +18 -0
  114. package/dist/src/ui/contexts/SessionContext.js +25 -1
  115. package/dist/src/ui/contexts/SessionContext.js.map +1 -1
  116. package/dist/src/ui/hooks/shellCommandProcessor.d.ts +1 -1
  117. package/dist/src/ui/hooks/shellCommandProcessor.js +13 -2
  118. package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
  119. package/dist/src/ui/hooks/slashCommandProcessor.d.ts +2 -2
  120. package/dist/src/ui/hooks/slashCommandProcessor.js +65 -29
  121. package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
  122. package/dist/src/ui/hooks/useAdaptiveStream.d.ts +2 -1
  123. package/dist/src/ui/hooks/useAdaptiveStream.js +2 -2
  124. package/dist/src/ui/hooks/useAdaptiveStream.js.map +1 -1
  125. package/dist/src/ui/hooks/useAgentCommandProcessor.d.ts +26 -0
  126. package/dist/src/ui/hooks/useAgentCommandProcessor.js +967 -0
  127. package/dist/src/ui/hooks/useAgentCommandProcessor.js.map +1 -0
  128. package/dist/src/ui/hooks/useAuthCommand.d.ts +2 -1
  129. package/dist/src/ui/hooks/useAuthCommand.js +22 -2
  130. package/dist/src/ui/hooks/useAuthCommand.js.map +1 -1
  131. package/dist/src/ui/hooks/useDialogClose.d.ts +2 -0
  132. package/dist/src/ui/hooks/useDialogClose.js +5 -0
  133. package/dist/src/ui/hooks/useDialogClose.js.map +1 -1
  134. package/dist/src/ui/hooks/useEncryptedStream.d.ts +2 -1
  135. package/dist/src/ui/hooks/useEncryptedStream.js +31 -3
  136. package/dist/src/ui/hooks/useEncryptedStream.js.map +1 -1
  137. package/dist/src/ui/hooks/useGeminiStream.d.ts +2 -1
  138. package/dist/src/ui/hooks/useGeminiStream.js +292 -22
  139. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  140. package/dist/src/ui/hooks/useInputBoxColor.d.ts +13 -0
  141. package/dist/src/ui/hooks/useInputBoxColor.js +62 -0
  142. package/dist/src/ui/hooks/useInputBoxColor.js.map +1 -0
  143. package/dist/src/ui/hooks/useMessageQueue.d.ts +7 -1
  144. package/dist/src/ui/hooks/useMessageQueue.js +9 -3
  145. package/dist/src/ui/hooks/useMessageQueue.js.map +1 -1
  146. package/dist/src/ui/hooks/useSingleAgentCommand.d.ts +10 -0
  147. package/dist/src/ui/hooks/useSingleAgentCommand.js +21 -0
  148. package/dist/src/ui/hooks/useSingleAgentCommand.js.map +1 -0
  149. package/dist/src/ui/themes/ansi-light.js +1 -0
  150. package/dist/src/ui/themes/ansi-light.js.map +1 -1
  151. package/dist/src/ui/themes/ansi.js +1 -0
  152. package/dist/src/ui/themes/ansi.js.map +1 -1
  153. package/dist/src/ui/themes/atom-one-dark.js +14 -13
  154. package/dist/src/ui/themes/atom-one-dark.js.map +1 -1
  155. package/dist/src/ui/themes/ayu-light.js +14 -13
  156. package/dist/src/ui/themes/ayu-light.js.map +1 -1
  157. package/dist/src/ui/themes/ayu.js +14 -13
  158. package/dist/src/ui/themes/ayu.js.map +1 -1
  159. package/dist/src/ui/themes/blackbox-dark.js +36 -31
  160. package/dist/src/ui/themes/blackbox-dark.js.map +1 -1
  161. package/dist/src/ui/themes/blackbox-light.js +41 -39
  162. package/dist/src/ui/themes/blackbox-light.js.map +1 -1
  163. package/dist/src/ui/themes/default-light.js +20 -20
  164. package/dist/src/ui/themes/default-light.js.map +1 -1
  165. package/dist/src/ui/themes/default.js +27 -27
  166. package/dist/src/ui/themes/default.js.map +1 -1
  167. package/dist/src/ui/themes/dracula.js +14 -13
  168. package/dist/src/ui/themes/dracula.js.map +1 -1
  169. package/dist/src/ui/themes/github-dark.js +14 -13
  170. package/dist/src/ui/themes/github-dark.js.map +1 -1
  171. package/dist/src/ui/themes/github-light.js +14 -13
  172. package/dist/src/ui/themes/github-light.js.map +1 -1
  173. package/dist/src/ui/themes/googlecode.js +1 -0
  174. package/dist/src/ui/themes/googlecode.js.map +1 -1
  175. package/dist/src/ui/themes/no-color.js +2 -0
  176. package/dist/src/ui/themes/no-color.js.map +1 -1
  177. package/dist/src/ui/themes/semantic-tokens.d.ts +1 -0
  178. package/dist/src/ui/themes/semantic-tokens.js +3 -0
  179. package/dist/src/ui/themes/semantic-tokens.js.map +1 -1
  180. package/dist/src/ui/themes/shades-of-purple.js +1 -0
  181. package/dist/src/ui/themes/shades-of-purple.js.map +1 -1
  182. package/dist/src/ui/themes/theme.d.ts +2 -0
  183. package/dist/src/ui/themes/theme.js +35 -28
  184. package/dist/src/ui/themes/theme.js.map +1 -1
  185. package/dist/src/ui/themes/xcode.js +1 -0
  186. package/dist/src/ui/themes/xcode.js.map +1 -1
  187. package/dist/src/ui/types.d.ts +5 -0
  188. package/dist/src/ui/utils/agentConversationUtils.d.ts +28 -0
  189. package/dist/src/ui/utils/agentConversationUtils.js +71 -0
  190. package/dist/src/ui/utils/agentConversationUtils.js.map +1 -0
  191. package/dist/src/ui/utils/colorBlend.d.ts +33 -0
  192. package/dist/src/ui/utils/colorBlend.js +61 -0
  193. package/dist/src/ui/utils/colorBlend.js.map +1 -0
  194. package/dist/src/ui/utils/commandUtils.d.ts +2 -1
  195. package/dist/src/ui/utils/commandUtils.js +14 -1
  196. package/dist/src/ui/utils/commandUtils.js.map +1 -1
  197. package/dist/src/ui/utils/commandUtils.test.js +25 -0
  198. package/dist/src/ui/utils/commandUtils.test.js.map +1 -1
  199. package/dist/src/ui/utils/terminalBackgroundDetector.d.ts +35 -0
  200. package/dist/src/ui/utils/terminalBackgroundDetector.js +200 -0
  201. package/dist/src/ui/utils/terminalBackgroundDetector.js.map +1 -0
  202. package/dist/src/utils/agentCommandBuilder.d.ts +48 -0
  203. package/dist/src/utils/agentCommandBuilder.js +328 -0
  204. package/dist/src/utils/agentCommandBuilder.js.map +1 -0
  205. package/dist/src/utils/apiKeyUtils.d.ts +47 -0
  206. package/dist/src/utils/apiKeyUtils.js +177 -0
  207. package/dist/src/utils/apiKeyUtils.js.map +1 -0
  208. package/dist/src/utils/backgroundUpdateCheck.js +104 -7
  209. package/dist/src/utils/backgroundUpdateCheck.js.map +1 -1
  210. package/dist/src/utils/memoryManager.d.ts +85 -0
  211. package/dist/src/utils/memoryManager.js +258 -0
  212. package/dist/src/utils/memoryManager.js.map +1 -0
  213. package/dist/src/utils/memoryMonitor.d.ts +89 -0
  214. package/dist/src/utils/memoryMonitor.js +238 -0
  215. package/dist/src/utils/memoryMonitor.js.map +1 -0
  216. package/dist/src/utils/memoryWrapper.d.ts +18 -0
  217. package/dist/src/utils/memoryWrapper.js +62 -0
  218. package/dist/src/utils/memoryWrapper.js.map +1 -0
  219. package/dist/src/utils/preLaunchUpdateCheck.js +17 -7
  220. package/dist/src/utils/preLaunchUpdateCheck.js.map +1 -1
  221. package/dist/src/utils/shellShortcut.d.ts +45 -0
  222. package/dist/src/utils/shellShortcut.js +221 -0
  223. package/dist/src/utils/shellShortcut.js.map +1 -0
  224. package/dist/src/utils/soundNotification.d.ts +15 -0
  225. package/dist/src/utils/soundNotification.js +138 -0
  226. package/dist/src/utils/soundNotification.js.map +1 -0
  227. package/dist/src/utils/userStartupWarnings.js +0 -20
  228. package/dist/src/utils/userStartupWarnings.js.map +1 -1
  229. package/dist/src/utils/versionStorage.d.ts +20 -0
  230. package/dist/src/utils/versionStorage.js +62 -0
  231. package/dist/src/utils/versionStorage.js.map +1 -1
  232. package/dist/tsconfig.tsbuildinfo +1 -1
  233. package/package.json +2 -2
@@ -24,6 +24,7 @@ import { useDialogCloseAnimation } from './hooks/useDialogCloseAnimation.js';
24
24
  import { useSlashCommandProcessor } from './hooks/slashCommandProcessor.js';
25
25
  import { useSubagentCreateDialog } from './hooks/useSubagentCreateDialog.js';
26
26
  import { useAgentsManagerDialog } from './hooks/useAgentsManagerDialog.js';
27
+ import { useSingleAgentCommand } from './hooks/useSingleAgentCommand.js';
27
28
  import { useMultiAgentConfigCommand } from './hooks/useMultiAgentConfigCommand.js';
28
29
  import { useAutoAcceptIndicator } from './hooks/useAutoAcceptIndicator.js';
29
30
  import { useMessageQueue } from './hooks/useMessageQueue.js';
@@ -42,7 +43,6 @@ import { BlackboxOAuthProgress } from './components/BlackboxOAuthProgress.js';
42
43
  import { EditorSettingsDialog } from './components/EditorSettingsDialog.js';
43
44
  import { FolderTrustDialog } from './components/FolderTrustDialog.js';
44
45
  import { ShellConfirmationDialog } from './components/ShellConfirmationDialog.js';
45
- import { QuitConfirmationDialog } from './components/QuitConfirmationDialog.js';
46
46
  import { RadioButtonSelect } from './components/shared/RadioButtonSelect.js';
47
47
  import { ModelSelectionDialog } from './components/ModelSelectionDialog.js';
48
48
  import { ModelSwitchDialog, } from './components/ModelSwitchDialog.js';
@@ -50,19 +50,20 @@ import { getOpenAIAvailableModelFromEnv, getFilteredBlackboxModels, } from './mo
50
50
  import { HistoryBrowserDialog } from './components/HistoryBrowserDialog.js';
51
51
  import { processVisionSwitchOutcome } from './hooks/useVisionAutoSwitch.js';
52
52
  import { AgentCreationWizard, AgentsManagerDialog, } from './components/subagents/index.js';
53
+ import { SingleAgentDialog } from './components/agents/SingleAgentDialog.js';
53
54
  import { MultiAgentConfigDialog } from './components/multiagent/MultiAgentConfigDialog.js';
54
55
  import { Colors } from './colors.js';
56
+ import { convertHistoryToConversationString } from './utils/agentConversationUtils.js';
55
57
  import { loadHierarchicalGeminiMemory } from '../config/config.js';
56
- import { SettingScope } from '../config/settings.js';
58
+ import { SettingScope, settingsChangeEmitter } from '../config/settings.js';
57
59
  import { Tips } from './components/Tips.js';
58
60
  import { ConsolePatcher } from './utils/ConsolePatcher.js';
59
61
  import { registerCleanup } from '../utils/cleanup.js';
60
62
  import { DetailedMessagesDisplay } from './components/DetailedMessagesDisplay.js';
61
63
  import { HistoryItemDisplay } from './components/HistoryItemDisplay.js';
62
- import { ContextSummaryDisplay } from './components/ContextSummaryDisplay.js';
63
64
  import { useHistory } from './hooks/useHistoryManager.js';
64
65
  import process from 'node:process';
65
- import { ApprovalMode, getAllGeminiMdFilenames, isEditorAvailable, getErrorMessage, AuthType, logFlashFallback, FlashFallbackEvent, ideContext, isProQuotaExceededError, isGenericQuotaExceededError, UserTierId, } from '@blackbox_ai/blackbox-cli-core';
66
+ import { ApprovalMode, isEditorAvailable, getErrorMessage, AuthType, logFlashFallback, FlashFallbackEvent, ideContext, isProQuotaExceededError, isGenericQuotaExceededError, UserTierId, } from '@blackbox_ai/blackbox-cli-core';
66
67
  import { validateAuthMethod } from '../config/auth.js';
67
68
  import { useLogger } from './hooks/useLogger.js';
68
69
  import { StreamingContext } from './contexts/StreamingContext.js';
@@ -95,6 +96,8 @@ import { WelcomeBackDialog } from './components/WelcomeBackDialog.js';
95
96
  import { TodoListDialog } from './components/TodoListDialog.js';
96
97
  import { ConfigureUI } from '../commands/configure/ConfigureUI.js';
97
98
  import { VoiceConfigDialog } from './components/VoiceConfigDialog.js';
99
+ import { QuitConfirmationDialog } from './components/QuitConfirmationDialog.js';
100
+ import { AgentSetDialog } from './components/AgentSetDialog.js';
98
101
  // Maximum number of queued messages to display in UI to prevent performance issues
99
102
  const MAX_DISPLAYED_QUEUED_MESSAGES = 3;
100
103
  function isToolExecuting(pendingHistoryItems) {
@@ -130,6 +133,7 @@ const ShellConfirmationDialogWithAnimation = ({ request }) => {
130
133
  return (_jsx(ShellConfirmationDialog, { request: wrappedRequest, isClosing: shellDialogAnimation.isClosing }));
131
134
  };
132
135
  const App = ({ config, settings, startupWarnings = [], version, initialSlashCommand, }) => {
136
+ const [_geminiMdFileCount, setGeminiMdFileCount] = useState(0);
133
137
  const isFocused = useFocus();
134
138
  useBracketedPaste();
135
139
  const [updateInfo, _setUpdateInfo] = useState(null);
@@ -149,7 +153,7 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
149
153
  consolePatcher.patch();
150
154
  registerCleanup(consolePatcher.cleanup);
151
155
  }, [handleNewMessage, config]);
152
- const { stats: sessionStats, initializeProgressiveAutoSave, setProgressiveAutoSaveEnabled } = useSessionStats();
156
+ const { stats: sessionStats, initializeProgressiveAutoSave, setProgressiveAutoSaveEnabled, setAgentRoutingDisabled, setActiveAgentSession } = useSessionStats();
153
157
  // Initialize progressive auto-save service
154
158
  useEffect(() => {
155
159
  const progressiveAutoSaveEnabled = settings.merged.general?.progressiveAutoSave?.enabled ?? true;
@@ -160,13 +164,29 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
160
164
  const enabled = settings.merged.general?.progressiveAutoSave?.enabled ?? true;
161
165
  setProgressiveAutoSaveEnabled(enabled);
162
166
  }, [settings.merged.general?.progressiveAutoSave?.enabled, setProgressiveAutoSaveEnabled]);
167
+ // Initialize agent routing disabled state from settings on startup
168
+ useEffect(() => {
169
+ const routingDisabled = settings.merged.singleAgent?.routingDisabled ?? false;
170
+ setAgentRoutingDisabled(routingDisabled);
171
+ }, [settings.merged.singleAgent?.routingDisabled, setAgentRoutingDisabled, settings.merged.singleAgent]);
172
+ // Watch for changes in singleAgent settings and update local state for Footer
173
+ useEffect(() => {
174
+ const selectedAgent = settings.merged.singleAgent?.selectedAgent;
175
+ setSelectedAgentForFooter(selectedAgent);
176
+ // Get model from per-agent configuration
177
+ const agentModel = selectedAgent
178
+ ? settings.merged.singleAgent?.agents?.[selectedAgent]?.model
179
+ : undefined;
180
+ setAgentModelForFooter(agentModel);
181
+ }, [settings.merged.singleAgent?.selectedAgent, settings.merged.singleAgent?.agents]);
163
182
  const [staticNeedsRefresh, setStaticNeedsRefresh] = useState(false);
164
183
  const [staticKey, setStaticKey] = useState(0);
165
184
  const refreshStatic = useCallback(() => {
166
- stdout.write(ansiEscapes.clearTerminal);
185
+ // Use cursor repositioning + erase instead of full terminal clear to reduce flickering
186
+ // This moves cursor to top-left and clears from there, which is less jarring than clearTerminal
187
+ stdout.write(ansiEscapes.cursorTo(0, 0) + ansiEscapes.eraseDown);
167
188
  setStaticKey((prev) => prev + 1);
168
189
  }, [setStaticKey, stdout]);
169
- const [geminiMdFileCount, setGeminiMdFileCount] = useState(0);
170
190
  const [debugMessage, setDebugMessage] = useState('');
171
191
  const [themeError, setThemeError] = useState(null);
172
192
  const [authError, setAuthError] = useState(null);
@@ -192,6 +212,80 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
192
212
  const [isProcessing, setIsProcessing] = useState(false);
193
213
  const [showTodoListDialog, setShowTodoListDialog] = useState(false);
194
214
  const [isVoiceConfigDialogOpen, setIsVoiceConfigDialogOpen] = useState(false);
215
+ const [showHelpTip, setShowHelpTip] = useState(true);
216
+ // Hide help tip if history is loaded (e.g., from previous session)
217
+ useEffect(() => {
218
+ if (history.length > 0) {
219
+ setShowHelpTip(false);
220
+ }
221
+ }, [history.length]);
222
+ const [isAgentSetDialogOpen, setIsAgentSetDialogOpen] = useState(false);
223
+ const [voiceRecordingActive, setVoiceRecordingActive] = useState(false);
224
+ // State for agent configuration to ensure Footer updates when /agent model changes
225
+ const [selectedAgentForFooter, setSelectedAgentForFooter] = useState(settings.merged.singleAgent?.selectedAgent);
226
+ const [agentModelForFooter, setAgentModelForFooter] = useState(() => {
227
+ const selectedAgent = settings.merged.singleAgent?.selectedAgent;
228
+ return selectedAgent
229
+ ? settings.merged.singleAgent?.agents?.[selectedAgent]?.model
230
+ : undefined;
231
+ });
232
+ // Listen for settings changes via event emitter to ensure UI reactivity
233
+ useEffect(() => {
234
+ const handleSettingsChange = (changeData) => {
235
+ // If singleAgent settings changed, update local state immediately
236
+ if (changeData.key === 'singleAgent') {
237
+ const singleAgentConfig = changeData.merged.singleAgent;
238
+ // Check if we should preserve the active session
239
+ // Only clear the session if the agent type changed, not just the model
240
+ const currentSession = sessionStats.activeAgentSession;
241
+ const newSelectedAgent = singleAgentConfig?.selectedAgent;
242
+ // Sync conversation history from UI history when switching agents
243
+ // This ensures CLI agents (claude, codex, gemini) have access to the full conversation
244
+ // that was built up while using blackbox (which uses Gemini client internal history)
245
+ const syncedConversationHistory = convertHistoryToConversationString(history);
246
+ // Get the model for the new agent
247
+ const newModel = newSelectedAgent
248
+ ? singleAgentConfig?.agents?.[newSelectedAgent]?.model
249
+ : currentSession?.model;
250
+ // Always preserve conversation history when switching agents
251
+ // History should only be cleared via /agent exit command
252
+ if (currentSession) {
253
+ // Update the existing session with new agent type, model, and synced conversation history
254
+ setActiveAgentSession({
255
+ ...currentSession,
256
+ agentType: newSelectedAgent,
257
+ model: newModel || currentSession.model,
258
+ conversationHistory: syncedConversationHistory,
259
+ });
260
+ }
261
+ else if (history.length > 0 && newSelectedAgent) {
262
+ // No current session but there's UI history - create a new session with synced history
263
+ // This handles the case where user was using blackbox (direct Gemini client) and switches to another agent
264
+ setActiveAgentSession({
265
+ agentType: newSelectedAgent,
266
+ model: newModel || 'default',
267
+ task: '', // No specific task, just continuing conversation
268
+ conversationHistory: syncedConversationHistory,
269
+ });
270
+ }
271
+ // If no current session and no history, don't set one - let the user start a new conversation
272
+ const selectedAgent = singleAgentConfig?.selectedAgent;
273
+ setSelectedAgentForFooter(selectedAgent);
274
+ // Get model from per-agent configuration
275
+ const agentModel = selectedAgent
276
+ ? singleAgentConfig?.agents?.[selectedAgent]?.model
277
+ : undefined;
278
+ setAgentModelForFooter(agentModel);
279
+ // Also update agent routing disabled state
280
+ const routingDisabled = singleAgentConfig?.routingDisabled ?? false;
281
+ setAgentRoutingDisabled(routingDisabled);
282
+ }
283
+ };
284
+ settingsChangeEmitter.on('settingsChanged', handleSettingsChange);
285
+ return () => {
286
+ settingsChangeEmitter.off('settingsChanged', handleSettingsChange);
287
+ };
288
+ }, [setAgentRoutingDisabled, setActiveAgentSession, sessionStats.activeAgentSession, history]);
195
289
  // Releases dialog hook
196
290
  const { isReleasesDialogOpen, openReleasesDialog, closeReleasesDialog, releasesVersions, isReleasesLoading, } = useReleasesDialog();
197
291
  const { showWorkspaceMigrationDialog, workspaceExtensions, onWorkspaceMigrationDialogOpen, onWorkspaceMigrationDialogClose, } = useWorkspaceMigration(settings);
@@ -350,8 +444,63 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
350
444
  const closeVoiceConfigDialog = useCallback(() => {
351
445
  voiceConfigDialogAnimation.startClosing();
352
446
  }, [voiceConfigDialogAnimation]);
447
+ // Agent set dialog handlers
448
+ const openAgentSetDialog = useCallback(() => {
449
+ setIsAgentSetDialogOpen(true);
450
+ }, []);
451
+ const closeAgentSetDialogOriginal = useCallback(() => {
452
+ setIsAgentSetDialogOpen(false);
453
+ }, []);
454
+ // Animation hook for agent set dialog
455
+ const agentSetDialogAnimation = useDialogCloseAnimation(() => {
456
+ closeAgentSetDialogOriginal();
457
+ });
458
+ const closeAgentSetDialog = useCallback(() => {
459
+ agentSetDialogAnimation.startClosing();
460
+ }, [agentSetDialogAnimation]);
461
+ const handleAgentSelected = useCallback((agentType, model) => {
462
+ // Get the stored model for this agent, or use default
463
+ const currentAgentConfigs = settings.merged?.singleAgent?.agents || {};
464
+ const storedModel = currentAgentConfigs[agentType]?.model;
465
+ const finalModel = storedModel || model;
466
+ // Update UI state immediately for instant visual feedback
467
+ setSelectedAgentForFooter(agentType);
468
+ setAgentModelForFooter(finalModel);
469
+ // Defer ALL operations to avoid blocking the dialog close
470
+ setTimeout(() => {
471
+ // Show success message
472
+ addItem({
473
+ type: MessageType.INFO,
474
+ text: `✓ Switched to ${agentType.charAt(0).toUpperCase() + agentType.slice(1)} agent with model: ${finalModel}`,
475
+ }, Date.now());
476
+ // Build the complete singleAgent config object and save it
477
+ const updatedAgentConfigs = {
478
+ ...currentAgentConfigs,
479
+ [agentType]: { model: finalModel },
480
+ };
481
+ const singleAgentConfig = {
482
+ ...settings.merged?.singleAgent,
483
+ selectedAgent: agentType,
484
+ model: finalModel,
485
+ routingDisabled: false,
486
+ agents: updatedAgentConfigs,
487
+ };
488
+ // Save settings in the background - this will trigger the settings change event
489
+ // which will update other parts of the UI that depend on settings
490
+ settings.setValue(SettingScope.User, 'singleAgent', singleAgentConfig);
491
+ }, 0);
492
+ }, [addItem, settings, setSelectedAgentForFooter, setAgentModelForFooter]);
353
493
  const { isSubagentCreateDialogOpen, openSubagentCreateDialog, closeSubagentCreateDialog, } = useSubagentCreateDialog();
354
494
  const { isAgentsManagerDialogOpen, openAgentsManagerDialog, closeAgentsManagerDialog, } = useAgentsManagerDialog();
495
+ const { isSingleAgentDialogOpen, openSingleAgentDialog, closeSingleAgentDialog, } = useSingleAgentCommand();
496
+ // State for single agent model-only dialog
497
+ const [isSingleAgentModelDialogOpen, setIsSingleAgentModelDialogOpen] = useState(false);
498
+ const openSingleAgentModelDialog = useCallback(() => {
499
+ setIsSingleAgentModelDialogOpen(true);
500
+ }, []);
501
+ const closeSingleAgentModelDialog = useCallback(() => {
502
+ setIsSingleAgentModelDialogOpen(false);
503
+ }, []);
355
504
  const { isMultiAgentConfigDialogOpen, openMultiAgentConfigDialog, closeMultiAgentConfigDialog, } = useMultiAgentConfigCommand();
356
505
  const { isFolderTrustDialogOpen, handleFolderTrustSelect, isRestarting } = useFolderTrust(settings, setIsTrustedFolder);
357
506
  const { showQuitConfirmation, handleQuitConfirmationSelect } = useQuitConfirmation();
@@ -365,7 +514,7 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
365
514
  if (reloadCommandsRef.current) {
366
515
  reloadCommandsRef.current();
367
516
  }
368
- });
517
+ }, setActiveAgentSession);
369
518
  // Animation hook for auth dialog
370
519
  const authDialogAnimation = useDialogCloseAnimation(() => {
371
520
  // Close handler - for ESC case
@@ -542,6 +691,29 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
542
691
  const isNarrow = isNarrowWidth(terminalWidth);
543
692
  const { stdin, setRawMode } = useStdin();
544
693
  const isInitialMount = useRef(true);
694
+ // Clear stdin on mount to prevent background detection leaks
695
+ useEffect(() => {
696
+ if (!process.stdin.isTTY) {
697
+ return;
698
+ }
699
+ // Drain any buffered data from stdin that might have leaked from background detection
700
+ const drainStdin = () => {
701
+ try {
702
+ // Read and discard any buffered data
703
+ while (process.stdin.read() !== null) {
704
+ // Keep reading until buffer is empty
705
+ }
706
+ }
707
+ catch (_err) {
708
+ // Ignore errors
709
+ }
710
+ };
711
+ // Drain immediately on mount
712
+ drainStdin();
713
+ // Also drain after a short delay to catch any late-arriving data
714
+ const timeoutId = setTimeout(drainStdin, 50);
715
+ return () => clearTimeout(timeoutId);
716
+ }, []);
545
717
  const widthFraction = 0.9;
546
718
  const inputWidth = Math.max(20, Math.floor(terminalWidth * widthFraction) - 3);
547
719
  const suggestionsWidth = Math.max(20, Math.floor(terminalWidth * 0.8));
@@ -559,7 +731,7 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
559
731
  const isValidEditor = isEditorAvailable(editorType);
560
732
  if (!isValidEditor) {
561
733
  openEditorDialog();
562
- return;
734
+ return undefined;
563
735
  }
564
736
  return editorType;
565
737
  }, [settings, openEditorDialog]);
@@ -674,7 +846,7 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
674
846
  }, [isModelSelectionDialogOpen, getAvailableModelsForCurrentAuth]);
675
847
  // Core hooks and processors
676
848
  const { vimEnabled: vimModeEnabled, vimMode, toggleVimEnabled, } = useVimMode();
677
- const { handleSlashCommand, slashCommands, pendingHistoryItems: pendingSlashCommandHistoryItems, commandContext, shellConfirmationRequest, confirmationRequest, quitConfirmationRequest, } = useSlashCommandProcessor(config, settings, addItem, clearItems, loadHistory, refreshStatic, setDebugMessage, openThemeDialog, openAuthDialog, openEditorDialog, toggleCorgiMode, setQuittingMessages, openPrivacyNotice, openSettingsDialog, openExtensionsDialog, handleModelSelectionOpen, openSubagentCreateDialog, openAgentsManagerDialog, openMultiAgentConfigDialog, openHistoryBrowser, openDbConfigureDialog, toggleVimEnabled, setIsProcessing, setGeminiMdFileCount, showQuitConfirmation, openVoiceConfigDialog, openReleasesDialog);
849
+ const { handleSlashCommand, slashCommands, pendingHistoryItems: pendingSlashCommandHistoryItems, commandContext, shellConfirmationRequest, confirmationRequest, quitConfirmationRequest, } = useSlashCommandProcessor(config, settings, addItem, clearItems, loadHistory, refreshStatic, setDebugMessage, openThemeDialog, openAuthDialog, openEditorDialog, toggleCorgiMode, setQuittingMessages, openPrivacyNotice, openSettingsDialog, openExtensionsDialog, handleModelSelectionOpen, openSubagentCreateDialog, openAgentsManagerDialog, openSingleAgentDialog, openSingleAgentModelDialog, openMultiAgentConfigDialog, openHistoryBrowser, openDbConfigureDialog, toggleVimEnabled, setIsProcessing, setGeminiMdFileCount, showQuitConfirmation, openVoiceConfigDialog, openReleasesDialog, openAgentSetDialog, setVoiceRecordingActive);
678
850
  // Set the reloadCommands ref when commandContext is available
679
851
  useEffect(() => {
680
852
  if (commandContext?.ui?.reloadCommands) {
@@ -692,7 +864,7 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
692
864
  const [userMessages, setUserMessages] = useState([]);
693
865
  // Stable reference for cancel handler to avoid circular dependency
694
866
  const cancelHandlerRef = useRef(() => { });
695
- const { streamingState, submitQuery, initError, pendingHistoryItems: pendingGeminiHistoryItems, thought, cancelOngoingRequest, isEncryptedMode: _isEncryptedMode, } = useAdaptiveStream(config, history, addItem, setDebugMessage, () => cancelHandlerRef.current(), {
867
+ const { streamingState, submitQuery, initError, pendingHistoryItems: pendingGeminiHistoryItems, thought, cancelOngoingRequest, isEncryptedMode: _isEncryptedMode, } = useAdaptiveStream(config, history, addItem, setDebugMessage, () => cancelHandlerRef.current(), settings, {
696
868
  handleSlashCommand,
697
869
  shellModeActive,
698
870
  getPreferredEditor,
@@ -740,6 +912,8 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
740
912
  closeExtensionsDialog,
741
913
  isVoiceConfigDialogOpen,
742
914
  closeVoiceConfigDialog,
915
+ isAgentSetDialogOpen,
916
+ closeAgentSetDialog,
743
917
  isReleasesDialogOpen,
744
918
  closeReleasesDialog,
745
919
  isFolderTrustDialogOpen,
@@ -784,8 +958,12 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
784
958
  ]);
785
959
  // Input handling - queue messages for processing
786
960
  const handleFinalSubmit = useCallback((submittedValue) => {
961
+ // Hide help tip on first command submission
962
+ if (showHelpTip) {
963
+ setShowHelpTip(false);
964
+ }
787
965
  addMessage(submittedValue);
788
- }, [addMessage]);
966
+ }, [addMessage, showHelpTip]);
789
967
  const { handleInput: vimHandleInput } = useVim(buffer, handleFinalSubmit);
790
968
  const { todos, currentTodo, nextTodo } = useTodoList(config.getSessionId());
791
969
  const { elapsedTime, currentLoadingPhrase } = useLoadingIndicator(streamingState, currentTodo?.content || null);
@@ -865,6 +1043,10 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
865
1043
  if (isAuthenticating) {
866
1044
  return;
867
1045
  }
1046
+ // When voice recording is active, let the voice command handle Ctrl+C.
1047
+ if (voiceRecordingActive) {
1048
+ return;
1049
+ }
868
1050
  handleExit(ctrlCPressedOnce, setCtrlCPressedOnce, ctrlCTimerRef);
869
1051
  }
870
1052
  else if (keyMatchers[Command.EXIT](key)) {
@@ -895,6 +1077,7 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
895
1077
  ctrlDTimerRef,
896
1078
  handleSlashCommand,
897
1079
  isAuthenticating,
1080
+ voiceRecordingActive,
898
1081
  settings.merged.general?.debugKeystrokeLogging,
899
1082
  ]);
900
1083
  useKeypress(handleGlobalKeypress, {
@@ -943,7 +1126,7 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
943
1126
  const handleClearScreen = useCallback(() => {
944
1127
  clearItems();
945
1128
  clearConsoleMessagesState();
946
- console.clear();
1129
+ // Removed redundant console.clear() - refreshStatic() already handles screen clearing
947
1130
  refreshStatic();
948
1131
  }, [clearItems, clearConsoleMessagesState, refreshStatic]);
949
1132
  const mainControlsRef = useRef(null);
@@ -984,13 +1167,6 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
984
1167
  return consoleMessages.filter((msg) => msg.type !== 'debug');
985
1168
  }, [consoleMessages, config]);
986
1169
  const branchName = useGitBranchName(config.getTargetDir());
987
- const contextFileNames = useMemo(() => {
988
- const fromSettings = settings.merged.context?.fileName;
989
- if (fromSettings) {
990
- return Array.isArray(fromSettings) ? fromSettings : [fromSettings];
991
- }
992
- return getAllGeminiMdFilenames();
993
- }, [settings.merged.context?.fileName]);
994
1170
  const initialPrompt = useMemo(() => config.getQuestion(), [config]);
995
1171
  const geminiClient = config.getGeminiClient();
996
1172
  useEffect(() => {
@@ -1067,7 +1243,7 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
1067
1243
  const placeholder = vimModeEnabled
1068
1244
  ? " Press 'i' for INSERT mode and 'Esc' for NORMAL mode."
1069
1245
  : ' Type your message or @path/to/file';
1070
- return (_jsx(StreamingContext.Provider, { value: streamingState, children: _jsxs(Box, { flexDirection: "column", width: "90%", children: [_jsx(Static, { items: [
1246
+ return (_jsx(StreamingContext.Provider, { value: streamingState, children: _jsxs(Box, { flexDirection: "column", width: "99%", children: [_jsx(Static, { items: [
1071
1247
  _jsxs(Box, { flexDirection: "column", children: [!(settings.merged.ui?.hideBanner || config.getScreenReader()) && _jsx(Header, { version: version, nightly: nightly }), !(settings.merged.ui?.hideTips || config.getScreenReader()) && (_jsx(Tips, { config: config }))] }, "header"),
1072
1248
  ...history.map((h) => (_jsx(HistoryItemDisplay, { terminalWidth: mainAreaWidth, availableTerminalHeight: staticAreaMaxItemHeight, item: h, isPending: false, config: config, commands: slashCommands }, h.id))),
1073
1249
  ], children: (item) => item }, staticKey), _jsx(OverflowProvider, { children: _jsxs(Box, { ref: pendingHistoryItemRef, flexDirection: "column", children: [pendingHistoryItems.map((item) => (_jsx(HistoryItemDisplay, { availableTerminalHeight: constrainHeight ? availableTerminalHeight : undefined, terminalWidth: mainAreaWidth, item: item, isPending: true, config: config, isFocused: !isEditorDialogOpen }, item.id))), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) }), _jsxs(Box, { flexDirection: "column", ref: mainControlsRef, children: [updateInfo && _jsx(UpdateNotification, { message: updateInfo.message }), startupWarnings.length > 0 && (_jsx(Box, { borderStyle: "round", borderColor: Colors.Gray, paddingX: 1, marginY: 1, flexDirection: "column", children: startupWarnings.map((warning, index) => (_jsx(Text, { color: Colors.Gray, children: warning }, index))) })), showWelcomeBackDialog && welcomeBackInfo?.hasHistory && (_jsx(WelcomeBackDialog, { welcomeBackInfo: welcomeBackInfo, onSelect: handleWelcomeBackSelection, onClose: handleWelcomeBackClose })), showWorkspaceMigrationDialog ? (_jsx(WorkspaceMigrationDialog, { workspaceExtensions: workspaceExtensions, onOpen: onWorkspaceMigrationDialogOpen, onClose: onWorkspaceMigrationDialogClose })) : isFolderTrustDialogOpen ? (_jsx(FolderTrustDialog, { onSelect: handleFolderTrustSelect, isRestarting: isRestarting })) : quitConfirmationRequest ? (_jsx(QuitConfirmationDialog, { onSelect: (choice) => {
@@ -1087,7 +1263,97 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
1087
1263
  ? terminalHeight - staticExtraHeight
1088
1264
  : undefined, terminalWidth: mainAreaWidth, isClosing: themeDialogAnimation.isClosing })] })) : isReleasesDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(ReleaseNotesDialog, { versions: releasesVersions, onClose: closeReleasesDialog, availableTerminalHeight: constrainHeight
1089
1265
  ? terminalHeight - staticExtraHeight
1090
- : undefined, terminalWidth: mainAreaWidth, isLoading: isReleasesLoading }) })) : isSettingsDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(SettingsDialog, { settings: settings, onSelect: () => closeSettingsDialog(), onRestartRequest: () => process.exit(0), isClosing: settingsDialogAnimation.isClosing }) })) : isExtensionsDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(ExtensionsDialog, { onClose: () => closeExtensionsDialog() }) })) : isSubagentCreateDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(AgentCreationWizard, { onClose: closeSubagentCreateDialog, config: config }) })) : isAgentsManagerDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(AgentsManagerDialog, { onClose: closeAgentsManagerDialog, config: config }) })) : isMultiAgentConfigDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(MultiAgentConfigDialog, { onComplete: (multiAgentConfig) => {
1266
+ : undefined, terminalWidth: mainAreaWidth, isLoading: isReleasesLoading }) })) : isSettingsDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(SettingsDialog, { settings: settings, onSelect: () => closeSettingsDialog(), onRestartRequest: () => process.exit(0), isClosing: settingsDialogAnimation.isClosing }) })) : isExtensionsDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(ExtensionsDialog, { onClose: () => closeExtensionsDialog() }) })) : isSubagentCreateDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(AgentCreationWizard, { onClose: closeSubagentCreateDialog, config: config }) })) : isAgentsManagerDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(AgentsManagerDialog, { onClose: closeAgentsManagerDialog, config: config }) })) : isSingleAgentDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(SingleAgentDialog, { onComplete: async (singleAgentConfig) => {
1267
+ // Always preserve conversation history when switching agents
1268
+ // History should only be cleared via /agent exit command
1269
+ const currentSession = sessionStats.activeAgentSession;
1270
+ if (currentSession) {
1271
+ // Update the session with new agent type and model, preserving conversation history
1272
+ setActiveAgentSession({
1273
+ ...currentSession,
1274
+ agentType: singleAgentConfig.selectedAgent,
1275
+ model: singleAgentConfig.model,
1276
+ });
1277
+ }
1278
+ // If no current session, don't set one - let the user start a new conversation
1279
+ // Get current agent configurations
1280
+ const currentAgentConfigs = settings.merged.singleAgent?.agents || {};
1281
+ // Update the model for the specific agent in the agents config
1282
+ const updatedAgentConfigs = {
1283
+ ...currentAgentConfigs,
1284
+ [singleAgentConfig.selectedAgent]: {
1285
+ model: singleAgentConfig.model,
1286
+ },
1287
+ };
1288
+ // Save configuration to settings with per-agent model storage
1289
+ settings.setValue(SettingScope.User, 'singleAgent', {
1290
+ selectedAgent: singleAgentConfig.selectedAgent,
1291
+ agents: updatedAgentConfigs,
1292
+ routingDisabled: false,
1293
+ });
1294
+ // Re-enable agent routing when configuration is saved
1295
+ setAgentRoutingDisabled(false);
1296
+ // Update local state to trigger Footer re-render
1297
+ setSelectedAgentForFooter(singleAgentConfig.selectedAgent);
1298
+ setAgentModelForFooter(singleAgentConfig.model);
1299
+ closeSingleAgentDialog();
1300
+ // Show appropriate message based on whether history was preserved
1301
+ const historyPreserved = currentSession && currentSession.agentType === singleAgentConfig.selectedAgent;
1302
+ addItem({
1303
+ type: MessageType.INFO,
1304
+ text: historyPreserved
1305
+ ? `Agent model changed to ${singleAgentConfig.model} for ${singleAgentConfig.selectedAgent}. Conversation context preserved.`
1306
+ : `Agent configuration saved: ${singleAgentConfig.selectedAgent} with model ${singleAgentConfig.model}. You're now working with ${singleAgentConfig.selectedAgent}.`,
1307
+ }, Date.now());
1308
+ }, onCancel: closeSingleAgentDialog, initialConfig: {
1309
+ selectedAgent: (settings.merged.singleAgent?.selectedAgent === 'blackbox' ? 'claude' : settings.merged.singleAgent?.selectedAgent || 'claude'),
1310
+ model: settings.merged.singleAgent?.agents?.[settings.merged.singleAgent?.selectedAgent === 'blackbox' ? 'claude' : settings.merged.singleAgent?.selectedAgent || 'claude']?.model ||
1311
+ settings.merged.singleAgent?.model ||
1312
+ 'blackboxai/anthropic/claude-sonnet-4.5',
1313
+ }, settings: settings }) })) : isSingleAgentModelDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(SingleAgentDialog, { onComplete: async (singleAgentConfig) => {
1314
+ // Always preserve conversation history when switching agents
1315
+ // History should only be cleared via /agent exit command
1316
+ const currentSession = sessionStats.activeAgentSession;
1317
+ if (currentSession) {
1318
+ // Update the session with new agent type and model, preserving conversation history
1319
+ setActiveAgentSession({
1320
+ ...currentSession,
1321
+ agentType: singleAgentConfig.selectedAgent,
1322
+ model: singleAgentConfig.model,
1323
+ });
1324
+ }
1325
+ // If no current session, don't set one - let the user start a new conversation
1326
+ // Get current agent configurations
1327
+ const currentAgentConfigs = settings.merged.singleAgent?.agents || {};
1328
+ // Update the model for the specific agent in the agents config
1329
+ const updatedAgentConfigs = {
1330
+ ...currentAgentConfigs,
1331
+ [singleAgentConfig.selectedAgent]: {
1332
+ model: singleAgentConfig.model,
1333
+ },
1334
+ };
1335
+ // Save the updated per-agent configuration
1336
+ settings.setValue(SettingScope.User, 'singleAgent', {
1337
+ selectedAgent: singleAgentConfig.selectedAgent,
1338
+ agents: updatedAgentConfigs,
1339
+ routingDisabled: false,
1340
+ });
1341
+ // Re-enable agent routing when configuration is saved
1342
+ setAgentRoutingDisabled(false);
1343
+ // Update local state to trigger Footer re-render
1344
+ setSelectedAgentForFooter(singleAgentConfig.selectedAgent);
1345
+ setAgentModelForFooter(singleAgentConfig.model);
1346
+ closeSingleAgentModelDialog();
1347
+ addItem({
1348
+ type: MessageType.INFO,
1349
+ text: `Agent model changed to ${singleAgentConfig.model} for ${singleAgentConfig.selectedAgent}. Conversation context preserved.`,
1350
+ }, Date.now());
1351
+ }, onCancel: closeSingleAgentModelDialog, initialConfig: {
1352
+ selectedAgent: (settings.merged.singleAgent?.selectedAgent === 'blackbox' ? 'claude' : settings.merged.singleAgent?.selectedAgent || 'claude'),
1353
+ model: settings.merged.singleAgent?.agents?.[settings.merged.singleAgent?.selectedAgent === 'blackbox' ? 'claude' : settings.merged.singleAgent?.selectedAgent || 'claude']?.model ||
1354
+ settings.merged.singleAgent?.model ||
1355
+ 'blackboxai/anthropic/claude-sonnet-4.5',
1356
+ }, settings: settings, startAtModelStep: true }) })) : isMultiAgentConfigDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(MultiAgentConfigDialog, { onComplete: (multiAgentConfig) => {
1091
1357
  // Save configuration to settings
1092
1358
  settings.setValue(SettingScope.User, 'multiAgent', multiAgentConfig);
1093
1359
  closeMultiAgentConfigDialog();
@@ -1137,7 +1403,7 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
1137
1403
  } })) : (_jsx(AuthInProgress, { onTimeout: () => {
1138
1404
  // User cancelled - just return to agent without showing dialog
1139
1405
  cancelAuthentication();
1140
- } })), showErrorDetails && (_jsx(OverflowProvider, { children: _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailedMessagesDisplay, { messages: filteredConsoleMessages, maxHeight: constrainHeight ? debugConsoleMaxHeight : undefined, width: inputWidth }), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) }))] })) : isAuthDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(AuthDialog, { onSelect: handleAuthSelect, settings: settings, initialErrorMessage: authError, isClosing: authDialogAnimation.isClosing }) })) : isEditorDialogOpen ? (_jsxs(Box, { flexDirection: "column", children: [editorError && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: Colors.AccentRed, children: editorError }) })), _jsx(EditorSettingsDialog, { onSelect: handleEditorSelect, settings: settings, onExit: exitEditorDialog })] })) : isModelSelectionDialogOpen ? (_jsx(ModelSelectionDialog, { availableModels: availableModelsForDialog, currentModel: currentModel, onSelect: handleModelSelect, onCancel: handleModelSelectionClose, isClosing: modelDialogAnimation.isClosing })) : isVisionSwitchDialogOpen ? (_jsx(ModelSwitchDialog, { onSelect: handleVisionSwitchSelect })) : isVoiceConfigDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(VoiceConfigDialog, { settings: settings, onClose: closeVoiceConfigDialog, isClosing: voiceConfigDialogAnimation.isClosing, onConfigComplete: handleVoiceConfigComplete }) })) : showPrivacyNotice ? (_jsx(PrivacyNotice, { onExit: () => setShowPrivacyNotice(false), config: config })) : (_jsxs(_Fragment, { children: [showTodoListDialog && (_jsx(TodoListDialog, { todos: todos, onClose: () => setShowTodoListDialog(false) })), _jsx(LoadingIndicator, { thought: streamingState === StreamingState.WaitingForConfirmation ||
1406
+ } })), showErrorDetails && (_jsx(OverflowProvider, { children: _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailedMessagesDisplay, { messages: filteredConsoleMessages, maxHeight: constrainHeight ? debugConsoleMaxHeight : undefined, width: inputWidth }), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) }))] })) : isAuthDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(AuthDialog, { onSelect: handleAuthSelect, settings: settings, initialErrorMessage: authError, isClosing: authDialogAnimation.isClosing }) })) : isEditorDialogOpen ? (_jsxs(Box, { flexDirection: "column", children: [editorError && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: Colors.AccentRed, children: editorError }) })), _jsx(EditorSettingsDialog, { onSelect: handleEditorSelect, settings: settings, onExit: exitEditorDialog })] })) : isModelSelectionDialogOpen ? (_jsx(ModelSelectionDialog, { availableModels: availableModelsForDialog, currentModel: currentModel, onSelect: handleModelSelect, onCancel: handleModelSelectionClose, isClosing: modelDialogAnimation.isClosing })) : isVisionSwitchDialogOpen ? (_jsx(ModelSwitchDialog, { onSelect: handleVisionSwitchSelect })) : isVoiceConfigDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(VoiceConfigDialog, { settings: settings, onClose: closeVoiceConfigDialog, isClosing: voiceConfigDialogAnimation.isClosing, onConfigComplete: handleVoiceConfigComplete }) })) : isAgentSetDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(AgentSetDialog, { settings: settings, onClose: closeAgentSetDialog, onAgentSelected: handleAgentSelected }) })) : showPrivacyNotice ? (_jsx(PrivacyNotice, { onExit: () => setShowPrivacyNotice(false), config: config })) : (_jsxs(_Fragment, { children: [showTodoListDialog && (_jsx(TodoListDialog, { todos: todos, onClose: () => setShowTodoListDialog(false) })), _jsx(LoadingIndicator, { thought: streamingState === StreamingState.WaitingForConfirmation ||
1141
1407
  config.getAccessibility()?.disableLoadingPhrases ||
1142
1408
  config.getScreenReader()
1143
1409
  ? undefined
@@ -1153,9 +1419,9 @@ const App = ({ config, settings, startupWarnings = [], version, initialSlashComm
1153
1419
  return (
1154
1420
  // Ensure the Box takes full width so truncation calculates correctly
1155
1421
  _jsx(Box, { paddingLeft: 2, width: "100%", children: _jsx(Text, { dimColor: true, wrap: "truncate", children: preview }) }, index));
1156
- }), messageQueue.length > MAX_DISPLAYED_QUEUED_MESSAGES && (_jsx(Box, { paddingLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["... (+", messageQueue.length - MAX_DISPLAYED_QUEUED_MESSAGES, "more)"] }) }))] })), _jsxs(Box, { marginTop: 1, justifyContent: "space-between", width: "100%", flexDirection: isNarrow ? 'column' : 'row', alignItems: isNarrow ? 'flex-start' : 'center', children: [_jsxs(Box, { children: [process.env['GEMINI_SYSTEM_MD'] && (_jsx(Text, { color: Colors.AccentRed, children: "|\u2310\u25A0_\u25A0| " })), ctrlCPressedOnce ? (_jsx(Text, { color: Colors.AccentYellow, children: "Press Ctrl+C again to confirm exit." })) : ctrlDPressedOnce ? (_jsx(Text, { color: Colors.AccentYellow, children: "Press Ctrl+D again to exit." })) : showEscapePrompt ? (_jsx(Text, { color: Colors.Gray, children: "Press Esc again to clear." })) : (_jsx(ContextSummaryDisplay, { ideContext: ideContextState, geminiMdFileCount: geminiMdFileCount, contextFileNames: contextFileNames, mcpServers: config.getMcpServers(), blockedMcpServers: config.getBlockedMcpServers(), showToolDescriptions: showToolDescriptions }))] }), _jsxs(Box, { paddingTop: isNarrow ? 1 : 0, children: [showAutoAcceptIndicator !== ApprovalMode.DEFAULT &&
1157
- !shellModeActive && (_jsx(AutoAcceptIndicator, { approvalMode: showAutoAcceptIndicator })), shellModeActive && _jsx(ShellModeIndicator, {})] })] }), showErrorDetails && (_jsx(OverflowProvider, { children: _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailedMessagesDisplay, { messages: filteredConsoleMessages, maxHeight: constrainHeight ? debugConsoleMaxHeight : undefined, width: inputWidth }), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) })), isInputActive && (_jsx(InputPrompt, { buffer: buffer, inputWidth: inputWidth, suggestionsWidth: suggestionsWidth, onSubmit: handleFinalSubmit, userMessages: userMessages, onClearScreen: handleClearScreen, config: config, slashCommands: slashCommands, commandContext: commandContext, shellModeActive: shellModeActive, setShellModeActive: setShellModeActive, onEscapePromptChange: handleEscapePromptChange, focus: isFocused, vimHandleInput: vimHandleInput, placeholder: placeholder }))] })), initError && streamingState !== StreamingState.Responding && (_jsx(Box, { borderStyle: "round", borderColor: Colors.AccentRed, paddingX: 1, marginBottom: 1, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text ? (_jsx(Text, { color: Colors.AccentRed, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { color: Colors.AccentRed, children: ["Initialization Error: ", initError] }), _jsxs(Text, { color: Colors.AccentRed, children: [' ', "Please check API key and configuration."] })] })) })), !settings.merged.ui?.hideFooter && (_jsx(Footer, { model: currentModel, targetDir: config.getTargetDir(), debugMode: config.getDebugMode(), branchName: branchName, debugMessage: debugMessage, corgiMode: corgiMode, errorCount: errorCount, showErrorDetails: showErrorDetails, showMemoryUsage: config.getDebugMode() ||
1422
+ }), messageQueue.length > MAX_DISPLAYED_QUEUED_MESSAGES && (_jsx(Box, { paddingLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["... (+", messageQueue.length - MAX_DISPLAYED_QUEUED_MESSAGES, "more)"] }) }))] })), _jsxs(Box, { marginTop: 1, justifyContent: "space-between", width: "100%", flexDirection: isNarrow ? 'column' : 'row', alignItems: isNarrow ? 'flex-start' : 'center', children: [_jsxs(Box, { children: [process.env['GEMINI_SYSTEM_MD'] && (_jsx(Text, { color: Colors.AccentRed, children: "|\u2310\u25A0_\u25A0| " })), ctrlCPressedOnce ? (_jsx(Text, { color: Colors.AccentYellow, children: "Press Ctrl+C again to confirm exit." })) : ctrlDPressedOnce ? (_jsx(Text, { color: Colors.AccentYellow, children: "Press Ctrl+D again to exit." })) : showEscapePrompt ? (_jsx(Text, { color: Colors.Gray, children: "Press Esc again to clear." })) : null] }), _jsxs(Box, { paddingTop: isNarrow ? 1 : 0, children: [showAutoAcceptIndicator !== ApprovalMode.DEFAULT &&
1423
+ !shellModeActive && (_jsx(AutoAcceptIndicator, { approvalMode: showAutoAcceptIndicator })), shellModeActive && _jsx(ShellModeIndicator, {})] })] }), showErrorDetails && (_jsx(OverflowProvider, { children: _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailedMessagesDisplay, { messages: filteredConsoleMessages, maxHeight: constrainHeight ? debugConsoleMaxHeight : undefined, width: inputWidth }), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) })), isInputActive && (_jsx(InputPrompt, { buffer: buffer, inputWidth: inputWidth, suggestionsWidth: suggestionsWidth, onSubmit: handleFinalSubmit, userMessages: userMessages, onClearScreen: handleClearScreen, config: config, slashCommands: slashCommands, commandContext: commandContext, shellModeActive: shellModeActive, setShellModeActive: setShellModeActive, onEscapePromptChange: handleEscapePromptChange, focus: isFocused, vimHandleInput: vimHandleInput, placeholder: placeholder })), showHelpTip && isInputActive && history.length === 0 && (_jsxs(Box, { marginTop: 1, marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { color: Colors.Gray, children: "Run /help for tips" }), _jsx(Text, { color: Colors.Gray, children: "Use Ctrl+J to newline" })] }))] })), initError && streamingState !== StreamingState.Responding && (_jsx(Box, { borderStyle: "round", borderColor: Colors.AccentRed, paddingX: 1, marginBottom: 1, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text ? (_jsx(Text, { color: Colors.AccentRed, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { color: Colors.AccentRed, children: ["Initialization Error: ", initError] }), _jsxs(Text, { color: Colors.AccentRed, children: [' ', "Please check API key and configuration."] })] })) })), !settings.merged.ui?.hideFooter && (_jsx(Footer, { model: currentModel, targetDir: config.getTargetDir(), debugMode: config.getDebugMode(), branchName: branchName, debugMessage: debugMessage, corgiMode: corgiMode, errorCount: errorCount, showErrorDetails: showErrorDetails, showMemoryUsage: config.getDebugMode() ||
1158
1424
  settings.merged.ui?.showMemoryUsage ||
1159
- false, promptTokenCount: sessionStats.lastPromptTokenCount, nightly: nightly, vimMode: vimModeEnabled ? vimMode : undefined, isTrustedFolder: isTrustedFolderState }))] })] }) }));
1425
+ false, promptTokenCount: sessionStats.lastPromptTokenCount, nightly: nightly, vimMode: vimModeEnabled ? vimMode : undefined, isTrustedFolder: isTrustedFolderState, selectedAgent: selectedAgentForFooter, agentModel: agentModelForFooter, activeAgentSession: sessionStats.activeAgentSession, agentRoutingDisabled: sessionStats.agentRoutingDisabled }))] })] }) }));
1160
1426
  };
1161
1427
  //# sourceMappingURL=App.js.map