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.
- package/.claude/settings.local.json +10 -0
- package/.eslintrc.json +27 -0
- package/.github/workflows/ci.yml +156 -0
- package/.huskyrc +7 -0
- package/.lintstagedrc +13 -0
- package/.prettierrc +12 -0
- package/CLAUDE.md +163 -0
- package/DESIGN_SUPERIOR.md +73 -0
- package/Dockerfile +64 -0
- package/INSTALLATION.md +152 -0
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/assets/skills/code-review/instructions.md +102 -0
- package/assets/skills/code-review/skill.yaml +15 -0
- package/assets/skills/refactoring/instructions.md +149 -0
- package/assets/skills/refactoring/skill.yaml +15 -0
- package/assets/skills/testing/skill.yaml +15 -0
- package/commitlint.config.js +23 -0
- package/dist/main/DatabaseManager.js +763 -0
- package/dist/main/DatabaseManager.js.map +1 -0
- package/dist/main/SettingsManager.js +61 -0
- package/dist/main/SettingsManager.js.map +1 -0
- package/dist/main/agents/AgentOrchestrator.js +787 -0
- package/dist/main/agents/AgentOrchestrator.js.map +1 -0
- package/dist/main/agents/AgentSDK.js +219 -0
- package/dist/main/agents/AgentSDK.js.map +1 -0
- package/dist/main/agents/AgentTools.js +348 -0
- package/dist/main/agents/AgentTools.js.map +1 -0
- package/dist/main/agents/CodeIndex.js +233 -0
- package/dist/main/agents/CodeIndex.js.map +1 -0
- package/dist/main/agents/EmbeddingService.js +80 -0
- package/dist/main/agents/EmbeddingService.js.map +1 -0
- package/dist/main/agents/NativeToolCalling.js +206 -0
- package/dist/main/agents/NativeToolCalling.js.map +1 -0
- package/dist/main/api/APIServer.js +278 -0
- package/dist/main/api/APIServer.js.map +1 -0
- package/dist/main/api/RateLimiter.js +138 -0
- package/dist/main/api/RateLimiter.js.map +1 -0
- package/dist/main/api/WebSocketManager.js +300 -0
- package/dist/main/api/WebSocketManager.js.map +1 -0
- package/dist/main/assistant/ContextOptimizer.js +192 -0
- package/dist/main/assistant/ContextOptimizer.js.map +1 -0
- package/dist/main/assistant/PredictedOutputManager.js +172 -0
- package/dist/main/assistant/PredictedOutputManager.js.map +1 -0
- package/dist/main/assistant/PromptCacheManager.js +193 -0
- package/dist/main/assistant/PromptCacheManager.js.map +1 -0
- package/dist/main/assistant/PromptOptimizer.js +626 -0
- package/dist/main/assistant/PromptOptimizer.js.map +1 -0
- package/dist/main/assistant/SmartCodeAssistant.js +224 -0
- package/dist/main/assistant/SmartCodeAssistant.js.map +1 -0
- package/dist/main/auth/SessionManager.js +300 -0
- package/dist/main/auth/SessionManager.js.map +1 -0
- package/dist/main/automations/AdvancedWebhookSystem.js +212 -0
- package/dist/main/automations/AdvancedWebhookSystem.js.map +1 -0
- package/dist/main/automations/AutomationScheduler.js +269 -0
- package/dist/main/automations/AutomationScheduler.js.map +1 -0
- package/dist/main/automations/BatchProcessingSystem.js +159 -0
- package/dist/main/automations/BatchProcessingSystem.js.map +1 -0
- package/dist/main/automations/BrowserAutomationManager.js +195 -0
- package/dist/main/automations/BrowserAutomationManager.js.map +1 -0
- package/dist/main/automations/GitHubActionsManager.js +129 -0
- package/dist/main/automations/GitHubActionsManager.js.map +1 -0
- package/dist/main/automations/GitLabCIManager.js +122 -0
- package/dist/main/automations/GitLabCIManager.js.map +1 -0
- package/dist/main/automations/PriorityQueueManager.js +240 -0
- package/dist/main/automations/PriorityQueueManager.js.map +1 -0
- package/dist/main/background/BackgroundModeManager.js +117 -0
- package/dist/main/background/BackgroundModeManager.js.map +1 -0
- package/dist/main/backup/BackupManager.js +254 -0
- package/dist/main/backup/BackupManager.js.map +1 -0
- package/dist/main/backup/MigrationManager.js +114 -0
- package/dist/main/backup/MigrationManager.js.map +1 -0
- package/dist/main/commands/SlashCommandManager.js +399 -0
- package/dist/main/commands/SlashCommandManager.js.map +1 -0
- package/dist/main/config/ClaudeMdParser.js +519 -0
- package/dist/main/config/ClaudeMdParser.js.map +1 -0
- package/dist/main/config/CustomizationManager.js +381 -0
- package/dist/main/config/CustomizationManager.js.map +1 -0
- package/dist/main/config/LaunchConfigManager.js +211 -0
- package/dist/main/config/LaunchConfigManager.js.map +1 -0
- package/dist/main/config/SettingsManager.js +166 -0
- package/dist/main/config/SettingsManager.js.map +1 -0
- package/dist/main/connectors/ConnectorManager.js +151 -0
- package/dist/main/connectors/ConnectorManager.js.map +1 -0
- package/dist/main/connectors/DatabaseConnector.js +222 -0
- package/dist/main/connectors/DatabaseConnector.js.map +1 -0
- package/dist/main/cowork/CoworkManager.js +324 -0
- package/dist/main/cowork/CoworkManager.js.map +1 -0
- package/dist/main/evals/AgentEvalFramework.js +538 -0
- package/dist/main/evals/AgentEvalFramework.js.map +1 -0
- package/dist/main/evals/GraderManager.js +285 -0
- package/dist/main/evals/GraderManager.js.map +1 -0
- package/dist/main/git/GitWorktreeManager.js +214 -0
- package/dist/main/git/GitWorktreeManager.js.map +1 -0
- package/dist/main/github/GitHubPRMonitor.js +244 -0
- package/dist/main/github/GitHubPRMonitor.js.map +1 -0
- package/dist/main/ide/ContinueInManager.js +181 -0
- package/dist/main/ide/ContinueInManager.js.map +1 -0
- package/dist/main/ide/IDEIntegration.js +277 -0
- package/dist/main/ide/IDEIntegration.js.map +1 -0
- package/dist/main/integrations/LinearManager.js +252 -0
- package/dist/main/integrations/LinearManager.js.map +1 -0
- package/dist/main/integrations/SlackBotManager.js +247 -0
- package/dist/main/integrations/SlackBotManager.js.map +1 -0
- package/dist/main/lsp/LSPManager.js +394 -0
- package/dist/main/lsp/LSPManager.js.map +1 -0
- package/dist/main/main.js +1087 -0
- package/dist/main/main.js.map +1 -0
- package/dist/main/mcp/MCPConfigurationManager.js +281 -0
- package/dist/main/mcp/MCPConfigurationManager.js.map +1 -0
- package/dist/main/mcp/MCPManager.js +710 -0
- package/dist/main/mcp/MCPManager.js.map +1 -0
- package/dist/main/mcp/MCPRegistry.js +272 -0
- package/dist/main/mcp/MCPRegistry.js.map +1 -0
- package/dist/main/monitoring/ErrorRecoveryManager.js +268 -0
- package/dist/main/monitoring/ErrorRecoveryManager.js.map +1 -0
- package/dist/main/monitoring/ErrorTracker.js +57 -0
- package/dist/main/monitoring/ErrorTracker.js.map +1 -0
- package/dist/main/monitoring/MetricsCollector.js +155 -0
- package/dist/main/monitoring/MetricsCollector.js.map +1 -0
- package/dist/main/monitoring/TraceGradingSystem.js +148 -0
- package/dist/main/monitoring/TraceGradingSystem.js.map +1 -0
- package/dist/main/notifications/NotificationManager.js +67 -0
- package/dist/main/notifications/NotificationManager.js.map +1 -0
- package/dist/main/pair/AIPairProgramming.js +200 -0
- package/dist/main/pair/AIPairProgramming.js.map +1 -0
- package/dist/main/plugins/PluginManager.js +222 -0
- package/dist/main/plugins/PluginManager.js.map +1 -0
- package/dist/main/plugins/PluginMarketplace.js +237 -0
- package/dist/main/plugins/PluginMarketplace.js.map +1 -0
- package/dist/main/preload.js +189 -0
- package/dist/main/preload.js.map +1 -0
- package/dist/main/preview/PreviewSessionManager.js +170 -0
- package/dist/main/preview/PreviewSessionManager.js.map +1 -0
- package/dist/main/providers/AIProviderManager.js +327 -0
- package/dist/main/providers/AIProviderManager.js.map +1 -0
- package/dist/main/providers/FineTuningManager.js +276 -0
- package/dist/main/providers/FineTuningManager.js.map +1 -0
- package/dist/main/providers/FreeModelsProvider.js +1104 -0
- package/dist/main/providers/FreeModelsProvider.js.map +1 -0
- package/dist/main/realtime/RealtimeManager.js +116 -0
- package/dist/main/realtime/RealtimeManager.js.map +1 -0
- package/dist/main/remote/CloudEnvironmentManager.js +232 -0
- package/dist/main/remote/CloudEnvironmentManager.js.map +1 -0
- package/dist/main/remote/RemoteSessionManager.js +255 -0
- package/dist/main/remote/RemoteSessionManager.js.map +1 -0
- package/dist/main/search/DeepResearchManager.js +335 -0
- package/dist/main/search/DeepResearchManager.js.map +1 -0
- package/dist/main/search/WebSearchIntegration.js +147 -0
- package/dist/main/search/WebSearchIntegration.js.map +1 -0
- package/dist/main/security/AdminConsoleManager.js +223 -0
- package/dist/main/security/AdminConsoleManager.js.map +1 -0
- package/dist/main/security/AuditLogger.js +136 -0
- package/dist/main/security/AuditLogger.js.map +1 -0
- package/dist/main/security/PermissionManager.js +144 -0
- package/dist/main/security/PermissionManager.js.map +1 -0
- package/dist/main/security/SSOManager.js +173 -0
- package/dist/main/security/SSOManager.js.map +1 -0
- package/dist/main/security/SecurityManager.js +152 -0
- package/dist/main/security/SecurityManager.js.map +1 -0
- package/dist/main/skills/SkillsManager.js +223 -0
- package/dist/main/skills/SkillsManager.js.map +1 -0
- package/dist/main/ssh/SSHManager.js +65 -0
- package/dist/main/ssh/SSHManager.js.map +1 -0
- package/dist/main/streaming/StreamingManager.js +225 -0
- package/dist/main/streaming/StreamingManager.js.map +1 -0
- package/dist/main/sync/CloudSyncManager.js +422 -0
- package/dist/main/sync/CloudSyncManager.js.map +1 -0
- package/dist/main/types.js +28 -0
- package/dist/main/types.js.map +1 -0
- package/dist/main/verification/AutoVerifyManager.js +235 -0
- package/dist/main/verification/AutoVerifyManager.js.map +1 -0
- package/dist/main/vision/ComputerUseManager.js +376 -0
- package/dist/main/vision/ComputerUseManager.js.map +1 -0
- package/dist/main/vision/ImageVideoGenerationManager.js +401 -0
- package/dist/main/vision/ImageVideoGenerationManager.js.map +1 -0
- package/dist/main/vision/VisionManager.js +172 -0
- package/dist/main/vision/VisionManager.js.map +1 -0
- package/dist/renderer/assets/main-DJlZQBCA.js +304 -0
- package/dist/renderer/assets/main-N33ZXEr8.css +1 -0
- package/dist/renderer/index.html +21 -0
- package/dist/renderer/manifest.json +42 -0
- package/dist/renderer/sw.ts +109 -0
- package/dist/shared/types.js +35 -0
- package/dist/shared/types.js.map +1 -0
- package/docker-compose.yml +65 -0
- package/docs/API.md +307 -0
- package/docs/USER_GUIDE.md +476 -0
- package/examples/plugins/sample-plugin/package.json +41 -0
- package/examples/plugins/sample-plugin/src/index.ts +75 -0
- package/index.html +20 -0
- package/jest.config.js +39 -0
- package/package.json +180 -0
- package/packages/cli/package.json +29 -0
- package/packages/cli/src/commands/agents.ts +199 -0
- package/packages/cli/src/commands/tasks.ts +61 -0
- package/packages/cli/src/index.ts +91 -0
- package/packages/cli/src/utils/api.ts +45 -0
- package/packages/cli/src/utils/config.ts +61 -0
- package/packages/npm-installer/bin/codex-linux +126 -0
- package/packages/npm-installer/lib/download.js +273 -0
- package/packages/npm-installer/package.json +42 -0
- package/packages/vscode-extension/package.json +167 -0
- package/packages/vscode-extension/src/api.ts +68 -0
- package/packages/vscode-extension/src/extension.ts +161 -0
- package/packages/vscode-extension/src/panels/chatPanel.ts +265 -0
- package/packages/vscode-extension/src/panels/createAgentPanel.ts +227 -0
- package/packages/vscode-extension/src/providers/agentsProvider.ts +80 -0
- package/postcss.config.js +6 -0
- package/public/manifest.json +42 -0
- package/public/sw.ts +109 -0
- package/scripts/install-dev.sh +103 -0
- package/scripts/install.sh +275 -0
- package/src/main/DatabaseManager.ts +950 -0
- package/src/main/SettingsManager.ts +63 -0
- package/src/main/agents/AgentOrchestrator.ts +930 -0
- package/src/main/agents/AgentSDK.ts +269 -0
- package/src/main/agents/AgentTools.ts +380 -0
- package/src/main/agents/CodeIndex.ts +240 -0
- package/src/main/agents/EmbeddingService.ts +88 -0
- package/src/main/agents/NativeToolCalling.ts +245 -0
- package/src/main/api/APIServer.ts +316 -0
- package/src/main/api/RateLimiter.ts +165 -0
- package/src/main/api/WebSocketManager.ts +398 -0
- package/src/main/assistant/ContextOptimizer.ts +214 -0
- package/src/main/assistant/PredictedOutputManager.ts +265 -0
- package/src/main/assistant/PromptCacheManager.ts +280 -0
- package/src/main/assistant/PromptOptimizer.ts +746 -0
- package/src/main/assistant/SmartCodeAssistant.ts +234 -0
- package/src/main/auth/SessionManager.ts +415 -0
- package/src/main/automations/AdvancedWebhookSystem.ts +281 -0
- package/src/main/automations/AutomationScheduler.ts +272 -0
- package/src/main/automations/BatchProcessingSystem.ts +207 -0
- package/src/main/automations/BrowserAutomationManager.ts +203 -0
- package/src/main/automations/GitHubActionsManager.ts +151 -0
- package/src/main/automations/GitLabCIManager.ts +206 -0
- package/src/main/automations/PriorityQueueManager.ts +328 -0
- package/src/main/background/BackgroundModeManager.ts +130 -0
- package/src/main/backup/BackupManager.ts +287 -0
- package/src/main/backup/MigrationManager.ts +132 -0
- package/src/main/commands/SlashCommandManager.ts +407 -0
- package/src/main/config/ClaudeMdParser.ts +539 -0
- package/src/main/config/CustomizationManager.ts +493 -0
- package/src/main/config/LaunchConfigManager.ts +212 -0
- package/src/main/config/SettingsManager.ts +163 -0
- package/src/main/connectors/ConnectorManager.ts +175 -0
- package/src/main/connectors/DatabaseConnector.ts +212 -0
- package/src/main/cowork/CoworkManager.ts +431 -0
- package/src/main/evals/AgentEvalFramework.ts +665 -0
- package/src/main/evals/GraderManager.ts +417 -0
- package/src/main/git/GitWorktreeManager.ts +211 -0
- package/src/main/github/GitHubPRMonitor.ts +317 -0
- package/src/main/ide/ContinueInManager.ts +180 -0
- package/src/main/ide/IDEIntegration.ts +288 -0
- package/src/main/integrations/LinearManager.ts +327 -0
- package/src/main/integrations/SlackBotManager.ts +312 -0
- package/src/main/lsp/LSPManager.ts +445 -0
- package/src/main/main.ts +1221 -0
- package/src/main/mcp/MCPConfigurationManager.ts +281 -0
- package/src/main/mcp/MCPManager.ts +799 -0
- package/src/main/mcp/MCPRegistry.ts +273 -0
- package/src/main/monitoring/ErrorRecoveryManager.ts +359 -0
- package/src/main/monitoring/ErrorTracker.ts +60 -0
- package/src/main/monitoring/MetricsCollector.ts +196 -0
- package/src/main/monitoring/TraceGradingSystem.ts +196 -0
- package/src/main/notifications/NotificationManager.ts +96 -0
- package/src/main/pair/AIPairProgramming.ts +290 -0
- package/src/main/plugins/PluginManager.ts +266 -0
- package/src/main/plugins/PluginMarketplace.ts +318 -0
- package/src/main/preload.ts +215 -0
- package/src/main/preview/PreviewSessionManager.ts +186 -0
- package/src/main/providers/AIProviderManager.ts +394 -0
- package/src/main/providers/FineTuningManager.ts +390 -0
- package/src/main/providers/FreeModelsProvider.ts +1156 -0
- package/src/main/realtime/RealtimeManager.ts +147 -0
- package/src/main/remote/CloudEnvironmentManager.ts +253 -0
- package/src/main/remote/RemoteSessionManager.ts +323 -0
- package/src/main/search/DeepResearchManager.ts +458 -0
- package/src/main/search/WebSearchIntegration.ts +203 -0
- package/src/main/security/AdminConsoleManager.ts +244 -0
- package/src/main/security/AuditLogger.ts +143 -0
- package/src/main/security/PermissionManager.ts +184 -0
- package/src/main/security/SSOManager.ts +241 -0
- package/src/main/security/SecurityManager.ts +139 -0
- package/src/main/skills/SkillsManager.ts +218 -0
- package/src/main/ssh/SSHManager.ts +86 -0
- package/src/main/streaming/StreamingManager.ts +306 -0
- package/src/main/sync/CloudSyncManager.ts +532 -0
- package/src/main/verification/AutoVerifyManager.ts +285 -0
- package/src/main/vision/ComputerUseManager.ts +475 -0
- package/src/main/vision/ImageVideoGenerationManager.ts +526 -0
- package/src/main/vision/VisionManager.ts +186 -0
- package/src/renderer/App.tsx +314 -0
- package/src/renderer/components/AdvancedSettingsPanel.tsx +225 -0
- package/src/renderer/components/AgentPanel.tsx +760 -0
- package/src/renderer/components/AppPreview.tsx +220 -0
- package/src/renderer/components/AuditTrailPanel.tsx +148 -0
- package/src/renderer/components/AutomationPanel.tsx +220 -0
- package/src/renderer/components/ChatInterface.tsx +595 -0
- package/src/renderer/components/ChatTab.tsx +296 -0
- package/src/renderer/components/CodeEditor.tsx +257 -0
- package/src/renderer/components/CodeReviewPanel.tsx +256 -0
- package/src/renderer/components/CodeWorkspace.tsx +192 -0
- package/src/renderer/components/CodebaseDashboard.tsx +295 -0
- package/src/renderer/components/ComputerUsePanel.tsx +262 -0
- package/src/renderer/components/ConnectorsPanel.tsx +471 -0
- package/src/renderer/components/ContextMenu.tsx +155 -0
- package/src/renderer/components/ContextUsageDisplay.tsx +248 -0
- package/src/renderer/components/CoworkPanel.tsx +415 -0
- package/src/renderer/components/DiffViewer.tsx +452 -0
- package/src/renderer/components/ErrorBoundary.tsx +273 -0
- package/src/renderer/components/ExtendedThinkingToggle.tsx +244 -0
- package/src/renderer/components/FileAttachments.tsx +247 -0
- package/src/renderer/components/FileExplorer.tsx +242 -0
- package/src/renderer/components/FileExplorerPanel.tsx +302 -0
- package/src/renderer/components/GitPanel.tsx +154 -0
- package/src/renderer/components/Header.tsx +113 -0
- package/src/renderer/components/MCPPanel.tsx +326 -0
- package/src/renderer/components/MentionAutocomplete.tsx +239 -0
- package/src/renderer/components/PermissionPanel.tsx +159 -0
- package/src/renderer/components/PermissionSelector.tsx +203 -0
- package/src/renderer/components/PluginMarketplace.tsx +325 -0
- package/src/renderer/components/PromptOptimizerPanel.tsx +399 -0
- package/src/renderer/components/SearchPanel.tsx +173 -0
- package/src/renderer/components/SearchReplace.tsx +284 -0
- package/src/renderer/components/SessionSidebar.tsx +367 -0
- package/src/renderer/components/SettingsPanel.tsx +426 -0
- package/src/renderer/components/Sidebar.tsx +100 -0
- package/src/renderer/components/SkillsPanel.tsx +245 -0
- package/src/renderer/components/SplitPane.tsx +173 -0
- package/src/renderer/components/Terminal.tsx +190 -0
- package/src/renderer/components/VoiceCommand.tsx +129 -0
- package/src/renderer/components/WorktreePanel.tsx +163 -0
- package/src/renderer/components/ui/AriaComponents.tsx +193 -0
- package/src/renderer/components/ui/Button.tsx +68 -0
- package/src/renderer/components/ui/Card.tsx +102 -0
- package/src/renderer/components/ui/Input.tsx +44 -0
- package/src/renderer/components/ui/Skeleton.tsx +55 -0
- package/src/renderer/components/ui/VirtualList.tsx +196 -0
- package/src/renderer/i18n/I18nProvider.tsx +101 -0
- package/src/renderer/i18n/de.ts +161 -0
- package/src/renderer/i18n/en.ts +163 -0
- package/src/renderer/i18n/es.ts +161 -0
- package/src/renderer/i18n/fr.ts +161 -0
- package/src/renderer/i18n/index.ts +44 -0
- package/src/renderer/index.css +129 -0
- package/src/renderer/lib/accessibility.tsx +287 -0
- package/src/renderer/lib/hooks.ts +304 -0
- package/src/renderer/lib/utils.ts +6 -0
- package/src/renderer/main.tsx +25 -0
- package/src/renderer/styles/minimalist.css +539 -0
- package/src/renderer/sw.ts +180 -0
- package/src/renderer/types.d.ts +138 -0
- package/src/shared/types.ts +813 -0
- package/supabase/schema.sql +234 -0
- package/tailwind.config.js +78 -0
- package/tests/e2e/package.json +15 -0
- package/tests/e2e/playwright.config.ts +31 -0
- package/tests/e2e/specs/app.spec.ts +194 -0
- package/tests/setup.ts +99 -0
- package/tests/unit/AgentOrchestrator.test.ts +274 -0
- package/tests/unit/DatabaseManager.test.ts +262 -0
- package/tests/unit/GitWorktreeManager.test.ts +150 -0
- package/tests/unit/SecurityManager.test.ts +110 -0
- package/tsconfig.main.json +22 -0
- package/tsconfig.renderer.json +27 -0
- package/vite.config.ts +28 -0
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { Agent, AIProvider, Skill, CodeChange, ChangeStatus } from '../../shared/types';
|
|
3
|
+
import {
|
|
4
|
+
Bot,
|
|
5
|
+
Plus,
|
|
6
|
+
Play,
|
|
7
|
+
Pause,
|
|
8
|
+
Square,
|
|
9
|
+
Trash2,
|
|
10
|
+
MessageSquare,
|
|
11
|
+
MoreVertical
|
|
12
|
+
} from 'lucide-react';
|
|
13
|
+
import DiffViewer from './DiffViewer';
|
|
14
|
+
|
|
15
|
+
interface AgentPanelProps {
|
|
16
|
+
agents: Agent[];
|
|
17
|
+
providers: AIProvider[];
|
|
18
|
+
skills: Skill[];
|
|
19
|
+
onCreateAgent: (config: any) => Promise<Agent>;
|
|
20
|
+
onDeleteAgent: (agentId: string) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const AgentPanel: React.FC<AgentPanelProps> = ({
|
|
24
|
+
agents,
|
|
25
|
+
providers,
|
|
26
|
+
skills,
|
|
27
|
+
onCreateAgent,
|
|
28
|
+
onDeleteAgent
|
|
29
|
+
}) => {
|
|
30
|
+
const [showCreateModal, setShowCreateModal] = useState(false);
|
|
31
|
+
const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null);
|
|
32
|
+
const [changes, setChanges] = useState<CodeChange[]>([]);
|
|
33
|
+
const [changesLoading, setChangesLoading] = useState(false);
|
|
34
|
+
const [queueItems, setQueueItems] = useState<Array<{ id: string; type: 'message' | 'task'; content: string; status?: 'pending' | 'running'; position?: number; createdAt?: number }>>([]);
|
|
35
|
+
const [queueHistory, setQueueHistory] = useState<Array<{ id: string; type: 'message' | 'task'; content: string; status: string; createdAt: Date; completedAt?: Date; error?: string }>>([]);
|
|
36
|
+
const [showQueueHistory, setShowQueueHistory] = useState(false);
|
|
37
|
+
const [inFlightByAgent, setInFlightByAgent] = useState<Record<string, boolean>>({});
|
|
38
|
+
const [claimedByAgent, setClaimedByAgent] = useState<Record<string, { id: string; type: 'message' | 'task'; content: string } | null>>({});
|
|
39
|
+
const [newQueueType, setNewQueueType] = useState<'message' | 'task'>('task');
|
|
40
|
+
const [newQueueContent, setNewQueueContent] = useState('');
|
|
41
|
+
const [newAgentConfig, setNewAgentConfig] = useState({
|
|
42
|
+
name: '',
|
|
43
|
+
projectPath: '',
|
|
44
|
+
providerId: providers[0]?.id || '',
|
|
45
|
+
model: '',
|
|
46
|
+
skills: [] as string[]
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const selectedAgentChanges = useMemo(() => {
|
|
50
|
+
if (!selectedAgent) return [];
|
|
51
|
+
return changes.filter(c => c.agentId === selectedAgent.id);
|
|
52
|
+
}, [changes, selectedAgent]);
|
|
53
|
+
|
|
54
|
+
const refreshChanges = async (agentId: string) => {
|
|
55
|
+
setChangesLoading(true);
|
|
56
|
+
try {
|
|
57
|
+
const list = await window.electronAPI.changes.list(agentId);
|
|
58
|
+
setChanges(list as CodeChange[]);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Failed to load code changes:', error);
|
|
61
|
+
} finally {
|
|
62
|
+
setChangesLoading(false);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const refreshQueue = async (agentId: string) => {
|
|
67
|
+
try {
|
|
68
|
+
const list = await window.electronAPI.queue.list(agentId);
|
|
69
|
+
setQueueItems(list as any);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error('Failed to load queue:', error);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const refreshQueueHistory = async (agentId: string) => {
|
|
76
|
+
try {
|
|
77
|
+
const history = await window.electronAPI.queue.history(agentId, 20);
|
|
78
|
+
setQueueHistory(history as any);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('Failed to load queue history:', error);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (!selectedAgent) return;
|
|
86
|
+
void refreshChanges(selectedAgent.id);
|
|
87
|
+
void refreshQueue(selectedAgent.id);
|
|
88
|
+
void refreshQueueHistory(selectedAgent.id);
|
|
89
|
+
}, [selectedAgent?.id]);
|
|
90
|
+
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
const onTaskStarted = (event: any, payload: { agentId: string }) => {
|
|
93
|
+
setInFlightByAgent(prev => ({ ...prev, [payload.agentId]: true }));
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const onTaskCompleted = async (event: any, payload: { agentId: string }) => {
|
|
97
|
+
const claimed = claimedByAgent[payload.agentId];
|
|
98
|
+
if (claimed) {
|
|
99
|
+
try {
|
|
100
|
+
await window.electronAPI.queue.complete(payload.agentId, claimed.id, 'completed');
|
|
101
|
+
await refreshQueue(payload.agentId);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Failed to complete queued item:', error);
|
|
104
|
+
}
|
|
105
|
+
setClaimedByAgent(prev => ({ ...prev, [payload.agentId]: null }));
|
|
106
|
+
}
|
|
107
|
+
setInFlightByAgent(prev => ({ ...prev, [payload.agentId]: false }));
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const onTaskFailed = async (event: any, payload: { agentId: string; error?: any }) => {
|
|
111
|
+
const claimed = claimedByAgent[payload.agentId];
|
|
112
|
+
if (claimed) {
|
|
113
|
+
try {
|
|
114
|
+
await window.electronAPI.queue.complete(payload.agentId, claimed.id, 'failed', payload.error ? String(payload.error) : undefined);
|
|
115
|
+
await refreshQueue(payload.agentId);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error('Failed to fail queued item:', error);
|
|
118
|
+
}
|
|
119
|
+
setClaimedByAgent(prev => ({ ...prev, [payload.agentId]: null }));
|
|
120
|
+
}
|
|
121
|
+
setInFlightByAgent(prev => ({ ...prev, [payload.agentId]: false }));
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
window.electronAPI.on('agent:taskStarted', onTaskStarted);
|
|
125
|
+
window.electronAPI.on('agent:taskCompleted', onTaskCompleted);
|
|
126
|
+
window.electronAPI.on('agent:taskFailed', onTaskFailed);
|
|
127
|
+
|
|
128
|
+
return () => {
|
|
129
|
+
window.electronAPI.removeListener('agent:taskStarted', onTaskStarted);
|
|
130
|
+
window.electronAPI.removeListener('agent:taskCompleted', onTaskCompleted);
|
|
131
|
+
window.electronAPI.removeListener('agent:taskFailed', onTaskFailed);
|
|
132
|
+
};
|
|
133
|
+
}, [claimedByAgent]);
|
|
134
|
+
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
if (!selectedAgent) return;
|
|
137
|
+
const agentId = selectedAgent.id;
|
|
138
|
+
const inFlight = Boolean(inFlightByAgent[agentId]);
|
|
139
|
+
|
|
140
|
+
if (inFlight) return;
|
|
141
|
+
if (claimedByAgent[agentId]) return;
|
|
142
|
+
|
|
143
|
+
const hasPending = queueItems.some(i => (i.status || 'pending') === 'pending');
|
|
144
|
+
if (!hasPending) return;
|
|
145
|
+
|
|
146
|
+
setInFlightByAgent(prev => ({ ...prev, [agentId]: true }));
|
|
147
|
+
|
|
148
|
+
(async () => {
|
|
149
|
+
try {
|
|
150
|
+
const claimed = await window.electronAPI.queue.claimNext(agentId);
|
|
151
|
+
if (!claimed) {
|
|
152
|
+
setInFlightByAgent(prev => ({ ...prev, [agentId]: false }));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
setClaimedByAgent(prev => ({ ...prev, [agentId]: claimed }));
|
|
157
|
+
await refreshQueue(agentId);
|
|
158
|
+
|
|
159
|
+
if (claimed.type === 'message') {
|
|
160
|
+
await window.electronAPI.agent.sendMessage(agentId, claimed.content);
|
|
161
|
+
await window.electronAPI.queue.complete(agentId, claimed.id, 'completed');
|
|
162
|
+
setClaimedByAgent(prev => ({ ...prev, [agentId]: null }));
|
|
163
|
+
await refreshQueue(agentId);
|
|
164
|
+
setInFlightByAgent(prev => ({ ...prev, [agentId]: false }));
|
|
165
|
+
} else {
|
|
166
|
+
await window.electronAPI.agent.executeTask(agentId, claimed.content);
|
|
167
|
+
// keep inFlight until taskCompleted/taskFailed
|
|
168
|
+
}
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error('Failed to dispatch queued item:', error);
|
|
171
|
+
const claimed = claimedByAgent[agentId];
|
|
172
|
+
if (claimed) {
|
|
173
|
+
try {
|
|
174
|
+
await window.electronAPI.queue.complete(agentId, claimed.id, 'failed', error instanceof Error ? error.message : String(error));
|
|
175
|
+
} catch {
|
|
176
|
+
// ignore
|
|
177
|
+
}
|
|
178
|
+
setClaimedByAgent(prev => ({ ...prev, [agentId]: null }));
|
|
179
|
+
}
|
|
180
|
+
setInFlightByAgent(prev => ({ ...prev, [agentId]: false }));
|
|
181
|
+
await refreshQueue(agentId);
|
|
182
|
+
await window.electronAPI.notification.show({
|
|
183
|
+
title: 'Queue dispatch failed',
|
|
184
|
+
body: error instanceof Error ? error.message : String(error)
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
})();
|
|
188
|
+
}, [queueItems, inFlightByAgent, claimedByAgent, selectedAgent?.id]);
|
|
189
|
+
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
const handler = (event: any, payload: { agentId: string; changeId: string }) => {
|
|
192
|
+
if (!selectedAgent) return;
|
|
193
|
+
if (payload.agentId !== selectedAgent.id) return;
|
|
194
|
+
void refreshChanges(selectedAgent.id);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
window.electronAPI.on('changes:created', handler);
|
|
198
|
+
return () => {
|
|
199
|
+
window.electronAPI.removeListener('changes:created', handler);
|
|
200
|
+
};
|
|
201
|
+
}, [selectedAgent?.id]);
|
|
202
|
+
|
|
203
|
+
const handleCreateAgent = async () => {
|
|
204
|
+
try {
|
|
205
|
+
await onCreateAgent(newAgentConfig);
|
|
206
|
+
setShowCreateModal(false);
|
|
207
|
+
setNewAgentConfig({
|
|
208
|
+
name: '',
|
|
209
|
+
projectPath: '',
|
|
210
|
+
providerId: providers[0]?.id || '',
|
|
211
|
+
model: '',
|
|
212
|
+
skills: []
|
|
213
|
+
});
|
|
214
|
+
} catch (error) {
|
|
215
|
+
console.error('Failed to create agent:', error);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const handleSendMessage = async (agentId: string, message: string) => {
|
|
220
|
+
try {
|
|
221
|
+
await window.electronAPI.agent.sendMessage(agentId, message);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error('Failed to send message:', error);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const handleExecuteTask = async (agentId: string, task: string) => {
|
|
228
|
+
try {
|
|
229
|
+
await window.electronAPI.agent.executeTask(agentId, task);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error('Failed to execute task:', error);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const handleApproveChange = async (changeId: string) => {
|
|
236
|
+
if (!selectedAgent) return;
|
|
237
|
+
try {
|
|
238
|
+
await window.electronAPI.changes.approve(changeId);
|
|
239
|
+
await refreshChanges(selectedAgent.id);
|
|
240
|
+
await window.electronAPI.notification.show({
|
|
241
|
+
title: 'Change approved',
|
|
242
|
+
body: 'The change is approved and ready to apply.'
|
|
243
|
+
});
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error('Failed to approve change:', error);
|
|
246
|
+
await window.electronAPI.notification.show({
|
|
247
|
+
title: 'Approve failed',
|
|
248
|
+
body: error instanceof Error ? error.message : String(error)
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const handleRejectChange = async (changeId: string, comment?: string) => {
|
|
254
|
+
if (!selectedAgent) return;
|
|
255
|
+
try {
|
|
256
|
+
const finalComment =
|
|
257
|
+
typeof comment === 'string'
|
|
258
|
+
? comment
|
|
259
|
+
: (window.prompt('Rejection comment (optional):') ?? undefined);
|
|
260
|
+
|
|
261
|
+
await window.electronAPI.changes.reject(changeId, finalComment);
|
|
262
|
+
await refreshChanges(selectedAgent.id);
|
|
263
|
+
await window.electronAPI.notification.show({
|
|
264
|
+
title: 'Change rejected',
|
|
265
|
+
body: 'The change was rejected.'
|
|
266
|
+
});
|
|
267
|
+
} catch (error) {
|
|
268
|
+
console.error('Failed to reject change:', error);
|
|
269
|
+
await window.electronAPI.notification.show({
|
|
270
|
+
title: 'Reject failed',
|
|
271
|
+
body: error instanceof Error ? error.message : String(error)
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const handleApplyChange = async (changeId: string) => {
|
|
277
|
+
if (!selectedAgent) return;
|
|
278
|
+
try {
|
|
279
|
+
await window.electronAPI.changes.apply(changeId);
|
|
280
|
+
await refreshChanges(selectedAgent.id);
|
|
281
|
+
await window.electronAPI.notification.show({
|
|
282
|
+
title: 'Change applied',
|
|
283
|
+
body: 'The change was written to the worktree.'
|
|
284
|
+
});
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error('Failed to apply change:', error);
|
|
287
|
+
await window.electronAPI.notification.show({
|
|
288
|
+
title: 'Apply failed',
|
|
289
|
+
body: error instanceof Error ? error.message : String(error)
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const getStatusIcon = (status: string) => {
|
|
295
|
+
switch (status) {
|
|
296
|
+
case 'running':
|
|
297
|
+
return <div className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />;
|
|
298
|
+
case 'paused':
|
|
299
|
+
return <div className="w-2 h-2 bg-yellow-500 rounded-full" />;
|
|
300
|
+
case 'error':
|
|
301
|
+
return <div className="w-2 h-2 bg-red-500 rounded-full" />;
|
|
302
|
+
default:
|
|
303
|
+
return <div className="w-2 h-2 bg-gray-400 rounded-full" />;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
<div className="h-full flex">
|
|
309
|
+
{/* Agents List */}
|
|
310
|
+
<div className="w-80 border-r border-border bg-card flex flex-col">
|
|
311
|
+
<div className="p-4 border-b border-border flex items-center justify-between">
|
|
312
|
+
<h2 className="font-semibold">Active Agents</h2>
|
|
313
|
+
<button
|
|
314
|
+
onClick={() => setShowCreateModal(true)}
|
|
315
|
+
className="p-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
|
|
316
|
+
>
|
|
317
|
+
<Plus className="w-4 h-4" />
|
|
318
|
+
</button>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
<div className="flex-1 overflow-auto p-2 space-y-2">
|
|
322
|
+
{agents.map(agent => (
|
|
323
|
+
<div
|
|
324
|
+
key={agent.id}
|
|
325
|
+
onClick={() => setSelectedAgent(agent)}
|
|
326
|
+
className={`p-3 rounded-lg cursor-pointer transition-colors ${
|
|
327
|
+
selectedAgent?.id === agent.id
|
|
328
|
+
? 'bg-primary/10 border border-primary/30'
|
|
329
|
+
: 'hover:bg-muted border border-transparent'
|
|
330
|
+
}`}
|
|
331
|
+
>
|
|
332
|
+
<div className="flex items-start justify-between">
|
|
333
|
+
<div className="flex items-center gap-2">
|
|
334
|
+
<Bot className="w-4 h-4 text-muted-foreground" />
|
|
335
|
+
<span className="font-medium text-sm">{agent.name}</span>
|
|
336
|
+
</div>
|
|
337
|
+
{getStatusIcon(agent.status)}
|
|
338
|
+
</div>
|
|
339
|
+
<div className="mt-2 text-xs text-muted-foreground">
|
|
340
|
+
<div className="truncate">{agent.projectPath}</div>
|
|
341
|
+
<div className="mt-1">{agent.model}</div>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
))}
|
|
345
|
+
|
|
346
|
+
{agents.length === 0 && (
|
|
347
|
+
<div className="text-center py-8 text-muted-foreground">
|
|
348
|
+
<Bot className="w-12 h-12 mx-auto mb-2 opacity-50" />
|
|
349
|
+
<p className="text-sm">No agents yet</p>
|
|
350
|
+
<p className="text-xs mt-1">Create your first agent to get started</p>
|
|
351
|
+
</div>
|
|
352
|
+
)}
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
{/* Agent Detail View */}
|
|
357
|
+
<div className="flex-1 flex flex-col">
|
|
358
|
+
{selectedAgent ? (
|
|
359
|
+
<>
|
|
360
|
+
<div className="p-4 border-b border-border flex items-center justify-between">
|
|
361
|
+
<div>
|
|
362
|
+
<h2 className="text-lg font-semibold">{selectedAgent.name}</h2>
|
|
363
|
+
<p className="text-sm text-muted-foreground">
|
|
364
|
+
{selectedAgent.projectPath} • {selectedAgent.model}
|
|
365
|
+
</p>
|
|
366
|
+
</div>
|
|
367
|
+
<div className="flex items-center gap-2">
|
|
368
|
+
<button
|
|
369
|
+
onClick={() => handleSendMessage(selectedAgent.id, 'Hello!')}
|
|
370
|
+
className="px-3 py-1.5 text-sm bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
|
|
371
|
+
>
|
|
372
|
+
<MessageSquare className="w-4 h-4 inline mr-1" />
|
|
373
|
+
Chat
|
|
374
|
+
</button>
|
|
375
|
+
<button
|
|
376
|
+
onClick={() => onDeleteAgent(selectedAgent.id)}
|
|
377
|
+
className="p-2 text-destructive hover:bg-destructive/10 rounded-md"
|
|
378
|
+
>
|
|
379
|
+
<Trash2 className="w-4 h-4" />
|
|
380
|
+
</button>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<div className="flex-1 overflow-auto p-4">
|
|
385
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
386
|
+
<div className="space-y-4">
|
|
387
|
+
<div className="bg-card border border-border rounded-lg p-3">
|
|
388
|
+
<div className="flex items-center justify-between">
|
|
389
|
+
<div>
|
|
390
|
+
<h3 className="text-sm font-medium">Queue</h3>
|
|
391
|
+
<p className="text-xs text-muted-foreground">Stack work while the agent is busy</p>
|
|
392
|
+
</div>
|
|
393
|
+
<span className="text-xs text-muted-foreground">
|
|
394
|
+
{queueItems.length} queued
|
|
395
|
+
{inFlightByAgent[selectedAgent.id] ? ' • running' : ''}
|
|
396
|
+
</span>
|
|
397
|
+
</div>
|
|
398
|
+
|
|
399
|
+
<div className="mt-3 flex gap-2">
|
|
400
|
+
<select
|
|
401
|
+
value={newQueueType}
|
|
402
|
+
onChange={e => setNewQueueType(e.target.value as any)}
|
|
403
|
+
className="px-2 py-2 bg-background border border-input rounded-md text-sm"
|
|
404
|
+
>
|
|
405
|
+
<option value="task">Task</option>
|
|
406
|
+
<option value="message">Message</option>
|
|
407
|
+
</select>
|
|
408
|
+
<input
|
|
409
|
+
value={newQueueContent}
|
|
410
|
+
onChange={e => setNewQueueContent(e.target.value)}
|
|
411
|
+
placeholder={newQueueType === 'task' ? 'Describe the task...' : 'Type a message...'}
|
|
412
|
+
className="flex-1 px-3 py-2 bg-background border border-input rounded-md text-sm"
|
|
413
|
+
/>
|
|
414
|
+
<button
|
|
415
|
+
onClick={() => {
|
|
416
|
+
if (!newQueueContent.trim() || !selectedAgent) return;
|
|
417
|
+
const agentId = selectedAgent.id;
|
|
418
|
+
(async () => {
|
|
419
|
+
try {
|
|
420
|
+
await window.electronAPI.queue.enqueue(agentId, newQueueType, newQueueContent.trim());
|
|
421
|
+
setNewQueueContent('');
|
|
422
|
+
await refreshQueue(agentId);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
await window.electronAPI.notification.show({
|
|
425
|
+
title: 'Enqueue failed',
|
|
426
|
+
body: error instanceof Error ? error.message : String(error)
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
})();
|
|
430
|
+
}}
|
|
431
|
+
className="px-3 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 text-sm"
|
|
432
|
+
>
|
|
433
|
+
Add
|
|
434
|
+
</button>
|
|
435
|
+
</div>
|
|
436
|
+
|
|
437
|
+
<div className="mt-3 space-y-2 max-h-40 overflow-auto">
|
|
438
|
+
{queueItems.map((item, idx) => (
|
|
439
|
+
<div key={item.id} className="flex items-center gap-2 p-2 bg-muted rounded-md">
|
|
440
|
+
<span className="text-xs px-2 py-0.5 rounded-full bg-background border border-border">
|
|
441
|
+
{item.type}
|
|
442
|
+
</span>
|
|
443
|
+
<span className="text-sm flex-1 truncate">{item.content}</span>
|
|
444
|
+
<button
|
|
445
|
+
onClick={() => {
|
|
446
|
+
const agentId = selectedAgent.id;
|
|
447
|
+
(async () => {
|
|
448
|
+
await window.electronAPI.queue.delete(agentId, item.id);
|
|
449
|
+
await refreshQueue(agentId);
|
|
450
|
+
})();
|
|
451
|
+
}}
|
|
452
|
+
className="text-xs text-destructive hover:underline"
|
|
453
|
+
>
|
|
454
|
+
Remove
|
|
455
|
+
</button>
|
|
456
|
+
<button
|
|
457
|
+
disabled={idx === 0}
|
|
458
|
+
onClick={() => {
|
|
459
|
+
const agentId = selectedAgent.id;
|
|
460
|
+
(async () => {
|
|
461
|
+
await window.electronAPI.queue.moveUp(agentId, item.id);
|
|
462
|
+
await refreshQueue(agentId);
|
|
463
|
+
})();
|
|
464
|
+
}}
|
|
465
|
+
className="text-xs text-muted-foreground disabled:opacity-50"
|
|
466
|
+
>
|
|
467
|
+
Up
|
|
468
|
+
</button>
|
|
469
|
+
</div>
|
|
470
|
+
))}
|
|
471
|
+
{queueItems.length === 0 && (
|
|
472
|
+
<div className="text-xs text-muted-foreground">No queued items</div>
|
|
473
|
+
)}
|
|
474
|
+
</div>
|
|
475
|
+
|
|
476
|
+
{/* Queue History */}
|
|
477
|
+
<div className="mt-3">
|
|
478
|
+
<button
|
|
479
|
+
onClick={() => setShowQueueHistory(!showQueueHistory)}
|
|
480
|
+
className="text-xs text-muted-foreground hover:text-foreground"
|
|
481
|
+
>
|
|
482
|
+
{showQueueHistory ? 'Hide' : 'Show'} history ({queueHistory.length})
|
|
483
|
+
</button>
|
|
484
|
+
{showQueueHistory && (
|
|
485
|
+
<div className="mt-2 space-y-1 max-h-32 overflow-auto">
|
|
486
|
+
{queueHistory.map(item => (
|
|
487
|
+
<div key={item.id} className="flex items-center gap-2 p-2 bg-muted/50 rounded-md text-xs">
|
|
488
|
+
<span className={`px-1.5 py-0.5 rounded-full ${
|
|
489
|
+
item.status === 'completed' ? 'bg-green-500/10 text-green-500' : 'bg-red-500/10 text-red-500'
|
|
490
|
+
}`}>
|
|
491
|
+
{item.status}
|
|
492
|
+
</span>
|
|
493
|
+
<span className="text-muted-foreground">{item.type}</span>
|
|
494
|
+
<span className="flex-1 truncate">{item.content}</span>
|
|
495
|
+
{item.error && (
|
|
496
|
+
<span className="text-red-500 truncate" title={item.error}>⚠️</span>
|
|
497
|
+
)}
|
|
498
|
+
</div>
|
|
499
|
+
))}
|
|
500
|
+
{queueHistory.length === 0 && (
|
|
501
|
+
<div className="text-xs text-muted-foreground">No completed items yet</div>
|
|
502
|
+
)}
|
|
503
|
+
</div>
|
|
504
|
+
)}
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
|
|
508
|
+
<div>
|
|
509
|
+
<h3 className="text-sm font-medium mb-2">Status</h3>
|
|
510
|
+
<div className="flex items-center gap-2">
|
|
511
|
+
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
|
|
512
|
+
selectedAgent.status === 'running' ? 'bg-green-500/10 text-green-500' :
|
|
513
|
+
selectedAgent.status === 'paused' ? 'bg-yellow-500/10 text-yellow-500' :
|
|
514
|
+
selectedAgent.status === 'error' ? 'bg-red-500/10 text-red-500' :
|
|
515
|
+
'bg-gray-500/10 text-gray-500'
|
|
516
|
+
}`}>
|
|
517
|
+
{selectedAgent.status}
|
|
518
|
+
</span>
|
|
519
|
+
</div>
|
|
520
|
+
</div>
|
|
521
|
+
|
|
522
|
+
<div>
|
|
523
|
+
<h3 className="text-sm font-medium mb-2">Skills</h3>
|
|
524
|
+
<div className="flex flex-wrap gap-2">
|
|
525
|
+
{selectedAgent.skills.map(skillId => {
|
|
526
|
+
const skill = skills.find(s => s.id === skillId);
|
|
527
|
+
return (
|
|
528
|
+
<span
|
|
529
|
+
key={skillId}
|
|
530
|
+
className="px-2 py-1 bg-muted rounded-md text-xs"
|
|
531
|
+
>
|
|
532
|
+
{skill?.name || skillId}
|
|
533
|
+
</span>
|
|
534
|
+
);
|
|
535
|
+
})}
|
|
536
|
+
{selectedAgent.skills.length === 0 && (
|
|
537
|
+
<span className="text-sm text-muted-foreground">No skills applied</span>
|
|
538
|
+
)}
|
|
539
|
+
</div>
|
|
540
|
+
</div>
|
|
541
|
+
|
|
542
|
+
<div>
|
|
543
|
+
<h3 className="text-sm font-medium mb-2">Recent Tasks</h3>
|
|
544
|
+
<div className="space-y-2">
|
|
545
|
+
{selectedAgent.tasks.slice(-5).map(task => (
|
|
546
|
+
<div
|
|
547
|
+
key={task.id}
|
|
548
|
+
className="p-3 bg-muted rounded-md"
|
|
549
|
+
>
|
|
550
|
+
<div className="flex items-center justify-between">
|
|
551
|
+
<span className="text-sm font-medium">{task.description}</span>
|
|
552
|
+
<span className={`text-xs ${
|
|
553
|
+
task.status === 'completed' ? 'text-green-500' :
|
|
554
|
+
task.status === 'failed' ? 'text-red-500' :
|
|
555
|
+
task.status === 'running' ? 'text-blue-500' :
|
|
556
|
+
'text-muted-foreground'
|
|
557
|
+
}`}>
|
|
558
|
+
{task.status}
|
|
559
|
+
</span>
|
|
560
|
+
</div>
|
|
561
|
+
{task.status === 'running' && (
|
|
562
|
+
<div className="mt-2 h-1 bg-muted-foreground/20 rounded-full overflow-hidden">
|
|
563
|
+
<div
|
|
564
|
+
className="h-full bg-primary transition-all"
|
|
565
|
+
style={{ width: `${task.progress}%` }}
|
|
566
|
+
/>
|
|
567
|
+
</div>
|
|
568
|
+
)}
|
|
569
|
+
</div>
|
|
570
|
+
))}
|
|
571
|
+
{selectedAgent.tasks.length === 0 && (
|
|
572
|
+
<span className="text-sm text-muted-foreground">No tasks yet</span>
|
|
573
|
+
)}
|
|
574
|
+
</div>
|
|
575
|
+
</div>
|
|
576
|
+
</div>
|
|
577
|
+
|
|
578
|
+
<div className="bg-card border border-border rounded-lg overflow-hidden min-h-[420px]">
|
|
579
|
+
<div className="p-3 border-b border-border flex items-center justify-between">
|
|
580
|
+
<div>
|
|
581
|
+
<h3 className="text-sm font-medium">Changes</h3>
|
|
582
|
+
<p className="text-xs text-muted-foreground">
|
|
583
|
+
Review and apply changes generated by the agent
|
|
584
|
+
</p>
|
|
585
|
+
</div>
|
|
586
|
+
<button
|
|
587
|
+
onClick={async () => {
|
|
588
|
+
if (!selectedAgent) return;
|
|
589
|
+
try {
|
|
590
|
+
const checkpoints = await window.electronAPI.checkpoints.list(selectedAgent.id);
|
|
591
|
+
const lastPending = (checkpoints as any[]).find(c => !c.restoredAt);
|
|
592
|
+
const restoredFilePath = lastPending?.filePath as string | undefined;
|
|
593
|
+
|
|
594
|
+
await window.electronAPI.checkpoints.restoreLast(selectedAgent.id);
|
|
595
|
+
await refreshChanges(selectedAgent.id);
|
|
596
|
+
await window.electronAPI.notification.show({
|
|
597
|
+
title: 'Checkpoint restored',
|
|
598
|
+
body: restoredFilePath
|
|
599
|
+
? `Rolled back: ${restoredFilePath}`
|
|
600
|
+
: 'Last applied change was rolled back.'
|
|
601
|
+
});
|
|
602
|
+
} catch (error) {
|
|
603
|
+
await window.electronAPI.notification.show({
|
|
604
|
+
title: 'Rollback failed',
|
|
605
|
+
body: error instanceof Error ? error.message : String(error)
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
}}
|
|
609
|
+
className="px-3 py-1.5 text-xs bg-muted hover:bg-muted/80 rounded-md"
|
|
610
|
+
>
|
|
611
|
+
Undo last apply
|
|
612
|
+
</button>
|
|
613
|
+
{changesLoading && (
|
|
614
|
+
<span className="text-xs text-muted-foreground">Loading...</span>
|
|
615
|
+
)}
|
|
616
|
+
</div>
|
|
617
|
+
|
|
618
|
+
<div className="h-[520px]">
|
|
619
|
+
<DiffViewer
|
|
620
|
+
changes={selectedAgentChanges.filter(c => c.status !== ChangeStatus.APPLIED)}
|
|
621
|
+
onApprove={handleApproveChange}
|
|
622
|
+
onReject={handleRejectChange}
|
|
623
|
+
onApply={handleApplyChange}
|
|
624
|
+
/>
|
|
625
|
+
</div>
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
628
|
+
</div>
|
|
629
|
+
</>
|
|
630
|
+
) : (
|
|
631
|
+
<div className="flex-1 flex items-center justify-center text-muted-foreground">
|
|
632
|
+
<div className="text-center">
|
|
633
|
+
<Bot className="w-16 h-16 mx-auto mb-4 opacity-30" />
|
|
634
|
+
<p>Select an agent to view details</p>
|
|
635
|
+
</div>
|
|
636
|
+
</div>
|
|
637
|
+
)}
|
|
638
|
+
</div>
|
|
639
|
+
|
|
640
|
+
{/* Create Agent Modal */}
|
|
641
|
+
{showCreateModal && (
|
|
642
|
+
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
|
643
|
+
<div className="bg-card border border-border rounded-lg p-6 w-[500px] max-w-[90vw]">
|
|
644
|
+
<h2 className="text-lg font-semibold mb-4">Create New Agent</h2>
|
|
645
|
+
|
|
646
|
+
<div className="space-y-4">
|
|
647
|
+
<div>
|
|
648
|
+
<label className="block text-sm font-medium mb-1">Name</label>
|
|
649
|
+
<input
|
|
650
|
+
type="text"
|
|
651
|
+
value={newAgentConfig.name}
|
|
652
|
+
onChange={e => setNewAgentConfig({ ...newAgentConfig, name: e.target.value })}
|
|
653
|
+
className="w-full px-3 py-2 bg-background border border-input rounded-md"
|
|
654
|
+
placeholder="My Coding Agent"
|
|
655
|
+
/>
|
|
656
|
+
</div>
|
|
657
|
+
|
|
658
|
+
<div>
|
|
659
|
+
<label className="block text-sm font-medium mb-1">Project Path</label>
|
|
660
|
+
<div className="flex gap-2">
|
|
661
|
+
<input
|
|
662
|
+
type="text"
|
|
663
|
+
value={newAgentConfig.projectPath}
|
|
664
|
+
onChange={e => setNewAgentConfig({ ...newAgentConfig, projectPath: e.target.value })}
|
|
665
|
+
className="flex-1 px-3 py-2 bg-background border border-input rounded-md"
|
|
666
|
+
placeholder="/path/to/project"
|
|
667
|
+
/>
|
|
668
|
+
<button
|
|
669
|
+
onClick={async () => {
|
|
670
|
+
const path = await window.electronAPI.dialog.selectFolder();
|
|
671
|
+
if (path) setNewAgentConfig({ ...newAgentConfig, projectPath: path });
|
|
672
|
+
}}
|
|
673
|
+
className="px-3 py-2 bg-muted rounded-md hover:bg-muted/80"
|
|
674
|
+
>
|
|
675
|
+
Browse
|
|
676
|
+
</button>
|
|
677
|
+
</div>
|
|
678
|
+
</div>
|
|
679
|
+
|
|
680
|
+
<div>
|
|
681
|
+
<label className="block text-sm font-medium mb-1">Provider</label>
|
|
682
|
+
<select
|
|
683
|
+
value={newAgentConfig.providerId}
|
|
684
|
+
onChange={e => {
|
|
685
|
+
const provider = providers.find(p => p.id === e.target.value);
|
|
686
|
+
setNewAgentConfig({
|
|
687
|
+
...newAgentConfig,
|
|
688
|
+
providerId: e.target.value,
|
|
689
|
+
model: provider?.models[0]?.id || ''
|
|
690
|
+
});
|
|
691
|
+
}}
|
|
692
|
+
className="w-full px-3 py-2 bg-background border border-input rounded-md"
|
|
693
|
+
>
|
|
694
|
+
{providers.map(provider => (
|
|
695
|
+
<option key={provider.id} value={provider.id}>{provider.name}</option>
|
|
696
|
+
))}
|
|
697
|
+
</select>
|
|
698
|
+
</div>
|
|
699
|
+
|
|
700
|
+
<div>
|
|
701
|
+
<label className="block text-sm font-medium mb-1">Model</label>
|
|
702
|
+
<select
|
|
703
|
+
value={newAgentConfig.model}
|
|
704
|
+
onChange={e => setNewAgentConfig({ ...newAgentConfig, model: e.target.value })}
|
|
705
|
+
className="w-full px-3 py-2 bg-background border border-input rounded-md"
|
|
706
|
+
>
|
|
707
|
+
{providers
|
|
708
|
+
.find(p => p.id === newAgentConfig.providerId)
|
|
709
|
+
?.models.map(model => (
|
|
710
|
+
<option key={model.id} value={model.id}>{model.name}</option>
|
|
711
|
+
))}
|
|
712
|
+
</select>
|
|
713
|
+
</div>
|
|
714
|
+
|
|
715
|
+
<div>
|
|
716
|
+
<label className="block text-sm font-medium mb-1">Skills</label>
|
|
717
|
+
<div className="flex flex-wrap gap-2">
|
|
718
|
+
{skills.map(skill => (
|
|
719
|
+
<button
|
|
720
|
+
key={skill.id}
|
|
721
|
+
onClick={() => {
|
|
722
|
+
const newSkills = newAgentConfig.skills.includes(skill.id)
|
|
723
|
+
? newAgentConfig.skills.filter(id => id !== skill.id)
|
|
724
|
+
: [...newAgentConfig.skills, skill.id];
|
|
725
|
+
setNewAgentConfig({ ...newAgentConfig, skills: newSkills });
|
|
726
|
+
}}
|
|
727
|
+
className={`px-3 py-1.5 rounded-md text-sm transition-colors ${
|
|
728
|
+
newAgentConfig.skills.includes(skill.id)
|
|
729
|
+
? 'bg-primary text-primary-foreground'
|
|
730
|
+
: 'bg-muted hover:bg-muted/80'
|
|
731
|
+
}`}
|
|
732
|
+
>
|
|
733
|
+
{skill.name}
|
|
734
|
+
</button>
|
|
735
|
+
))}
|
|
736
|
+
</div>
|
|
737
|
+
</div>
|
|
738
|
+
</div>
|
|
739
|
+
|
|
740
|
+
<div className="flex justify-end gap-2 mt-6">
|
|
741
|
+
<button
|
|
742
|
+
onClick={() => setShowCreateModal(false)}
|
|
743
|
+
className="px-4 py-2 text-muted-foreground hover:text-foreground"
|
|
744
|
+
>
|
|
745
|
+
Cancel
|
|
746
|
+
</button>
|
|
747
|
+
<button
|
|
748
|
+
onClick={handleCreateAgent}
|
|
749
|
+
disabled={!newAgentConfig.name || !newAgentConfig.projectPath}
|
|
750
|
+
className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 disabled:opacity-50"
|
|
751
|
+
>
|
|
752
|
+
Create Agent
|
|
753
|
+
</button>
|
|
754
|
+
</div>
|
|
755
|
+
</div>
|
|
756
|
+
</div>
|
|
757
|
+
)}
|
|
758
|
+
</div>
|
|
759
|
+
);
|
|
760
|
+
};
|