@blackbox_ai/blackbox-cli-core 0.0.9 → 0.8.1

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 (247) hide show
  1. package/README.md +11 -183
  2. package/dist/index.d.ts +2 -1
  3. package/dist/index.js +2 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/src/blackbox/blackboxOAuth2.js +17 -1
  6. package/dist/src/blackbox/blackboxOAuth2.js.map +1 -1
  7. package/dist/src/code_assist/oauth2.js +15 -3
  8. package/dist/src/code_assist/oauth2.js.map +1 -1
  9. package/dist/src/config/blackboxModels.d.ts +3 -2
  10. package/dist/src/config/blackboxModels.js +262 -33
  11. package/dist/src/config/blackboxModels.js.map +1 -1
  12. package/dist/src/config/config.d.ts +65 -0
  13. package/dist/src/config/config.js +282 -17
  14. package/dist/src/config/config.js.map +1 -1
  15. package/dist/src/config/models.d.ts +1 -1
  16. package/dist/src/config/models.js +1 -1
  17. package/dist/src/config/models.js.map +1 -1
  18. package/dist/src/config/multiAgentModels.d.ts +63 -0
  19. package/dist/src/config/multiAgentModels.js +194 -0
  20. package/dist/src/config/multiAgentModels.js.map +1 -0
  21. package/dist/src/core/client.js +8 -2
  22. package/dist/src/core/client.js.map +1 -1
  23. package/dist/src/core/contentGenerator.d.ts +1 -0
  24. package/dist/src/core/contentGenerator.js +57 -7
  25. package/dist/src/core/contentGenerator.js.map +1 -1
  26. package/dist/src/core/encryptedClientFactory.d.ts +17 -0
  27. package/dist/src/core/encryptedClientFactory.js +92 -0
  28. package/dist/src/core/encryptedClientFactory.js.map +1 -0
  29. package/dist/src/core/encryptedContentGenerator.d.ts +47 -0
  30. package/dist/src/core/encryptedContentGenerator.js +445 -0
  31. package/dist/src/core/encryptedContentGenerator.js.map +1 -0
  32. package/dist/src/core/encryptedGeminiClient.d.ts +59 -0
  33. package/dist/src/core/encryptedGeminiClient.js +177 -0
  34. package/dist/src/core/encryptedGeminiClient.js.map +1 -0
  35. package/dist/src/core/encryptedGeminiClientBridge.d.ts +107 -0
  36. package/dist/src/core/encryptedGeminiClientBridge.js +808 -0
  37. package/dist/src/core/encryptedGeminiClientBridge.js.map +1 -0
  38. package/dist/src/core/encryptedGeminiClientWrapper.d.ts +129 -0
  39. package/dist/src/core/encryptedGeminiClientWrapper.js +305 -0
  40. package/dist/src/core/encryptedGeminiClientWrapper.js.map +1 -0
  41. package/dist/src/core/encryptedTurn.d.ts +40 -0
  42. package/dist/src/core/encryptedTurn.js +114 -0
  43. package/dist/src/core/encryptedTurn.js.map +1 -0
  44. package/dist/src/core/logger.d.ts +21 -0
  45. package/dist/src/core/logger.js +110 -0
  46. package/dist/src/core/logger.js.map +1 -1
  47. package/dist/src/core/openaiContentGenerator/constants.d.ts +2 -0
  48. package/dist/src/core/openaiContentGenerator/constants.js +2 -0
  49. package/dist/src/core/openaiContentGenerator/constants.js.map +1 -1
  50. package/dist/src/core/openaiContentGenerator/converter.d.ts +16 -1
  51. package/dist/src/core/openaiContentGenerator/converter.js +135 -4
  52. package/dist/src/core/openaiContentGenerator/converter.js.map +1 -1
  53. package/dist/src/core/openaiContentGenerator/pipeline.js +6 -2
  54. package/dist/src/core/openaiContentGenerator/pipeline.js.map +1 -1
  55. package/dist/src/core/openaiContentGenerator/provider/default.js +10 -1
  56. package/dist/src/core/openaiContentGenerator/provider/default.js.map +1 -1
  57. package/dist/src/core/prompts.d.ts +17 -0
  58. package/dist/src/core/prompts.js +347 -19
  59. package/dist/src/core/prompts.js.map +1 -1
  60. package/dist/src/core/tokenLimits.d.ts +1 -0
  61. package/dist/src/core/tokenLimits.js +37 -2
  62. package/dist/src/core/tokenLimits.js.map +1 -1
  63. package/dist/src/core/tokenLimits.test.js +36 -1
  64. package/dist/src/core/tokenLimits.test.js.map +1 -1
  65. package/dist/src/encrypt/attestation.d.ts +5 -0
  66. package/dist/src/encrypt/attestation.js +100 -0
  67. package/dist/src/encrypt/attestation.js.map +1 -0
  68. package/dist/src/encrypt/client.d.ts +14 -0
  69. package/dist/src/encrypt/client.js +132 -0
  70. package/dist/src/encrypt/client.js.map +1 -0
  71. package/dist/src/encrypt/config.d.ts +22 -0
  72. package/dist/src/encrypt/config.js +43 -0
  73. package/dist/src/encrypt/config.js.map +1 -0
  74. package/dist/src/encrypt/crypto-utils.d.ts +57 -0
  75. package/dist/src/encrypt/crypto-utils.js +257 -0
  76. package/dist/src/encrypt/crypto-utils.js.map +1 -0
  77. package/dist/src/encrypt/history-manager.d.ts +43 -0
  78. package/dist/src/encrypt/history-manager.js +164 -0
  79. package/dist/src/encrypt/history-manager.js.map +1 -0
  80. package/dist/src/encrypt/minimax-template.d.ts +73 -0
  81. package/dist/src/encrypt/minimax-template.js +276 -0
  82. package/dist/src/encrypt/minimax-template.js.map +1 -0
  83. package/dist/src/encrypt/sessions.d.ts +17 -0
  84. package/dist/src/encrypt/sessions.js +221 -0
  85. package/dist/src/encrypt/sessions.js.map +1 -0
  86. package/dist/src/encrypt/streaming-client.d.ts +29 -0
  87. package/dist/src/encrypt/streaming-client.js +232 -0
  88. package/dist/src/encrypt/streaming-client.js.map +1 -0
  89. package/dist/src/encrypt/tool-formatter.d.ts +36 -0
  90. package/dist/src/encrypt/tool-formatter.js +353 -0
  91. package/dist/src/encrypt/tool-formatter.js.map +1 -0
  92. package/dist/src/encrypt/tool-parser.d.ts +93 -0
  93. package/dist/src/encrypt/tool-parser.js +567 -0
  94. package/dist/src/encrypt/tool-parser.js.map +1 -0
  95. package/dist/src/encrypt/types.d.ts +81 -0
  96. package/dist/src/encrypt/types.js +2 -0
  97. package/dist/src/encrypt/types.js.map +1 -0
  98. package/dist/src/generated/git-commit.d.ts +3 -3
  99. package/dist/src/generated/git-commit.js +3 -3
  100. package/dist/src/ide/ide-client.js +9 -19
  101. package/dist/src/ide/ide-client.js.map +1 -1
  102. package/dist/src/index.d.ts +15 -0
  103. package/dist/src/index.js +15 -0
  104. package/dist/src/index.js.map +1 -1
  105. package/dist/src/mcp/oauth-provider.js +2 -6
  106. package/dist/src/mcp/oauth-provider.js.map +1 -1
  107. package/dist/src/mcp/oauth-token-storage.d.ts +7 -0
  108. package/dist/src/mcp/oauth-token-storage.js +24 -0
  109. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  110. package/dist/src/services/EncryptedChatService.d.ts +80 -0
  111. package/dist/src/services/EncryptedChatService.js +202 -0
  112. package/dist/src/services/EncryptedChatService.js.map +1 -0
  113. package/dist/src/services/StatsHistoryService.d.ts +131 -0
  114. package/dist/src/services/StatsHistoryService.js +427 -0
  115. package/dist/src/services/StatsHistoryService.js.map +1 -0
  116. package/dist/src/services/checkpointApiService.d.ts +101 -0
  117. package/dist/src/services/checkpointApiService.js +215 -0
  118. package/dist/src/services/checkpointApiService.js.map +1 -0
  119. package/dist/src/services/environmentSanitization.d.ts +24 -0
  120. package/dist/src/services/environmentSanitization.js +152 -0
  121. package/dist/src/services/environmentSanitization.js.map +1 -0
  122. package/dist/src/telemetry/blackbox-logger/blackbox-logger.d.ts +2 -6
  123. package/dist/src/telemetry/blackbox-logger/blackbox-logger.js +29 -135
  124. package/dist/src/telemetry/blackbox-logger/blackbox-logger.js.map +1 -1
  125. package/dist/src/telemetry/blackbox-logger/blackbox-logger.test.js +1 -1
  126. package/dist/src/telemetry/blackbox-logger/blackbox-logger.test.js.map +1 -1
  127. package/dist/src/telemetry/uiTelemetry.d.ts +8 -0
  128. package/dist/src/telemetry/uiTelemetry.js +17 -0
  129. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  130. package/dist/src/tools/browser-interactive.d.ts +63 -0
  131. package/dist/src/tools/browser-interactive.js +394 -0
  132. package/dist/src/tools/browser-interactive.js.map +1 -0
  133. package/dist/src/tools/browser_use.d.ts +22 -1
  134. package/dist/src/tools/browser_use.js +403 -23
  135. package/dist/src/tools/browser_use.js.map +1 -1
  136. package/dist/src/tools/data-file-constants.d.ts +17 -0
  137. package/dist/src/tools/data-file-constants.js +30 -0
  138. package/dist/src/tools/data-file-constants.js.map +1 -0
  139. package/dist/src/tools/edit.js +44 -7
  140. package/dist/src/tools/edit.js.map +1 -1
  141. package/dist/src/tools/ls.js +40 -6
  142. package/dist/src/tools/ls.js.map +1 -1
  143. package/dist/src/tools/ls.test.js +4 -4
  144. package/dist/src/tools/ls.test.js.map +1 -1
  145. package/dist/src/tools/mcp-client-manager.d.ts +28 -2
  146. package/dist/src/tools/mcp-client-manager.js +62 -4
  147. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  148. package/dist/src/tools/mcp-client.d.ts +5 -3
  149. package/dist/src/tools/mcp-client.js +39 -11
  150. package/dist/src/tools/mcp-client.js.map +1 -1
  151. package/dist/src/tools/mcp-tool.d.ts +3 -1
  152. package/dist/src/tools/mcp-tool.js +37 -9
  153. package/dist/src/tools/mcp-tool.js.map +1 -1
  154. package/dist/src/tools/memoryTool.d.ts +14 -4
  155. package/dist/src/tools/memoryTool.js +98 -39
  156. package/dist/src/tools/memoryTool.js.map +1 -1
  157. package/dist/src/tools/read-data-file.d.ts +31 -0
  158. package/dist/src/tools/read-data-file.js +469 -0
  159. package/dist/src/tools/read-data-file.js.map +1 -0
  160. package/dist/src/tools/read-file.js +64 -5
  161. package/dist/src/tools/read-file.js.map +1 -1
  162. package/dist/src/tools/read-file.test.js +40 -6
  163. package/dist/src/tools/read-file.test.js.map +1 -1
  164. package/dist/src/tools/shell.d.ts +3 -1
  165. package/dist/src/tools/shell.js +25 -4
  166. package/dist/src/tools/shell.js.map +1 -1
  167. package/dist/src/tools/skill.d.ts +34 -0
  168. package/dist/src/tools/skill.js +143 -0
  169. package/dist/src/tools/skill.js.map +1 -0
  170. package/dist/src/tools/sql_db.d.ts +101 -0
  171. package/dist/src/tools/sql_db.js +1033 -0
  172. package/dist/src/tools/sql_db.js.map +1 -0
  173. package/dist/src/tools/sql_db_configure.d.ts +18 -0
  174. package/dist/src/tools/sql_db_configure.js +96 -0
  175. package/dist/src/tools/sql_db_configure.js.map +1 -0
  176. package/dist/src/tools/taskCompletion.d.ts +29 -0
  177. package/dist/src/tools/taskCompletion.js +231 -0
  178. package/dist/src/tools/taskCompletion.js.map +1 -0
  179. package/dist/src/tools/tool-error.d.ts +3 -1
  180. package/dist/src/tools/tool-error.js +3 -0
  181. package/dist/src/tools/tool-error.js.map +1 -1
  182. package/dist/src/tools/tool-names.d.ts +8 -0
  183. package/dist/src/tools/tool-names.js +8 -0
  184. package/dist/src/tools/tool-names.js.map +1 -1
  185. package/dist/src/tools/tool-registry.d.ts +22 -0
  186. package/dist/src/tools/tool-registry.js +41 -1
  187. package/dist/src/tools/tool-registry.js.map +1 -1
  188. package/dist/src/tools/tools.d.ts +18 -2
  189. package/dist/src/tools/tools.js +3 -0
  190. package/dist/src/tools/tools.js.map +1 -1
  191. package/dist/src/tools/web-fetch.js +24 -4
  192. package/dist/src/tools/web-fetch.js.map +1 -1
  193. package/dist/src/tools/web-search.js +160 -2
  194. package/dist/src/tools/web-search.js.map +1 -1
  195. package/dist/src/tools/workspace-error-helper.d.ts +9 -0
  196. package/dist/src/tools/workspace-error-helper.js +43 -0
  197. package/dist/src/tools/workspace-error-helper.js.map +1 -0
  198. package/dist/src/tools/workspace-error-helper.test.js +85 -0
  199. package/dist/src/tools/workspace-error-helper.test.js.map +1 -0
  200. package/dist/src/tools/write-file.js +42 -7
  201. package/dist/src/tools/write-file.js.map +1 -1
  202. package/dist/src/utils/environmentContext.js +3 -1
  203. package/dist/src/utils/environmentContext.js.map +1 -1
  204. package/dist/src/utils/environmentContext.test.js +3 -2
  205. package/dist/src/utils/environmentContext.test.js.map +1 -1
  206. package/dist/src/utils/fetch.d.ts +3 -1
  207. package/dist/src/utils/fetch.js +35 -2
  208. package/dist/src/utils/fetch.js.map +1 -1
  209. package/dist/src/utils/fileUtils.js +30 -3
  210. package/dist/src/utils/fileUtils.js.map +1 -1
  211. package/dist/src/utils/filesearch/fileSearch.d.ts +2 -0
  212. package/dist/src/utils/filesearch/fileSearch.js +38 -7
  213. package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
  214. package/dist/src/utils/git-worktree-utils.d.ts +56 -0
  215. package/dist/src/utils/git-worktree-utils.js +176 -0
  216. package/dist/src/utils/git-worktree-utils.js.map +1 -0
  217. package/dist/src/utils/imageCompression.d.ts +34 -0
  218. package/dist/src/utils/imageCompression.js +170 -0
  219. package/dist/src/utils/imageCompression.js.map +1 -0
  220. package/dist/src/utils/messageTruncator.d.ts +51 -0
  221. package/dist/src/utils/messageTruncator.js +346 -0
  222. package/dist/src/utils/messageTruncator.js.map +1 -0
  223. package/dist/src/utils/pathReader.js +26 -6
  224. package/dist/src/utils/pathReader.js.map +1 -1
  225. package/dist/src/utils/skill.d.ts +65 -0
  226. package/dist/src/utils/skill.js +241 -0
  227. package/dist/src/utils/skill.js.map +1 -0
  228. package/dist/src/utils/textCleaning.d.ts +51 -0
  229. package/dist/src/utils/textCleaning.js +327 -0
  230. package/dist/src/utils/textCleaning.js.map +1 -0
  231. package/dist/tsconfig.tsbuildinfo +1 -1
  232. package/package.json +16 -2
  233. package/dist/src/tools/mcp-client-manager.test.js +0 -39
  234. package/dist/src/tools/mcp-client-manager.test.js.map +0 -1
  235. package/dist/src/tools/mcp-client.test.d.ts +0 -6
  236. package/dist/src/tools/mcp-client.test.js +0 -454
  237. package/dist/src/tools/mcp-client.test.js.map +0 -1
  238. package/dist/src/tools/mcp-tool.test.d.ts +0 -6
  239. package/dist/src/tools/mcp-tool.test.js +0 -576
  240. package/dist/src/tools/mcp-tool.test.js.map +0 -1
  241. package/dist/src/tools/memoryTool.test.d.ts +0 -6
  242. package/dist/src/tools/memoryTool.test.js +0 -420
  243. package/dist/src/tools/memoryTool.test.js.map +0 -1
  244. package/dist/src/tools/tool-registry.test.d.ts +0 -6
  245. package/dist/src/tools/tool-registry.test.js +0 -332
  246. package/dist/src/tools/tool-registry.test.js.map +0 -1
  247. /package/dist/src/tools/{mcp-client-manager.test.d.ts → workspace-error-helper.test.d.ts} +0 -0
@@ -0,0 +1,808 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { EncryptedTurn } from './encryptedTurn.js';
7
+ import { getCoreSystemPrompt } from './prompts.js';
8
+ import { ApprovalMode } from '../config/config.js';
9
+ import { getEnvironmentContext } from '../utils/environmentContext.js';
10
+ import { formatThinkContent as formatThinkContentUtil } from '../utils/textCleaning.js';
11
+ import { MessageTruncator } from '../utils/messageTruncator.js';
12
+ /**
13
+ * EncryptedGeminiClientBridge provides a bridge between the encrypted provider
14
+ * and the existing GeminiClient interface, enabling tool execution through
15
+ * the event-based system that the UI layer expects.
16
+ */
17
+ export class EncryptedGeminiClientBridge {
18
+ encryptedService;
19
+ config;
20
+ tools = [];
21
+ history = [];
22
+ pendingToolResults = new Map();
23
+ messageTruncator;
24
+ constructor(encryptedService, config) {
25
+ this.encryptedService = encryptedService;
26
+ this.config = config;
27
+ this.messageTruncator = new MessageTruncator(config?.getDebugMode() ?? false);
28
+ }
29
+ /**
30
+ * Set the available tools for this client
31
+ */
32
+ setTools(tools) {
33
+ this.tools = tools || [];
34
+ }
35
+ /**
36
+ * Add content to history
37
+ */
38
+ addHistory(content) {
39
+ this.history.push(content);
40
+ }
41
+ /**
42
+ * Get current history
43
+ */
44
+ getHistory() {
45
+ return [...this.history];
46
+ }
47
+ /**
48
+ * Send a message and get streaming events that match the regular GeminiClient interface
49
+ * This is the key method that bridges the encrypted provider with the event system
50
+ */
51
+ async *sendMessageStream(request, signal, prompt_id) {
52
+ try {
53
+ // Convert request to text
54
+ const messageText = this.convertRequestToText(request);
55
+ // Get tools from config directly instead of relying on stored tools
56
+ let currentTools = [];
57
+ if (this.config) {
58
+ try {
59
+ const toolRegistry = this.config.getToolRegistry();
60
+ if (toolRegistry) {
61
+ currentTools = toolRegistry.getFunctionDeclarations();
62
+ // Update our stored tools to match
63
+ this.tools = currentTools;
64
+ }
65
+ }
66
+ catch (error) {
67
+ // Silently handle tool registry access errors
68
+ console.error(error);
69
+ }
70
+ }
71
+ // Create tools system prompt if we have tools
72
+ const toolsSystemPrompt = EncryptedTurn.createToolsSystemPrompt(currentTools || [], 'minimax');
73
+ // CRITICAL FIX: Add the current user message to history before converting
74
+ // This ensures the user message is included in the message history
75
+ const currentUserMessage = {
76
+ role: 'user',
77
+ parts: [{ text: messageText }]
78
+ };
79
+ // Create a temporary history that includes the current message
80
+ const historyWithCurrentMessage = [...this.history, currentUserMessage];
81
+ // Convert history to message format (including current user message)
82
+ const messageHistory = await this.convertHistoryToMessageFormat(historyWithCurrentMessage, toolsSystemPrompt);
83
+ // Initialize the encrypted service if needed
84
+ if (!this.encryptedService.isReady()) {
85
+ await this.encryptedService.initialize(toolsSystemPrompt);
86
+ }
87
+ // Send message through encrypted service
88
+ const encryptedStream = await this.encryptedService.sendMessage(messageText, messageHistory);
89
+ // Read the entire response first to detect tool calls
90
+ const reader = encryptedStream.getReader();
91
+ const decoder = new TextDecoder();
92
+ let fullResponse = '';
93
+ // BUFFERING LOGIC START
94
+ let streamBuffer = '';
95
+ let inToolCall = false;
96
+ // Safety margin: larger than the longest possible token + potential function name length
97
+ const SAFETY_MARGIN = 60;
98
+ try {
99
+ while (true) {
100
+ if (signal.aborted) {
101
+ yield { type: 'user_cancelled' };
102
+ return;
103
+ }
104
+ const { done, value } = await reader.read();
105
+ if (done)
106
+ break;
107
+ const chunk = decoder.decode(value, { stream: true });
108
+ fullResponse += chunk;
109
+ streamBuffer += chunk;
110
+ // Loop to process the buffer as much as possible
111
+ let processing = true;
112
+ while (processing) {
113
+ processing = false; // Default to stopping unless we find a token
114
+ if (inToolCall) {
115
+ // WE ARE INSIDE A TOOL CALL: Hunt for END tokens only
116
+ const endTokens = [
117
+ '</minimax:tool_call>', // MiniMax format - this is the REAL end
118
+ '<|tool▁calls▁end|>', // DeepSeek format
119
+ '</tool_call>', // Qwen format
120
+ '</think>', // Think end (can appear in tool calls)
121
+ // Bare tool call end tokens
122
+ '</read_file>',
123
+ '</read_data_file>',
124
+ '</glob>',
125
+ '</search_files>',
126
+ '</list_files>',
127
+ '</create_file>',
128
+ '</edit_file>',
129
+ '</execute_command>',
130
+ '</browser_action>',
131
+ '</ask_followup_question>',
132
+ '</attempt_completion>',
133
+ '</new_task>',
134
+ '</retrieve_knowledge>',
135
+ '</read_many_files>'
136
+ ];
137
+ let endIdx = -1;
138
+ let tokenLen = 0;
139
+ // Find earliest end token
140
+ for (const token of endTokens) {
141
+ const idx = streamBuffer.indexOf(token);
142
+ if (idx !== -1 && (endIdx === -1 || idx < endIdx)) {
143
+ endIdx = idx;
144
+ tokenLen = token.length;
145
+ }
146
+ }
147
+ if (endIdx !== -1) {
148
+ // Found end token!
149
+ // Discard everything up to the end of this token (hiding the tool call)
150
+ streamBuffer = streamBuffer.substring(endIdx + tokenLen);
151
+ inToolCall = false;
152
+ processing = true; // Loop again to see if there is text after
153
+ }
154
+ }
155
+ else {
156
+ // WE ARE IN TEXT MODE: Hunt for START tokens (or rogue END tokens)
157
+ const startTokens = [
158
+ '<minimax:tool_call>', // MiniMax format
159
+ '<invoke name=', // MiniMax invoke start
160
+ '<|tool▁calls▁begin|>', // DeepSeek format
161
+ '<|tool▁call▁begin|>', // DeepSeek format
162
+ '<tool_call>', // Qwen format
163
+ '<think>', // Think tags
164
+ ];
165
+ const sepToken = '<|tool▁sep|>'; // Special case: hides preceding word
166
+ // Also look for standalone end tokens to clean up artifacts
167
+ // CRITICAL FIX: Don't treat </invoke> as orphaned - it's part of valid tool calls
168
+ const endTokens = [
169
+ '</minimax:tool_call>', // MiniMax format
170
+ '<|tool▁call▁end|>', // DeepSeek format
171
+ '<|tool▁calls▁end|>', // DeepSeek format
172
+ '</tool_call>', // Qwen format
173
+ '</think>', // Think end
174
+ ];
175
+ let matchIdx = -1;
176
+ let matchLen = 0;
177
+ let matchType = null;
178
+ // Helper to find earliest token
179
+ const check = (idx, len, type) => {
180
+ if (idx !== -1 && (matchIdx === -1 || idx < matchIdx)) {
181
+ matchIdx = idx;
182
+ matchLen = len;
183
+ matchType = type;
184
+ }
185
+ };
186
+ // Check start tokens
187
+ for (const token of startTokens) {
188
+ const idx = streamBuffer.indexOf(token);
189
+ if (token === '<think>') {
190
+ check(idx, token.length, 'think');
191
+ }
192
+ else {
193
+ check(idx, token.length, 'start');
194
+ }
195
+ }
196
+ // CRITICAL FIX: Also check for bare tool call start tokens (both opening and self-closing)
197
+ const bareToolNames = [
198
+ 'read_file',
199
+ 'read_data_file',
200
+ 'glob',
201
+ 'search_files',
202
+ 'list_files',
203
+ 'create_file',
204
+ 'edit_file',
205
+ 'execute_command',
206
+ 'browser_action',
207
+ 'ask_followup_question',
208
+ 'attempt_completion',
209
+ 'new_task',
210
+ 'retrieve_knowledge',
211
+ 'read_many_files'
212
+ ];
213
+ for (const toolName of bareToolNames) {
214
+ // Check for opening tag format: <tool_name>
215
+ const openingTag = `<${toolName}>`;
216
+ const openingIdx = streamBuffer.indexOf(openingTag);
217
+ if (openingIdx !== -1) {
218
+ check(openingIdx, openingTag.length, 'start');
219
+ }
220
+ // Check for self-closing tag format: <tool_name ... />
221
+ const selfClosingPattern = new RegExp(`<${toolName}\\s[^>]*\\s*/>`, 'g');
222
+ const selfClosingMatch = selfClosingPattern.exec(streamBuffer);
223
+ if (selfClosingMatch && typeof selfClosingMatch.index === 'number') {
224
+ check(selfClosingMatch.index, selfClosingMatch[0].length, 'start');
225
+ }
226
+ // Check for opening tag with attributes: <tool_name ...>
227
+ const attributePattern = new RegExp(`<${toolName}\\s[^>]*>`, 'g');
228
+ const attributeMatch = attributePattern.exec(streamBuffer);
229
+ if (attributeMatch && typeof attributeMatch.index === 'number') {
230
+ check(attributeMatch.index, attributeMatch[0].length, 'start');
231
+ }
232
+ // Check for pipe-delimited format: <|tool_name|>
233
+ const pipeDelimitedTag = `<|${toolName}|>`;
234
+ const pipeIdx = streamBuffer.indexOf(pipeDelimitedTag);
235
+ if (pipeIdx !== -1) {
236
+ check(pipeIdx, pipeDelimitedTag.length, 'start');
237
+ }
238
+ }
239
+ // Check separator token
240
+ const idxSep = streamBuffer.indexOf(sepToken);
241
+ check(idxSep, sepToken.length, 'sep');
242
+ // Check end tokens
243
+ for (const token of endTokens) {
244
+ const idx = streamBuffer.indexOf(token);
245
+ check(idx, token.length, 'end');
246
+ }
247
+ if (matchIdx !== -1 && matchType !== null) {
248
+ if (matchType === 'think') {
249
+ // Handle think blocks - find the end
250
+ const thinkEnd = streamBuffer.indexOf('</think>', matchIdx);
251
+ if (thinkEnd !== -1) {
252
+ // Complete think block - yield text before, then show think content
253
+ const textToYield = streamBuffer.substring(0, matchIdx);
254
+ if (textToYield && textToYield.length > 0) {
255
+ yield { type: 'content', value: textToYield };
256
+ }
257
+ // Extract and format think content differently from assistant messages
258
+ const thinkContent = streamBuffer.substring(matchIdx + matchLen, thinkEnd);
259
+ if (thinkContent.trim().length > 0) {
260
+ const formattedThinkContent = this.formatThinkContent(thinkContent.trim());
261
+ // Add a single newline after think content to separate it from following content
262
+ const thinkWithNewline = formattedThinkContent + '\n';
263
+ yield { type: 'content', value: thinkWithNewline };
264
+ }
265
+ // Remove the think block and any trailing whitespace/newlines
266
+ let afterThink = streamBuffer.substring(thinkEnd + '</think>'.length);
267
+ // Clean up any excessive newlines that might follow the think block
268
+ afterThink = afterThink.replace(/^\s*\n+/, '\n');
269
+ streamBuffer = afterThink;
270
+ processing = true;
271
+ }
272
+ else {
273
+ // Incomplete think block - wait for more data
274
+ if (streamBuffer.length > SAFETY_MARGIN) {
275
+ const yieldLen = matchIdx;
276
+ if (yieldLen > 0) {
277
+ const textToYield = streamBuffer.substring(0, yieldLen);
278
+ if (textToYield.length > 0) {
279
+ yield { type: 'content', value: textToYield };
280
+ }
281
+ streamBuffer = streamBuffer.substring(yieldLen);
282
+ }
283
+ }
284
+ processing = false;
285
+ }
286
+ }
287
+ else if (matchType === 'sep') {
288
+ // Special case: "FunctionName<sep>Args"
289
+ // We need to remove the "FunctionName" which is likely at the end of the text before matchIdx
290
+ let nameStart = matchIdx;
291
+ // Backtrack to find the start of the function name
292
+ while (nameStart > 0 && /[a-zA-Z0-9_]/.test(streamBuffer[nameStart - 1])) {
293
+ nameStart--;
294
+ }
295
+ // Yield text BEFORE the function name
296
+ const textToYield = streamBuffer.substring(0, nameStart);
297
+ if (textToYield && textToYield.length > 0) {
298
+ yield { type: 'content', value: textToYield };
299
+ }
300
+ // Remove the name + token from buffer and switch to tool mode
301
+ streamBuffer = streamBuffer.substring(matchIdx + matchLen);
302
+ inToolCall = true;
303
+ processing = true;
304
+ }
305
+ else if (matchType === 'end') {
306
+ // Found a standalone end token (hallucination/orphan).
307
+ // Yield text before it, discard token, stay in text mode.
308
+ const textToYield = streamBuffer.substring(0, matchIdx);
309
+ if (textToYield && textToYield.length > 0) {
310
+ yield { type: 'content', value: textToYield };
311
+ }
312
+ streamBuffer = streamBuffer.substring(matchIdx + matchLen);
313
+ processing = true;
314
+ }
315
+ else if (matchType === 'start') {
316
+ // Normal Start Token
317
+ // Yield everything before the token
318
+ const textToYield = streamBuffer.substring(0, matchIdx);
319
+ if (textToYield && textToYield.length > 0) {
320
+ yield { type: 'content', value: textToYield };
321
+ }
322
+ // Remove token, switch to tool mode
323
+ streamBuffer = streamBuffer.substring(matchIdx + matchLen);
324
+ inToolCall = true;
325
+ processing = true;
326
+ }
327
+ }
328
+ else {
329
+ // No tokens found in buffer.
330
+ // Keep the last SAFETY_MARGIN chars in buffer to handle split tokens.
331
+ if (streamBuffer.length > SAFETY_MARGIN) {
332
+ const yieldLen = streamBuffer.length - SAFETY_MARGIN;
333
+ const textToYield = streamBuffer.substring(0, yieldLen);
334
+ if (textToYield && textToYield.length > 0) {
335
+ yield { type: 'content', value: textToYield };
336
+ }
337
+ streamBuffer = streamBuffer.substring(yieldLen);
338
+ }
339
+ // Stop processing loop, wait for more data
340
+ processing = false;
341
+ }
342
+ }
343
+ }
344
+ }
345
+ // STREAM FINISHED: Flush remaining buffer
346
+ // Only flush if we are NOT in a tool call.
347
+ if (!inToolCall && streamBuffer.length > 0) {
348
+ yield { type: 'content', value: streamBuffer };
349
+ }
350
+ }
351
+ finally {
352
+ reader.releaseLock();
353
+ }
354
+ // Add the user message to history after successful send
355
+ this.addHistory(currentUserMessage);
356
+ // Add the model response to history
357
+ const modelResponse = {
358
+ role: 'model',
359
+ parts: [{ text: fullResponse }]
360
+ };
361
+ this.addHistory(modelResponse);
362
+ // CRITICAL FIX: Process tool calls IMMEDIATELY after streaming completes
363
+ // This ensures tool call events are emitted while the generator is still active
364
+ const encryptedTurn = new EncryptedTurn(this, prompt_id);
365
+ // Check if we have tool calls
366
+ if (currentTools && currentTools.length > 0 && this.containsToolCalls(fullResponse)) {
367
+ // Process the response and emit tool call events
368
+ // IMPORTANT: We must yield ALL events from processEncryptedResponse
369
+ for await (const event of encryptedTurn.processEncryptedResponse(fullResponse, true)) {
370
+ // Yield ALL events including tool_call_request and finished
371
+ // Don't skip any events - the UI needs them all
372
+ if (event.type === 'tool_call_request') {
373
+ yield event;
374
+ }
375
+ else if (event.type === 'finished') {
376
+ // Yield the finished event from processEncryptedResponse
377
+ yield event;
378
+ // Don't yield another finished event below
379
+ return;
380
+ }
381
+ // Skip content events since we already streamed them
382
+ }
383
+ }
384
+ else {
385
+ // No tool calls, just emit finished event
386
+ yield {
387
+ type: 'finished',
388
+ value: 'STOP'
389
+ };
390
+ }
391
+ }
392
+ catch (error) {
393
+ console.error('❌ EncryptedGeminiClientBridge.sendMessageStream failed:', error);
394
+ yield {
395
+ type: 'error',
396
+ value: {
397
+ error: {
398
+ message: error instanceof Error ? error.message : 'Unknown error in encrypted provider',
399
+ status: 500
400
+ }
401
+ }
402
+ };
403
+ }
404
+ }
405
+ /**
406
+ * Convert PartListUnion request to text string
407
+ */
408
+ convertRequestToText(request) {
409
+ if (typeof request === 'string') {
410
+ return request;
411
+ }
412
+ if (Array.isArray(request)) {
413
+ return request
414
+ .map(part => {
415
+ if (typeof part === 'string') {
416
+ return part;
417
+ }
418
+ else if (part && typeof part === 'object' && 'text' in part) {
419
+ return part.text;
420
+ }
421
+ else if (part && typeof part === 'object' && 'functionResponse' in part) {
422
+ // Handle function response parts
423
+ const funcResp = part.functionResponse;
424
+ return `Function ${funcResp?.name} returned: ${JSON.stringify(funcResp?.response)}`;
425
+ }
426
+ return '';
427
+ })
428
+ .join('\n');
429
+ }
430
+ return String(request);
431
+ }
432
+ /**
433
+ * Convert Content[] history to message format expected by EncryptedChatService
434
+ * Now uses TOKEN-BASED truncation like the normal Blackbox provider
435
+ */
436
+ async convertHistoryToMessageFormat(history, toolsSystemPrompt = '') {
437
+ const messages = history.map(content => {
438
+ // CRITICAL FIX: Safely handle undefined or missing parts
439
+ // Ensure parts is always an array before filtering
440
+ let parts = content.parts;
441
+ // Validate that parts exists and is an array
442
+ if (!parts || !Array.isArray(parts)) {
443
+ console.warn('⚠️ [EncryptedGeminiClientBridge] Content has invalid or missing parts:', content);
444
+ parts = [];
445
+ }
446
+ // Extract text from parts
447
+ const textParts = parts.filter((part) => typeof part === 'object' && part !== null && 'text' in part);
448
+ const text = textParts.map(part => part.text).join('\n');
449
+ // Map roles
450
+ const role = content.role === 'model' ? 'assistant' : content.role;
451
+ return { role, content: text };
452
+ });
453
+ // Get the core system prompt like Gemini does
454
+ let coreSystemPrompt = 'You are a helpful AI assistant.';
455
+ let environmentContext = '';
456
+ if (this.config) {
457
+ const userMemory = this.config.getUserMemory();
458
+ coreSystemPrompt = getCoreSystemPrompt(userMemory, {}, this.config.getModel(), this.config.getApprovalMode() === ApprovalMode.YOLO);
459
+ // CRITICAL FIX: Get environment context like regular Gemini does
460
+ const envParts = await getEnvironmentContext(this.config);
461
+ environmentContext = envParts.map(part => typeof part === 'string' ? part : part.text || '').join('\n');
462
+ }
463
+ // Combine core system prompt with environment context (like regular Gemini)
464
+ const systemPromptWithEnvironment = environmentContext.trim()
465
+ ? `${coreSystemPrompt}\n\n${environmentContext}`
466
+ : coreSystemPrompt;
467
+ // Then combine with tools system prompt
468
+ const fullSystemPrompt = toolsSystemPrompt
469
+ ? `${systemPromptWithEnvironment}\n\n${toolsSystemPrompt}`
470
+ : systemPromptWithEnvironment;
471
+ // Add or update system message
472
+ if (messages.length > 0 && messages[0].role === 'system') {
473
+ // Replace existing system message with our full system prompt
474
+ messages[0].content = fullSystemPrompt;
475
+ }
476
+ else {
477
+ // Insert system message at the beginning
478
+ const systemMessage = {
479
+ role: 'system',
480
+ content: fullSystemPrompt
481
+ };
482
+ messages.unshift(systemMessage);
483
+ }
484
+ // CRITICAL FIX: Use TOKEN-BASED truncation like normal Blackbox provider
485
+ // This is more accurate and accounts for actual model limits
486
+ const model = this.config?.getModel() || 'blackbox-pro';
487
+ const maxOutputTokens = 8192; // Standard output limit
488
+ // Apply token-based truncation
489
+ const truncatedMessagesRaw = this.messageTruncator.truncateLongMessages(model, messages, maxOutputTokens, null // system prompt already in messages
490
+ );
491
+ // Convert back to the expected type (ensure role is properly typed)
492
+ const truncatedMessages = truncatedMessagesRaw.map(msg => ({
493
+ role: msg.role,
494
+ content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)
495
+ }));
496
+ // Log truncation if it occurred
497
+ if (truncatedMessages.length < messages.length) {
498
+ console.log(`📦 [Bridge-Token] Truncated: ${messages.length} → ${truncatedMessages.length} messages`);
499
+ }
500
+ return truncatedMessages;
501
+ }
502
+ /**
503
+ * Check if text contains tool call markup
504
+ */
505
+ containsToolCalls(text) {
506
+ // Check for wrapped formats
507
+ const hasWrappedFormats = text.includes('<minimax:tool_call>') ||
508
+ text.includes('<invoke name=') ||
509
+ text.includes('<tool_call>') ||
510
+ text.includes('<|tool▁calls▁begin|>') ||
511
+ text.includes('<|tool▁call▁begin|>');
512
+ if (hasWrappedFormats) {
513
+ return true;
514
+ }
515
+ // Check for bare tool call formats
516
+ const bareToolPatterns = [
517
+ /<read_file[\s>]/,
518
+ /<read_data_file[\s>]/,
519
+ /<glob[\s>]/,
520
+ /<search_files[\s>]/,
521
+ /<list_files[\s>]/,
522
+ /<create_file[\s>]/,
523
+ /<edit_file[\s>]/,
524
+ /<execute_command[\s>]/,
525
+ /<browser_action[\s>]/,
526
+ /<ask_followup_question[\s>]/,
527
+ /<attempt_completion[\s>]/,
528
+ /<new_task[\s>]/,
529
+ /<retrieve_knowledge[\s>]/,
530
+ /<read_many_files[\s>]/
531
+ ];
532
+ if (bareToolPatterns.some(pattern => pattern.test(text))) {
533
+ return true;
534
+ }
535
+ // Check for pipe-delimited formats
536
+ const pipeDelimitedPatterns = [
537
+ /<\|read_file\|>/,
538
+ /<\|read_data_file\|>/,
539
+ /<\|glob\|>/,
540
+ /<\|search_files\|>/,
541
+ /<\|list_files\|>/,
542
+ /<\|create_file\|>/,
543
+ /<\|edit_file\|>/,
544
+ /<\|execute_command\|>/,
545
+ /<\|browser_action\|>/,
546
+ /<\|ask_followup_question\|>/,
547
+ /<\|attempt_completion\|>/,
548
+ /<\|new_task\|>/,
549
+ /<\|retrieve_knowledge\|>/,
550
+ /<\|read_many_files\|>/
551
+ ];
552
+ return pipeDelimitedPatterns.some(pattern => pattern.test(text));
553
+ }
554
+ /**
555
+ * Get available tools
556
+ */
557
+ getTools() {
558
+ return this.tools || [];
559
+ }
560
+ /**
561
+ * Add tool execution result for processing
562
+ */
563
+ addToolResult(callId, toolName, result) {
564
+ this.pendingToolResults.set(callId, { toolName, result, callId });
565
+ }
566
+ /**
567
+ * Send tool results back to encrypted service and continue conversation
568
+ */
569
+ async *sendToolResultsAndContinue(signal, prompt_id) {
570
+ if (this.pendingToolResults.size === 0) {
571
+ return;
572
+ }
573
+ try {
574
+ // Convert tool results to array format
575
+ const toolResults = Array.from(this.pendingToolResults.values());
576
+ // Clear pending results
577
+ this.pendingToolResults.clear();
578
+ // Create a message indicating tool results are available
579
+ const toolResultsMessage = this.formatToolResultsMessage(toolResults);
580
+ // Convert current history to message format
581
+ const messageHistory = await this.convertHistoryToMessageFormat(this.history);
582
+ // Send tool results back to encrypted service
583
+ const encryptedStream = await this.encryptedService.sendMessageWithToolResults(toolResultsMessage, toolResults, messageHistory);
584
+ // Process the response stream
585
+ const reader = encryptedStream.getReader();
586
+ const decoder = new TextDecoder();
587
+ let fullResponse = '';
588
+ // Buffer for streaming logic to hide tool calls
589
+ let streamBuffer = '';
590
+ let inToolCall = false;
591
+ try {
592
+ while (true) {
593
+ if (signal.aborted) {
594
+ yield { type: 'user_cancelled' };
595
+ return;
596
+ }
597
+ const { done, value } = await reader.read();
598
+ if (done)
599
+ break;
600
+ const chunk = decoder.decode(value, { stream: true });
601
+ fullResponse += chunk;
602
+ streamBuffer += chunk;
603
+ // Process buffer to yield clean text
604
+ while (true) {
605
+ if (inToolCall) {
606
+ // Look for end tokens
607
+ const endToken1 = '<|tool▁call▁end|>';
608
+ const endToken2 = '<|tool▁calls▁end|>';
609
+ const idx1 = streamBuffer.indexOf(endToken1);
610
+ const idx2 = streamBuffer.indexOf(endToken2);
611
+ let endIdx = -1;
612
+ let tokenLen = 0;
613
+ if (idx1 !== -1 && (idx2 === -1 || idx1 < idx2)) {
614
+ endIdx = idx1;
615
+ tokenLen = endToken1.length;
616
+ }
617
+ else if (idx2 !== -1) {
618
+ endIdx = idx2;
619
+ tokenLen = endToken2.length;
620
+ }
621
+ if (endIdx !== -1) {
622
+ // Found end token - discard everything up to end of token
623
+ streamBuffer = streamBuffer.substring(endIdx + tokenLen);
624
+ inToolCall = false;
625
+ continue; // Check buffer again in normal mode
626
+ }
627
+ else {
628
+ // No end token yet. Keep buffer small but safe.
629
+ const maxTokenLen = Math.max(endToken1.length, endToken2.length);
630
+ if (streamBuffer.length > maxTokenLen) {
631
+ streamBuffer = streamBuffer.substring(streamBuffer.length - maxTokenLen);
632
+ }
633
+ break; // Need more data
634
+ }
635
+ }
636
+ else {
637
+ // Normal mode - check for start tokens
638
+ const startToken1 = '<|tool▁calls▁begin|>';
639
+ const startToken2 = '<|tool▁call▁begin|>';
640
+ const sepToken = '<|tool▁sep|>';
641
+ const idx1 = streamBuffer.indexOf(startToken1);
642
+ const idx2 = streamBuffer.indexOf(startToken2);
643
+ const idxSep = streamBuffer.indexOf(sepToken);
644
+ // Find the earliest start token
645
+ let startIdx = -1;
646
+ let tokenLen = 0;
647
+ let isSep = false;
648
+ // Helper to update best match
649
+ const updateMatch = (idx, len, sep) => {
650
+ if (idx !== -1 && (startIdx === -1 || idx < startIdx)) {
651
+ startIdx = idx;
652
+ tokenLen = len;
653
+ isSep = sep;
654
+ }
655
+ };
656
+ updateMatch(idx1, startToken1.length, false);
657
+ updateMatch(idx2, startToken2.length, false);
658
+ updateMatch(idxSep, sepToken.length, true);
659
+ if (startIdx !== -1) {
660
+ // Found start token
661
+ if (isSep) {
662
+ // For separator, we need to remove the preceding tool name
663
+ // The tool name is [a-zA-Z0-9_]+ immediately before startIdx
664
+ let nameStart = startIdx;
665
+ while (nameStart > 0 && /[a-zA-Z0-9_]/.test(streamBuffer[nameStart - 1])) {
666
+ nameStart--;
667
+ }
668
+ // Yield everything up to nameStart
669
+ const textToYield = streamBuffer.substring(0, nameStart);
670
+ if (textToYield) {
671
+ yield {
672
+ type: 'content',
673
+ value: textToYield
674
+ };
675
+ }
676
+ // Remove up to end of token
677
+ streamBuffer = streamBuffer.substring(startIdx + tokenLen);
678
+ inToolCall = true;
679
+ continue;
680
+ }
681
+ else {
682
+ // Normal start token
683
+ const textToYield = streamBuffer.substring(0, startIdx);
684
+ if (textToYield) {
685
+ yield {
686
+ type: 'content',
687
+ value: textToYield
688
+ };
689
+ }
690
+ streamBuffer = streamBuffer.substring(startIdx + tokenLen);
691
+ inToolCall = true;
692
+ continue;
693
+ }
694
+ }
695
+ else {
696
+ // No start token found.
697
+ // Keep the last 50 chars to be safe (covers max token length + max tool name)
698
+ const keepLen = 50;
699
+ if (streamBuffer.length > keepLen) {
700
+ const yieldLen = streamBuffer.length - keepLen;
701
+ const textToYield = streamBuffer.substring(0, yieldLen);
702
+ yield {
703
+ type: 'content',
704
+ value: textToYield
705
+ };
706
+ streamBuffer = streamBuffer.substring(yieldLen);
707
+ }
708
+ break; // Need more data
709
+ }
710
+ }
711
+ }
712
+ }
713
+ // Flush remaining buffer if not in tool call
714
+ if (!inToolCall && streamBuffer.length > 0) {
715
+ yield {
716
+ type: 'content',
717
+ value: streamBuffer
718
+ };
719
+ }
720
+ }
721
+ finally {
722
+ reader.releaseLock();
723
+ }
724
+ // CRITICAL FIX: Process tool calls IMMEDIATELY after streaming completes
725
+ const encryptedTurn = new EncryptedTurn(this, prompt_id);
726
+ if (this.tools && this.tools.length > 0 && this.containsToolCalls(fullResponse)) {
727
+ // Process the response and emit any additional tool call events
728
+ for await (const event of encryptedTurn.processEncryptedResponse(fullResponse, true)) {
729
+ if (event.type === 'tool_call_request') {
730
+ yield event;
731
+ }
732
+ else if (event.type === 'finished') {
733
+ // Yield the finished event from processEncryptedResponse
734
+ yield event;
735
+ // Don't yield another finished event below
736
+ return;
737
+ }
738
+ // Skip content events since we already streamed them
739
+ }
740
+ }
741
+ else {
742
+ // No additional tool calls, just emit finished event
743
+ yield {
744
+ type: 'finished',
745
+ value: 'STOP'
746
+ };
747
+ }
748
+ }
749
+ catch (error) {
750
+ console.error('❌ EncryptedGeminiClientBridge.sendToolResultsAndContinue failed:', error);
751
+ yield {
752
+ type: 'error',
753
+ value: {
754
+ error: {
755
+ message: error instanceof Error ? error.message : 'Unknown error in tool results processing',
756
+ status: 500
757
+ }
758
+ }
759
+ };
760
+ }
761
+ }
762
+ /**
763
+ * Format tool results into a readable message
764
+ */
765
+ formatToolResultsMessage(toolResults) {
766
+ const resultSummaries = toolResults.map(({ toolName, result }) => {
767
+ const resultText = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
768
+ return `${toolName}: ${resultText.substring(0, 500)}${resultText.length > 500 ? '...' : ''}`;
769
+ });
770
+ return `Tool execution completed. Results: ${resultSummaries.join('; ')}. Please continue with the user's original request.`;
771
+ }
772
+ /**
773
+ * Check if there are pending tool results
774
+ */
775
+ hasPendingToolResults() {
776
+ return this.pendingToolResults.size > 0;
777
+ }
778
+ /**
779
+ * Clear pending tool results
780
+ */
781
+ clearPendingToolResults() {
782
+ this.pendingToolResults.clear();
783
+ }
784
+ /**
785
+ * Reset all chat history and state
786
+ */
787
+ resetHistory() {
788
+ this.history = [];
789
+ this.pendingToolResults.clear();
790
+ }
791
+ /**
792
+ * Check if client is ready
793
+ */
794
+ isReady() {
795
+ return this.encryptedService.isReady();
796
+ }
797
+ /**
798
+ * Format think content with special styling to distinguish from assistant messages
799
+ */
800
+ formatThinkContent(thinkContent) {
801
+ return formatThinkContentUtil(thinkContent, {
802
+ debug: false,
803
+ maxConsecutiveNewlines: 2,
804
+ trimEnds: true
805
+ });
806
+ }
807
+ }
808
+ //# sourceMappingURL=encryptedGeminiClientBridge.js.map