camel-ai 0.2.67__py3-none-any.whl → 0.2.80a2__py3-none-any.whl

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 (224) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/_types.py +6 -2
  3. camel/agents/_utils.py +38 -0
  4. camel/agents/chat_agent.py +4014 -410
  5. camel/agents/mcp_agent.py +30 -27
  6. camel/agents/repo_agent.py +2 -1
  7. camel/benchmarks/browsecomp.py +6 -6
  8. camel/configs/__init__.py +15 -0
  9. camel/configs/aihubmix_config.py +88 -0
  10. camel/configs/amd_config.py +70 -0
  11. camel/configs/cometapi_config.py +104 -0
  12. camel/configs/minimax_config.py +93 -0
  13. camel/configs/nebius_config.py +103 -0
  14. camel/configs/vllm_config.py +2 -0
  15. camel/data_collectors/alpaca_collector.py +15 -6
  16. camel/datagen/self_improving_cot.py +1 -1
  17. camel/datasets/base_generator.py +39 -10
  18. camel/environments/__init__.py +12 -0
  19. camel/environments/rlcards_env.py +860 -0
  20. camel/environments/single_step.py +28 -3
  21. camel/environments/tic_tac_toe.py +1 -1
  22. camel/interpreters/__init__.py +2 -0
  23. camel/interpreters/docker/Dockerfile +4 -16
  24. camel/interpreters/docker_interpreter.py +3 -2
  25. camel/interpreters/e2b_interpreter.py +34 -1
  26. camel/interpreters/internal_python_interpreter.py +51 -2
  27. camel/interpreters/microsandbox_interpreter.py +395 -0
  28. camel/loaders/__init__.py +11 -2
  29. camel/loaders/base_loader.py +85 -0
  30. camel/loaders/chunkr_reader.py +9 -0
  31. camel/loaders/firecrawl_reader.py +4 -4
  32. camel/logger.py +1 -1
  33. camel/memories/agent_memories.py +84 -1
  34. camel/memories/base.py +34 -0
  35. camel/memories/blocks/chat_history_block.py +122 -4
  36. camel/memories/blocks/vectordb_block.py +8 -1
  37. camel/memories/context_creators/score_based.py +29 -237
  38. camel/memories/records.py +88 -8
  39. camel/messages/base.py +166 -40
  40. camel/messages/func_message.py +32 -5
  41. camel/models/__init__.py +10 -0
  42. camel/models/aihubmix_model.py +83 -0
  43. camel/models/aiml_model.py +1 -16
  44. camel/models/amd_model.py +101 -0
  45. camel/models/anthropic_model.py +117 -18
  46. camel/models/aws_bedrock_model.py +2 -33
  47. camel/models/azure_openai_model.py +205 -91
  48. camel/models/base_audio_model.py +3 -1
  49. camel/models/base_model.py +189 -24
  50. camel/models/cohere_model.py +5 -17
  51. camel/models/cometapi_model.py +83 -0
  52. camel/models/crynux_model.py +1 -16
  53. camel/models/deepseek_model.py +6 -16
  54. camel/models/fish_audio_model.py +6 -0
  55. camel/models/gemini_model.py +71 -20
  56. camel/models/groq_model.py +1 -17
  57. camel/models/internlm_model.py +1 -16
  58. camel/models/litellm_model.py +49 -32
  59. camel/models/lmstudio_model.py +1 -17
  60. camel/models/minimax_model.py +83 -0
  61. camel/models/mistral_model.py +1 -16
  62. camel/models/model_factory.py +27 -1
  63. camel/models/model_manager.py +24 -6
  64. camel/models/modelscope_model.py +1 -16
  65. camel/models/moonshot_model.py +185 -19
  66. camel/models/nebius_model.py +83 -0
  67. camel/models/nemotron_model.py +0 -5
  68. camel/models/netmind_model.py +1 -16
  69. camel/models/novita_model.py +1 -16
  70. camel/models/nvidia_model.py +1 -16
  71. camel/models/ollama_model.py +4 -19
  72. camel/models/openai_compatible_model.py +171 -46
  73. camel/models/openai_model.py +205 -77
  74. camel/models/openrouter_model.py +1 -17
  75. camel/models/ppio_model.py +1 -16
  76. camel/models/qianfan_model.py +1 -16
  77. camel/models/qwen_model.py +1 -16
  78. camel/models/reka_model.py +1 -16
  79. camel/models/samba_model.py +34 -47
  80. camel/models/sglang_model.py +64 -31
  81. camel/models/siliconflow_model.py +1 -16
  82. camel/models/stub_model.py +0 -4
  83. camel/models/togetherai_model.py +1 -16
  84. camel/models/vllm_model.py +1 -16
  85. camel/models/volcano_model.py +0 -17
  86. camel/models/watsonx_model.py +1 -16
  87. camel/models/yi_model.py +1 -16
  88. camel/models/zhipuai_model.py +60 -16
  89. camel/parsers/__init__.py +18 -0
  90. camel/parsers/mcp_tool_call_parser.py +176 -0
  91. camel/retrievers/auto_retriever.py +1 -0
  92. camel/runtimes/configs.py +11 -11
  93. camel/runtimes/daytona_runtime.py +15 -16
  94. camel/runtimes/docker_runtime.py +6 -6
  95. camel/runtimes/remote_http_runtime.py +5 -5
  96. camel/services/agent_openapi_server.py +380 -0
  97. camel/societies/__init__.py +2 -0
  98. camel/societies/role_playing.py +26 -28
  99. camel/societies/workforce/__init__.py +2 -0
  100. camel/societies/workforce/events.py +122 -0
  101. camel/societies/workforce/prompts.py +249 -38
  102. camel/societies/workforce/role_playing_worker.py +82 -20
  103. camel/societies/workforce/single_agent_worker.py +634 -34
  104. camel/societies/workforce/structured_output_handler.py +512 -0
  105. camel/societies/workforce/task_channel.py +169 -23
  106. camel/societies/workforce/utils.py +176 -9
  107. camel/societies/workforce/worker.py +77 -23
  108. camel/societies/workforce/workflow_memory_manager.py +772 -0
  109. camel/societies/workforce/workforce.py +3168 -478
  110. camel/societies/workforce/workforce_callback.py +74 -0
  111. camel/societies/workforce/workforce_logger.py +203 -175
  112. camel/societies/workforce/workforce_metrics.py +33 -0
  113. camel/storages/__init__.py +4 -0
  114. camel/storages/key_value_storages/json.py +15 -2
  115. camel/storages/key_value_storages/mem0_cloud.py +48 -47
  116. camel/storages/object_storages/google_cloud.py +1 -1
  117. camel/storages/vectordb_storages/__init__.py +6 -0
  118. camel/storages/vectordb_storages/chroma.py +731 -0
  119. camel/storages/vectordb_storages/oceanbase.py +13 -13
  120. camel/storages/vectordb_storages/pgvector.py +349 -0
  121. camel/storages/vectordb_storages/qdrant.py +3 -3
  122. camel/storages/vectordb_storages/surreal.py +365 -0
  123. camel/storages/vectordb_storages/tidb.py +8 -6
  124. camel/tasks/task.py +244 -27
  125. camel/toolkits/__init__.py +46 -8
  126. camel/toolkits/aci_toolkit.py +64 -19
  127. camel/toolkits/arxiv_toolkit.py +6 -6
  128. camel/toolkits/base.py +63 -5
  129. camel/toolkits/code_execution.py +28 -1
  130. camel/toolkits/context_summarizer_toolkit.py +684 -0
  131. camel/toolkits/craw4ai_toolkit.py +93 -0
  132. camel/toolkits/dappier_toolkit.py +10 -6
  133. camel/toolkits/dingtalk.py +1135 -0
  134. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  135. camel/toolkits/excel_toolkit.py +901 -67
  136. camel/toolkits/file_toolkit.py +1402 -0
  137. camel/toolkits/function_tool.py +30 -6
  138. camel/toolkits/github_toolkit.py +107 -20
  139. camel/toolkits/gmail_toolkit.py +1839 -0
  140. camel/toolkits/google_calendar_toolkit.py +38 -4
  141. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  142. camel/toolkits/human_toolkit.py +34 -10
  143. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  144. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  145. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  146. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  147. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  148. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +3749 -0
  149. camel/toolkits/hybrid_browser_toolkit/ts/package.json +32 -0
  150. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  151. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1815 -0
  152. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  153. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +590 -0
  154. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  155. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  156. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  157. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  158. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +130 -0
  159. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
  160. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  161. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1032 -0
  162. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  163. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  164. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  165. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  166. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  167. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  168. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  169. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  170. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  171. camel/toolkits/image_generation_toolkit.py +390 -0
  172. camel/toolkits/jina_reranker_toolkit.py +3 -4
  173. camel/toolkits/klavis_toolkit.py +5 -1
  174. camel/toolkits/markitdown_toolkit.py +104 -0
  175. camel/toolkits/math_toolkit.py +64 -10
  176. camel/toolkits/mcp_toolkit.py +370 -45
  177. camel/toolkits/memory_toolkit.py +5 -1
  178. camel/toolkits/message_agent_toolkit.py +608 -0
  179. camel/toolkits/message_integration.py +724 -0
  180. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  181. camel/toolkits/note_taking_toolkit.py +277 -0
  182. camel/toolkits/notion_mcp_toolkit.py +224 -0
  183. camel/toolkits/openbb_toolkit.py +5 -1
  184. camel/toolkits/origene_mcp_toolkit.py +56 -0
  185. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  186. camel/toolkits/pptx_toolkit.py +25 -12
  187. camel/toolkits/resend_toolkit.py +168 -0
  188. camel/toolkits/screenshot_toolkit.py +213 -0
  189. camel/toolkits/search_toolkit.py +437 -142
  190. camel/toolkits/slack_toolkit.py +104 -50
  191. camel/toolkits/sympy_toolkit.py +1 -1
  192. camel/toolkits/task_planning_toolkit.py +3 -3
  193. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  194. camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
  195. camel/toolkits/terminal_toolkit/utils.py +532 -0
  196. camel/toolkits/thinking_toolkit.py +1 -1
  197. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  198. camel/toolkits/video_analysis_toolkit.py +106 -26
  199. camel/toolkits/video_download_toolkit.py +17 -14
  200. camel/toolkits/web_deploy_toolkit.py +1219 -0
  201. camel/toolkits/wechat_official_toolkit.py +483 -0
  202. camel/toolkits/zapier_toolkit.py +5 -1
  203. camel/types/__init__.py +2 -2
  204. camel/types/agents/tool_calling_record.py +4 -1
  205. camel/types/enums.py +316 -40
  206. camel/types/openai_types.py +2 -2
  207. camel/types/unified_model_type.py +31 -4
  208. camel/utils/commons.py +36 -5
  209. camel/utils/constants.py +3 -0
  210. camel/utils/context_utils.py +1003 -0
  211. camel/utils/mcp.py +138 -4
  212. camel/utils/mcp_client.py +45 -1
  213. camel/utils/message_summarizer.py +148 -0
  214. camel/utils/token_counting.py +43 -20
  215. camel/utils/tool_result.py +44 -0
  216. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +296 -85
  217. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +219 -146
  218. camel/loaders/pandas_reader.py +0 -368
  219. camel/toolkits/dalle_toolkit.py +0 -175
  220. camel/toolkits/file_write_toolkit.py +0 -444
  221. camel/toolkits/openai_agent_toolkit.py +0 -135
  222. camel/toolkits/terminal_toolkit.py +0 -1037
  223. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
  224. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,590 @@
1
+ import {HybridBrowserSession} from './browser-session';
2
+ import {ActionResult, BrowserAction, BrowserToolkitConfig, SnapshotResult, TabInfo, VisualMarkResult} from './types';
3
+ import {ConfigLoader} from './config-loader';
4
+ import {ConsoleMessage} from 'playwright';
5
+ import {SomScreenshotInjected} from './som-screenshot-injected';
6
+ import {filterClickableByHierarchy} from './snapshot-parser';
7
+
8
+ export class HybridBrowserToolkit {
9
+ private session: HybridBrowserSession;
10
+ private config: BrowserToolkitConfig;
11
+ private configLoader: ConfigLoader;
12
+ private viewportLimit: boolean;
13
+ private fullVisualMode: boolean;
14
+
15
+ constructor(config: BrowserToolkitConfig = {}) {
16
+ this.configLoader = ConfigLoader.fromPythonConfig(config);
17
+ this.config = config; // Store original config for backward compatibility
18
+ this.session = new HybridBrowserSession(config); // Pass original config
19
+ this.viewportLimit = this.configLoader.getWebSocketConfig().viewport_limit;
20
+ this.fullVisualMode = this.configLoader.getWebSocketConfig().fullVisualMode || false;
21
+ }
22
+
23
+ async openBrowser(startUrl?: string): Promise<ActionResult> {
24
+ const startTime = Date.now();
25
+
26
+ try {
27
+ await this.session.ensureBrowser();
28
+
29
+ // Check if we should skip navigation in CDP keep-current-page mode
30
+ const browserConfig = this.configLoader.getBrowserConfig();
31
+ if (browserConfig.cdpUrl && browserConfig.cdpKeepCurrentPage && !startUrl) {
32
+ // In CDP keep-current-page mode without explicit URL, just ensure browser and return current page
33
+ const snapshotStart = Date.now();
34
+ const snapshot = await this.getSnapshotForAction(this.viewportLimit);
35
+ const snapshotTime = Date.now() - snapshotStart;
36
+
37
+ const page = await this.session.getCurrentPage();
38
+ const currentUrl = page ? await page.url() : 'unknown';
39
+
40
+ const totalTime = Date.now() - startTime;
41
+
42
+ return {
43
+ success: true,
44
+ message: `Browser opened in CDP keep-current-page mode (current page: ${currentUrl})`,
45
+ snapshot,
46
+ timing: {
47
+ total_time_ms: totalTime,
48
+ snapshot_time_ms: snapshotTime,
49
+ },
50
+ };
51
+ }
52
+
53
+ // For normal mode or CDP with cdpKeepCurrentPage=false: navigate to URL
54
+ if (!browserConfig.cdpUrl || !browserConfig.cdpKeepCurrentPage) {
55
+ const url = startUrl || this.config.defaultStartUrl || 'https://google.com/';
56
+ const result = await this.session.visitPage(url);
57
+
58
+ const snapshotStart = Date.now();
59
+ const snapshot = await this.getSnapshotForAction(this.viewportLimit);
60
+ const snapshotTime = Date.now() - snapshotStart;
61
+
62
+ const totalTime = Date.now() - startTime;
63
+
64
+ return {
65
+ success: true,
66
+ message: result.message,
67
+ snapshot,
68
+ timing: {
69
+ total_time_ms: totalTime,
70
+ page_load_time_ms: result.timing?.page_load_time_ms || 0,
71
+ snapshot_time_ms: snapshotTime,
72
+ },
73
+ };
74
+ }
75
+
76
+ // Fallback: Just return current page snapshot without any navigation
77
+ const snapshotStart = Date.now();
78
+ const snapshot = await this.getSnapshotForAction(this.viewportLimit);
79
+ const snapshotTime = Date.now() - snapshotStart;
80
+
81
+ const totalTime = Date.now() - startTime;
82
+
83
+ return {
84
+ success: true,
85
+ message: `Browser opened without navigation`,
86
+ snapshot,
87
+ timing: {
88
+ total_time_ms: totalTime,
89
+ snapshot_time_ms: snapshotTime,
90
+ },
91
+ };
92
+ } catch (error) {
93
+ const totalTime = Date.now() - startTime;
94
+ return {
95
+ success: false,
96
+ message: `Failed to open browser: ${error}`,
97
+ timing: {
98
+ total_time_ms: totalTime,
99
+ },
100
+ };
101
+ }
102
+ }
103
+
104
+ async closeBrowser(): Promise<ActionResult> {
105
+ try {
106
+ await this.session.close();
107
+ return {
108
+ success: true,
109
+ message: 'Browser closed successfully',
110
+ };
111
+ } catch (error) {
112
+ return {
113
+ success: false,
114
+ message: `Failed to close browser: ${error}`,
115
+ };
116
+ }
117
+ }
118
+
119
+ async visitPage(url: string): Promise<any> {
120
+ try {
121
+ // Ensure browser is initialized before visiting page
122
+ await this.session.ensureBrowser();
123
+
124
+ const result = await this.session.visitPage(url);
125
+
126
+ // Format response for Python layer compatibility
127
+ const response: any = {
128
+ result: result.message,
129
+ snapshot: '',
130
+ };
131
+
132
+ if (result.success) {
133
+ const snapshotStart = Date.now();
134
+ response.snapshot = await this.getSnapshotForAction(this.viewportLimit);
135
+ const snapshotTime = Date.now() - snapshotStart;
136
+
137
+ if (result.timing) {
138
+ result.timing.snapshot_time_ms = snapshotTime;
139
+ }
140
+ }
141
+
142
+ // Include timing if available
143
+ if (result.timing) {
144
+ response.timing = result.timing;
145
+ }
146
+
147
+ // Include newTabId if present
148
+ if (result.newTabId) {
149
+ response.newTabId = result.newTabId;
150
+ }
151
+
152
+ return response;
153
+ } catch (error) {
154
+ console.error('[visitPage] Error:', error);
155
+ return {
156
+ result: `Navigation to ${url} failed: ${error}`,
157
+ snapshot: '',
158
+ timing: {
159
+ total_time_ms: 0,
160
+ navigation_time_ms: 0,
161
+ dom_content_loaded_time_ms: 0,
162
+ network_idle_time_ms: 0,
163
+ }
164
+ };
165
+ }
166
+ }
167
+
168
+ async getPageSnapshot(viewportLimit: boolean = false): Promise<string> {
169
+ try {
170
+ // Always return real snapshot when explicitly called
171
+ // If viewport limiting is enabled, we need coordinates for filtering
172
+ const snapshotResult = await this.session.getSnapshotForAI(viewportLimit, viewportLimit);
173
+ return snapshotResult.snapshot;
174
+ } catch (error) {
175
+ return `Error capturing snapshot: ${error}`;
176
+ }
177
+ }
178
+
179
+ // Internal method for getting snapshot in actions (respects fullVisualMode)
180
+ private async getSnapshotForAction(viewportLimit: boolean = false): Promise<string> {
181
+ if (this.fullVisualMode) {
182
+ return 'full visual mode';
183
+ }
184
+ return this.getPageSnapshot(viewportLimit);
185
+ }
186
+
187
+
188
+ async getSnapshotForAI(): Promise<SnapshotResult> {
189
+ return await this.session.getSnapshotForAI();
190
+ }
191
+
192
+ async getSomScreenshot(): Promise<VisualMarkResult & { timing: any }> {
193
+ const startTime = Date.now();
194
+ console.log('[HybridBrowserToolkit] Starting getSomScreenshot...');
195
+
196
+ try {
197
+ // Get page and snapshot data
198
+ const page = await this.session.getCurrentPage();
199
+ const snapshotResult = await this.session.getSnapshotForAI(true); // Include coordinates
200
+
201
+ // Parse clickable elements from snapshot text
202
+ const clickableElements = this.parseClickableElements(snapshotResult.snapshot);
203
+ console.log(`[HybridBrowserToolkit] Found ${clickableElements.size} clickable elements`);
204
+
205
+ // Apply hierarchy-based filtering
206
+ const filteredElements = filterClickableByHierarchy(snapshotResult.snapshot, clickableElements);
207
+ console.log(`[HybridBrowserToolkit] After filtering: ${filteredElements.size} elements remain`);
208
+
209
+ // Use injected SOM-screenshot method without export path
210
+ const result = await SomScreenshotInjected.captureOptimized(
211
+ page,
212
+ snapshotResult,
213
+ filteredElements,
214
+ undefined // No export path - don't generate files
215
+ );
216
+
217
+ // Add snapshot timing info to result
218
+ result.timing.snapshot_time_ms = snapshotResult.timing.snapshot_time_ms;
219
+ result.timing.coordinate_enrichment_time_ms = snapshotResult.timing.coordinate_enrichment_time_ms;
220
+
221
+ return result;
222
+ } catch (error) {
223
+ const totalTime = Date.now() - startTime;
224
+ return {
225
+ text: `Error capturing screenshot: ${error}`,
226
+ images: [],
227
+ timing: {
228
+ total_time_ms: totalTime,
229
+ screenshot_time_ms: 0,
230
+ snapshot_time_ms: 0,
231
+ coordinate_enrichment_time_ms: 0,
232
+ visual_marking_time_ms: 0,
233
+ },
234
+ };
235
+ }
236
+ }
237
+
238
+
239
+ /**
240
+ * Parse clickable elements from snapshot text
241
+ */
242
+ private parseClickableElements(snapshotText: string): Set<string> {
243
+ const clickableElements = new Set<string>();
244
+ const lines = snapshotText.split('\n');
245
+
246
+ for (const line of lines) {
247
+ // Look for lines containing [cursor=pointer] or [active] and extract ref
248
+ if (line.includes('[cursor=pointer]') || line.includes('[active]')) {
249
+ const refMatch = line.match(/\[ref=([^\]]+)\]/);
250
+ if (refMatch) {
251
+ clickableElements.add(refMatch[1]);
252
+ }
253
+ }
254
+ }
255
+
256
+ return clickableElements;
257
+ }
258
+
259
+
260
+ private async executeActionWithSnapshot(action: BrowserAction): Promise<any> {
261
+ const result = await this.session.executeAction(action);
262
+
263
+ const response: any = {
264
+ result: result.message,
265
+ snapshot: '',
266
+ };
267
+
268
+ if (result.success) {
269
+ if (result.details?.diffSnapshot) {
270
+ response.snapshot = result.details.diffSnapshot;
271
+
272
+ if (result.timing) {
273
+ result.timing.snapshot_time_ms = 0; // Diff snapshot time is included in action time
274
+ }
275
+ } else {
276
+ // Get full snapshot as usual
277
+ const snapshotStart = Date.now();
278
+ response.snapshot = await this.getPageSnapshot(this.viewportLimit);
279
+ const snapshotTime = Date.now() - snapshotStart;
280
+
281
+ if (result.timing) {
282
+ result.timing.snapshot_time_ms = snapshotTime;
283
+ }
284
+ }
285
+ }
286
+
287
+ // Include timing if available
288
+ if (result.timing) {
289
+ response.timing = result.timing;
290
+ }
291
+
292
+ // Include newTabId if present
293
+ if (result.newTabId) {
294
+ response.newTabId = result.newTabId;
295
+ }
296
+
297
+ // Include details if present (excluding diffSnapshot as it's already in snapshot)
298
+ if (result.details) {
299
+ const { diffSnapshot, ...otherDetails } = result.details;
300
+ if (Object.keys(otherDetails).length > 0) {
301
+ response.details = otherDetails;
302
+ }
303
+ }
304
+
305
+ return response;
306
+ }
307
+
308
+ async click(ref: string): Promise<any> {
309
+ const action: BrowserAction = { type: 'click', ref };
310
+ return this.executeActionWithSnapshot(action);
311
+ }
312
+
313
+ async type(refOrInputs: string | Array<{ ref: string; text: string }>, text?: string): Promise<any> {
314
+ let action: BrowserAction;
315
+
316
+ if (typeof refOrInputs === 'string') {
317
+ // Single input mode (backward compatibility)
318
+ if (text === undefined) {
319
+ throw new Error('Text parameter is required when ref is a string');
320
+ }
321
+ action = { type: 'type', ref: refOrInputs, text };
322
+ } else {
323
+ // Multiple inputs mode
324
+ action = { type: 'type', inputs: refOrInputs };
325
+ }
326
+
327
+ return this.executeActionWithSnapshot(action);
328
+ }
329
+
330
+ async select(ref: string, value: string): Promise<any> {
331
+ const action: BrowserAction = { type: 'select', ref, value };
332
+ return this.executeActionWithSnapshot(action);
333
+ }
334
+
335
+ async scroll(direction: 'up' | 'down', amount: number): Promise<any> {
336
+ const action: BrowserAction = { type: 'scroll', direction, amount };
337
+ return this.executeActionWithSnapshot(action);
338
+ }
339
+
340
+ async enter(): Promise<any> {
341
+ const action: BrowserAction = { type: 'enter' };
342
+ return this.executeActionWithSnapshot(action);
343
+ }
344
+
345
+ async mouseControl(control: 'click' | 'right_click'| 'dblclick', x: number, y: number): Promise<any> {
346
+ const action: BrowserAction = { type: 'mouse_control', control, x, y };
347
+ return this.executeActionWithSnapshot(action);
348
+ }
349
+
350
+ async mouseDrag(from_ref: string, to_ref: string): Promise<any> {
351
+ const action: BrowserAction = { type: 'mouse_drag', from_ref, to_ref };
352
+ return this.executeActionWithSnapshot(action);
353
+ }
354
+
355
+ async pressKeys(keys: string[]): Promise<any> {
356
+ const action: BrowserAction = { type: 'press_key', keys};
357
+ return this.executeActionWithSnapshot(action);
358
+ }
359
+
360
+ async batchKeyboardInput(operations: Array<{type: string, keys?: string[], text?: string, delay?: number}>, skipStabilityWait: boolean = true): Promise<any> {
361
+ return this.session.batchKeyboardInput(operations, skipStabilityWait);
362
+ }
363
+
364
+ async back(): Promise<ActionResult> {
365
+ const startTime = Date.now();
366
+
367
+ try {
368
+ const page = await this.session.getCurrentPage();
369
+
370
+ const navigationStart = Date.now();
371
+ await page.goBack({ waitUntil: 'domcontentloaded' });
372
+ const navigationTime = Date.now() - navigationStart;
373
+
374
+ const snapshotStart = Date.now();
375
+ const snapshot = await this.getSnapshotForAction(this.viewportLimit);
376
+ const snapshotTime = Date.now() - snapshotStart;
377
+
378
+ const totalTime = Date.now() - startTime;
379
+
380
+ return {
381
+ success: true,
382
+ message: 'Navigated back successfully',
383
+ snapshot,
384
+ timing: {
385
+ total_time_ms: totalTime,
386
+ navigation_time_ms: navigationTime,
387
+ snapshot_time_ms: snapshotTime,
388
+ },
389
+ };
390
+ } catch (error) {
391
+ const totalTime = Date.now() - startTime;
392
+ return {
393
+ success: false,
394
+ message: `Back navigation failed: ${error}`,
395
+ timing: {
396
+ total_time_ms: totalTime,
397
+ navigation_time_ms: 0,
398
+ snapshot_time_ms: 0,
399
+ },
400
+ };
401
+ }
402
+ }
403
+
404
+ async forward(): Promise<ActionResult> {
405
+ const startTime = Date.now();
406
+
407
+ try {
408
+ const page = await this.session.getCurrentPage();
409
+
410
+ const navigationStart = Date.now();
411
+ await page.goForward({ waitUntil: 'domcontentloaded' });
412
+ const navigationTime = Date.now() - navigationStart;
413
+
414
+ const snapshotStart = Date.now();
415
+ const snapshot = await this.getSnapshotForAction(this.viewportLimit);
416
+ const snapshotTime = Date.now() - snapshotStart;
417
+
418
+ const totalTime = Date.now() - startTime;
419
+
420
+ return {
421
+ success: true,
422
+ message: 'Navigated forward successfully',
423
+ snapshot,
424
+ timing: {
425
+ total_time_ms: totalTime,
426
+ navigation_time_ms: navigationTime,
427
+ snapshot_time_ms: snapshotTime,
428
+ },
429
+ };
430
+ } catch (error) {
431
+ const totalTime = Date.now() - startTime;
432
+ return {
433
+ success: false,
434
+ message: `Forward navigation failed: ${error}`,
435
+ timing: {
436
+ total_time_ms: totalTime,
437
+ navigation_time_ms: 0,
438
+ snapshot_time_ms: 0,
439
+ },
440
+ };
441
+ }
442
+ }
443
+
444
+
445
+ async switchTab(tabId: string): Promise<any> {
446
+ const startTime = Date.now();
447
+
448
+ try {
449
+ const success = await this.session.switchToTab(tabId);
450
+
451
+ if (success) {
452
+ const snapshotStart = Date.now();
453
+ const snapshot = await this.getPageSnapshot(this.viewportLimit);
454
+ const snapshotTime = Date.now() - snapshotStart;
455
+
456
+ const totalTime = Date.now() - startTime;
457
+
458
+ return {
459
+ result: `Switched to tab ${tabId}`,
460
+ snapshot: snapshot,
461
+ timing: {
462
+ total_time_ms: totalTime,
463
+ snapshot_time_ms: snapshotTime,
464
+ },
465
+ };
466
+ } else {
467
+ return {
468
+ result: `Failed to switch to tab ${tabId}`,
469
+ snapshot: '',
470
+ };
471
+ }
472
+ } catch (error) {
473
+ return {
474
+ result: `Error switching tab: ${error}`,
475
+ snapshot: '',
476
+ };
477
+ }
478
+ }
479
+
480
+ async closeTab(tabId: string): Promise<ActionResult> {
481
+ const success = await this.session.closeTab(tabId);
482
+
483
+ if (success) {
484
+ return {
485
+ success: true,
486
+ message: `Closed tab ${tabId}`,
487
+ snapshot: await this.getSnapshotForAction(this.viewportLimit),
488
+ };
489
+ } else {
490
+ return {
491
+ success: false,
492
+ message: `Failed to close tab ${tabId}`,
493
+ };
494
+ }
495
+ }
496
+
497
+ async getTabInfo(): Promise<TabInfo[]> {
498
+ return await this.session.getTabInfo();
499
+ }
500
+
501
+ async getConsoleView(): Promise<any> {
502
+ const currentLogs = await this.session.getCurrentLogs();
503
+ // Format logs
504
+ return currentLogs.map(item => ({
505
+ type: item.type(),
506
+ text: item.text(),
507
+ }));
508
+ }
509
+
510
+ async consoleExecute(code: string): Promise<any> {
511
+ const startTime = Date.now();
512
+ try {
513
+ const page = await this.session.getCurrentPage();
514
+
515
+ // Wrap the code to capture console.log output
516
+ const wrappedCode = `
517
+ (function() {
518
+ const _logs = [];
519
+ const originalLog = console.log;
520
+ console.log = function(...args) {
521
+ _logs.push(args.map(arg => {
522
+ try {
523
+ return typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
524
+ } catch (e) {
525
+ return String(arg);
526
+ }
527
+ }).join(' '));
528
+ originalLog.apply(console, args);
529
+ };
530
+
531
+ let result;
532
+ try {
533
+ result = eval(${JSON.stringify(code)});
534
+ } catch (e) {
535
+ try {
536
+ result = (function() { ${code} })();
537
+ } catch (error) {
538
+ console.log = originalLog;
539
+ throw error;
540
+ }
541
+ }
542
+
543
+ console.log = originalLog;
544
+ return { result, logs: _logs };
545
+ })()
546
+ `;
547
+
548
+ const evalResult = await page.evaluate(wrappedCode) as { result: any; logs: string[] };
549
+ const { result, logs } = evalResult;
550
+
551
+ const snapshotStart = Date.now();
552
+ const snapshot = await this.getSnapshotForAction(this.viewportLimit);
553
+ const snapshotTime = Date.now() - snapshotStart;
554
+ const totalTime = Date.now() - startTime;
555
+
556
+ // Properly serialize the result
557
+ let resultStr: string;
558
+ try {
559
+ resultStr = JSON.stringify(result, null, 2);
560
+ } catch (e) {
561
+ // Fallback for non-serializable values
562
+ resultStr = String(result);
563
+ }
564
+
565
+ return {
566
+ result: `Console execution result: ${resultStr}`,
567
+ console_output: logs,
568
+ snapshot: snapshot,
569
+ timing: {
570
+ total_time_ms: totalTime,
571
+ snapshot_time_ms: snapshotTime,
572
+ },
573
+ };
574
+
575
+ } catch (error) {
576
+ const totalTime = Date.now() - startTime;
577
+ return {
578
+ result: `Console execution failed: ${error}`,
579
+ console_output: [],
580
+ snapshot: '',
581
+ timing: {
582
+ total_time_ms: totalTime,
583
+ snapshot_time_ms: 0,
584
+ },
585
+ };
586
+ }
587
+ }
588
+
589
+ }
590
+
@@ -0,0 +1,7 @@
1
+ export { HybridBrowserToolkit } from './hybrid-browser-toolkit';
2
+ export { HybridBrowserSession } from './browser-session';
3
+ export { ConfigLoader, StealthConfig, BrowserConfig, WebSocketConfig } from './config-loader';
4
+ export * from './types';
5
+
6
+ // Default export for convenience
7
+ export { HybridBrowserToolkit as default } from './hybrid-browser-toolkit';