codex-linux 1.0.0

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 (367) hide show
  1. package/.claude/settings.local.json +10 -0
  2. package/.eslintrc.json +27 -0
  3. package/.github/workflows/ci.yml +156 -0
  4. package/.huskyrc +7 -0
  5. package/.lintstagedrc +13 -0
  6. package/.prettierrc +12 -0
  7. package/CLAUDE.md +163 -0
  8. package/DESIGN_SUPERIOR.md +73 -0
  9. package/Dockerfile +64 -0
  10. package/INSTALLATION.md +152 -0
  11. package/LICENSE +21 -0
  12. package/README.md +245 -0
  13. package/assets/skills/code-review/instructions.md +102 -0
  14. package/assets/skills/code-review/skill.yaml +15 -0
  15. package/assets/skills/refactoring/instructions.md +149 -0
  16. package/assets/skills/refactoring/skill.yaml +15 -0
  17. package/assets/skills/testing/skill.yaml +15 -0
  18. package/commitlint.config.js +23 -0
  19. package/dist/main/DatabaseManager.js +763 -0
  20. package/dist/main/DatabaseManager.js.map +1 -0
  21. package/dist/main/SettingsManager.js +61 -0
  22. package/dist/main/SettingsManager.js.map +1 -0
  23. package/dist/main/agents/AgentOrchestrator.js +787 -0
  24. package/dist/main/agents/AgentOrchestrator.js.map +1 -0
  25. package/dist/main/agents/AgentSDK.js +219 -0
  26. package/dist/main/agents/AgentSDK.js.map +1 -0
  27. package/dist/main/agents/AgentTools.js +348 -0
  28. package/dist/main/agents/AgentTools.js.map +1 -0
  29. package/dist/main/agents/CodeIndex.js +233 -0
  30. package/dist/main/agents/CodeIndex.js.map +1 -0
  31. package/dist/main/agents/EmbeddingService.js +80 -0
  32. package/dist/main/agents/EmbeddingService.js.map +1 -0
  33. package/dist/main/agents/NativeToolCalling.js +206 -0
  34. package/dist/main/agents/NativeToolCalling.js.map +1 -0
  35. package/dist/main/api/APIServer.js +278 -0
  36. package/dist/main/api/APIServer.js.map +1 -0
  37. package/dist/main/api/RateLimiter.js +138 -0
  38. package/dist/main/api/RateLimiter.js.map +1 -0
  39. package/dist/main/api/WebSocketManager.js +300 -0
  40. package/dist/main/api/WebSocketManager.js.map +1 -0
  41. package/dist/main/assistant/ContextOptimizer.js +192 -0
  42. package/dist/main/assistant/ContextOptimizer.js.map +1 -0
  43. package/dist/main/assistant/PredictedOutputManager.js +172 -0
  44. package/dist/main/assistant/PredictedOutputManager.js.map +1 -0
  45. package/dist/main/assistant/PromptCacheManager.js +193 -0
  46. package/dist/main/assistant/PromptCacheManager.js.map +1 -0
  47. package/dist/main/assistant/PromptOptimizer.js +626 -0
  48. package/dist/main/assistant/PromptOptimizer.js.map +1 -0
  49. package/dist/main/assistant/SmartCodeAssistant.js +224 -0
  50. package/dist/main/assistant/SmartCodeAssistant.js.map +1 -0
  51. package/dist/main/auth/SessionManager.js +300 -0
  52. package/dist/main/auth/SessionManager.js.map +1 -0
  53. package/dist/main/automations/AdvancedWebhookSystem.js +212 -0
  54. package/dist/main/automations/AdvancedWebhookSystem.js.map +1 -0
  55. package/dist/main/automations/AutomationScheduler.js +269 -0
  56. package/dist/main/automations/AutomationScheduler.js.map +1 -0
  57. package/dist/main/automations/BatchProcessingSystem.js +159 -0
  58. package/dist/main/automations/BatchProcessingSystem.js.map +1 -0
  59. package/dist/main/automations/BrowserAutomationManager.js +195 -0
  60. package/dist/main/automations/BrowserAutomationManager.js.map +1 -0
  61. package/dist/main/automations/GitHubActionsManager.js +129 -0
  62. package/dist/main/automations/GitHubActionsManager.js.map +1 -0
  63. package/dist/main/automations/GitLabCIManager.js +122 -0
  64. package/dist/main/automations/GitLabCIManager.js.map +1 -0
  65. package/dist/main/automations/PriorityQueueManager.js +240 -0
  66. package/dist/main/automations/PriorityQueueManager.js.map +1 -0
  67. package/dist/main/background/BackgroundModeManager.js +117 -0
  68. package/dist/main/background/BackgroundModeManager.js.map +1 -0
  69. package/dist/main/backup/BackupManager.js +254 -0
  70. package/dist/main/backup/BackupManager.js.map +1 -0
  71. package/dist/main/backup/MigrationManager.js +114 -0
  72. package/dist/main/backup/MigrationManager.js.map +1 -0
  73. package/dist/main/commands/SlashCommandManager.js +399 -0
  74. package/dist/main/commands/SlashCommandManager.js.map +1 -0
  75. package/dist/main/config/ClaudeMdParser.js +519 -0
  76. package/dist/main/config/ClaudeMdParser.js.map +1 -0
  77. package/dist/main/config/CustomizationManager.js +381 -0
  78. package/dist/main/config/CustomizationManager.js.map +1 -0
  79. package/dist/main/config/LaunchConfigManager.js +211 -0
  80. package/dist/main/config/LaunchConfigManager.js.map +1 -0
  81. package/dist/main/config/SettingsManager.js +166 -0
  82. package/dist/main/config/SettingsManager.js.map +1 -0
  83. package/dist/main/connectors/ConnectorManager.js +151 -0
  84. package/dist/main/connectors/ConnectorManager.js.map +1 -0
  85. package/dist/main/connectors/DatabaseConnector.js +222 -0
  86. package/dist/main/connectors/DatabaseConnector.js.map +1 -0
  87. package/dist/main/cowork/CoworkManager.js +324 -0
  88. package/dist/main/cowork/CoworkManager.js.map +1 -0
  89. package/dist/main/evals/AgentEvalFramework.js +538 -0
  90. package/dist/main/evals/AgentEvalFramework.js.map +1 -0
  91. package/dist/main/evals/GraderManager.js +285 -0
  92. package/dist/main/evals/GraderManager.js.map +1 -0
  93. package/dist/main/git/GitWorktreeManager.js +214 -0
  94. package/dist/main/git/GitWorktreeManager.js.map +1 -0
  95. package/dist/main/github/GitHubPRMonitor.js +244 -0
  96. package/dist/main/github/GitHubPRMonitor.js.map +1 -0
  97. package/dist/main/ide/ContinueInManager.js +181 -0
  98. package/dist/main/ide/ContinueInManager.js.map +1 -0
  99. package/dist/main/ide/IDEIntegration.js +277 -0
  100. package/dist/main/ide/IDEIntegration.js.map +1 -0
  101. package/dist/main/integrations/LinearManager.js +252 -0
  102. package/dist/main/integrations/LinearManager.js.map +1 -0
  103. package/dist/main/integrations/SlackBotManager.js +247 -0
  104. package/dist/main/integrations/SlackBotManager.js.map +1 -0
  105. package/dist/main/lsp/LSPManager.js +394 -0
  106. package/dist/main/lsp/LSPManager.js.map +1 -0
  107. package/dist/main/main.js +1087 -0
  108. package/dist/main/main.js.map +1 -0
  109. package/dist/main/mcp/MCPConfigurationManager.js +281 -0
  110. package/dist/main/mcp/MCPConfigurationManager.js.map +1 -0
  111. package/dist/main/mcp/MCPManager.js +710 -0
  112. package/dist/main/mcp/MCPManager.js.map +1 -0
  113. package/dist/main/mcp/MCPRegistry.js +272 -0
  114. package/dist/main/mcp/MCPRegistry.js.map +1 -0
  115. package/dist/main/monitoring/ErrorRecoveryManager.js +268 -0
  116. package/dist/main/monitoring/ErrorRecoveryManager.js.map +1 -0
  117. package/dist/main/monitoring/ErrorTracker.js +57 -0
  118. package/dist/main/monitoring/ErrorTracker.js.map +1 -0
  119. package/dist/main/monitoring/MetricsCollector.js +155 -0
  120. package/dist/main/monitoring/MetricsCollector.js.map +1 -0
  121. package/dist/main/monitoring/TraceGradingSystem.js +148 -0
  122. package/dist/main/monitoring/TraceGradingSystem.js.map +1 -0
  123. package/dist/main/notifications/NotificationManager.js +67 -0
  124. package/dist/main/notifications/NotificationManager.js.map +1 -0
  125. package/dist/main/pair/AIPairProgramming.js +200 -0
  126. package/dist/main/pair/AIPairProgramming.js.map +1 -0
  127. package/dist/main/plugins/PluginManager.js +222 -0
  128. package/dist/main/plugins/PluginManager.js.map +1 -0
  129. package/dist/main/plugins/PluginMarketplace.js +237 -0
  130. package/dist/main/plugins/PluginMarketplace.js.map +1 -0
  131. package/dist/main/preload.js +189 -0
  132. package/dist/main/preload.js.map +1 -0
  133. package/dist/main/preview/PreviewSessionManager.js +170 -0
  134. package/dist/main/preview/PreviewSessionManager.js.map +1 -0
  135. package/dist/main/providers/AIProviderManager.js +327 -0
  136. package/dist/main/providers/AIProviderManager.js.map +1 -0
  137. package/dist/main/providers/FineTuningManager.js +276 -0
  138. package/dist/main/providers/FineTuningManager.js.map +1 -0
  139. package/dist/main/providers/FreeModelsProvider.js +1104 -0
  140. package/dist/main/providers/FreeModelsProvider.js.map +1 -0
  141. package/dist/main/realtime/RealtimeManager.js +116 -0
  142. package/dist/main/realtime/RealtimeManager.js.map +1 -0
  143. package/dist/main/remote/CloudEnvironmentManager.js +232 -0
  144. package/dist/main/remote/CloudEnvironmentManager.js.map +1 -0
  145. package/dist/main/remote/RemoteSessionManager.js +255 -0
  146. package/dist/main/remote/RemoteSessionManager.js.map +1 -0
  147. package/dist/main/search/DeepResearchManager.js +335 -0
  148. package/dist/main/search/DeepResearchManager.js.map +1 -0
  149. package/dist/main/search/WebSearchIntegration.js +147 -0
  150. package/dist/main/search/WebSearchIntegration.js.map +1 -0
  151. package/dist/main/security/AdminConsoleManager.js +223 -0
  152. package/dist/main/security/AdminConsoleManager.js.map +1 -0
  153. package/dist/main/security/AuditLogger.js +136 -0
  154. package/dist/main/security/AuditLogger.js.map +1 -0
  155. package/dist/main/security/PermissionManager.js +144 -0
  156. package/dist/main/security/PermissionManager.js.map +1 -0
  157. package/dist/main/security/SSOManager.js +173 -0
  158. package/dist/main/security/SSOManager.js.map +1 -0
  159. package/dist/main/security/SecurityManager.js +152 -0
  160. package/dist/main/security/SecurityManager.js.map +1 -0
  161. package/dist/main/skills/SkillsManager.js +223 -0
  162. package/dist/main/skills/SkillsManager.js.map +1 -0
  163. package/dist/main/ssh/SSHManager.js +65 -0
  164. package/dist/main/ssh/SSHManager.js.map +1 -0
  165. package/dist/main/streaming/StreamingManager.js +225 -0
  166. package/dist/main/streaming/StreamingManager.js.map +1 -0
  167. package/dist/main/sync/CloudSyncManager.js +422 -0
  168. package/dist/main/sync/CloudSyncManager.js.map +1 -0
  169. package/dist/main/types.js +28 -0
  170. package/dist/main/types.js.map +1 -0
  171. package/dist/main/verification/AutoVerifyManager.js +235 -0
  172. package/dist/main/verification/AutoVerifyManager.js.map +1 -0
  173. package/dist/main/vision/ComputerUseManager.js +376 -0
  174. package/dist/main/vision/ComputerUseManager.js.map +1 -0
  175. package/dist/main/vision/ImageVideoGenerationManager.js +401 -0
  176. package/dist/main/vision/ImageVideoGenerationManager.js.map +1 -0
  177. package/dist/main/vision/VisionManager.js +172 -0
  178. package/dist/main/vision/VisionManager.js.map +1 -0
  179. package/dist/renderer/assets/main-DJlZQBCA.js +304 -0
  180. package/dist/renderer/assets/main-N33ZXEr8.css +1 -0
  181. package/dist/renderer/index.html +21 -0
  182. package/dist/renderer/manifest.json +42 -0
  183. package/dist/renderer/sw.ts +109 -0
  184. package/dist/shared/types.js +35 -0
  185. package/dist/shared/types.js.map +1 -0
  186. package/docker-compose.yml +65 -0
  187. package/docs/API.md +307 -0
  188. package/docs/USER_GUIDE.md +476 -0
  189. package/examples/plugins/sample-plugin/package.json +41 -0
  190. package/examples/plugins/sample-plugin/src/index.ts +75 -0
  191. package/index.html +20 -0
  192. package/jest.config.js +39 -0
  193. package/package.json +180 -0
  194. package/packages/cli/package.json +29 -0
  195. package/packages/cli/src/commands/agents.ts +199 -0
  196. package/packages/cli/src/commands/tasks.ts +61 -0
  197. package/packages/cli/src/index.ts +91 -0
  198. package/packages/cli/src/utils/api.ts +45 -0
  199. package/packages/cli/src/utils/config.ts +61 -0
  200. package/packages/npm-installer/bin/codex-linux +126 -0
  201. package/packages/npm-installer/lib/download.js +273 -0
  202. package/packages/npm-installer/package.json +42 -0
  203. package/packages/vscode-extension/package.json +167 -0
  204. package/packages/vscode-extension/src/api.ts +68 -0
  205. package/packages/vscode-extension/src/extension.ts +161 -0
  206. package/packages/vscode-extension/src/panels/chatPanel.ts +265 -0
  207. package/packages/vscode-extension/src/panels/createAgentPanel.ts +227 -0
  208. package/packages/vscode-extension/src/providers/agentsProvider.ts +80 -0
  209. package/postcss.config.js +6 -0
  210. package/public/manifest.json +42 -0
  211. package/public/sw.ts +109 -0
  212. package/scripts/install-dev.sh +103 -0
  213. package/scripts/install.sh +275 -0
  214. package/src/main/DatabaseManager.ts +950 -0
  215. package/src/main/SettingsManager.ts +63 -0
  216. package/src/main/agents/AgentOrchestrator.ts +930 -0
  217. package/src/main/agents/AgentSDK.ts +269 -0
  218. package/src/main/agents/AgentTools.ts +380 -0
  219. package/src/main/agents/CodeIndex.ts +240 -0
  220. package/src/main/agents/EmbeddingService.ts +88 -0
  221. package/src/main/agents/NativeToolCalling.ts +245 -0
  222. package/src/main/api/APIServer.ts +316 -0
  223. package/src/main/api/RateLimiter.ts +165 -0
  224. package/src/main/api/WebSocketManager.ts +398 -0
  225. package/src/main/assistant/ContextOptimizer.ts +214 -0
  226. package/src/main/assistant/PredictedOutputManager.ts +265 -0
  227. package/src/main/assistant/PromptCacheManager.ts +280 -0
  228. package/src/main/assistant/PromptOptimizer.ts +746 -0
  229. package/src/main/assistant/SmartCodeAssistant.ts +234 -0
  230. package/src/main/auth/SessionManager.ts +415 -0
  231. package/src/main/automations/AdvancedWebhookSystem.ts +281 -0
  232. package/src/main/automations/AutomationScheduler.ts +272 -0
  233. package/src/main/automations/BatchProcessingSystem.ts +207 -0
  234. package/src/main/automations/BrowserAutomationManager.ts +203 -0
  235. package/src/main/automations/GitHubActionsManager.ts +151 -0
  236. package/src/main/automations/GitLabCIManager.ts +206 -0
  237. package/src/main/automations/PriorityQueueManager.ts +328 -0
  238. package/src/main/background/BackgroundModeManager.ts +130 -0
  239. package/src/main/backup/BackupManager.ts +287 -0
  240. package/src/main/backup/MigrationManager.ts +132 -0
  241. package/src/main/commands/SlashCommandManager.ts +407 -0
  242. package/src/main/config/ClaudeMdParser.ts +539 -0
  243. package/src/main/config/CustomizationManager.ts +493 -0
  244. package/src/main/config/LaunchConfigManager.ts +212 -0
  245. package/src/main/config/SettingsManager.ts +163 -0
  246. package/src/main/connectors/ConnectorManager.ts +175 -0
  247. package/src/main/connectors/DatabaseConnector.ts +212 -0
  248. package/src/main/cowork/CoworkManager.ts +431 -0
  249. package/src/main/evals/AgentEvalFramework.ts +665 -0
  250. package/src/main/evals/GraderManager.ts +417 -0
  251. package/src/main/git/GitWorktreeManager.ts +211 -0
  252. package/src/main/github/GitHubPRMonitor.ts +317 -0
  253. package/src/main/ide/ContinueInManager.ts +180 -0
  254. package/src/main/ide/IDEIntegration.ts +288 -0
  255. package/src/main/integrations/LinearManager.ts +327 -0
  256. package/src/main/integrations/SlackBotManager.ts +312 -0
  257. package/src/main/lsp/LSPManager.ts +445 -0
  258. package/src/main/main.ts +1221 -0
  259. package/src/main/mcp/MCPConfigurationManager.ts +281 -0
  260. package/src/main/mcp/MCPManager.ts +799 -0
  261. package/src/main/mcp/MCPRegistry.ts +273 -0
  262. package/src/main/monitoring/ErrorRecoveryManager.ts +359 -0
  263. package/src/main/monitoring/ErrorTracker.ts +60 -0
  264. package/src/main/monitoring/MetricsCollector.ts +196 -0
  265. package/src/main/monitoring/TraceGradingSystem.ts +196 -0
  266. package/src/main/notifications/NotificationManager.ts +96 -0
  267. package/src/main/pair/AIPairProgramming.ts +290 -0
  268. package/src/main/plugins/PluginManager.ts +266 -0
  269. package/src/main/plugins/PluginMarketplace.ts +318 -0
  270. package/src/main/preload.ts +215 -0
  271. package/src/main/preview/PreviewSessionManager.ts +186 -0
  272. package/src/main/providers/AIProviderManager.ts +394 -0
  273. package/src/main/providers/FineTuningManager.ts +390 -0
  274. package/src/main/providers/FreeModelsProvider.ts +1156 -0
  275. package/src/main/realtime/RealtimeManager.ts +147 -0
  276. package/src/main/remote/CloudEnvironmentManager.ts +253 -0
  277. package/src/main/remote/RemoteSessionManager.ts +323 -0
  278. package/src/main/search/DeepResearchManager.ts +458 -0
  279. package/src/main/search/WebSearchIntegration.ts +203 -0
  280. package/src/main/security/AdminConsoleManager.ts +244 -0
  281. package/src/main/security/AuditLogger.ts +143 -0
  282. package/src/main/security/PermissionManager.ts +184 -0
  283. package/src/main/security/SSOManager.ts +241 -0
  284. package/src/main/security/SecurityManager.ts +139 -0
  285. package/src/main/skills/SkillsManager.ts +218 -0
  286. package/src/main/ssh/SSHManager.ts +86 -0
  287. package/src/main/streaming/StreamingManager.ts +306 -0
  288. package/src/main/sync/CloudSyncManager.ts +532 -0
  289. package/src/main/verification/AutoVerifyManager.ts +285 -0
  290. package/src/main/vision/ComputerUseManager.ts +475 -0
  291. package/src/main/vision/ImageVideoGenerationManager.ts +526 -0
  292. package/src/main/vision/VisionManager.ts +186 -0
  293. package/src/renderer/App.tsx +314 -0
  294. package/src/renderer/components/AdvancedSettingsPanel.tsx +225 -0
  295. package/src/renderer/components/AgentPanel.tsx +760 -0
  296. package/src/renderer/components/AppPreview.tsx +220 -0
  297. package/src/renderer/components/AuditTrailPanel.tsx +148 -0
  298. package/src/renderer/components/AutomationPanel.tsx +220 -0
  299. package/src/renderer/components/ChatInterface.tsx +595 -0
  300. package/src/renderer/components/ChatTab.tsx +296 -0
  301. package/src/renderer/components/CodeEditor.tsx +257 -0
  302. package/src/renderer/components/CodeReviewPanel.tsx +256 -0
  303. package/src/renderer/components/CodeWorkspace.tsx +192 -0
  304. package/src/renderer/components/CodebaseDashboard.tsx +295 -0
  305. package/src/renderer/components/ComputerUsePanel.tsx +262 -0
  306. package/src/renderer/components/ConnectorsPanel.tsx +471 -0
  307. package/src/renderer/components/ContextMenu.tsx +155 -0
  308. package/src/renderer/components/ContextUsageDisplay.tsx +248 -0
  309. package/src/renderer/components/CoworkPanel.tsx +415 -0
  310. package/src/renderer/components/DiffViewer.tsx +452 -0
  311. package/src/renderer/components/ErrorBoundary.tsx +273 -0
  312. package/src/renderer/components/ExtendedThinkingToggle.tsx +244 -0
  313. package/src/renderer/components/FileAttachments.tsx +247 -0
  314. package/src/renderer/components/FileExplorer.tsx +242 -0
  315. package/src/renderer/components/FileExplorerPanel.tsx +302 -0
  316. package/src/renderer/components/GitPanel.tsx +154 -0
  317. package/src/renderer/components/Header.tsx +113 -0
  318. package/src/renderer/components/MCPPanel.tsx +326 -0
  319. package/src/renderer/components/MentionAutocomplete.tsx +239 -0
  320. package/src/renderer/components/PermissionPanel.tsx +159 -0
  321. package/src/renderer/components/PermissionSelector.tsx +203 -0
  322. package/src/renderer/components/PluginMarketplace.tsx +325 -0
  323. package/src/renderer/components/PromptOptimizerPanel.tsx +399 -0
  324. package/src/renderer/components/SearchPanel.tsx +173 -0
  325. package/src/renderer/components/SearchReplace.tsx +284 -0
  326. package/src/renderer/components/SessionSidebar.tsx +367 -0
  327. package/src/renderer/components/SettingsPanel.tsx +426 -0
  328. package/src/renderer/components/Sidebar.tsx +100 -0
  329. package/src/renderer/components/SkillsPanel.tsx +245 -0
  330. package/src/renderer/components/SplitPane.tsx +173 -0
  331. package/src/renderer/components/Terminal.tsx +190 -0
  332. package/src/renderer/components/VoiceCommand.tsx +129 -0
  333. package/src/renderer/components/WorktreePanel.tsx +163 -0
  334. package/src/renderer/components/ui/AriaComponents.tsx +193 -0
  335. package/src/renderer/components/ui/Button.tsx +68 -0
  336. package/src/renderer/components/ui/Card.tsx +102 -0
  337. package/src/renderer/components/ui/Input.tsx +44 -0
  338. package/src/renderer/components/ui/Skeleton.tsx +55 -0
  339. package/src/renderer/components/ui/VirtualList.tsx +196 -0
  340. package/src/renderer/i18n/I18nProvider.tsx +101 -0
  341. package/src/renderer/i18n/de.ts +161 -0
  342. package/src/renderer/i18n/en.ts +163 -0
  343. package/src/renderer/i18n/es.ts +161 -0
  344. package/src/renderer/i18n/fr.ts +161 -0
  345. package/src/renderer/i18n/index.ts +44 -0
  346. package/src/renderer/index.css +129 -0
  347. package/src/renderer/lib/accessibility.tsx +287 -0
  348. package/src/renderer/lib/hooks.ts +304 -0
  349. package/src/renderer/lib/utils.ts +6 -0
  350. package/src/renderer/main.tsx +25 -0
  351. package/src/renderer/styles/minimalist.css +539 -0
  352. package/src/renderer/sw.ts +180 -0
  353. package/src/renderer/types.d.ts +138 -0
  354. package/src/shared/types.ts +813 -0
  355. package/supabase/schema.sql +234 -0
  356. package/tailwind.config.js +78 -0
  357. package/tests/e2e/package.json +15 -0
  358. package/tests/e2e/playwright.config.ts +31 -0
  359. package/tests/e2e/specs/app.spec.ts +194 -0
  360. package/tests/setup.ts +99 -0
  361. package/tests/unit/AgentOrchestrator.test.ts +274 -0
  362. package/tests/unit/DatabaseManager.test.ts +262 -0
  363. package/tests/unit/GitWorktreeManager.test.ts +150 -0
  364. package/tests/unit/SecurityManager.test.ts +110 -0
  365. package/tsconfig.main.json +22 -0
  366. package/tsconfig.renderer.json +27 -0
  367. package/vite.config.ts +28 -0
@@ -0,0 +1,595 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { Agent, AgentMessage, AgentStatus, PermissionMode } from '../../shared/types';
3
+ import { Send, Bot, User, Loader2, Paperclip, MoreVertical, Copy, Check, ChevronDown, Sparkles, Zap } from 'lucide-react';
4
+ import { format } from 'date-fns';
5
+ import { PermissionSelector } from './PermissionSelector';
6
+
7
+ interface ModelOption {
8
+ id: string;
9
+ name: string;
10
+ backend: string;
11
+ isFree?: boolean;
12
+ contextWindow?: number;
13
+ supportsVision?: boolean;
14
+ }
15
+
16
+ interface ChatInterfaceProps {
17
+ agent: Agent;
18
+ onSendMessage: (message: string) => Promise<void>;
19
+ onExecuteTask: (task: string) => Promise<void>;
20
+ onPermissionModeChange?: (mode: PermissionMode) => void;
21
+ onModelChange?: (modelId: string) => void;
22
+ allowBypassMode?: boolean;
23
+ isLoading?: boolean;
24
+ availableModels?: ModelOption[];
25
+ currentModel?: string;
26
+ }
27
+
28
+ const FREE_MODELS: ModelOption[] = [
29
+ { id: 'meta-llama/llama-3.3-70b-instruct:free', name: 'Llama 3.3 70B', backend: 'openrouter', isFree: true, contextWindow: 128000 },
30
+ { id: 'deepseek/deepseek-r1-0528:free', name: 'DeepSeek R1', backend: 'openrouter', isFree: true, contextWindow: 128000 },
31
+ { id: 'google/gemma-3-27b-it:free', name: 'Gemma 3 27B', backend: 'openrouter', isFree: true, contextWindow: 32768 },
32
+ { id: 'google/gemma-3-12b-it:free', name: 'Gemma 3 12B', backend: 'openrouter', isFree: true, contextWindow: 32768 },
33
+ { id: 'mistralai/mistral-small-3.1-24b-instruct:free', name: 'Mistral Small 3.1', backend: 'openrouter', isFree: true, contextWindow: 128000, supportsVision: true },
34
+ { id: 'qwen/qwen3-coder:free', name: 'Qwen 3 Coder', backend: 'openrouter', isFree: true, contextWindow: 32768 },
35
+ { id: 'nousresearch/hermes-3-llama-3.1-405b:free', name: 'Hermes 3 405B', backend: 'openrouter', isFree: true, contextWindow: 128000 },
36
+ { id: 'openai/gpt-oss-120b:free', name: 'GPT-OSS 120B', backend: 'openrouter', isFree: true, contextWindow: 128000 },
37
+ { id: 'nvidia/nemotron-nano-12b-v2-vl:free', name: 'Nemotron 12B VL', backend: 'openrouter', isFree: true, contextWindow: 32768, supportsVision: true },
38
+ { id: 'z-ai/glm-4.5-air:free', name: 'GLM 4.5 Air', backend: 'openrouter', isFree: true, contextWindow: 128000 },
39
+ { id: 'liquid/lfm-2.5-1.2b-instruct:free', name: 'LFM 2.5 1.2B', backend: 'openrouter', isFree: true, contextWindow: 8192 },
40
+ { id: 'upstage/solar-pro-3:free', name: 'Solar Pro 3', backend: 'openrouter', isFree: true, contextWindow: 32768 },
41
+ { id: 'moonshotai/kimi-k2.5', name: 'Kimi K2.5', backend: 'nvidia', isFree: true, contextWindow: 128000 },
42
+ { id: 'meta/llama-3.3-70b-instruct', name: 'Llama 3.3 70B (NVIDIA)', backend: 'nvidia', isFree: true, contextWindow: 128000 },
43
+ { id: 'google/gemma-2-27b-it', name: 'Gemma 2 27B (NVIDIA)', backend: 'nvidia', isFree: true, contextWindow: 8192 },
44
+ { id: 'microsoft/phi-3-medium-128k-instruct', name: 'Phi-3 Medium 128K', backend: 'nvidia', isFree: true, contextWindow: 128000 },
45
+ { id: 'llama-3.3-70b-versatile', name: 'Llama 3.3 70B (Groq)', backend: 'groq', isFree: true, contextWindow: 128000 },
46
+ { id: 'llama-3.1-8b-instant', name: 'Llama 3.1 8B (Groq)', backend: 'groq', isFree: true, contextWindow: 128000 },
47
+ { id: 'mixtral-8x7b-32768', name: 'Mixtral 8x7B (Groq)', backend: 'groq', isFree: true, contextWindow: 32768 },
48
+ { id: 'gemma2-9b-it', name: 'Gemma 2 9B (Groq)', backend: 'groq', isFree: true, contextWindow: 8192 },
49
+ { id: 'minimax-m2.5:cloud', name: 'Minimax M2.5 (Ollama)', backend: 'ollama', isFree: true, contextWindow: 128000 },
50
+ { id: 'llama3.2:latest', name: 'Llama 3.2 (Ollama)', backend: 'ollama', isFree: true, contextWindow: 128000, supportsVision: true },
51
+ { id: 'llama3.3:latest', name: 'Llama 3.3 70B (Ollama)', backend: 'ollama', isFree: true, contextWindow: 128000 },
52
+ { id: 'mistral:latest', name: 'Mistral 7B (Ollama)', backend: 'ollama', isFree: true, contextWindow: 32768 },
53
+ { id: 'deepseek-r1:latest', name: 'DeepSeek R1 (Ollama)', backend: 'ollama', isFree: true, contextWindow: 128000 },
54
+ { id: 'qwen2.5:latest', name: 'Qwen 2.5 (Ollama)', backend: 'ollama', isFree: true, contextWindow: 128000 },
55
+ { id: 'qwen2.5-coder:latest', name: 'Qwen 2.5 Coder (Ollama)', backend: 'ollama', isFree: true, contextWindow: 32768 },
56
+ { id: 'gemma3:latest', name: 'Gemma 3 (Ollama)', backend: 'ollama', isFree: true, contextWindow: 32768, supportsVision: true },
57
+ { id: 'phi3:latest', name: 'Phi-3 (Ollama)', backend: 'ollama', isFree: true, contextWindow: 128000 },
58
+ { id: 'codellama:latest', name: 'Code Llama (Ollama)', backend: 'ollama', isFree: true, contextWindow: 16384 },
59
+ { id: 'deepseek-coder:latest', name: 'DeepSeek Coder (Ollama)', backend: 'ollama', isFree: true, contextWindow: 16384 },
60
+ ];
61
+
62
+ const BACKEND_LABELS: Record<string, string> = {
63
+ openrouter: 'OpenRouter',
64
+ nvidia: 'NVIDIA NIM',
65
+ groq: 'Groq',
66
+ ollama: 'Ollama',
67
+ google: 'Google AI',
68
+ cerebras: 'Cerebras',
69
+ };
70
+
71
+ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
72
+ agent,
73
+ onSendMessage,
74
+ onExecuteTask,
75
+ onPermissionModeChange,
76
+ onModelChange,
77
+ allowBypassMode = false,
78
+ isLoading = false,
79
+ availableModels,
80
+ currentModel,
81
+ }) => {
82
+ const [input, setInput] = useState('');
83
+ const [copiedId, setCopiedId] = useState<string | null>(null);
84
+ const [expandedAutoContext, setExpandedAutoContext] = useState<Record<string, boolean>>({});
85
+ const [showModelSelector, setShowModelSelector] = useState(false);
86
+ const [selectedModel, setSelectedModel] = useState(currentModel || agent.model);
87
+ const [searchQuery, setSearchQuery] = useState('');
88
+ const [selectedBackend, setSelectedBackend] = useState<string>('all');
89
+ const messagesEndRef = useRef<HTMLDivElement>(null);
90
+ const inputRef = useRef<HTMLTextAreaElement>(null);
91
+ const modelSelectorRef = useRef<HTMLDivElement>(null);
92
+
93
+ const models = availableModels || FREE_MODELS;
94
+
95
+ useEffect(() => {
96
+ scrollToBottom();
97
+ }, [agent.messages]);
98
+
99
+ useEffect(() => {
100
+ const handleClickOutside = (event: MouseEvent) => {
101
+ if (modelSelectorRef.current && !modelSelectorRef.current.contains(event.target as Node)) {
102
+ setShowModelSelector(false);
103
+ }
104
+ };
105
+ document.addEventListener('mousedown', handleClickOutside);
106
+ return () => document.removeEventListener('mousedown', handleClickOutside);
107
+ }, []);
108
+
109
+ useEffect(() => {
110
+ if (currentModel) {
111
+ setSelectedModel(currentModel);
112
+ }
113
+ }, [currentModel]);
114
+
115
+ const scrollToBottom = () => {
116
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
117
+ };
118
+
119
+ const handleModelSelect = (modelId: string) => {
120
+ setSelectedModel(modelId);
121
+ setShowModelSelector(false);
122
+ onModelChange?.(modelId);
123
+ };
124
+
125
+ const filteredModels = models.filter(model => {
126
+ const matchesSearch = model.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
127
+ model.id.toLowerCase().includes(searchQuery.toLowerCase());
128
+ const matchesBackend = selectedBackend === 'all' || model.backend === selectedBackend;
129
+ return matchesSearch && matchesBackend;
130
+ });
131
+
132
+ const groupedModels = filteredModels.reduce((acc, model) => {
133
+ const backend = model.backend || 'other';
134
+ if (!acc[backend]) acc[backend] = [];
135
+ acc[backend].push(model);
136
+ return acc;
137
+ }, {} as Record<string, ModelOption[]>);
138
+
139
+ const getCurrentModelInfo = () => {
140
+ return models.find(m => m.id === selectedModel) || { name: selectedModel, backend: 'unknown' };
141
+ };
142
+
143
+ const handleSend = async () => {
144
+ if (!input.trim() || isLoading) return;
145
+
146
+ const message = input;
147
+ setInput('');
148
+
149
+ if (message.startsWith('/task ')) {
150
+ await onExecuteTask(message.slice(6));
151
+ } else if (message.startsWith('/search ')) {
152
+ const searchQuery = message.slice(8);
153
+ await onSendMessage(`[SEARCH] ${searchQuery}`);
154
+ } else if (message.startsWith('/vision ') || message.startsWith('/analyze ')) {
155
+ const visionQuery = message.split(' ').slice(1).join(' ');
156
+ await onSendMessage(`[VISION] ${visionQuery}`);
157
+ } else {
158
+ await onSendMessage(message);
159
+ }
160
+ };
161
+
162
+ const handleImageUpload = async (file: File) => {
163
+ if (isLoading) return;
164
+
165
+ try {
166
+ const reader = new FileReader();
167
+ reader.onloadend = async () => {
168
+ const base64 = reader.result as string;
169
+ await onSendMessage(`[IMAGE] ${base64}`);
170
+ };
171
+ reader.readAsDataURL(file);
172
+ } catch (error) {
173
+ console.error('Failed to upload image:', error);
174
+ }
175
+ };
176
+
177
+ const handleKeyDown = (e: React.KeyboardEvent) => {
178
+ if (e.key === 'Enter' && !e.shiftKey) {
179
+ e.preventDefault();
180
+ handleSend();
181
+ }
182
+ };
183
+
184
+ const copyToClipboard = async (text: string, messageId: string) => {
185
+ await navigator.clipboard.writeText(text);
186
+ setCopiedId(messageId);
187
+ setTimeout(() => setCopiedId(null), 2000);
188
+ };
189
+
190
+ const getAutoContext = (message: AgentMessage): null | { files: Array<{ path: string; reason: string }>; totalChars?: number } => {
191
+ const raw = (message as any)?.metadata?.autoContext;
192
+ if (!raw || typeof raw !== 'object') return null;
193
+ const files = Array.isArray(raw.files) ? raw.files : [];
194
+ return {
195
+ files: files
196
+ .filter((f: any) => f && typeof f.path === 'string')
197
+ .map((f: any) => ({ path: String(f.path), reason: String(f.reason || '') })),
198
+ totalChars: typeof raw.totalChars === 'number' ? raw.totalChars : undefined,
199
+ };
200
+ };
201
+
202
+ const renderMessageContent = (content: string) => {
203
+ const lines = content.split('\n');
204
+ return lines.map((line, index) => {
205
+ if (line.startsWith('```')) {
206
+ return null;
207
+ }
208
+
209
+ if (line.includes('`')) {
210
+ const parts = line.split(/(`[^`]+`)/);
211
+ return (
212
+ <p key={index} className="my-1">
213
+ {parts.map((part, i) =>
214
+ part.startsWith('`') && part.endsWith('`') ? (
215
+ <code key={i} className="bg-muted px-1 py-0.5 rounded text-sm font-mono">
216
+ {part.slice(1, -1)}
217
+ </code>
218
+ ) : (
219
+ part
220
+ )
221
+ )}
222
+ </p>
223
+ );
224
+ }
225
+
226
+ if (line.startsWith('### ')) {
227
+ return <h3 key={index} className="text-lg font-semibold mt-4 mb-2">{line.slice(4)}</h3>;
228
+ }
229
+ if (line.startsWith('## ')) {
230
+ return <h2 key={index} className="text-xl font-semibold mt-4 mb-2">{line.slice(3)}</h2>;
231
+ }
232
+ if (line.startsWith('# ')) {
233
+ return <h1 key={index} className="text-2xl font-bold mt-4 mb-2">{line.slice(2)}</h1>;
234
+ }
235
+
236
+ if (line.startsWith('- ') || line.startsWith('* ')) {
237
+ return <li key={index} className="ml-4 my-1">{line.slice(2)}</li>;
238
+ }
239
+
240
+ if (line.trim() === '') {
241
+ return <br key={index} />;
242
+ }
243
+
244
+ return <p key={index} className="my-1">{line}</p>;
245
+ });
246
+ };
247
+
248
+ const extractCodeBlocks = (content: string) => {
249
+ const codeBlocks: { language: string; code: string }[] = [];
250
+ const regex = /```(\w+)?\n([\s\S]*?)```/g;
251
+ let match: RegExpExecArray | null;
252
+ while ((match = regex.exec(content)) !== null) {
253
+ codeBlocks.push({
254
+ language: match[1] || 'text',
255
+ code: match[2].trim()
256
+ });
257
+ }
258
+ return codeBlocks;
259
+ };
260
+
261
+ const currentModelInfo = getCurrentModelInfo();
262
+
263
+ return (
264
+ <div className="flex flex-col h-full bg-card">
265
+ {/* Chat Header */}
266
+ <div className="px-4 py-3 border-b border-border flex items-center justify-between bg-background/50">
267
+ <div className="flex items-center gap-3">
268
+ <div className={`w-2 h-2 rounded-full ${
269
+ agent.status === 'running' ? 'bg-green-500 animate-pulse' :
270
+ agent.status === 'error' ? 'bg-red-500' :
271
+ agent.status === 'paused' ? 'bg-yellow-500' :
272
+ 'bg-gray-400'
273
+ }`} />
274
+ <div>
275
+ <h3 className="font-medium">{agent.name}</h3>
276
+ </div>
277
+ </div>
278
+
279
+ {/* Model Selector */}
280
+ <div className="relative" ref={modelSelectorRef}>
281
+ <button
282
+ onClick={() => setShowModelSelector(!showModelSelector)}
283
+ className="flex items-center gap-2 px-3 py-1.5 bg-muted hover:bg-muted/80 rounded-lg transition-colors text-sm"
284
+ >
285
+ <Sparkles className="w-4 h-4 text-green-500" />
286
+ <span className="font-medium truncate max-w-[150px]">{currentModelInfo.name}</span>
287
+ <span className="text-xs text-muted-foreground px-1.5 py-0.5 bg-background rounded">
288
+ {BACKEND_LABELS[currentModelInfo.backend || 'unknown'] || currentModelInfo.backend}
289
+ </span>
290
+ <ChevronDown className={`w-4 h-4 transition-transform ${showModelSelector ? 'rotate-180' : ''}`} />
291
+ </button>
292
+
293
+ {showModelSelector && (
294
+ <div className="absolute top-full right-0 mt-2 w-80 bg-background border border-border rounded-lg shadow-xl z-50 max-h-[70vh] overflow-hidden">
295
+ <div className="p-3 border-b border-border">
296
+ <input
297
+ type="text"
298
+ placeholder="Search models..."
299
+ value={searchQuery}
300
+ onChange={(e) => setSearchQuery(e.target.value)}
301
+ className="w-full px-3 py-2 bg-muted rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-primary"
302
+ />
303
+ <div className="flex gap-1 mt-2 flex-wrap">
304
+ <button
305
+ onClick={() => setSelectedBackend('all')}
306
+ className={`px-2 py-1 text-xs rounded-md transition-colors ${
307
+ selectedBackend === 'all' ? 'bg-primary text-primary-foreground' : 'bg-muted hover:bg-muted/80'
308
+ }`}
309
+ >
310
+ All
311
+ </button>
312
+ {Object.entries(BACKEND_LABELS).map(([key, label]) => (
313
+ <button
314
+ key={key}
315
+ onClick={() => setSelectedBackend(key)}
316
+ className={`px-2 py-1 text-xs rounded-md transition-colors ${
317
+ selectedBackend === key ? 'bg-primary text-primary-foreground' : 'bg-muted hover:bg-muted/80'
318
+ }`}
319
+ >
320
+ {label}
321
+ </button>
322
+ ))}
323
+ </div>
324
+ </div>
325
+
326
+ <div className="overflow-y-auto max-h-[50vh]">
327
+ {Object.entries(groupedModels).map(([backend, backendModels]) => (
328
+ <div key={backend}>
329
+ <div className="px-3 py-2 bg-muted/50 text-xs font-medium text-muted-foreground sticky top-0">
330
+ {BACKEND_LABELS[backend] || backend} ({backendModels.length})
331
+ </div>
332
+ {backendModels.map((model) => (
333
+ <button
334
+ key={model.id}
335
+ onClick={() => handleModelSelect(model.id)}
336
+ className={`w-full px-3 py-2 text-left hover:bg-muted/50 transition-colors flex items-center justify-between ${
337
+ selectedModel === model.id ? 'bg-primary/10' : ''
338
+ }`}
339
+ >
340
+ <div className="flex-1 min-w-0">
341
+ <div className="flex items-center gap-2">
342
+ <span className="font-medium text-sm truncate">{model.name}</span>
343
+ {model.isFree && (
344
+ <span className="px-1.5 py-0.5 text-[10px] bg-green-500/20 text-green-500 rounded">
345
+ FREE
346
+ </span>
347
+ )}
348
+ {model.supportsVision && (
349
+ <span className="px-1.5 py-0.5 text-[10px] bg-purple-500/20 text-purple-500 rounded">
350
+ VISION
351
+ </span>
352
+ )}
353
+ </div>
354
+ <div className="text-xs text-muted-foreground truncate">{model.id}</div>
355
+ </div>
356
+ {model.contextWindow && model.contextWindow >= 100000 && (
357
+ <span className="text-xs text-muted-foreground ml-2">
358
+ {model.contextWindow >= 1000000 ? '1M' : `${model.contextWindow / 1000}K`}
359
+ </span>
360
+ )}
361
+ {selectedModel === model.id && (
362
+ <Check className="w-4 h-4 text-primary ml-2" />
363
+ )}
364
+ </button>
365
+ ))}
366
+ </div>
367
+ ))}
368
+ </div>
369
+ </div>
370
+ )}
371
+ </div>
372
+
373
+ <div className="flex items-center gap-3">
374
+ {onPermissionModeChange && (
375
+ <PermissionSelector
376
+ currentMode={agent.permissionMode}
377
+ onModeChange={onPermissionModeChange}
378
+ allowBypass={allowBypassMode}
379
+ />
380
+ )}
381
+ <span className="text-xs text-muted-foreground">
382
+ {agent.messages.length} messages
383
+ </span>
384
+ <button className="p-2 hover:bg-muted rounded-md">
385
+ <MoreVertical className="w-4 h-4" />
386
+ </button>
387
+ </div>
388
+ </div>
389
+
390
+ {/* Messages */}
391
+ <div className="flex-1 overflow-y-auto p-4 space-y-4">
392
+ {agent.messages.length === 0 ? (
393
+ <div className="flex flex-col items-center justify-center h-full text-muted-foreground">
394
+ <Bot className="w-16 h-16 mb-4 opacity-30" />
395
+ <p className="text-lg font-medium">Start a conversation</p>
396
+ <p className="text-sm mt-2">Send a message or use /task to assign a task</p>
397
+ <div className="mt-6 space-y-2 text-sm">
398
+ <button
399
+ onClick={() => setInput('Can you help me refactor this code?')}
400
+ className="block px-4 py-2 bg-muted rounded-lg hover:bg-muted/80 transition-colors"
401
+ >
402
+ "Can you help me refactor this code?"
403
+ </button>
404
+ <button
405
+ onClick={() => setInput('/task Review all JavaScript files for potential bugs')}
406
+ className="block px-4 py-2 bg-muted rounded-lg hover:bg-muted/80 transition-colors"
407
+ >
408
+ "/task Review all JavaScript files..."
409
+ </button>
410
+ <button
411
+ onClick={() => setInput('Explain how this function works')}
412
+ className="block px-4 py-2 bg-muted rounded-lg hover:bg-muted/80 transition-colors"
413
+ >
414
+ "Explain how this function works"
415
+ </button>
416
+ </div>
417
+ </div>
418
+ ) : (
419
+ agent.messages.map((message) => (
420
+ <div
421
+ key={message.id}
422
+ className={`group flex gap-3 ${
423
+ message.role === 'user' ? 'flex-row-reverse' : ''
424
+ }`}
425
+ >
426
+ <div className={`w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 ${
427
+ message.role === 'user'
428
+ ? 'bg-primary text-primary-foreground'
429
+ : message.role === 'system'
430
+ ? 'bg-muted text-muted-foreground'
431
+ : 'bg-green-500/10 text-green-500'
432
+ }`}>
433
+ {message.role === 'user' ? (
434
+ <User className="w-4 h-4" />
435
+ ) : message.role === 'system' ? (
436
+ <span className="text-xs font-bold">S</span>
437
+ ) : (
438
+ <Bot className="w-4 h-4" />
439
+ )}
440
+ </div>
441
+
442
+ <div className={`flex-1 max-w-[85%] ${
443
+ message.role === 'user' ? 'items-end' : 'items-start'
444
+ }`}>
445
+ <div className={`relative rounded-2xl px-4 py-3 ${
446
+ message.role === 'user'
447
+ ? 'bg-primary text-primary-foreground'
448
+ : message.role === 'system'
449
+ ? 'bg-muted text-muted-foreground text-sm italic'
450
+ : 'bg-muted'
451
+ }`}>
452
+ <button
453
+ onClick={() => copyToClipboard(message.content, message.id)}
454
+ className="absolute top-2 right-2 p-1.5 opacity-0 group-hover:opacity-100 transition-opacity bg-background/80 rounded-md"
455
+ >
456
+ {copiedId === message.id ? (
457
+ <Check className="w-3 h-3 text-green-500" />
458
+ ) : (
459
+ <Copy className="w-3 h-3" />
460
+ )}
461
+ </button>
462
+
463
+ <div className="prose prose-sm dark:prose-invert max-w-none pr-8">
464
+ {message.role === 'assistant' && (() => {
465
+ const ac = getAutoContext(message);
466
+ if (!ac || ac.files.length === 0) return null;
467
+ const isExpanded = Boolean(expandedAutoContext[message.id]);
468
+ return (
469
+ <div className="mb-3">
470
+ <button
471
+ onClick={() => setExpandedAutoContext(prev => ({ ...prev, [message.id]: !isExpanded }))}
472
+ className="text-xs px-2 py-1 rounded-md bg-background/60 hover:bg-background/80 border border-border text-muted-foreground"
473
+ type="button"
474
+ >
475
+ Auto-context: {ac.files.length} files{typeof ac.totalChars === 'number' ? ` ${ac.totalChars} chars` : ''}
476
+ {isExpanded ? ' (hide)' : ' (show)'}
477
+ </button>
478
+
479
+ {isExpanded && (
480
+ <div className="mt-2 rounded-md border border-border bg-background/50 p-2 text-xs">
481
+ <div className="space-y-1">
482
+ {ac.files.map((f, idx) => (
483
+ <div key={`${message.id}-ac-${idx}`} className="flex gap-2">
484
+ <span className="font-mono text-foreground truncate max-w-[260px]">{f.path}</span>
485
+ <span className="text-muted-foreground">- {f.reason}</span>
486
+ </div>
487
+ ))}
488
+ </div>
489
+ </div>
490
+ )}
491
+ </div>
492
+ );
493
+ })()}
494
+
495
+ {renderMessageContent(message.content)}
496
+ </div>
497
+
498
+ {extractCodeBlocks(message.content).map((block, idx) => (
499
+ <div key={idx} className="mt-4 rounded-lg overflow-hidden border border-border">
500
+ <div className="flex items-center justify-between px-3 py-2 bg-background/50 border-b border-border">
501
+ <span className="text-xs font-medium text-muted-foreground">{block.language}</span>
502
+ <button
503
+ onClick={() => copyToClipboard(block.code, `code-${idx}`)}
504
+ className="p-1 hover:bg-background rounded"
505
+ >
506
+ {copiedId === `code-${idx}` ? (
507
+ <Check className="w-3 h-3 text-green-500" />
508
+ ) : (
509
+ <Copy className="w-3 h-3" />
510
+ )}
511
+ </button>
512
+ </div>
513
+ <pre className="bg-black/50 p-3 rounded-b-lg overflow-x-auto">
514
+ <code className="text-sm font-mono text-green-400">{block.code}</code>
515
+ </pre>
516
+ </div>
517
+ ))}
518
+
519
+ <div className={`text-xs mt-2 ${
520
+ message.role === 'user'
521
+ ? 'text-primary-foreground/60'
522
+ : 'text-muted-foreground'
523
+ }`}>
524
+ {format(new Date(message.timestamp), 'HH:mm')}
525
+ </div>
526
+ </div>
527
+ </div>
528
+ </div>
529
+ ))
530
+ )}
531
+
532
+ {isLoading && (
533
+ <div className="flex gap-3">
534
+ <div className="w-8 h-8 rounded-lg bg-green-500/10 text-green-500 flex items-center justify-center">
535
+ <Bot className="w-4 h-4" />
536
+ </div>
537
+ <div className="bg-muted rounded-2xl px-4 py-3 flex items-center gap-2">
538
+ <Loader2 className="w-4 h-4 animate-spin" />
539
+ <span className="text-sm text-muted-foreground">Thinking...</span>
540
+ </div>
541
+ </div>
542
+ )}
543
+
544
+ <div ref={messagesEndRef} />
545
+ </div>
546
+
547
+ {/* Input Area */}
548
+ <div className="p-4 border-t border-border bg-background/50">
549
+ <div className="relative flex items-end gap-2">
550
+ <button className="p-2 text-muted-foreground hover:text-foreground transition-colors">
551
+ <Paperclip className="w-5 h-5" />
552
+ </button>
553
+
554
+ <div className="flex-1 relative">
555
+ <textarea
556
+ ref={inputRef}
557
+ value={input}
558
+ onChange={e => setInput(e.target.value)}
559
+ onKeyDown={handleKeyDown}
560
+ placeholder="Type a message... (Shift+Enter for new line, /task for tasks)"
561
+ className="w-full px-4 py-3 pr-12 bg-background border border-input rounded-xl resize-none focus:outline-none focus:ring-2 focus:ring-primary min-h-[56px] max-h-[200px]"
562
+ rows={1}
563
+ style={{ height: 'auto' }}
564
+ onInput={(e) => {
565
+ const target = e.target as HTMLTextAreaElement;
566
+ target.style.height = 'auto';
567
+ target.style.height = Math.min(target.scrollHeight, 200) + 'px';
568
+ }}
569
+ />
570
+ </div>
571
+
572
+ <button
573
+ onClick={handleSend}
574
+ disabled={!input.trim() || isLoading}
575
+ className="p-3 bg-primary text-primary-foreground rounded-xl hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
576
+ >
577
+ <Send className="w-5 h-5" />
578
+ </button>
579
+ </div>
580
+
581
+ <div className="flex items-center justify-between mt-2 text-xs text-muted-foreground">
582
+ <div className="flex gap-4">
583
+ <span className="flex items-center gap-1">
584
+ <kbd className="px-1.5 py-0.5 bg-muted rounded">Enter</kbd> to send
585
+ </span>
586
+ <span className="flex items-center gap-1">
587
+ <kbd className="px-1.5 py-0.5 bg-muted rounded">Shift</kbd> + <kbd className="px-1.5 py-0.5 bg-muted rounded">Enter</kbd> for new line
588
+ </span>
589
+ </div>
590
+ <span>{input.length} characters</span>
591
+ </div>
592
+ </div>
593
+ </div>
594
+ );
595
+ };