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,930 @@
1
+ import { EventEmitter } from 'events';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import * as path from 'path';
4
+ import * as fs from 'fs/promises';
5
+ import log from 'electron-log';
6
+ import { Agent, AgentStatus, AgentMessage, AgentTask, TaskStatus, CodeChange, ChangeStatus, PermissionMode, ClaudeMdConfig } from '../../shared/types';
7
+ import { GitWorktreeManager } from '../git/GitWorktreeManager';
8
+ import { SkillsManager } from '../skills/SkillsManager';
9
+ import { DatabaseManager } from '../DatabaseManager';
10
+ import { AIProviderManager } from '../providers/AIProviderManager';
11
+ import { PermissionManager } from '../security/PermissionManager';
12
+ import { ClaudeMdParser } from '../config/ClaudeMdParser';
13
+
14
+ interface AgentConfig {
15
+ name: string;
16
+ projectPath: string;
17
+ providerId: string;
18
+ model: string;
19
+ skills?: string[];
20
+ systemPrompt?: string;
21
+ metadata?: Record<string, any>;
22
+ }
23
+
24
+ interface StreamCallbacks {
25
+ onChunk: (chunk: string) => void;
26
+ onComplete: () => void;
27
+ onError: (error: Error) => void;
28
+ }
29
+
30
+ export class AgentOrchestrator extends EventEmitter {
31
+ private agents: Map<string, Agent> = new Map();
32
+ private activeTasks: Map<string, AbortController> = new Map();
33
+ private lastActivity: Map<string, Date> = new Map();
34
+ private cleanupInterval: NodeJS.Timeout | null = null;
35
+ private readonly INACTIVE_THRESHOLD = 24 * 60 * 60 * 1000; // 24 hours
36
+ private readonly MAX_RETRIES = 3;
37
+ private readonly RETRY_DELAY = 1000; // 1 second
38
+ private readonly AUTO_CONTEXT_MAX_FILES = 12;
39
+ private readonly AUTO_CONTEXT_MAX_CHARS_PER_FILE = 4000;
40
+ private readonly AUTO_CONTEXT_MAX_TOTAL_CHARS = 24000;
41
+
42
+ private permissionManager: PermissionManager;
43
+
44
+ constructor(
45
+ private aiProviderManager: AIProviderManager,
46
+ private gitWorktreeManager: GitWorktreeManager,
47
+ private skillsManager: SkillsManager,
48
+ private dbManager: DatabaseManager
49
+ ) {
50
+ super();
51
+ this.permissionManager = new PermissionManager();
52
+ this.startCleanupInterval();
53
+ }
54
+
55
+ private startCleanupInterval(): void {
56
+ // Cleanup inactive agents every hour
57
+ this.cleanupInterval = setInterval(() => {
58
+ this.cleanupInactiveAgents();
59
+ }, 60 * 60 * 1000);
60
+
61
+ // Don't keep the process alive (important for tests)
62
+ this.cleanupInterval.unref?.();
63
+ }
64
+
65
+ private async getAgentOrThrow(agentId: string): Promise<Agent> {
66
+ const cached = this.agents.get(agentId);
67
+ if (cached) return cached;
68
+
69
+ const fromDb = await this.dbManager.getAgent(agentId);
70
+ if (!fromDb) {
71
+ throw new Error(`Agent ${agentId} not found`);
72
+ }
73
+
74
+ this.agents.set(agentId, fromDb);
75
+ this.lastActivity.set(agentId, fromDb.lastActiveAt || fromDb.updatedAt);
76
+ return fromDb;
77
+ }
78
+
79
+ private async cleanupInactiveAgents(): Promise<void> {
80
+ const now = new Date();
81
+ const agentsToDelete: string[] = [];
82
+
83
+ for (const [agentId, lastActive] of this.lastActivity) {
84
+ const inactiveTime = now.getTime() - lastActive.getTime();
85
+ if (inactiveTime > this.INACTIVE_THRESHOLD) {
86
+ const agent = this.agents.get(agentId);
87
+ if (agent && agent.status !== AgentStatus.RUNNING) {
88
+ agentsToDelete.push(agentId);
89
+ }
90
+ }
91
+ }
92
+
93
+ for (const agentId of agentsToDelete) {
94
+ try {
95
+ await this.deleteAgent(agentId);
96
+ log.info(`Cleaned up inactive agent: ${agentId}`);
97
+ } catch (error) {
98
+ log.error(`Failed to cleanup agent ${agentId}:`, error);
99
+ }
100
+ }
101
+ }
102
+
103
+ async initialize(): Promise<void> {
104
+ // Load existing agents from database
105
+ const agents = await this.dbManager.getAllAgents();
106
+ for (const agent of agents) {
107
+ this.agents.set(agent.id, agent);
108
+ this.lastActivity.set(agent.id, agent.lastActiveAt || agent.updatedAt);
109
+
110
+ if (agent.status === AgentStatus.RUNNING) {
111
+ // Mark as error since we can't restore running state
112
+ agent.status = AgentStatus.ERROR;
113
+ await this.dbManager.updateAgent(agent);
114
+ }
115
+ }
116
+ log.info(`Loaded ${agents.length} agents from database`);
117
+ }
118
+
119
+ async cleanup(): Promise<void> {
120
+ // Stop cleanup interval
121
+ if (this.cleanupInterval) {
122
+ clearInterval(this.cleanupInterval);
123
+ this.cleanupInterval = null;
124
+ }
125
+
126
+ // Stop all running agents
127
+ const cleanupPromises: Promise<void>[] = [];
128
+ for (const [agentId, agent] of this.agents) {
129
+ if (agent.status === AgentStatus.RUNNING) {
130
+ cleanupPromises.push(this.pauseAgent(agentId));
131
+ }
132
+ }
133
+
134
+ await Promise.all(cleanupPromises);
135
+
136
+ // Clear maps
137
+ this.agents.clear();
138
+ this.activeTasks.clear();
139
+ this.lastActivity.clear();
140
+ }
141
+
142
+ async createAgent(config: AgentConfig): Promise<Agent> {
143
+ const agentId = uuidv4();
144
+ const worktreeName = `codex-agent-${agentId.slice(0, 8)}`;
145
+
146
+ // Create worktree for isolation
147
+ const worktree = await this.gitWorktreeManager.createWorktree(config.projectPath, worktreeName);
148
+
149
+ // Load CLAUDE.md configuration
150
+ const claudeMdParser = new ClaudeMdParser();
151
+ const claudeMdConfig = await claudeMdParser.load(config.projectPath);
152
+
153
+ // Merge CLAUDE.md config with provided config
154
+ const mergedConfig: AgentConfig = {
155
+ ...config,
156
+ // Override with CLAUDE.md agent settings if present
157
+ model: claudeMdConfig?.agents?.defaultModel || config.model,
158
+ providerId: claudeMdConfig?.agents?.defaultProvider || config.providerId,
159
+ skills: [...(config.skills || []), ...(claudeMdConfig?.agents?.skills || [])],
160
+ systemPrompt: this.buildSystemPrompt(config.systemPrompt, claudeMdConfig)
161
+ };
162
+
163
+ const agent: Agent = {
164
+ id: agentId,
165
+ name: mergedConfig.name,
166
+ status: AgentStatus.IDLE,
167
+ projectPath: mergedConfig.projectPath,
168
+ worktreeName,
169
+ worktreePath: worktree.path,
170
+ providerId: mergedConfig.providerId,
171
+ model: mergedConfig.model,
172
+ skills: mergedConfig.skills || [],
173
+ permissionMode: claudeMdConfig?.agents?.permissionMode || this.permissionManager.getDefaultMode(),
174
+ createdAt: new Date(),
175
+ updatedAt: new Date(),
176
+ lastActiveAt: null,
177
+ messages: [],
178
+ tasks: [],
179
+ metadata: {
180
+ ...mergedConfig.metadata,
181
+ worktreePath: worktree.path,
182
+ claudeMdConfig: claudeMdConfig ? {
183
+ version: claudeMdConfig.version,
184
+ projectName: claudeMdConfig.project?.name,
185
+ loadedAt: new Date().toISOString()
186
+ } : undefined
187
+ }
188
+ };
189
+
190
+ // Add system message if provided
191
+ if (mergedConfig.systemPrompt) {
192
+ agent.messages.push({
193
+ id: uuidv4(),
194
+ role: 'system',
195
+ content: mergedConfig.systemPrompt,
196
+ timestamp: new Date()
197
+ });
198
+ }
199
+
200
+ // Apply skills
201
+ if (agent.skills.length > 0) {
202
+ await this.applySkillsInternal(agent, agent.skills);
203
+ }
204
+
205
+ this.agents.set(agentId, agent);
206
+ this.lastActivity.set(agentId, new Date());
207
+ await this.dbManager.createAgent(agent);
208
+
209
+ this.emit('agent:created', agent);
210
+ log.info(`Created agent ${agentId} (${agent.name})`);
211
+
212
+ return agent;
213
+ }
214
+
215
+ async listAgents(): Promise<Agent[]> {
216
+ return Array.from(this.agents.values());
217
+ }
218
+
219
+ async getAgent(agentId: string): Promise<Agent | null> {
220
+ return this.agents.get(agentId) || null;
221
+ }
222
+
223
+ async sendMessage(
224
+ agentId: string,
225
+ message: string,
226
+ options?: {
227
+ extendedThinking?: boolean;
228
+ reasoningEffort?: 'low' | 'medium' | 'high';
229
+ }
230
+ ): Promise<AgentMessage> {
231
+ const agent = await this.getAgentOrThrow(agentId);
232
+
233
+ const userMessage: AgentMessage = {
234
+ id: uuidv4(),
235
+ role: 'user',
236
+ content: message,
237
+ timestamp: new Date()
238
+ };
239
+
240
+ agent.messages.push(userMessage);
241
+ agent.lastActiveAt = new Date();
242
+ agent.status = AgentStatus.RUNNING;
243
+ this.lastActivity.set(agentId, new Date());
244
+ await this.dbManager.updateAgent(agent);
245
+
246
+ this.emit('agent:message', { agentId, message: userMessage });
247
+
248
+ // Get AI response with retry logic
249
+ try {
250
+ const autoContext = await this.buildAutoContext(agent, message);
251
+ const response = await this.getAIResponseWithRetry(agent, options, autoContext?.systemMessage);
252
+
253
+ const assistantMessage: AgentMessage = {
254
+ id: uuidv4(),
255
+ role: 'assistant',
256
+ content: response.content,
257
+ timestamp: new Date(),
258
+ metadata: {
259
+ ...response.metadata,
260
+ autoContext: autoContext?.metadata
261
+ }
262
+ };
263
+
264
+ agent.messages.push(assistantMessage);
265
+ agent.status = AgentStatus.IDLE;
266
+ await this.dbManager.updateAgent(agent);
267
+
268
+ this.emit('agent:message', { agentId, message: assistantMessage });
269
+
270
+ return assistantMessage;
271
+ } catch (error) {
272
+ agent.status = AgentStatus.ERROR;
273
+ await this.dbManager.updateAgent(agent);
274
+ throw error;
275
+ }
276
+ }
277
+
278
+ async sendMessageStream(agentId: string, message: string, callbacks: StreamCallbacks): Promise<void> {
279
+ const agent = this.agents.get(agentId);
280
+ if (!agent) {
281
+ throw new Error(`Agent ${agentId} not found`);
282
+ }
283
+
284
+ const userMessage: AgentMessage = {
285
+ id: uuidv4(),
286
+ role: 'user',
287
+ content: message,
288
+ timestamp: new Date()
289
+ };
290
+
291
+ agent.messages.push(userMessage);
292
+ agent.lastActiveAt = new Date();
293
+ agent.status = AgentStatus.RUNNING;
294
+ this.lastActivity.set(agentId, new Date());
295
+ await this.dbManager.updateAgent(agent);
296
+
297
+ this.emit('agent:message', { agentId, message: userMessage });
298
+
299
+ try {
300
+ const provider = this.aiProviderManager.getProvider(agent.providerId);
301
+ if (!provider) {
302
+ throw new Error(`Provider ${agent.providerId} not found`);
303
+ }
304
+
305
+ const autoContext = await this.buildAutoContext(agent, message);
306
+ const modelMessages = (
307
+ autoContext?.systemMessage
308
+ ? [...agent.messages, autoContext.systemMessage]
309
+ : agent.messages
310
+ ) as Array<{ role: string; content: string }>;
311
+
312
+ // Check if provider supports streaming
313
+ if (typeof provider.sendMessageStream === 'function') {
314
+ await provider.sendMessageStream(agent.model, modelMessages, {
315
+ onChunk: (chunk: string) => {
316
+ callbacks.onChunk(chunk);
317
+ },
318
+ onComplete: async () => {
319
+ const response = await this.getAIResponseWithRetry(agent, undefined, autoContext?.systemMessage);
320
+ const assistantMessage: AgentMessage = {
321
+ id: uuidv4(),
322
+ role: 'assistant',
323
+ content: response.content,
324
+ timestamp: new Date(),
325
+ metadata: {
326
+ ...response.metadata,
327
+ autoContext: autoContext?.metadata
328
+ }
329
+ };
330
+
331
+ agent.messages.push(assistantMessage);
332
+ agent.status = AgentStatus.IDLE;
333
+ await this.dbManager.updateAgent(agent);
334
+
335
+ this.emit('agent:message', { agentId, message: assistantMessage });
336
+ callbacks.onComplete();
337
+ },
338
+ onError: async (error: Error) => {
339
+ agent.status = AgentStatus.ERROR;
340
+ await this.dbManager.updateAgent(agent);
341
+ callbacks.onError(error);
342
+ }
343
+ });
344
+ } else {
345
+ // Fallback to non-streaming
346
+ const response = await this.getAIResponseWithRetry(agent, undefined, autoContext?.systemMessage);
347
+ callbacks.onChunk(response.content);
348
+
349
+ const assistantMessage: AgentMessage = {
350
+ id: uuidv4(),
351
+ role: 'assistant',
352
+ content: response.content,
353
+ timestamp: new Date(),
354
+ metadata: {
355
+ ...response.metadata,
356
+ autoContext: autoContext?.metadata
357
+ }
358
+ };
359
+
360
+ agent.messages.push(assistantMessage);
361
+ agent.status = AgentStatus.IDLE;
362
+ await this.dbManager.updateAgent(agent);
363
+
364
+ this.emit('agent:message', { agentId, message: assistantMessage });
365
+ callbacks.onComplete();
366
+ }
367
+ } catch (error) {
368
+ agent.status = AgentStatus.ERROR;
369
+ await this.dbManager.updateAgent(agent);
370
+ callbacks.onError(error as Error);
371
+ }
372
+ }
373
+
374
+ private async getAIResponseWithRetry(
375
+ agent: Agent,
376
+ options?: {
377
+ extendedThinking?: boolean;
378
+ reasoningEffort?: 'low' | 'medium' | 'high';
379
+ },
380
+ ephemeralSystemMessage?: AgentMessage,
381
+ attempt: number = 1
382
+ ): Promise<{ content: string; metadata?: Record<string, any> }> {
383
+ try {
384
+ return await this.getAIResponse(agent, options, ephemeralSystemMessage);
385
+ } catch (error) {
386
+ if (attempt < this.MAX_RETRIES && this.isRetryableError(error)) {
387
+ log.warn(`Retrying AI request for agent ${agent.id}, attempt ${attempt + 1}/${this.MAX_RETRIES}`);
388
+ await this.delay(this.RETRY_DELAY * attempt); // Exponential backoff
389
+ return this.getAIResponseWithRetry(agent, options, ephemeralSystemMessage, attempt + 1);
390
+ }
391
+ throw error;
392
+ }
393
+ }
394
+
395
+ private async getAIResponse(
396
+ agent: Agent,
397
+ options?: {
398
+ signal?: AbortSignal;
399
+ onProgress?: (progress: number) => void;
400
+ extendedThinking?: boolean;
401
+ reasoningEffort?: 'low' | 'medium' | 'high';
402
+ },
403
+ ephemeralSystemMessage?: AgentMessage
404
+ ): Promise<{ content: string; metadata?: Record<string, any> }> {
405
+ const provider = this.aiProviderManager.getProvider(agent.providerId);
406
+ if (!provider) {
407
+ throw new Error(`Provider ${agent.providerId} not found`);
408
+ }
409
+
410
+ const messages = (
411
+ ephemeralSystemMessage
412
+ ? [...agent.messages, ephemeralSystemMessage]
413
+ : agent.messages
414
+ ).map(m => ({ role: m.role, content: m.content })) as Array<{ role: string; content: string }>;
415
+
416
+ return await provider.sendMessage(agent.model, messages, {
417
+ signal: options?.signal,
418
+ onProgress: options?.onProgress,
419
+ extendedThinking: options?.extendedThinking,
420
+ reasoningEffort: options?.reasoningEffort
421
+ });
422
+ }
423
+
424
+ private async buildAutoContext(
425
+ agent: Agent,
426
+ userIntent: string
427
+ ): Promise<{ systemMessage: AgentMessage; metadata: { files: Array<{ path: string; reason: string }>; totalChars: number } } | null> {
428
+ const enabled = agent.metadata?.autoContext !== false;
429
+ if (!enabled) return null;
430
+
431
+ if (!agent.projectPath || typeof agent.projectPath !== 'string') {
432
+ return null;
433
+ }
434
+
435
+ const worktreePath = agent.worktreePath
436
+ ? agent.worktreePath
437
+ : (agent.worktreeName
438
+ ? path.join(agent.projectPath, '.codex', 'worktrees', agent.worktreeName)
439
+ : agent.projectPath);
440
+
441
+ if (!worktreePath || typeof worktreePath !== 'string') {
442
+ return null;
443
+ }
444
+
445
+ const candidates: Array<{ relPath: string; reason: string }> = [];
446
+ const addCandidate = (relPath: string, reason: string) => {
447
+ if (!relPath) return;
448
+ if (candidates.some(c => c.relPath === relPath)) return;
449
+ candidates.push({ relPath, reason });
450
+ };
451
+
452
+ // Always-valuable files
453
+ addCandidate('CLAUDE.md', 'Project conventions');
454
+ addCandidate('README.md', 'Project overview');
455
+ addCandidate('package.json', 'Dependencies/scripts');
456
+ addCandidate('tsconfig.json', 'TypeScript config');
457
+ addCandidate('tsconfig.main.json', 'Main TS config');
458
+ addCandidate('tsconfig.renderer.json', 'Renderer TS config');
459
+
460
+ // Recently changed files in this worktree
461
+ try {
462
+ const changes = await this.gitWorktreeManager.getChanges(worktreePath);
463
+ const recent = [...changes.staged, ...changes.unstaged].slice(0, 6);
464
+ for (const f of recent) {
465
+ addCandidate(f, 'Recently changed in worktree');
466
+ }
467
+ } catch {
468
+ // ignore
469
+ }
470
+
471
+ const selected = candidates.slice(0, this.AUTO_CONTEXT_MAX_FILES);
472
+ let totalChars = 0;
473
+ const used: Array<{ path: string; reason: string }> = [];
474
+ const parts: string[] = [];
475
+
476
+ for (const c of selected) {
477
+ const abs = path.join(worktreePath, c.relPath);
478
+ try {
479
+ const raw = await fs.readFile(abs, 'utf-8');
480
+ const clipped = raw.length > this.AUTO_CONTEXT_MAX_CHARS_PER_FILE
481
+ ? raw.slice(0, this.AUTO_CONTEXT_MAX_CHARS_PER_FILE) + '\n\n[TRUNCATED]'
482
+ : raw;
483
+
484
+ if (totalChars + clipped.length > this.AUTO_CONTEXT_MAX_TOTAL_CHARS) break;
485
+
486
+ totalChars += clipped.length;
487
+ used.push({ path: c.relPath, reason: c.reason });
488
+ parts.push(
489
+ `## ${c.relPath}\nReason: ${c.reason}\n\n${clipped}`
490
+ );
491
+ } catch {
492
+ // ignore
493
+ }
494
+ }
495
+
496
+ if (parts.length === 0) return null;
497
+
498
+ const systemMessage: AgentMessage = {
499
+ id: uuidv4(),
500
+ role: 'system',
501
+ content: `# Auto-context (ephemeral)\nUser intent: ${userIntent}\n\n${parts.join('\n\n')}`,
502
+ timestamp: new Date(),
503
+ metadata: {
504
+ autoContext: {
505
+ files: used,
506
+ totalChars
507
+ }
508
+ }
509
+ };
510
+
511
+ return {
512
+ systemMessage,
513
+ metadata: {
514
+ files: used,
515
+ totalChars
516
+ }
517
+ };
518
+ }
519
+
520
+ private isRetryableError(error: any): boolean {
521
+ // Retry on network errors, timeouts, rate limits
522
+ if (!error) return false;
523
+
524
+ const retryableCodes = ['ECONNRESET', 'ETIMEDOUT', 'ECONNREFUSED', 'RATE_LIMITED'];
525
+ const retryableStatuses = [429, 502, 503, 504];
526
+
527
+ return retryableCodes.includes(error.code) ||
528
+ retryableStatuses.includes(error.status) ||
529
+ error.message?.includes('timeout') ||
530
+ error.message?.includes('rate limit');
531
+ }
532
+
533
+ private delay(ms: number): Promise<void> {
534
+ return new Promise(resolve => setTimeout(resolve, ms));
535
+ }
536
+
537
+ async executeTask(agentId: string, task: string, timeout: number = 30 * 60 * 1000): Promise<AgentTask> {
538
+ const agent = await this.getAgentOrThrow(agentId);
539
+
540
+ const taskId = uuidv4();
541
+ const agentTask: AgentTask = {
542
+ id: taskId,
543
+ description: task,
544
+ status: TaskStatus.RUNNING,
545
+ progress: 0,
546
+ startedAt: new Date()
547
+ };
548
+
549
+ agent.tasks.push(agentTask);
550
+ agent.status = AgentStatus.RUNNING;
551
+ this.lastActivity.set(agentId, new Date());
552
+ await this.dbManager.updateAgent(agent);
553
+
554
+ this.emit('agent:taskStarted', { agentId, task: agentTask });
555
+
556
+ // Create abort controller for this task
557
+ const abortController = new AbortController();
558
+ this.activeTasks.set(taskId, abortController);
559
+
560
+ // Set up timeout
561
+ const timeoutId = setTimeout(() => {
562
+ abortController.abort();
563
+ log.warn(`Task ${taskId} timed out after ${timeout}ms`);
564
+ }, timeout);
565
+
566
+ // Execute task in background
567
+ this.runTask(agent, agentTask, abortController)
568
+ .then(() => {
569
+ clearTimeout(timeoutId);
570
+ })
571
+ .catch(error => {
572
+ clearTimeout(timeoutId);
573
+ log.error(`Task ${taskId} failed:`, error);
574
+ agentTask.status = TaskStatus.FAILED;
575
+ agentTask.error = error.message;
576
+ agentTask.completedAt = new Date();
577
+ this.emit('agent:taskFailed', { agentId, task: agentTask, error });
578
+ });
579
+
580
+ return agentTask;
581
+ }
582
+
583
+ private async runTask(
584
+ agent: Agent,
585
+ task: AgentTask,
586
+ abortController: AbortController
587
+ ): Promise<void> {
588
+ try {
589
+ // Set up task context
590
+ const taskMessage: AgentMessage = {
591
+ id: uuidv4(),
592
+ role: 'user',
593
+ content: `[TASK] ${task.description}`,
594
+ timestamp: new Date()
595
+ };
596
+
597
+ agent.messages.push(taskMessage);
598
+
599
+ // Process the task with streaming updates
600
+ const autoContext = await this.buildAutoContext(agent, task.description);
601
+ const response = await this.getAIResponse(agent, {
602
+ signal: abortController.signal,
603
+ onProgress: (progress) => {
604
+ task.progress = progress;
605
+ this.emit('agent:progress', { agentId: agent.id, taskId: task.id, progress });
606
+ }
607
+ }, autoContext?.systemMessage);
608
+
609
+ // Parse and apply code changes
610
+ const changes = await this.parseAndApplyChanges(agent, response.content);
611
+
612
+ task.result = response.content;
613
+ task.status = TaskStatus.COMPLETED;
614
+ task.progress = 100;
615
+ task.completedAt = new Date();
616
+
617
+ const assistantMessage: AgentMessage = {
618
+ id: uuidv4(),
619
+ role: 'assistant',
620
+ content: response.content,
621
+ timestamp: new Date(),
622
+ metadata: {
623
+ changes: changes.map(c => c.id),
624
+ autoContext: autoContext?.metadata
625
+ }
626
+ };
627
+
628
+ agent.messages.push(assistantMessage);
629
+ agent.status = AgentStatus.IDLE;
630
+ agent.lastActiveAt = new Date();
631
+
632
+ await this.dbManager.updateAgent(agent);
633
+
634
+ this.emit('agent:taskCompleted', { agentId: agent.id, task });
635
+ } catch (error) {
636
+ if (abortController.signal.aborted) {
637
+ task.status = TaskStatus.CANCELLED;
638
+ } else {
639
+ task.status = TaskStatus.FAILED;
640
+ task.error = error instanceof Error ? error.message : String(error);
641
+ }
642
+ task.completedAt = new Date();
643
+ agent.status = AgentStatus.ERROR;
644
+ await this.dbManager.updateAgent(agent);
645
+ throw error;
646
+ } finally {
647
+ this.activeTasks.delete(task.id);
648
+ }
649
+ }
650
+
651
+ async pauseAgent(agentId: string): Promise<void> {
652
+ const agent = await this.getAgentOrThrow(agentId);
653
+
654
+ // Abort any running tasks
655
+ for (const [taskId, controller] of this.activeTasks) {
656
+ const task = agent.tasks.find(t => t.id === taskId);
657
+ if (task && task.status === TaskStatus.RUNNING) {
658
+ controller.abort();
659
+ task.status = TaskStatus.PAUSED;
660
+ }
661
+ }
662
+
663
+ agent.status = AgentStatus.PAUSED;
664
+ await this.dbManager.updateAgent(agent);
665
+
666
+ this.emit('agent:paused', { agentId });
667
+ }
668
+
669
+ async resumeAgent(agentId: string): Promise<void> {
670
+ const agent = await this.getAgentOrThrow(agentId);
671
+
672
+ agent.status = AgentStatus.IDLE;
673
+ await this.dbManager.updateAgent(agent);
674
+
675
+ this.emit('agent:resumed', { agentId });
676
+ }
677
+
678
+ async stopAgent(agentId: string): Promise<void> {
679
+ const agent = await this.getAgentOrThrow(agentId);
680
+
681
+ // Abort all tasks
682
+ for (const [taskId, controller] of this.activeTasks) {
683
+ const task = agent.tasks.find(t => t.id === taskId);
684
+ if (task) {
685
+ controller.abort();
686
+ task.status = TaskStatus.CANCELLED;
687
+ task.completedAt = new Date();
688
+ }
689
+ }
690
+
691
+ agent.status = AgentStatus.IDLE;
692
+ await this.dbManager.updateAgent(agent);
693
+
694
+ this.emit('agent:stopped', { agentId });
695
+ }
696
+
697
+ async deleteAgent(agentId: string): Promise<void> {
698
+ const agent = await this.getAgentOrThrow(agentId);
699
+
700
+ // Stop any running tasks
701
+ await this.stopAgent(agentId);
702
+
703
+ // Remove worktree
704
+ try {
705
+ await this.gitWorktreeManager.removeWorktree(agent.projectPath, agent.worktreeName);
706
+ } catch (error) {
707
+ log.warn(`Failed to remove worktree for agent ${agentId}:`, error);
708
+ }
709
+
710
+ this.agents.delete(agentId);
711
+ this.lastActivity.delete(agentId);
712
+ await this.dbManager.deleteAgent(agentId);
713
+
714
+ this.emit('agent:deleted', { agentId });
715
+ log.info(`Deleted agent ${agentId}`);
716
+ }
717
+
718
+ async applySkills(agentId: string, skillIds: string[]): Promise<void> {
719
+ const agent = await this.getAgentOrThrow(agentId);
720
+
721
+ await this.applySkillsInternal(agent, skillIds);
722
+ agent.skills = [...new Set([...agent.skills, ...skillIds])];
723
+ await this.dbManager.updateAgent(agent);
724
+
725
+ this.emit('skill:applied', { agentId, skillIds });
726
+ }
727
+
728
+ private async applySkillsInternal(agent: Agent, skillIds: string[]): Promise<void> {
729
+ for (const skillId of skillIds) {
730
+ const skill = await this.skillsManager.getSkill(skillId);
731
+ if (skill) {
732
+ // Add skill instructions to system context
733
+ const instructionFiles = skill.files.filter(f => f.type === 'instruction');
734
+ for (const file of instructionFiles) {
735
+ agent.messages.push({
736
+ id: uuidv4(),
737
+ role: 'system',
738
+ content: `[SKILL: ${skill.name}]\n${file.content}`,
739
+ timestamp: new Date(),
740
+ metadata: { skillId, skillName: skill.name }
741
+ });
742
+ }
743
+ }
744
+ }
745
+ }
746
+
747
+ private async parseAndApplyChanges(agent: Agent, content: string): Promise<CodeChange[]> {
748
+ const changes: CodeChange[] = [];
749
+
750
+ const diffRegex = /diff --git a\/(.+?) b\/(.+?)\n([\s\S]*?)(?=\ndiff --git|$)/g;
751
+
752
+ let match;
753
+ const worktreePath = agent.worktreePath
754
+ ? agent.worktreePath
755
+ : (agent.worktreeName
756
+ ? path.join(agent.projectPath, '.codex', 'worktrees', agent.worktreeName)
757
+ : agent.projectPath);
758
+
759
+ while ((match = diffRegex.exec(content)) !== null) {
760
+ const [, oldFile, newFile, diffContent] = match;
761
+ const filePath = newFile.trim();
762
+
763
+ let originalContent = '';
764
+ let newContent = '';
765
+
766
+ try {
767
+ const fullFilePath = path.join(worktreePath, filePath);
768
+ originalContent = await fs.readFile(fullFilePath, 'utf-8');
769
+ } catch {
770
+ log.warn(`Could not read original file: ${filePath}, file may be new`);
771
+ }
772
+
773
+ newContent = this.applyDiff(originalContent, diffContent);
774
+
775
+ const change: CodeChange = {
776
+ id: uuidv4(),
777
+ filePath,
778
+ originalContent,
779
+ newContent,
780
+ diff: diffContent,
781
+ agentId: agent.id,
782
+ taskId: agent.tasks[agent.tasks.length - 1]?.id || '',
783
+ status: ChangeStatus.PENDING,
784
+ createdAt: new Date()
785
+ };
786
+
787
+ changes.push(change);
788
+ await this.dbManager.createCodeChange(change);
789
+ this.emit('changes:created', { agentId: agent.id, changeId: change.id });
790
+ }
791
+
792
+ return changes;
793
+ }
794
+
795
+ private applyDiff(originalContent: string, diffContent: string): string {
796
+ const lines = diffContent.split('\n');
797
+ const result: string[] = [];
798
+
799
+ let i = 0;
800
+ while (i < lines.length) {
801
+ const line = lines[i];
802
+
803
+ if (line.startsWith('@@')) {
804
+ const hunkHeader = line;
805
+ const match = hunkHeader.match(/@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@/);
806
+
807
+ if (match) {
808
+ const [, oldStart, oldCount, newStart, newCount] = match.map(Number);
809
+ const originalLines = originalContent.split('\n');
810
+
811
+ const beforeContext = originalLines.slice(Math.max(0, oldStart - 2), oldStart - 1);
812
+ result.push(...beforeContext);
813
+
814
+ i++;
815
+
816
+ const hunkOldLines: string[] = [];
817
+ const hunkNewLines: string[] = [];
818
+
819
+ while (i < lines.length && !lines[i].startsWith('@@') && !lines[i].startsWith('diff ')) {
820
+ if (lines[i].startsWith('-')) {
821
+ hunkOldLines.push(lines[i].substring(1));
822
+ } else if (lines[i].startsWith('+')) {
823
+ hunkNewLines.push(lines[i].substring(1));
824
+ } else if (!lines[i].startsWith('\\')) {
825
+ hunkOldLines.push(lines[i]);
826
+ hunkNewLines.push(lines[i]);
827
+ }
828
+ i++;
829
+ }
830
+
831
+ result.push(...hunkNewLines);
832
+
833
+ const afterContext = originalLines.slice(
834
+ oldStart - 1 + (oldCount || hunkOldLines.length),
835
+ oldStart - 1 + (oldCount || hunkOldLines.length) + 2
836
+ );
837
+ result.push(...afterContext);
838
+
839
+ continue;
840
+ }
841
+ }
842
+ i++;
843
+ }
844
+
845
+ if (result.length === 0 && diffContent.includes('new file')) {
846
+ const newFileMatch = diffContent.match(/\+\+\+ b\/(.+)/);
847
+ if (newFileMatch) {
848
+ const newLines: string[] = [];
849
+ for (const line of lines) {
850
+ if (line.startsWith('+') && !line.startsWith('+++')) {
851
+ newLines.push(line.substring(1));
852
+ } else if (line.startsWith(' ')) {
853
+ newLines.push(line.substring(1));
854
+ }
855
+ }
856
+ return newLines.join('\n');
857
+ }
858
+ }
859
+
860
+ return result.join('\n');
861
+ }
862
+
863
+ // Permission Management
864
+ setAgentPermissionMode(agentId: string, mode: PermissionMode): void {
865
+ const agent = this.agents.get(agentId);
866
+ if (!agent) {
867
+ throw new Error(`Agent ${agentId} not found`);
868
+ }
869
+
870
+ agent.permissionMode = mode;
871
+ this.permissionManager.setAgentMode(agentId, mode);
872
+ this.emit('agent:permissionModeChanged', { agentId, mode });
873
+ log.info(`Permission mode changed to ${mode} for agent ${agentId}`);
874
+ }
875
+
876
+ getAgentPermissionMode(agentId: string): PermissionMode {
877
+ return this.permissionManager.getAgentMode(agentId);
878
+ }
879
+
880
+ getPermissionManager(): PermissionManager {
881
+ return this.permissionManager;
882
+ }
883
+
884
+ setAllowBypassMode(allowed: boolean): void {
885
+ this.permissionManager.setAllowBypassMode(allowed);
886
+ }
887
+
888
+ isBypassModeAllowed(): boolean {
889
+ return this.permissionManager.isBypassAllowed();
890
+ }
891
+
892
+ async checkPermission(
893
+ agentId: string,
894
+ action: { type: 'edit' | 'command' | 'tool'; action: string; details: Record<string, any> }
895
+ ): Promise<{ allowed: boolean; requestId?: string }> {
896
+ return this.permissionManager.checkPermission(agentId, action);
897
+ }
898
+
899
+ async approvePermissionRequest(requestId: string): Promise<void> {
900
+ await this.permissionManager.approveRequest(requestId);
901
+ }
902
+
903
+ async rejectPermissionRequest(requestId: string): Promise<void> {
904
+ await this.permissionManager.rejectRequest(requestId);
905
+ }
906
+
907
+ getPendingPermissionRequests(agentId?: string): import('../../shared/types').PermissionRequest[] {
908
+ return this.permissionManager.getPendingRequests(agentId);
909
+ }
910
+
911
+ private buildSystemPrompt(userPrompt?: string, claudeMdConfig?: ClaudeMdConfig | null): string {
912
+ const parts: string[] = [];
913
+
914
+ if (claudeMdConfig) {
915
+ const parser = new ClaudeMdParser();
916
+ // Temporarily set config to generate prompt
917
+ (parser as any).config = claudeMdConfig;
918
+ const claudePrompt = parser.generateSystemPrompt();
919
+ if (claudePrompt) {
920
+ parts.push(claudePrompt);
921
+ }
922
+ }
923
+
924
+ if (userPrompt) {
925
+ parts.push(userPrompt);
926
+ }
927
+
928
+ return parts.join('\n\n');
929
+ }
930
+ }