@mragentix/cli 4.2.37
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/LICENSE +21 -0
- package/README.md +149 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +772 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +29 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent-session.d.ts +87 -0
- package/dist/core/agent-session.d.ts.map +1 -0
- package/dist/core/agent-session.js +498 -0
- package/dist/core/agent-session.js.map +1 -0
- package/dist/core/agents.d.ts +30 -0
- package/dist/core/agents.d.ts.map +1 -0
- package/dist/core/agents.js +91 -0
- package/dist/core/agents.js.map +1 -0
- package/dist/core/auth-storage.d.ts +35 -0
- package/dist/core/auth-storage.d.ts.map +1 -0
- package/dist/core/auth-storage.js +144 -0
- package/dist/core/auth-storage.js.map +1 -0
- package/dist/core/auto-update.d.ts +8 -0
- package/dist/core/auto-update.d.ts.map +1 -0
- package/dist/core/auto-update.js +152 -0
- package/dist/core/auto-update.js.map +1 -0
- package/dist/core/compaction/compactor.d.ts +69 -0
- package/dist/core/compaction/compactor.d.ts.map +1 -0
- package/dist/core/compaction/compactor.js +405 -0
- package/dist/core/compaction/compactor.js.map +1 -0
- package/dist/core/compaction/compactor.test.d.ts +2 -0
- package/dist/core/compaction/compactor.test.d.ts.map +1 -0
- package/dist/core/compaction/compactor.test.js +461 -0
- package/dist/core/compaction/compactor.test.js.map +1 -0
- package/dist/core/compaction/token-estimator.d.ts +10 -0
- package/dist/core/compaction/token-estimator.d.ts.map +1 -0
- package/dist/core/compaction/token-estimator.js +75 -0
- package/dist/core/compaction/token-estimator.js.map +1 -0
- package/dist/core/compaction/token-estimator.test.d.ts +2 -0
- package/dist/core/compaction/token-estimator.test.d.ts.map +1 -0
- package/dist/core/compaction/token-estimator.test.js +137 -0
- package/dist/core/compaction/token-estimator.test.js.map +1 -0
- package/dist/core/custom-commands.d.ts +13 -0
- package/dist/core/custom-commands.d.ts.map +1 -0
- package/dist/core/custom-commands.js +40 -0
- package/dist/core/custom-commands.js.map +1 -0
- package/dist/core/event-bus.d.ts +95 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +99 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/extensions/loader.d.ts +8 -0
- package/dist/core/extensions/loader.d.ts.map +1 -0
- package/dist/core/extensions/loader.js +48 -0
- package/dist/core/extensions/loader.js.map +1 -0
- package/dist/core/extensions/types.d.ts +19 -0
- package/dist/core/extensions/types.d.ts.map +1 -0
- package/dist/core/extensions/types.js +2 -0
- package/dist/core/extensions/types.js.map +1 -0
- package/dist/core/file-lock.d.ts +6 -0
- package/dist/core/file-lock.d.ts.map +1 -0
- package/dist/core/file-lock.js +76 -0
- package/dist/core/file-lock.js.map +1 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logger.d.ts +26 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +132 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/mcp/client.d.ts +9 -0
- package/dist/core/mcp/client.d.ts.map +1 -0
- package/dist/core/mcp/client.js +126 -0
- package/dist/core/mcp/client.js.map +1 -0
- package/dist/core/mcp/defaults.d.ts +9 -0
- package/dist/core/mcp/defaults.d.ts.map +1 -0
- package/dist/core/mcp/defaults.js +47 -0
- package/dist/core/mcp/defaults.js.map +1 -0
- package/dist/core/mcp/index.d.ts +4 -0
- package/dist/core/mcp/index.d.ts.map +1 -0
- package/dist/core/mcp/index.js +3 -0
- package/dist/core/mcp/index.js.map +1 -0
- package/dist/core/mcp/types.d.ts +15 -0
- package/dist/core/mcp/types.d.ts.map +1 -0
- package/dist/core/mcp/types.js +2 -0
- package/dist/core/mcp/types.js.map +1 -0
- package/dist/core/model-registry.d.ts +25 -0
- package/dist/core/model-registry.d.ts.map +1 -0
- package/dist/core/model-registry.js +135 -0
- package/dist/core/model-registry.js.map +1 -0
- package/dist/core/oauth/anthropic.d.ts +4 -0
- package/dist/core/oauth/anthropic.d.ts.map +1 -0
- package/dist/core/oauth/anthropic.js +75 -0
- package/dist/core/oauth/anthropic.js.map +1 -0
- package/dist/core/oauth/openai.d.ts +4 -0
- package/dist/core/oauth/openai.d.ts.map +1 -0
- package/dist/core/oauth/openai.js +186 -0
- package/dist/core/oauth/openai.js.map +1 -0
- package/dist/core/oauth/pkce.d.ts +5 -0
- package/dist/core/oauth/pkce.d.ts.map +1 -0
- package/dist/core/oauth/pkce.js +17 -0
- package/dist/core/oauth/pkce.js.map +1 -0
- package/dist/core/oauth/types.d.ts +12 -0
- package/dist/core/oauth/types.d.ts.map +1 -0
- package/dist/core/oauth/types.js +2 -0
- package/dist/core/oauth/types.js.map +1 -0
- package/dist/core/process-manager.d.ts +30 -0
- package/dist/core/process-manager.d.ts.map +1 -0
- package/dist/core/process-manager.js +130 -0
- package/dist/core/process-manager.js.map +1 -0
- package/dist/core/prompt-commands.d.ts +14 -0
- package/dist/core/prompt-commands.d.ts.map +1 -0
- package/dist/core/prompt-commands.js +496 -0
- package/dist/core/prompt-commands.js.map +1 -0
- package/dist/core/session-manager.d.ts +112 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +326 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/core/settings-manager.d.ts +43 -0
- package/dist/core/settings-manager.d.ts.map +1 -0
- package/dist/core/settings-manager.js +64 -0
- package/dist/core/settings-manager.js.map +1 -0
- package/dist/core/skills.d.ts +23 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +89 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/slash-commands.d.ts +35 -0
- package/dist/core/slash-commands.d.ts.map +1 -0
- package/dist/core/slash-commands.js +183 -0
- package/dist/core/slash-commands.js.map +1 -0
- package/dist/core/telegram.d.ts +94 -0
- package/dist/core/telegram.d.ts.map +1 -0
- package/dist/core/telegram.js +227 -0
- package/dist/core/telegram.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive.d.ts +3 -0
- package/dist/interactive.d.ts.map +1 -0
- package/dist/interactive.js +173 -0
- package/dist/interactive.js.map +1 -0
- package/dist/modes/index.d.ts +3 -0
- package/dist/modes/index.d.ts.map +1 -0
- package/dist/modes/index.js +3 -0
- package/dist/modes/index.js.map +1 -0
- package/dist/modes/json-mode.d.ts +13 -0
- package/dist/modes/json-mode.d.ts.map +1 -0
- package/dist/modes/json-mode.js +74 -0
- package/dist/modes/json-mode.js.map +1 -0
- package/dist/modes/print-mode.d.ts +12 -0
- package/dist/modes/print-mode.d.ts.map +1 -0
- package/dist/modes/print-mode.js +49 -0
- package/dist/modes/print-mode.js.map +1 -0
- package/dist/modes/rpc-mode.d.ts +28 -0
- package/dist/modes/rpc-mode.d.ts.map +1 -0
- package/dist/modes/rpc-mode.js +145 -0
- package/dist/modes/rpc-mode.js.map +1 -0
- package/dist/modes/serve-mode.d.ts +21 -0
- package/dist/modes/serve-mode.d.ts.map +1 -0
- package/dist/modes/serve-mode.js +649 -0
- package/dist/modes/serve-mode.js.map +1 -0
- package/dist/session.d.ts +16 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +129 -0
- package/dist/session.js.map +1 -0
- package/dist/system-prompt.d.ts +6 -0
- package/dist/system-prompt.d.ts.map +1 -0
- package/dist/system-prompt.js +115 -0
- package/dist/system-prompt.js.map +1 -0
- package/dist/tools/bash.d.ts +13 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +165 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit-diff.d.ts +18 -0
- package/dist/tools/edit-diff.d.ts.map +1 -0
- package/dist/tools/edit-diff.js +92 -0
- package/dist/tools/edit-diff.js.map +1 -0
- package/dist/tools/edit.d.ts +11 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +57 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/find.d.ts +9 -0
- package/dist/tools/find.d.ts.map +1 -0
- package/dist/tools/find.js +59 -0
- package/dist/tools/find.js.map +1 -0
- package/dist/tools/grep.d.ts +13 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +121 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/index.d.ts +30 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +50 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/ls.d.ts +10 -0
- package/dist/tools/ls.d.ts.map +1 -0
- package/dist/tools/ls.js +56 -0
- package/dist/tools/ls.js.map +1 -0
- package/dist/tools/operations.d.ts +39 -0
- package/dist/tools/operations.d.ts.map +1 -0
- package/dist/tools/operations.js +27 -0
- package/dist/tools/operations.js.map +1 -0
- package/dist/tools/path-utils.d.ts +7 -0
- package/dist/tools/path-utils.d.ts.map +1 -0
- package/dist/tools/path-utils.js +27 -0
- package/dist/tools/path-utils.js.map +1 -0
- package/dist/tools/read.d.ts +12 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +113 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/subagent.d.ts +26 -0
- package/dist/tools/subagent.d.ts.map +1 -0
- package/dist/tools/subagent.js +210 -0
- package/dist/tools/subagent.js.map +1 -0
- package/dist/tools/task-output.d.ts +10 -0
- package/dist/tools/task-output.d.ts.map +1 -0
- package/dist/tools/task-output.js +33 -0
- package/dist/tools/task-output.js.map +1 -0
- package/dist/tools/task-stop.d.ts +9 -0
- package/dist/tools/task-stop.d.ts.map +1 -0
- package/dist/tools/task-stop.js +15 -0
- package/dist/tools/task-stop.js.map +1 -0
- package/dist/tools/tasks.d.ts +16 -0
- package/dist/tools/tasks.d.ts.map +1 -0
- package/dist/tools/tasks.js +132 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tools/truncate.d.ts +19 -0
- package/dist/tools/truncate.d.ts.map +1 -0
- package/dist/tools/truncate.js +59 -0
- package/dist/tools/truncate.js.map +1 -0
- package/dist/tools/truncate.test.d.ts +2 -0
- package/dist/tools/truncate.test.d.ts.map +1 -0
- package/dist/tools/truncate.test.js +100 -0
- package/dist/tools/truncate.test.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +9 -0
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +97 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/write.d.ts +10 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +30 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/tools/write.test.d.ts +2 -0
- package/dist/tools/write.test.d.ts.map +1 -0
- package/dist/tools/write.test.js +84 -0
- package/dist/tools/write.test.js.map +1 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/App.d.ts +148 -0
- package/dist/ui/App.d.ts.map +1 -0
- package/dist/ui/App.js +1191 -0
- package/dist/ui/App.js.map +1 -0
- package/dist/ui/components/ActivityIndicator.d.ts +13 -0
- package/dist/ui/components/ActivityIndicator.d.ts.map +1 -0
- package/dist/ui/components/ActivityIndicator.js +313 -0
- package/dist/ui/components/ActivityIndicator.js.map +1 -0
- package/dist/ui/components/AnimationContext.d.ts +22 -0
- package/dist/ui/components/AnimationContext.d.ts.map +1 -0
- package/dist/ui/components/AnimationContext.js +35 -0
- package/dist/ui/components/AnimationContext.js.map +1 -0
- package/dist/ui/components/AssistantMessage.d.ts +9 -0
- package/dist/ui/components/AssistantMessage.d.ts.map +1 -0
- package/dist/ui/components/AssistantMessage.js +11 -0
- package/dist/ui/components/AssistantMessage.js.map +1 -0
- package/dist/ui/components/BackgroundTasksBar.d.ts +15 -0
- package/dist/ui/components/BackgroundTasksBar.d.ts.map +1 -0
- package/dist/ui/components/BackgroundTasksBar.js +74 -0
- package/dist/ui/components/BackgroundTasksBar.js.map +1 -0
- package/dist/ui/components/Banner.d.ts +11 -0
- package/dist/ui/components/Banner.d.ts.map +1 -0
- package/dist/ui/components/Banner.js +55 -0
- package/dist/ui/components/Banner.js.map +1 -0
- package/dist/ui/components/CompactionNotice.d.ts +10 -0
- package/dist/ui/components/CompactionNotice.d.ts.map +1 -0
- package/dist/ui/components/CompactionNotice.js +27 -0
- package/dist/ui/components/CompactionNotice.js.map +1 -0
- package/dist/ui/components/DiffView.d.ts +4 -0
- package/dist/ui/components/DiffView.d.ts.map +1 -0
- package/dist/ui/components/DiffView.js +20 -0
- package/dist/ui/components/DiffView.js.map +1 -0
- package/dist/ui/components/Footer.d.ts +10 -0
- package/dist/ui/components/Footer.d.ts.map +1 -0
- package/dist/ui/components/Footer.js +105 -0
- package/dist/ui/components/Footer.js.map +1 -0
- package/dist/ui/components/InputArea.d.ts +21 -0
- package/dist/ui/components/InputArea.d.ts.map +1 -0
- package/dist/ui/components/InputArea.js +465 -0
- package/dist/ui/components/InputArea.js.map +1 -0
- package/dist/ui/components/Markdown.d.ts +9 -0
- package/dist/ui/components/Markdown.d.ts.map +1 -0
- package/dist/ui/components/Markdown.js +246 -0
- package/dist/ui/components/Markdown.js.map +1 -0
- package/dist/ui/components/ModelSelector.d.ts +11 -0
- package/dist/ui/components/ModelSelector.d.ts.map +1 -0
- package/dist/ui/components/ModelSelector.js +20 -0
- package/dist/ui/components/ModelSelector.js.map +1 -0
- package/dist/ui/components/Overlay.d.ts +8 -0
- package/dist/ui/components/Overlay.d.ts.map +1 -0
- package/dist/ui/components/Overlay.js +9 -0
- package/dist/ui/components/Overlay.js.map +1 -0
- package/dist/ui/components/SelectList.d.ts +14 -0
- package/dist/ui/components/SelectList.d.ts.map +1 -0
- package/dist/ui/components/SelectList.js +46 -0
- package/dist/ui/components/SelectList.js.map +1 -0
- package/dist/ui/components/ServerToolExecution.d.ts +17 -0
- package/dist/ui/components/ServerToolExecution.d.ts.map +1 -0
- package/dist/ui/components/ServerToolExecution.js +26 -0
- package/dist/ui/components/ServerToolExecution.js.map +1 -0
- package/dist/ui/components/SessionSelector.d.ts +9 -0
- package/dist/ui/components/SessionSelector.d.ts.map +1 -0
- package/dist/ui/components/SessionSelector.js +13 -0
- package/dist/ui/components/SessionSelector.js.map +1 -0
- package/dist/ui/components/SettingsSelector.d.ts +9 -0
- package/dist/ui/components/SettingsSelector.d.ts.map +1 -0
- package/dist/ui/components/SettingsSelector.js +13 -0
- package/dist/ui/components/SettingsSelector.js.map +1 -0
- package/dist/ui/components/SlashCommandMenu.d.ts +15 -0
- package/dist/ui/components/SlashCommandMenu.d.ts.map +1 -0
- package/dist/ui/components/SlashCommandMenu.js +32 -0
- package/dist/ui/components/SlashCommandMenu.js.map +1 -0
- package/dist/ui/components/Spinner.d.ts +4 -0
- package/dist/ui/components/Spinner.d.ts.map +1 -0
- package/dist/ui/components/Spinner.js +13 -0
- package/dist/ui/components/Spinner.js.map +1 -0
- package/dist/ui/components/StreamingArea.d.ts +10 -0
- package/dist/ui/components/StreamingArea.d.ts.map +1 -0
- package/dist/ui/components/StreamingArea.js +58 -0
- package/dist/ui/components/StreamingArea.js.map +1 -0
- package/dist/ui/components/SubAgentPanel.d.ts +21 -0
- package/dist/ui/components/SubAgentPanel.d.ts.map +1 -0
- package/dist/ui/components/SubAgentPanel.js +71 -0
- package/dist/ui/components/SubAgentPanel.js.map +1 -0
- package/dist/ui/components/TaskOverlay.d.ts +10 -0
- package/dist/ui/components/TaskOverlay.d.ts.map +1 -0
- package/dist/ui/components/TaskOverlay.js +263 -0
- package/dist/ui/components/TaskOverlay.js.map +1 -0
- package/dist/ui/components/ThinkingBlock.d.ts +11 -0
- package/dist/ui/components/ThinkingBlock.d.ts.map +1 -0
- package/dist/ui/components/ThinkingBlock.js +43 -0
- package/dist/ui/components/ThinkingBlock.js.map +1 -0
- package/dist/ui/components/ThinkingIndicator.d.ts +6 -0
- package/dist/ui/components/ThinkingIndicator.d.ts.map +1 -0
- package/dist/ui/components/ThinkingIndicator.js +144 -0
- package/dist/ui/components/ThinkingIndicator.js.map +1 -0
- package/dist/ui/components/ToolExecution.d.ts +16 -0
- package/dist/ui/components/ToolExecution.d.ts.map +1 -0
- package/dist/ui/components/ToolExecution.js +490 -0
- package/dist/ui/components/ToolExecution.js.map +1 -0
- package/dist/ui/components/ToolGroupExecution.d.ts +7 -0
- package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -0
- package/dist/ui/components/ToolGroupExecution.js +115 -0
- package/dist/ui/components/ToolGroupExecution.js.map +1 -0
- package/dist/ui/components/UserMessage.d.ts +7 -0
- package/dist/ui/components/UserMessage.d.ts.map +1 -0
- package/dist/ui/components/UserMessage.js +16 -0
- package/dist/ui/components/UserMessage.js.map +1 -0
- package/dist/ui/components/index.d.ts +15 -0
- package/dist/ui/components/index.d.ts.map +1 -0
- package/dist/ui/components/index.js +15 -0
- package/dist/ui/components/index.js.map +1 -0
- package/dist/ui/hooks/useAgentLoop.d.ts +69 -0
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -0
- package/dist/ui/hooks/useAgentLoop.js +483 -0
- package/dist/ui/hooks/useAgentLoop.js.map +1 -0
- package/dist/ui/hooks/useSessionManager.d.ts +13 -0
- package/dist/ui/hooks/useSessionManager.d.ts.map +1 -0
- package/dist/ui/hooks/useSessionManager.js +43 -0
- package/dist/ui/hooks/useSessionManager.js.map +1 -0
- package/dist/ui/hooks/useSlashCommands.d.ts +7 -0
- package/dist/ui/hooks/useSlashCommands.d.ts.map +1 -0
- package/dist/ui/hooks/useSlashCommands.js +11 -0
- package/dist/ui/hooks/useSlashCommands.js.map +1 -0
- package/dist/ui/hooks/useTerminalSize.d.ts +20 -0
- package/dist/ui/hooks/useTerminalSize.d.ts.map +1 -0
- package/dist/ui/hooks/useTerminalSize.js +55 -0
- package/dist/ui/hooks/useTerminalSize.js.map +1 -0
- package/dist/ui/hooks/useTerminalTitle.d.ts +3 -0
- package/dist/ui/hooks/useTerminalTitle.d.ts.map +1 -0
- package/dist/ui/hooks/useTerminalTitle.js +41 -0
- package/dist/ui/hooks/useTerminalTitle.js.map +1 -0
- package/dist/ui/live-item-flush.d.ts +59 -0
- package/dist/ui/live-item-flush.d.ts.map +1 -0
- package/dist/ui/live-item-flush.js +135 -0
- package/dist/ui/live-item-flush.js.map +1 -0
- package/dist/ui/live-item-flush.test.d.ts +2 -0
- package/dist/ui/live-item-flush.test.d.ts.map +1 -0
- package/dist/ui/live-item-flush.test.js +307 -0
- package/dist/ui/live-item-flush.test.js.map +1 -0
- package/dist/ui/login.d.ts +3 -0
- package/dist/ui/login.d.ts.map +1 -0
- package/dist/ui/login.js +117 -0
- package/dist/ui/login.js.map +1 -0
- package/dist/ui/render.d.ts +38 -0
- package/dist/ui/render.d.ts.map +1 -0
- package/dist/ui/render.js +72 -0
- package/dist/ui/render.js.map +1 -0
- package/dist/ui/sessions.d.ts +2 -0
- package/dist/ui/sessions.d.ts.map +1 -0
- package/dist/ui/sessions.js +208 -0
- package/dist/ui/sessions.js.map +1 -0
- package/dist/ui/spinner-frames.d.ts +3 -0
- package/dist/ui/spinner-frames.d.ts.map +1 -0
- package/dist/ui/spinner-frames.js +7 -0
- package/dist/ui/spinner-frames.js.map +1 -0
- package/dist/ui/theme/dark.json +24 -0
- package/dist/ui/theme/detect-theme.d.ts +12 -0
- package/dist/ui/theme/detect-theme.d.ts.map +1 -0
- package/dist/ui/theme/detect-theme.js +152 -0
- package/dist/ui/theme/detect-theme.js.map +1 -0
- package/dist/ui/theme/light.json +24 -0
- package/dist/ui/theme/theme.d.ts +29 -0
- package/dist/ui/theme/theme.d.ts.map +1 -0
- package/dist/ui/theme/theme.js +11 -0
- package/dist/ui/theme/theme.js.map +1 -0
- package/dist/ui/utils/highlight.d.ts +8 -0
- package/dist/ui/utils/highlight.d.ts.map +1 -0
- package/dist/ui/utils/highlight.js +49 -0
- package/dist/ui/utils/highlight.js.map +1 -0
- package/dist/ui/utils/table-text.d.ts +25 -0
- package/dist/ui/utils/table-text.d.ts.map +1 -0
- package/dist/ui/utils/table-text.js +78 -0
- package/dist/ui/utils/table-text.js.map +1 -0
- package/dist/ui/utils/table-text.test.d.ts +2 -0
- package/dist/ui/utils/table-text.test.d.ts.map +1 -0
- package/dist/ui/utils/table-text.test.js +202 -0
- package/dist/ui/utils/table-text.test.js.map +1 -0
- package/dist/utils/error-handler.d.ts +5 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +120 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/format.d.ts +21 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +120 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/git.d.ts +2 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +13 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/image.d.ts +30 -0
- package/dist/utils/image.d.ts.map +1 -0
- package/dist/utils/image.js +231 -0
- package/dist/utils/image.js.map +1 -0
- package/dist/utils/markdown.d.ts +6 -0
- package/dist/utils/markdown.d.ts.map +1 -0
- package/dist/utils/markdown.js +25 -0
- package/dist/utils/markdown.js.map +1 -0
- package/dist/utils/process.d.ts +6 -0
- package/dist/utils/process.d.ts.map +1 -0
- package/dist/utils/process.js +19 -0
- package/dist/utils/process.js.map +1 -0
- package/dist/utils/shell.d.ts +3 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +8 -0
- package/dist/utils/shell.js.map +1 -0
- package/dist/utils/sound.d.ts +2 -0
- package/dist/utils/sound.d.ts.map +1 -0
- package/dist/utils/sound.js +11 -0
- package/dist/utils/sound.js.map +1 -0
- package/package.json +56 -0
package/dist/ui/App.js
ADDED
|
@@ -0,0 +1,1191 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState, useRef, useCallback, useEffect, useMemo } from "react";
|
|
3
|
+
import { Box, Text, Static, useStdout } from "ink";
|
|
4
|
+
import { useTerminalSize } from "./hooks/useTerminalSize.js";
|
|
5
|
+
import crypto, { createHash } from "node:crypto";
|
|
6
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { playNotificationSound } from "../utils/sound.js";
|
|
10
|
+
import { extractImagePaths } from "../utils/image.js";
|
|
11
|
+
import { useAgentLoop } from "./hooks/useAgentLoop.js";
|
|
12
|
+
import { UserMessage } from "./components/UserMessage.js";
|
|
13
|
+
import { AssistantMessage } from "./components/AssistantMessage.js";
|
|
14
|
+
import { ToolExecution } from "./components/ToolExecution.js";
|
|
15
|
+
import { ToolGroupExecution } from "./components/ToolGroupExecution.js";
|
|
16
|
+
import { ServerToolExecution } from "./components/ServerToolExecution.js";
|
|
17
|
+
import { SubAgentPanel } from "./components/SubAgentPanel.js";
|
|
18
|
+
import { CompactionSpinner, CompactionDone } from "./components/CompactionNotice.js";
|
|
19
|
+
import { StreamingArea } from "./components/StreamingArea.js";
|
|
20
|
+
import { ActivityIndicator } from "./components/ActivityIndicator.js";
|
|
21
|
+
import { InputArea } from "./components/InputArea.js";
|
|
22
|
+
import { Footer } from "./components/Footer.js";
|
|
23
|
+
import { Banner } from "./components/Banner.js";
|
|
24
|
+
import { ModelSelector } from "./components/ModelSelector.js";
|
|
25
|
+
import { TaskOverlay } from "./components/TaskOverlay.js";
|
|
26
|
+
import { BackgroundTasksBar } from "./components/BackgroundTasksBar.js";
|
|
27
|
+
import { useTheme } from "./theme/theme.js";
|
|
28
|
+
import { useAnimationTick, deriveFrame } from "./components/AnimationContext.js";
|
|
29
|
+
import { useTerminalTitle } from "./hooks/useTerminalTitle.js";
|
|
30
|
+
import { getGitBranch } from "../utils/git.js";
|
|
31
|
+
import { getModel, getContextWindow } from "../core/model-registry.js";
|
|
32
|
+
import { SessionManager } from "../core/session-manager.js";
|
|
33
|
+
import { log } from "../core/logger.js";
|
|
34
|
+
import { SettingsManager } from "../core/settings-manager.js";
|
|
35
|
+
import { shouldCompact, compact } from "../core/compaction/compactor.js";
|
|
36
|
+
import { estimateConversationTokens } from "../core/compaction/token-estimator.js";
|
|
37
|
+
import { PROMPT_COMMANDS, getPromptCommand } from "../core/prompt-commands.js";
|
|
38
|
+
import { loadCustomCommands } from "../core/custom-commands.js";
|
|
39
|
+
import { getMCPServers } from "../core/mcp/index.js";
|
|
40
|
+
import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd } from "./live-item-flush.js";
|
|
41
|
+
// ── Provider Error Hints ──────────────────────────────────
|
|
42
|
+
/** Detect provider-side errors and return a user-facing hint. */
|
|
43
|
+
function getProviderErrorHint(message) {
|
|
44
|
+
const lower = message.toLowerCase();
|
|
45
|
+
if (lower.includes("overloaded") || lower.includes("engine_overloaded")) {
|
|
46
|
+
return "This is a provider-side issue — their servers are under heavy load. Try again in a moment.";
|
|
47
|
+
}
|
|
48
|
+
if (lower.includes("insufficient balance") ||
|
|
49
|
+
lower.includes("no resource package") ||
|
|
50
|
+
lower.includes("quota exceeded") ||
|
|
51
|
+
lower.includes("recharge")) {
|
|
52
|
+
return "The provider reports a billing or quota issue. Check your account balance or resource package.";
|
|
53
|
+
}
|
|
54
|
+
if (lower.includes("rate limit") ||
|
|
55
|
+
lower.includes("too many requests") ||
|
|
56
|
+
lower.includes("429")) {
|
|
57
|
+
return "You've hit the provider's rate limit. Wait a moment before retrying.";
|
|
58
|
+
}
|
|
59
|
+
if (lower.includes("502") || lower.includes("bad gateway")) {
|
|
60
|
+
return "The provider returned a server error. This is not a mragentix issue — try again shortly.";
|
|
61
|
+
}
|
|
62
|
+
if (lower.includes("503") || lower.includes("service unavailable")) {
|
|
63
|
+
return "The provider's service is temporarily unavailable. Try again in a moment.";
|
|
64
|
+
}
|
|
65
|
+
if (lower.includes("timeout") || lower.includes("timed out")) {
|
|
66
|
+
return "The request to the provider timed out. Their servers may be slow — try again.";
|
|
67
|
+
}
|
|
68
|
+
if (lower.includes("500") && lower.includes("internal server error")) {
|
|
69
|
+
return "The provider experienced an internal error. This is not a mragentix issue.";
|
|
70
|
+
}
|
|
71
|
+
if (lower.includes("does not recognize the requested model") ||
|
|
72
|
+
(lower.includes("model") &&
|
|
73
|
+
(lower.includes("not exist") || lower.includes("not found") || lower.includes("no access")))) {
|
|
74
|
+
return "Use /model to switch to a different model, or check that your account has access to the requested model.";
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
/** Tools that get aggregated into a single compact group when concurrent. */
|
|
79
|
+
const AGGREGATABLE_TOOLS = new Set(["read", "grep", "find", "ls"]);
|
|
80
|
+
/**
|
|
81
|
+
* Cap memory by replacing old items with tiny tombstones. Ink's <Static>
|
|
82
|
+
* tracks rendered items by array length — the array must never shrink, but
|
|
83
|
+
* we can swap out heavy objects for lightweight `{ kind: "tombstone", id }`
|
|
84
|
+
* entries so GC can reclaim the original data.
|
|
85
|
+
*/
|
|
86
|
+
const MAX_LIVE_HISTORY = 200;
|
|
87
|
+
function compactHistory(items) {
|
|
88
|
+
if (items.length <= MAX_LIVE_HISTORY)
|
|
89
|
+
return items;
|
|
90
|
+
const cutoff = items.length - MAX_LIVE_HISTORY;
|
|
91
|
+
const compacted = new Array(items.length);
|
|
92
|
+
for (let i = 0; i < cutoff; i++) {
|
|
93
|
+
const it = items[i];
|
|
94
|
+
compacted[i] = it.kind === "tombstone" ? it : { kind: "tombstone", id: it.id };
|
|
95
|
+
}
|
|
96
|
+
for (let i = cutoff; i < items.length; i++) {
|
|
97
|
+
compacted[i] = items[i];
|
|
98
|
+
}
|
|
99
|
+
return compacted;
|
|
100
|
+
}
|
|
101
|
+
// flushOnTurnText, flushOnTurnEnd are imported from ./live-item-flush.ts
|
|
102
|
+
// ── Duration summary ─────────────────────────────────────
|
|
103
|
+
function formatDuration(ms) {
|
|
104
|
+
const totalSec = Math.round(ms / 1000);
|
|
105
|
+
if (totalSec < 60)
|
|
106
|
+
return `${totalSec}s`;
|
|
107
|
+
const min = Math.floor(totalSec / 60);
|
|
108
|
+
const sec = totalSec % 60;
|
|
109
|
+
return sec > 0 ? `${min}m ${sec}s` : `${min}m`;
|
|
110
|
+
}
|
|
111
|
+
function pickDurationVerb(toolsUsed) {
|
|
112
|
+
const has = (name) => toolsUsed.includes(name);
|
|
113
|
+
const hasAny = (...names) => names.some(has);
|
|
114
|
+
const writing = has("edit") || has("write");
|
|
115
|
+
const reading = has("read") || has("grep") || has("find") || has("ls");
|
|
116
|
+
// Multi-tool combos (most specific first)
|
|
117
|
+
if (has("subagent") && writing)
|
|
118
|
+
return "Orchestrated changes for";
|
|
119
|
+
if (has("subagent"))
|
|
120
|
+
return "Delegated work for";
|
|
121
|
+
if (has("web-fetch") && writing)
|
|
122
|
+
return "Researched & coded for";
|
|
123
|
+
if (has("web-fetch") && reading)
|
|
124
|
+
return "Researched for";
|
|
125
|
+
if (has("web-fetch"))
|
|
126
|
+
return "Fetched the web for";
|
|
127
|
+
if (has("bash") && writing)
|
|
128
|
+
return "Built & ran for";
|
|
129
|
+
if (has("edit") && has("write"))
|
|
130
|
+
return "Crafted code for";
|
|
131
|
+
if (has("edit") && has("bash"))
|
|
132
|
+
return "Refactored & tested for";
|
|
133
|
+
if (has("edit") && reading)
|
|
134
|
+
return "Refactored for";
|
|
135
|
+
if (has("edit"))
|
|
136
|
+
return "Refactored for";
|
|
137
|
+
if (has("write") && has("bash"))
|
|
138
|
+
return "Wrote & ran for";
|
|
139
|
+
if (has("write") && reading)
|
|
140
|
+
return "Wrote code for";
|
|
141
|
+
if (has("write"))
|
|
142
|
+
return "Wrote code for";
|
|
143
|
+
if (has("bash") && has("grep"))
|
|
144
|
+
return "Hacked away for";
|
|
145
|
+
if (has("bash") && reading)
|
|
146
|
+
return "Ran & investigated for";
|
|
147
|
+
if (has("bash"))
|
|
148
|
+
return "Executed commands for";
|
|
149
|
+
if (hasAny("tasks", "task-output", "task-stop"))
|
|
150
|
+
return "Managed tasks for";
|
|
151
|
+
if (has("grep") && has("read"))
|
|
152
|
+
return "Investigated for";
|
|
153
|
+
if (has("grep") && has("find"))
|
|
154
|
+
return "Scoured the codebase for";
|
|
155
|
+
if (has("grep"))
|
|
156
|
+
return "Searched for";
|
|
157
|
+
if (has("read") && has("find"))
|
|
158
|
+
return "Explored for";
|
|
159
|
+
if (has("read"))
|
|
160
|
+
return "Studied the code for";
|
|
161
|
+
if (has("find") || has("ls"))
|
|
162
|
+
return "Browsed files for";
|
|
163
|
+
// No tools used — pure text response
|
|
164
|
+
const phrases = [
|
|
165
|
+
"Pondered for",
|
|
166
|
+
"Thought for",
|
|
167
|
+
"Reasoned for",
|
|
168
|
+
"Mulled it over for",
|
|
169
|
+
"Noodled on it for",
|
|
170
|
+
"Brewed up a response in",
|
|
171
|
+
"Cooked up an answer in",
|
|
172
|
+
"Worked out a reply in",
|
|
173
|
+
"Channeled wisdom for",
|
|
174
|
+
"Conjured a response in",
|
|
175
|
+
];
|
|
176
|
+
return phrases[Math.floor(Math.random() * phrases.length)];
|
|
177
|
+
}
|
|
178
|
+
// ── Animated thinking border ────────────────────────────────
|
|
179
|
+
const THINKING_BORDER_COLORS = ["#60a5fa", "#818cf8", "#a78bfa", "#818cf8", "#60a5fa"];
|
|
180
|
+
// ── Task count helper ───────────────────────────────────────
|
|
181
|
+
function getTaskCount(cwd) {
|
|
182
|
+
try {
|
|
183
|
+
const hash = createHash("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
184
|
+
const data = readFileSync(join(homedir(), ".mragentix-tasks", "projects", hash, "tasks.json"), "utf-8");
|
|
185
|
+
const tasks = JSON.parse(data);
|
|
186
|
+
return tasks.filter((t) => t.status !== "done").length;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function getNextPendingTask(cwd) {
|
|
193
|
+
try {
|
|
194
|
+
const hash = createHash("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
195
|
+
const data = readFileSync(join(homedir(), ".mragentix-tasks", "projects", hash, "tasks.json"), "utf-8");
|
|
196
|
+
const tasks = JSON.parse(data);
|
|
197
|
+
const pending = tasks.find((t) => t.status === "pending");
|
|
198
|
+
if (!pending)
|
|
199
|
+
return null;
|
|
200
|
+
return {
|
|
201
|
+
id: pending.id,
|
|
202
|
+
title: pending.title,
|
|
203
|
+
prompt: pending.prompt || pending.text || pending.title,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function markTaskInProgress(cwd, taskId) {
|
|
211
|
+
try {
|
|
212
|
+
const hash = createHash("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
213
|
+
const filePath = join(homedir(), ".mragentix-tasks", "projects", hash, "tasks.json");
|
|
214
|
+
const data = readFileSync(filePath, "utf-8");
|
|
215
|
+
const tasks = JSON.parse(data);
|
|
216
|
+
const updated = tasks.map((t) => (t.id === taskId ? { ...t, status: "in-progress" } : t));
|
|
217
|
+
writeFileSync(filePath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// ignore
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// ── App Component ──────────────────────────────────────────
|
|
224
|
+
export function App(props) {
|
|
225
|
+
const theme = useTheme();
|
|
226
|
+
const { stdout } = useStdout();
|
|
227
|
+
const { resizeKey } = useTerminalSize();
|
|
228
|
+
// Terminal title — updated later after agentLoop is created
|
|
229
|
+
// (hoisted here so the hook is always called in the same order)
|
|
230
|
+
const [titlePhase, setTitlePhase] = useState("idle");
|
|
231
|
+
const [titleRunning, setTitleRunning] = useState(false);
|
|
232
|
+
useTerminalTitle(titlePhase, titleRunning);
|
|
233
|
+
// Items scrolled into Static (history). For restored sessions, skip the
|
|
234
|
+
// banner and add restored items via useEffect so Ink's <Static> treats them
|
|
235
|
+
// as incremental additions (large initial arrays can race with Static's
|
|
236
|
+
// internal useLayoutEffect and get dropped before being flushed).
|
|
237
|
+
const isRestoredSession = props.initialHistory && props.initialHistory.length > 0;
|
|
238
|
+
const [history, setHistory] = useState(isRestoredSession ? [] : [{ kind: "banner", id: "banner" }]);
|
|
239
|
+
const restoredRef = useRef(false);
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
if (isRestoredSession && !restoredRef.current) {
|
|
242
|
+
restoredRef.current = true;
|
|
243
|
+
setHistory((prev) => compactHistory([...prev, ...trimFlushedItems(props.initialHistory)]));
|
|
244
|
+
}
|
|
245
|
+
}, [isRestoredSession, props.initialHistory]);
|
|
246
|
+
// Items from the current/last turn — rendered in the live area so they stay visible
|
|
247
|
+
const [liveItems, setLiveItems] = useState([]);
|
|
248
|
+
const [overlay, setOverlay] = useState(null);
|
|
249
|
+
const [taskCount, setTaskCount] = useState(() => getTaskCount(props.cwd));
|
|
250
|
+
const [runAllTasks, setRunAllTasks] = useState(false);
|
|
251
|
+
const runAllTasksRef = useRef(false);
|
|
252
|
+
const startTaskRef = useRef(() => { });
|
|
253
|
+
const cwdRef = useRef(props.cwd);
|
|
254
|
+
const [staticKey, setStaticKey] = useState(0);
|
|
255
|
+
const [lastUserMessage, setLastUserMessage] = useState("");
|
|
256
|
+
const [doneStatus, setDoneStatus] = useState(null);
|
|
257
|
+
const [gitBranch, setGitBranch] = useState(null);
|
|
258
|
+
const [currentModel, setCurrentModel] = useState(props.model);
|
|
259
|
+
const [currentProvider, setCurrentProvider] = useState(props.provider);
|
|
260
|
+
const [currentTools, setCurrentTools] = useState(props.tools);
|
|
261
|
+
const [thinkingEnabled, setThinkingEnabled] = useState(!!props.thinking);
|
|
262
|
+
const messagesRef = useRef(props.messages);
|
|
263
|
+
const nextIdRef = useRef(0);
|
|
264
|
+
const sessionManagerRef = useRef(props.sessionsDir ? new SessionManager(props.sessionsDir) : null);
|
|
265
|
+
const sessionPathRef = useRef(props.sessionPath);
|
|
266
|
+
const persistedIndexRef = useRef(messagesRef.current.length);
|
|
267
|
+
const getId = () => String(nextIdRef.current++);
|
|
268
|
+
// Two-phase flush: items waiting to be moved to Static history after the
|
|
269
|
+
// live area has been cleared and Ink has committed the smaller output.
|
|
270
|
+
const pendingFlushRef = useRef([]);
|
|
271
|
+
// Derive credentials for the current provider
|
|
272
|
+
const currentCreds = props.credentialsByProvider?.[currentProvider];
|
|
273
|
+
const activeApiKey = currentCreds?.accessToken ?? props.apiKey;
|
|
274
|
+
const activeAccountId = currentCreds?.accountId ?? props.accountId;
|
|
275
|
+
// Load git branch
|
|
276
|
+
useEffect(() => {
|
|
277
|
+
getGitBranch(props.cwd).then(setGitBranch);
|
|
278
|
+
}, [props.cwd]);
|
|
279
|
+
// Load custom commands from .mragentix/commands/
|
|
280
|
+
const [customCommands, setCustomCommands] = useState([]);
|
|
281
|
+
const reloadCustomCommands = useCallback(() => {
|
|
282
|
+
loadCustomCommands(props.cwd).then(setCustomCommands);
|
|
283
|
+
}, [props.cwd]);
|
|
284
|
+
useEffect(() => {
|
|
285
|
+
reloadCustomCommands();
|
|
286
|
+
}, [reloadCustomCommands]);
|
|
287
|
+
const persistNewMessages = useCallback(async () => {
|
|
288
|
+
const sm = sessionManagerRef.current;
|
|
289
|
+
const sp = sessionPathRef.current;
|
|
290
|
+
if (!sm || !sp)
|
|
291
|
+
return;
|
|
292
|
+
const allMsgs = messagesRef.current;
|
|
293
|
+
for (let i = persistedIndexRef.current; i < allMsgs.length; i++) {
|
|
294
|
+
const msg = allMsgs[i];
|
|
295
|
+
if (msg.role === "system")
|
|
296
|
+
continue;
|
|
297
|
+
const entry = {
|
|
298
|
+
type: "message",
|
|
299
|
+
id: crypto.randomUUID(),
|
|
300
|
+
parentId: null,
|
|
301
|
+
timestamp: new Date().toISOString(),
|
|
302
|
+
message: msg,
|
|
303
|
+
};
|
|
304
|
+
await sm.appendEntry(sp, entry);
|
|
305
|
+
}
|
|
306
|
+
persistedIndexRef.current = allMsgs.length;
|
|
307
|
+
}, []);
|
|
308
|
+
// ── Compaction ─────────────────────────────────────────
|
|
309
|
+
// Load settings for auto-compaction
|
|
310
|
+
const settingsRef = useRef(null);
|
|
311
|
+
useEffect(() => {
|
|
312
|
+
if (props.settingsFile) {
|
|
313
|
+
const sm = new SettingsManager(props.settingsFile);
|
|
314
|
+
sm.load().then(() => {
|
|
315
|
+
settingsRef.current = sm;
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}, [props.settingsFile]);
|
|
319
|
+
const compactConversation = useCallback(async (messages) => {
|
|
320
|
+
const contextWindow = getContextWindow(currentModel);
|
|
321
|
+
const tokensBefore = estimateConversationTokens(messages);
|
|
322
|
+
const spinId = getId();
|
|
323
|
+
log("INFO", "compaction", `Running compaction`, {
|
|
324
|
+
messages: String(messages.length),
|
|
325
|
+
estimatedTokens: String(tokensBefore),
|
|
326
|
+
contextWindow: String(contextWindow),
|
|
327
|
+
});
|
|
328
|
+
// Show animated spinner
|
|
329
|
+
setLiveItems((prev) => [...prev, { kind: "compacting", id: spinId }]);
|
|
330
|
+
try {
|
|
331
|
+
// Resolve fresh credentials for compaction too
|
|
332
|
+
let compactApiKey = activeApiKey;
|
|
333
|
+
if (props.authStorage) {
|
|
334
|
+
const creds = await props.authStorage.resolveCredentials(currentProvider);
|
|
335
|
+
compactApiKey = creds.accessToken;
|
|
336
|
+
}
|
|
337
|
+
const result = await compact(messages, {
|
|
338
|
+
provider: currentProvider,
|
|
339
|
+
model: currentModel,
|
|
340
|
+
apiKey: compactApiKey,
|
|
341
|
+
contextWindow,
|
|
342
|
+
signal: undefined,
|
|
343
|
+
});
|
|
344
|
+
// Replace spinner with completed notice
|
|
345
|
+
setLiveItems((prev) => prev.map((item) => item.id === spinId
|
|
346
|
+
? {
|
|
347
|
+
kind: "compacted",
|
|
348
|
+
originalCount: result.result.originalCount,
|
|
349
|
+
newCount: result.result.newCount,
|
|
350
|
+
tokensBefore: result.result.tokensBeforeEstimate,
|
|
351
|
+
tokensAfter: result.result.tokensAfterEstimate,
|
|
352
|
+
id: spinId,
|
|
353
|
+
}
|
|
354
|
+
: item));
|
|
355
|
+
return result.messages;
|
|
356
|
+
}
|
|
357
|
+
catch (err) {
|
|
358
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
359
|
+
log("ERROR", "compaction", `Compaction failed: ${msg}`);
|
|
360
|
+
// Replace spinner with error
|
|
361
|
+
setLiveItems((prev) => prev.map((item) => item.id === spinId
|
|
362
|
+
? { kind: "error", message: `Compaction failed: ${msg}`, id: spinId }
|
|
363
|
+
: item));
|
|
364
|
+
return messages; // Return unchanged on failure
|
|
365
|
+
}
|
|
366
|
+
}, [currentModel, currentProvider, activeApiKey]);
|
|
367
|
+
/**
|
|
368
|
+
* transformContext callback for the agent loop.
|
|
369
|
+
* Called before each LLM call and on context overflow.
|
|
370
|
+
* Checks if auto-compaction is needed and runs it.
|
|
371
|
+
*/
|
|
372
|
+
const transformContext = useCallback(async (messages, options) => {
|
|
373
|
+
const settings = settingsRef.current;
|
|
374
|
+
const autoCompact = settings?.get("autoCompact") ?? true;
|
|
375
|
+
const threshold = settings?.get("compactThreshold") ?? 0.8;
|
|
376
|
+
// Force-compact on context overflow regardless of settings
|
|
377
|
+
if (options?.force) {
|
|
378
|
+
return compactConversation(messages);
|
|
379
|
+
}
|
|
380
|
+
if (!autoCompact)
|
|
381
|
+
return messages;
|
|
382
|
+
const contextWindow = getContextWindow(currentModel);
|
|
383
|
+
if (shouldCompact(messages, contextWindow, threshold)) {
|
|
384
|
+
return compactConversation(messages);
|
|
385
|
+
}
|
|
386
|
+
return messages;
|
|
387
|
+
}, [currentModel, compactConversation]);
|
|
388
|
+
// ── Background task bar state ───────────────────────────
|
|
389
|
+
const [bgTasks, setBgTasks] = useState([]);
|
|
390
|
+
const [taskBarFocused, setTaskBarFocused] = useState(false);
|
|
391
|
+
const [taskBarExpanded, setTaskBarExpanded] = useState(false);
|
|
392
|
+
const [selectedTaskIndex, setSelectedTaskIndex] = useState(0);
|
|
393
|
+
// Poll ProcessManager every 2s for running tasks
|
|
394
|
+
useEffect(() => {
|
|
395
|
+
if (!props.processManager)
|
|
396
|
+
return;
|
|
397
|
+
const pm = props.processManager;
|
|
398
|
+
const poll = () => {
|
|
399
|
+
const running = pm.list().filter((p) => p.exitCode === null);
|
|
400
|
+
setBgTasks(running);
|
|
401
|
+
};
|
|
402
|
+
poll();
|
|
403
|
+
const interval = setInterval(poll, 2000);
|
|
404
|
+
return () => clearInterval(interval);
|
|
405
|
+
}, [props.processManager]);
|
|
406
|
+
// Auto-exit task panel when all tasks gone
|
|
407
|
+
useEffect(() => {
|
|
408
|
+
if (bgTasks.length === 0) {
|
|
409
|
+
setTaskBarFocused(false);
|
|
410
|
+
setTaskBarExpanded(false);
|
|
411
|
+
}
|
|
412
|
+
// Clamp selected index
|
|
413
|
+
const maxIdx = Math.min(bgTasks.length, 5) - 1;
|
|
414
|
+
if (selectedTaskIndex > maxIdx && maxIdx >= 0) {
|
|
415
|
+
setSelectedTaskIndex(maxIdx);
|
|
416
|
+
}
|
|
417
|
+
}, [bgTasks.length, selectedTaskIndex]);
|
|
418
|
+
const handleFocusTaskBar = useCallback(() => {
|
|
419
|
+
if (bgTasks.length > 0) {
|
|
420
|
+
setTaskBarFocused(true);
|
|
421
|
+
}
|
|
422
|
+
}, [bgTasks.length]);
|
|
423
|
+
const handleTaskBarExit = useCallback(() => {
|
|
424
|
+
setTaskBarFocused(false);
|
|
425
|
+
setTaskBarExpanded(false);
|
|
426
|
+
}, []);
|
|
427
|
+
const handleTaskBarExpand = useCallback(() => {
|
|
428
|
+
setTaskBarExpanded(true);
|
|
429
|
+
setSelectedTaskIndex(0);
|
|
430
|
+
}, []);
|
|
431
|
+
const handleTaskBarCollapse = useCallback(() => {
|
|
432
|
+
setTaskBarExpanded(false);
|
|
433
|
+
}, []);
|
|
434
|
+
const handleTaskKill = useCallback((id) => {
|
|
435
|
+
props.processManager?.stop(id);
|
|
436
|
+
}, [props.processManager]);
|
|
437
|
+
const handleTaskNavigate = useCallback((index) => {
|
|
438
|
+
setSelectedTaskIndex(index);
|
|
439
|
+
}, []);
|
|
440
|
+
// Resolve fresh OAuth credentials before each agent loop run.
|
|
441
|
+
// Falls back to the static props when authStorage is not available.
|
|
442
|
+
const resolveCredentials = useCallback(async () => {
|
|
443
|
+
if (props.authStorage) {
|
|
444
|
+
const creds = await props.authStorage.resolveCredentials(currentProvider);
|
|
445
|
+
return { apiKey: creds.accessToken, accountId: creds.accountId };
|
|
446
|
+
}
|
|
447
|
+
return { apiKey: activeApiKey, accountId: activeAccountId };
|
|
448
|
+
}, [props.authStorage, currentProvider, activeApiKey, activeAccountId]);
|
|
449
|
+
const agentLoop = useAgentLoop(messagesRef, {
|
|
450
|
+
provider: currentProvider,
|
|
451
|
+
model: currentModel,
|
|
452
|
+
tools: currentTools,
|
|
453
|
+
webSearch: props.webSearch,
|
|
454
|
+
maxTokens: props.maxTokens,
|
|
455
|
+
thinking: thinkingEnabled ? (props.thinking ?? "medium") : undefined,
|
|
456
|
+
apiKey: activeApiKey,
|
|
457
|
+
baseUrl: props.baseUrl,
|
|
458
|
+
accountId: activeAccountId,
|
|
459
|
+
resolveCredentials,
|
|
460
|
+
transformContext,
|
|
461
|
+
}, {
|
|
462
|
+
onComplete: useCallback(() => {
|
|
463
|
+
persistNewMessages();
|
|
464
|
+
}, [persistNewMessages]),
|
|
465
|
+
onTurnText: useCallback((text, thinking, thinkingMs) => {
|
|
466
|
+
// Flush all completed items from the previous turn to Static history.
|
|
467
|
+
// This keeps liveItems bounded per-turn, preventing Ink's live area from
|
|
468
|
+
// growing unbounded, which makes Ink's live-area re-renders expensive.
|
|
469
|
+
setLiveItems((prev) => {
|
|
470
|
+
const flushed = flushOnTurnText(prev);
|
|
471
|
+
if (flushed.length > 0) {
|
|
472
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
|
|
473
|
+
}
|
|
474
|
+
return [{ kind: "assistant", text, thinking, thinkingMs, id: getId() }];
|
|
475
|
+
});
|
|
476
|
+
}, []),
|
|
477
|
+
onToolStart: useCallback((toolCallId, name, args) => {
|
|
478
|
+
log("INFO", "tool", `Tool call started: ${name}`, { id: toolCallId });
|
|
479
|
+
if (name === "subagent") {
|
|
480
|
+
// Create or update the sub-agent group item
|
|
481
|
+
const newAgent = {
|
|
482
|
+
toolCallId,
|
|
483
|
+
task: String(args.task ?? ""),
|
|
484
|
+
agentName: String(args.agent ?? "default"),
|
|
485
|
+
status: "running",
|
|
486
|
+
toolUseCount: 0,
|
|
487
|
+
tokenUsage: { input: 0, output: 0 },
|
|
488
|
+
};
|
|
489
|
+
setLiveItems((prev) => {
|
|
490
|
+
const groupIdx = prev.findIndex((item) => item.kind === "subagent_group");
|
|
491
|
+
if (groupIdx !== -1) {
|
|
492
|
+
const group = prev[groupIdx];
|
|
493
|
+
const next = [...prev];
|
|
494
|
+
next[groupIdx] = {
|
|
495
|
+
...group,
|
|
496
|
+
agents: [...group.agents, newAgent],
|
|
497
|
+
};
|
|
498
|
+
return next;
|
|
499
|
+
}
|
|
500
|
+
return [...prev, { kind: "subagent_group", agents: [newAgent], id: getId() }];
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
else if (AGGREGATABLE_TOOLS.has(name)) {
|
|
504
|
+
// Group concurrent read-only tools into a single compact item
|
|
505
|
+
setLiveItems((prev) => {
|
|
506
|
+
// Find an active tool group (has at least one running tool)
|
|
507
|
+
const groupIdx = prev.findIndex((item) => item.kind === "tool_group" &&
|
|
508
|
+
item.tools.some((t) => t.status === "running"));
|
|
509
|
+
if (groupIdx !== -1) {
|
|
510
|
+
const group = prev[groupIdx];
|
|
511
|
+
const next = [...prev];
|
|
512
|
+
next[groupIdx] = {
|
|
513
|
+
...group,
|
|
514
|
+
tools: [...group.tools, { toolCallId, name, args, status: "running" }],
|
|
515
|
+
};
|
|
516
|
+
return next;
|
|
517
|
+
}
|
|
518
|
+
return [
|
|
519
|
+
...prev,
|
|
520
|
+
{
|
|
521
|
+
kind: "tool_group",
|
|
522
|
+
tools: [{ toolCallId, name, args, status: "running" }],
|
|
523
|
+
id: getId(),
|
|
524
|
+
},
|
|
525
|
+
];
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
setLiveItems((prev) => [
|
|
530
|
+
...prev,
|
|
531
|
+
{ kind: "tool_start", toolCallId, name, args, id: getId() },
|
|
532
|
+
]);
|
|
533
|
+
}
|
|
534
|
+
}, []),
|
|
535
|
+
onToolUpdate: useCallback((toolCallId, update) => {
|
|
536
|
+
setLiveItems((prev) => {
|
|
537
|
+
const groupIdx = prev.findIndex((item) => item.kind === "subagent_group");
|
|
538
|
+
if (groupIdx === -1)
|
|
539
|
+
return prev;
|
|
540
|
+
const group = prev[groupIdx];
|
|
541
|
+
const agentIdx = group.agents.findIndex((a) => a.toolCallId === toolCallId);
|
|
542
|
+
if (agentIdx === -1)
|
|
543
|
+
return prev;
|
|
544
|
+
const saUpdate = update;
|
|
545
|
+
const updatedAgents = [...group.agents];
|
|
546
|
+
updatedAgents[agentIdx] = {
|
|
547
|
+
...updatedAgents[agentIdx],
|
|
548
|
+
toolUseCount: saUpdate.toolUseCount,
|
|
549
|
+
tokenUsage: { ...saUpdate.tokenUsage },
|
|
550
|
+
currentActivity: saUpdate.currentActivity,
|
|
551
|
+
};
|
|
552
|
+
const next = [...prev];
|
|
553
|
+
next[groupIdx] = { ...group, agents: updatedAgents };
|
|
554
|
+
return next;
|
|
555
|
+
});
|
|
556
|
+
}, []),
|
|
557
|
+
onToolEnd: useCallback((toolCallId, name, result, isError, durationMs, details) => {
|
|
558
|
+
const level = isError ? "ERROR" : "INFO";
|
|
559
|
+
log(level, "tool", `Tool call ended: ${name}`, {
|
|
560
|
+
id: toolCallId,
|
|
561
|
+
duration: `${durationMs}ms`,
|
|
562
|
+
isError: String(isError),
|
|
563
|
+
});
|
|
564
|
+
if (name === "subagent") {
|
|
565
|
+
setLiveItems((prev) => {
|
|
566
|
+
const groupIdx = prev.findIndex((item) => item.kind === "subagent_group");
|
|
567
|
+
if (groupIdx === -1)
|
|
568
|
+
return prev;
|
|
569
|
+
const group = prev[groupIdx];
|
|
570
|
+
const agentIdx = group.agents.findIndex((a) => a.toolCallId === toolCallId);
|
|
571
|
+
if (agentIdx === -1)
|
|
572
|
+
return prev;
|
|
573
|
+
const saDetails = details;
|
|
574
|
+
const updatedAgents = [...group.agents];
|
|
575
|
+
updatedAgents[agentIdx] = {
|
|
576
|
+
...updatedAgents[agentIdx],
|
|
577
|
+
status: isError ? "error" : "done",
|
|
578
|
+
result,
|
|
579
|
+
durationMs: saDetails?.durationMs ?? durationMs,
|
|
580
|
+
toolUseCount: saDetails?.toolUseCount ?? updatedAgents[agentIdx].toolUseCount,
|
|
581
|
+
tokenUsage: saDetails?.tokenUsage ?? updatedAgents[agentIdx].tokenUsage,
|
|
582
|
+
};
|
|
583
|
+
const next = [...prev];
|
|
584
|
+
next[groupIdx] = { ...group, agents: updatedAgents };
|
|
585
|
+
return next;
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
setLiveItems((prev) => {
|
|
590
|
+
// Check if this tool is in a tool_group
|
|
591
|
+
const groupIdx = prev.findIndex((item) => item.kind === "tool_group" &&
|
|
592
|
+
item.tools.some((t) => t.toolCallId === toolCallId));
|
|
593
|
+
if (groupIdx !== -1) {
|
|
594
|
+
const group = prev[groupIdx];
|
|
595
|
+
const next = [...prev];
|
|
596
|
+
next[groupIdx] = {
|
|
597
|
+
...group,
|
|
598
|
+
tools: group.tools.map((t) => t.toolCallId === toolCallId
|
|
599
|
+
? { ...t, status: "done", result, isError }
|
|
600
|
+
: t),
|
|
601
|
+
};
|
|
602
|
+
return next;
|
|
603
|
+
}
|
|
604
|
+
// Find the matching tool_start and replace it with tool_done
|
|
605
|
+
const startIdx = prev.findIndex((item) => item.kind === "tool_start" && item.toolCallId === toolCallId);
|
|
606
|
+
if (startIdx !== -1) {
|
|
607
|
+
const startItem = prev[startIdx];
|
|
608
|
+
const doneItem = {
|
|
609
|
+
kind: "tool_done",
|
|
610
|
+
name,
|
|
611
|
+
args: startItem.args,
|
|
612
|
+
result,
|
|
613
|
+
isError,
|
|
614
|
+
durationMs,
|
|
615
|
+
id: startItem.id,
|
|
616
|
+
};
|
|
617
|
+
const next = [...prev];
|
|
618
|
+
next[startIdx] = doneItem;
|
|
619
|
+
return next;
|
|
620
|
+
}
|
|
621
|
+
// Fallback: just append
|
|
622
|
+
return [
|
|
623
|
+
...prev,
|
|
624
|
+
{ kind: "tool_done", name, args: {}, result, isError, durationMs, id: getId() },
|
|
625
|
+
];
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}, []),
|
|
629
|
+
onServerToolCall: useCallback((id, name, input) => {
|
|
630
|
+
log("INFO", "server_tool", `Server tool call: ${name}`, { id });
|
|
631
|
+
setLiveItems((prev) => [
|
|
632
|
+
...prev,
|
|
633
|
+
{
|
|
634
|
+
kind: "server_tool_start",
|
|
635
|
+
serverToolCallId: id,
|
|
636
|
+
name,
|
|
637
|
+
input,
|
|
638
|
+
startedAt: Date.now(),
|
|
639
|
+
id: getId(),
|
|
640
|
+
},
|
|
641
|
+
]);
|
|
642
|
+
}, []),
|
|
643
|
+
onServerToolResult: useCallback((toolUseId, resultType, data) => {
|
|
644
|
+
log("INFO", "server_tool", `Server tool result`, { toolUseId, resultType });
|
|
645
|
+
setLiveItems((prev) => {
|
|
646
|
+
const startIdx = prev.findIndex((item) => item.kind === "server_tool_start" && item.serverToolCallId === toolUseId);
|
|
647
|
+
if (startIdx !== -1) {
|
|
648
|
+
const startItem = prev[startIdx];
|
|
649
|
+
const doneItem = {
|
|
650
|
+
kind: "server_tool_done",
|
|
651
|
+
name: startItem.name,
|
|
652
|
+
input: startItem.input,
|
|
653
|
+
resultType,
|
|
654
|
+
data,
|
|
655
|
+
durationMs: Date.now() - startItem.startedAt,
|
|
656
|
+
id: startItem.id,
|
|
657
|
+
};
|
|
658
|
+
const next = [...prev];
|
|
659
|
+
next[startIdx] = doneItem;
|
|
660
|
+
return next;
|
|
661
|
+
}
|
|
662
|
+
return [
|
|
663
|
+
...prev,
|
|
664
|
+
{
|
|
665
|
+
kind: "server_tool_done",
|
|
666
|
+
name: "unknown",
|
|
667
|
+
input: {},
|
|
668
|
+
resultType,
|
|
669
|
+
data,
|
|
670
|
+
durationMs: 0,
|
|
671
|
+
id: getId(),
|
|
672
|
+
},
|
|
673
|
+
];
|
|
674
|
+
});
|
|
675
|
+
}, []),
|
|
676
|
+
onTurnEnd: useCallback((turn, stopReason, usage) => {
|
|
677
|
+
log("INFO", "turn", `Turn ${turn} ended`, {
|
|
678
|
+
stopReason,
|
|
679
|
+
inputTokens: String(usage.inputTokens),
|
|
680
|
+
outputTokens: String(usage.outputTokens),
|
|
681
|
+
...(usage.cacheRead != null && { cacheRead: String(usage.cacheRead) }),
|
|
682
|
+
...(usage.cacheWrite != null && { cacheWrite: String(usage.cacheWrite) }),
|
|
683
|
+
});
|
|
684
|
+
// For tool-only turns (no text), flush completed items to Static so
|
|
685
|
+
// liveItems doesn't grow unbounded across consecutive tool-only turns.
|
|
686
|
+
setLiveItems((prev) => {
|
|
687
|
+
const { flushed, remaining } = flushOnTurnEnd(prev, stopReason);
|
|
688
|
+
if (flushed.length > 0) {
|
|
689
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
|
|
690
|
+
}
|
|
691
|
+
return remaining;
|
|
692
|
+
});
|
|
693
|
+
}, []),
|
|
694
|
+
onDone: useCallback((durationMs, toolsUsed) => {
|
|
695
|
+
log("INFO", "agent", `Agent done`, {
|
|
696
|
+
duration: `${durationMs}ms`,
|
|
697
|
+
toolsUsed: toolsUsed.join(",") || "none",
|
|
698
|
+
});
|
|
699
|
+
setDoneStatus({ durationMs, toolsUsed, verb: pickDurationVerb(toolsUsed) });
|
|
700
|
+
playNotificationSound();
|
|
701
|
+
// Two-phase flush to avoid Ink text clipping.
|
|
702
|
+
// Phase 1 (here): clear the live area so Ink commits a render with
|
|
703
|
+
// the smaller output and updates its internal line counter.
|
|
704
|
+
// Phase 2 (useEffect below): push items to Static history in a
|
|
705
|
+
// separate render cycle so the Static write never coincides with
|
|
706
|
+
// a live-area height change in the same frame.
|
|
707
|
+
setLiveItems((prev) => {
|
|
708
|
+
if (prev.length > 0) {
|
|
709
|
+
pendingFlushRef.current = prev;
|
|
710
|
+
}
|
|
711
|
+
return [];
|
|
712
|
+
});
|
|
713
|
+
// Run-all: auto-start next pending task after a short delay
|
|
714
|
+
// (allow the two-phase flush to complete first)
|
|
715
|
+
if (runAllTasksRef.current) {
|
|
716
|
+
setTimeout(() => {
|
|
717
|
+
const cwd = cwdRef.current;
|
|
718
|
+
const next = getNextPendingTask(cwd);
|
|
719
|
+
if (next) {
|
|
720
|
+
markTaskInProgress(cwd, next.id);
|
|
721
|
+
startTaskRef.current(next.title, next.prompt, next.id);
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
setRunAllTasks(false);
|
|
725
|
+
log("INFO", "tasks", "Run-all complete — no more pending tasks");
|
|
726
|
+
}
|
|
727
|
+
}, 500);
|
|
728
|
+
}
|
|
729
|
+
}, []),
|
|
730
|
+
onAborted: useCallback(() => {
|
|
731
|
+
log("WARN", "agent", "Agent run aborted by user");
|
|
732
|
+
setRunAllTasks(false);
|
|
733
|
+
setLiveItems((prev) => {
|
|
734
|
+
const next = prev.map((item) => {
|
|
735
|
+
if (item.kind === "subagent_group")
|
|
736
|
+
return { ...item, aborted: true };
|
|
737
|
+
// Convert running tools to stopped state so spinners stop
|
|
738
|
+
if (item.kind === "tool_start") {
|
|
739
|
+
return {
|
|
740
|
+
kind: "tool_done",
|
|
741
|
+
name: item.name,
|
|
742
|
+
args: item.args,
|
|
743
|
+
result: "Stopped.",
|
|
744
|
+
isError: true,
|
|
745
|
+
durationMs: 0,
|
|
746
|
+
id: item.id,
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
if (item.kind === "server_tool_start") {
|
|
750
|
+
return {
|
|
751
|
+
kind: "server_tool_done",
|
|
752
|
+
name: item.name,
|
|
753
|
+
input: item.input,
|
|
754
|
+
resultType: "aborted",
|
|
755
|
+
data: null,
|
|
756
|
+
durationMs: 0,
|
|
757
|
+
id: item.id,
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
if (item.kind === "tool_group") {
|
|
761
|
+
const tools = item.tools.map((t) => t.status === "running"
|
|
762
|
+
? { ...t, status: "done", result: "Stopped.", isError: true }
|
|
763
|
+
: t);
|
|
764
|
+
return { ...item, tools };
|
|
765
|
+
}
|
|
766
|
+
// Remove compaction spinner (compaction can't complete after abort)
|
|
767
|
+
if (item.kind === "compacting") {
|
|
768
|
+
return { kind: "tombstone", id: item.id };
|
|
769
|
+
}
|
|
770
|
+
return item;
|
|
771
|
+
});
|
|
772
|
+
return [...next, { kind: "info", text: "Request was stopped.", id: getId() }];
|
|
773
|
+
});
|
|
774
|
+
}, []),
|
|
775
|
+
});
|
|
776
|
+
// Phase 2 of the two-phase flush: after onDone clears liveItems (phase 1)
|
|
777
|
+
// and Ink renders the smaller live area (updating its internal line
|
|
778
|
+
// counter), this effect pushes the stashed items into Static history.
|
|
779
|
+
// Because the Static write happens in a SEPARATE render cycle from the
|
|
780
|
+
// live-area shrink, Ink's log-update never needs to erase the old tall
|
|
781
|
+
// live area AND write Static content in the same frame — avoiding the
|
|
782
|
+
// cursor-math mismatch that caused text clipping.
|
|
783
|
+
useEffect(() => {
|
|
784
|
+
if (pendingFlushRef.current.length > 0) {
|
|
785
|
+
const items = pendingFlushRef.current;
|
|
786
|
+
pendingFlushRef.current = [];
|
|
787
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(items)]));
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
// Sync terminal title with agent loop state
|
|
791
|
+
useEffect(() => {
|
|
792
|
+
setTitlePhase(agentLoop.activityPhase);
|
|
793
|
+
setTitleRunning(agentLoop.isRunning);
|
|
794
|
+
}, [agentLoop.activityPhase, agentLoop.isRunning]);
|
|
795
|
+
// Animated thinking border — derived from global animation tick
|
|
796
|
+
const animTick = useAnimationTick();
|
|
797
|
+
const thinkingBorderFrame = agentLoop.activityPhase === "thinking"
|
|
798
|
+
? deriveFrame(animTick, 1000, THINKING_BORDER_COLORS.length)
|
|
799
|
+
: 0;
|
|
800
|
+
const handleSubmit = useCallback(async (input, inputImages = [], pasteInfo) => {
|
|
801
|
+
const trimmed = input.trim();
|
|
802
|
+
if (trimmed.startsWith("/")) {
|
|
803
|
+
log("INFO", "command", `Slash command: ${trimmed}`);
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
const truncated = trimmed.length > 100 ? trimmed.slice(0, 100) + "..." : trimmed;
|
|
807
|
+
log("INFO", "input", `User input: ${truncated}${inputImages.length > 0 ? ` (+${inputImages.length} image${inputImages.length > 1 ? "s" : ""})` : ""}`);
|
|
808
|
+
}
|
|
809
|
+
// Handle /model directly — open inline selector
|
|
810
|
+
if (trimmed === "/model" || trimmed === "/m") {
|
|
811
|
+
setOverlay("model");
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
// Handle /compact — compact conversation
|
|
815
|
+
if (trimmed === "/compact" || trimmed === "/c") {
|
|
816
|
+
const compacted = await compactConversation(messagesRef.current);
|
|
817
|
+
if (compacted !== messagesRef.current) {
|
|
818
|
+
messagesRef.current = compacted;
|
|
819
|
+
persistedIndexRef.current = 0; // Re-persist after compaction
|
|
820
|
+
}
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
// Handle /quit — exit the agent
|
|
824
|
+
if (trimmed === "/quit" || trimmed === "/q" || trimmed === "/exit") {
|
|
825
|
+
process.exit(0);
|
|
826
|
+
}
|
|
827
|
+
// Handle /clear — reset session and clear terminal
|
|
828
|
+
if (trimmed === "/clear") {
|
|
829
|
+
// Clear terminal screen + scrollback — needed because Ink's <Static>
|
|
830
|
+
// writes directly to stdout and can't be removed by clearing React state
|
|
831
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
832
|
+
setHistory([{ kind: "banner", id: "banner" }]);
|
|
833
|
+
setLiveItems([]);
|
|
834
|
+
setDoneStatus(null);
|
|
835
|
+
messagesRef.current = messagesRef.current.slice(0, 1); // keep system prompt
|
|
836
|
+
agentLoop.reset();
|
|
837
|
+
setLiveItems([{ kind: "info", text: "Session cleared.", id: getId() }]);
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
// Handle prompt-template commands (built-in + custom from .mragentix/commands/)
|
|
841
|
+
if (trimmed.startsWith("/")) {
|
|
842
|
+
const parts = trimmed.slice(1).split(" ");
|
|
843
|
+
const cmdName = parts[0];
|
|
844
|
+
const cmdArgs = parts.slice(1).join(" ").trim();
|
|
845
|
+
const builtinCmd = getPromptCommand(cmdName);
|
|
846
|
+
const customCmd = !builtinCmd ? customCommands.find((c) => c.name === cmdName) : undefined;
|
|
847
|
+
const promptText = builtinCmd?.prompt ?? customCmd?.prompt;
|
|
848
|
+
if (promptText) {
|
|
849
|
+
log("INFO", "command", `Prompt command: /${cmdName}${cmdArgs ? ` (args: ${cmdArgs})` : ""}`);
|
|
850
|
+
// Move live items into history before starting
|
|
851
|
+
setLiveItems((prev) => {
|
|
852
|
+
if (prev.length > 0) {
|
|
853
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(prev)]));
|
|
854
|
+
}
|
|
855
|
+
return [];
|
|
856
|
+
});
|
|
857
|
+
// Show the command name as the user message
|
|
858
|
+
const userItem = { kind: "user", text: trimmed, id: getId() };
|
|
859
|
+
setLastUserMessage(trimmed);
|
|
860
|
+
setDoneStatus(null);
|
|
861
|
+
setLiveItems([userItem]);
|
|
862
|
+
// Send the full prompt to the agent, with user args appended if provided
|
|
863
|
+
const fullPrompt = cmdArgs
|
|
864
|
+
? `${promptText}\n\n## User Instructions\n\n${cmdArgs}`
|
|
865
|
+
: promptText;
|
|
866
|
+
try {
|
|
867
|
+
await agentLoop.run(fullPrompt);
|
|
868
|
+
}
|
|
869
|
+
catch (err) {
|
|
870
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
871
|
+
log("ERROR", "error", msg);
|
|
872
|
+
const isAbort = msg.includes("aborted") || msg.includes("abort");
|
|
873
|
+
setLiveItems((prev) => [
|
|
874
|
+
...prev,
|
|
875
|
+
isAbort
|
|
876
|
+
? { kind: "info", text: "Request was stopped.", id: getId() }
|
|
877
|
+
: { kind: "error", message: msg, id: getId() },
|
|
878
|
+
]);
|
|
879
|
+
}
|
|
880
|
+
// Reload custom commands in case a setup command created new ones
|
|
881
|
+
reloadCustomCommands();
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
// Check slash commands
|
|
886
|
+
if (props.onSlashCommand && input.startsWith("/")) {
|
|
887
|
+
const result = await props.onSlashCommand(input);
|
|
888
|
+
if (result !== null) {
|
|
889
|
+
setLiveItems((prev) => [...prev, { kind: "info", text: result, id: getId() }]);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
// Move any remaining live items into history (Static) before starting new turn
|
|
894
|
+
setLiveItems((prev) => {
|
|
895
|
+
if (prev.length > 0) {
|
|
896
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(prev)]));
|
|
897
|
+
}
|
|
898
|
+
return [];
|
|
899
|
+
});
|
|
900
|
+
// Build display text — strip image paths, show badges instead
|
|
901
|
+
const hasImages = inputImages.length > 0;
|
|
902
|
+
let displayText = input;
|
|
903
|
+
if (hasImages) {
|
|
904
|
+
const { cleanText } = await extractImagePaths(input, props.cwd);
|
|
905
|
+
displayText = cleanText;
|
|
906
|
+
}
|
|
907
|
+
const userItem = {
|
|
908
|
+
kind: "user",
|
|
909
|
+
text: displayText,
|
|
910
|
+
imageCount: hasImages ? inputImages.length : undefined,
|
|
911
|
+
pasteInfo,
|
|
912
|
+
id: getId(),
|
|
913
|
+
};
|
|
914
|
+
setLastUserMessage(input);
|
|
915
|
+
setDoneStatus(null);
|
|
916
|
+
setLiveItems([userItem]);
|
|
917
|
+
// Build user content — plain string or content array with images
|
|
918
|
+
const modelInfo = getModel(currentModel);
|
|
919
|
+
const modelSupportsImages = modelInfo?.supportsImages ?? true;
|
|
920
|
+
let userContent;
|
|
921
|
+
if (hasImages) {
|
|
922
|
+
const parts = [];
|
|
923
|
+
if (trimmed) {
|
|
924
|
+
parts.push({ type: "text", text: trimmed });
|
|
925
|
+
}
|
|
926
|
+
for (const img of inputImages) {
|
|
927
|
+
if (img.kind === "text") {
|
|
928
|
+
parts.push({
|
|
929
|
+
type: "text",
|
|
930
|
+
text: `<file name="${img.fileName}">\n${img.data}\n</file>`,
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
else if (modelSupportsImages) {
|
|
934
|
+
parts.push({ type: "image", mediaType: img.mediaType, data: img.data });
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
// GLM models: save image to temp file and instruct model to use vision MCP tool
|
|
938
|
+
const ext = img.mediaType.split("/")[1] ?? "png";
|
|
939
|
+
const tmpPath = `/tmp/mragentix-img-${Date.now()}.${ext}`;
|
|
940
|
+
try {
|
|
941
|
+
writeFileSync(tmpPath, Buffer.from(img.data, "base64"));
|
|
942
|
+
parts.push({
|
|
943
|
+
type: "text",
|
|
944
|
+
text: `[User attached an image saved at: ${tmpPath} — use the image_analysis tool to view and analyze it]`,
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
catch {
|
|
948
|
+
parts.push({
|
|
949
|
+
type: "text",
|
|
950
|
+
text: `[User attached an image but it could not be saved for analysis]`,
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
// If only text parts remain after stripping images, simplify to plain string
|
|
956
|
+
userContent = parts.length === 1 && parts[0].type === "text" ? parts[0].text : parts;
|
|
957
|
+
}
|
|
958
|
+
else {
|
|
959
|
+
userContent = input;
|
|
960
|
+
}
|
|
961
|
+
// Run agent
|
|
962
|
+
try {
|
|
963
|
+
await agentLoop.run(userContent);
|
|
964
|
+
}
|
|
965
|
+
catch (err) {
|
|
966
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
967
|
+
log("ERROR", "error", msg);
|
|
968
|
+
const isAbort = msg.includes("aborted") || msg.includes("abort");
|
|
969
|
+
setLiveItems((prev) => [
|
|
970
|
+
...prev,
|
|
971
|
+
isAbort
|
|
972
|
+
? { kind: "info", text: "Request was stopped.", id: getId() }
|
|
973
|
+
: { kind: "error", message: msg, id: getId() },
|
|
974
|
+
]);
|
|
975
|
+
}
|
|
976
|
+
}, [agentLoop, props.onSlashCommand, compactConversation]);
|
|
977
|
+
const handleAbort = useCallback(() => {
|
|
978
|
+
if (agentLoop.isRunning) {
|
|
979
|
+
agentLoop.abort();
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
process.exit(0);
|
|
983
|
+
}
|
|
984
|
+
}, [agentLoop]);
|
|
985
|
+
const handleToggleThinking = useCallback(() => {
|
|
986
|
+
setThinkingEnabled((prev) => {
|
|
987
|
+
const next = !prev;
|
|
988
|
+
log("INFO", "thinking", `Thinking ${next ? "enabled" : "disabled"}`);
|
|
989
|
+
setLiveItems((items) => [
|
|
990
|
+
...items,
|
|
991
|
+
{ kind: "info", text: `Thinking ${next ? "on" : "off"}`, id: getId() },
|
|
992
|
+
]);
|
|
993
|
+
if (props.settingsFile) {
|
|
994
|
+
const sm = new SettingsManager(props.settingsFile);
|
|
995
|
+
sm.load().then(() => sm.set("thinkingEnabled", next));
|
|
996
|
+
}
|
|
997
|
+
return next;
|
|
998
|
+
});
|
|
999
|
+
}, [props.settingsFile]);
|
|
1000
|
+
const handleModelSelect = useCallback((value) => {
|
|
1001
|
+
setOverlay(null);
|
|
1002
|
+
const colonIdx = value.indexOf(":");
|
|
1003
|
+
if (colonIdx === -1)
|
|
1004
|
+
return;
|
|
1005
|
+
const newProvider = value.slice(0, colonIdx);
|
|
1006
|
+
const newModelId = value.slice(colonIdx + 1);
|
|
1007
|
+
log("INFO", "model", `Model changed`, { provider: newProvider, model: newModelId });
|
|
1008
|
+
// Reconnect MCP servers when provider changes
|
|
1009
|
+
setCurrentProvider((prevProvider) => {
|
|
1010
|
+
if (newProvider !== prevProvider && props.mcpManager) {
|
|
1011
|
+
void (async () => {
|
|
1012
|
+
// Disconnect old MCP servers
|
|
1013
|
+
await props.mcpManager.dispose();
|
|
1014
|
+
// Remove old MCP tools, connect new ones
|
|
1015
|
+
let apiKey;
|
|
1016
|
+
if (newProvider === "glm" && props.authStorage) {
|
|
1017
|
+
try {
|
|
1018
|
+
const glmCreds = await props.authStorage.resolveCredentials("glm");
|
|
1019
|
+
apiKey = glmCreds.accessToken;
|
|
1020
|
+
}
|
|
1021
|
+
catch {
|
|
1022
|
+
// GLM not configured — skip Z.AI MCP servers
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
else if (newProvider === "glm") {
|
|
1026
|
+
apiKey = props.credentialsByProvider?.["glm"]?.accessToken;
|
|
1027
|
+
}
|
|
1028
|
+
try {
|
|
1029
|
+
const mcpTools = await props.mcpManager.connectAll(getMCPServers(newProvider, apiKey));
|
|
1030
|
+
setCurrentTools((prev) => [
|
|
1031
|
+
...prev.filter((t) => !t.name.startsWith("mcp__")),
|
|
1032
|
+
...mcpTools,
|
|
1033
|
+
]);
|
|
1034
|
+
log("INFO", "mcp", `MCP servers reconnected for provider ${newProvider}`);
|
|
1035
|
+
}
|
|
1036
|
+
catch (err) {
|
|
1037
|
+
log("WARN", "mcp", `MCP reconnection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1038
|
+
// Still remove old MCP tools even if reconnection fails
|
|
1039
|
+
setCurrentTools((prev) => prev.filter((t) => !t.name.startsWith("mcp__")));
|
|
1040
|
+
}
|
|
1041
|
+
})();
|
|
1042
|
+
}
|
|
1043
|
+
return newProvider;
|
|
1044
|
+
});
|
|
1045
|
+
setCurrentModel(newModelId);
|
|
1046
|
+
const modelInfo = getModel(newModelId);
|
|
1047
|
+
const displayName = modelInfo?.name ?? newModelId;
|
|
1048
|
+
setLiveItems((prev) => [
|
|
1049
|
+
...prev,
|
|
1050
|
+
{ kind: "info", text: `Switched to ${displayName}`, id: getId() },
|
|
1051
|
+
]);
|
|
1052
|
+
// Persist model selection for next CLI launch
|
|
1053
|
+
if (props.settingsFile) {
|
|
1054
|
+
const sm = new SettingsManager(props.settingsFile);
|
|
1055
|
+
sm.load().then(async () => {
|
|
1056
|
+
await sm.set("defaultProvider", newProvider);
|
|
1057
|
+
await sm.set("defaultModel", newModelId);
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
}, [props.settingsFile, props.mcpManager, props.credentialsByProvider, props.authStorage]);
|
|
1061
|
+
// All available slash commands for the command palette
|
|
1062
|
+
const allCommands = useMemo(() => [
|
|
1063
|
+
{ name: "model", aliases: ["m"], description: "Switch model" },
|
|
1064
|
+
{ name: "compact", aliases: ["c"], description: "Compact conversation" },
|
|
1065
|
+
{ name: "clear", aliases: [], description: "Clear session and terminal" },
|
|
1066
|
+
{ name: "quit", aliases: ["q", "exit"], description: "Exit the agent" },
|
|
1067
|
+
...PROMPT_COMMANDS.map((cmd) => ({
|
|
1068
|
+
name: cmd.name,
|
|
1069
|
+
aliases: cmd.aliases,
|
|
1070
|
+
description: cmd.description,
|
|
1071
|
+
})),
|
|
1072
|
+
...customCommands.map((cmd) => ({
|
|
1073
|
+
name: cmd.name,
|
|
1074
|
+
aliases: [],
|
|
1075
|
+
description: cmd.description,
|
|
1076
|
+
})),
|
|
1077
|
+
], [customCommands]);
|
|
1078
|
+
const renderItem = (item) => {
|
|
1079
|
+
switch (item.kind) {
|
|
1080
|
+
case "tombstone":
|
|
1081
|
+
return null;
|
|
1082
|
+
case "banner":
|
|
1083
|
+
return (_jsx(Banner, { version: props.version, model: props.model, provider: props.provider, cwd: props.cwd, taskCount: taskCount }, item.id));
|
|
1084
|
+
case "user":
|
|
1085
|
+
return (_jsx(UserMessage, { text: item.text, imageCount: item.imageCount, pasteInfo: item.pasteInfo }, item.id));
|
|
1086
|
+
case "task":
|
|
1087
|
+
return (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.success, bold: true, children: "▶ " }), _jsx(Text, { color: theme.textDim, children: "Task: " }), _jsx(Text, { color: theme.success, children: item.title })] }) }, item.id));
|
|
1088
|
+
case "assistant":
|
|
1089
|
+
return (_jsx(AssistantMessage, { text: item.text, thinking: item.thinking, thinkingMs: item.thinkingMs, showThinking: props.showThinking }, item.id));
|
|
1090
|
+
case "tool_start":
|
|
1091
|
+
return _jsx(ToolExecution, { status: "running", name: item.name, args: item.args }, item.id);
|
|
1092
|
+
case "tool_done":
|
|
1093
|
+
return (_jsx(ToolExecution, { status: "done", name: item.name, args: item.args, result: item.result, isError: item.isError }, item.id));
|
|
1094
|
+
case "tool_group":
|
|
1095
|
+
return _jsx(ToolGroupExecution, { tools: item.tools }, item.id);
|
|
1096
|
+
case "server_tool_start":
|
|
1097
|
+
return (_jsx(ServerToolExecution, { status: "running", name: item.name, input: item.input, startedAt: item.startedAt }, item.id));
|
|
1098
|
+
case "server_tool_done":
|
|
1099
|
+
return (_jsx(ServerToolExecution, { status: "done", name: item.name, input: item.input, durationMs: item.durationMs, resultType: item.resultType }, item.id));
|
|
1100
|
+
case "error": {
|
|
1101
|
+
const providerHint = getProviderErrorHint(item.message);
|
|
1102
|
+
return (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { color: theme.error, children: ["✗ ", item.message] }), providerHint && (_jsxs(Text, { color: theme.textDim, children: [" Hint: ", providerHint] }))] }, item.id));
|
|
1103
|
+
}
|
|
1104
|
+
case "info":
|
|
1105
|
+
return (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.textDim, children: item.text }) }, item.id));
|
|
1106
|
+
case "compacting":
|
|
1107
|
+
return _jsx(CompactionSpinner, {}, item.id);
|
|
1108
|
+
case "compacted":
|
|
1109
|
+
return (_jsx(CompactionDone, { originalCount: item.originalCount, newCount: item.newCount, tokensBefore: item.tokensBefore, tokensAfter: item.tokensAfter }, item.id));
|
|
1110
|
+
case "duration":
|
|
1111
|
+
return (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.textDim, children: ["✻ ", item.verb, " ", formatDuration(item.durationMs)] }) }, item.id));
|
|
1112
|
+
case "subagent_group":
|
|
1113
|
+
return _jsx(SubAgentPanel, { agents: item.agents, aborted: item.aborted }, item.id);
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
// ── Start a task (shared by manual "work on it" and run-all) ──
|
|
1117
|
+
const startTask = useCallback((title, prompt, taskId) => {
|
|
1118
|
+
setTaskCount(getTaskCount(props.cwd));
|
|
1119
|
+
// Reset to a fresh session before sending the task
|
|
1120
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1121
|
+
setHistory([{ kind: "banner", id: "banner" }]);
|
|
1122
|
+
setLiveItems([]);
|
|
1123
|
+
messagesRef.current = messagesRef.current.slice(0, 1);
|
|
1124
|
+
agentLoop.reset();
|
|
1125
|
+
persistedIndexRef.current = messagesRef.current.length;
|
|
1126
|
+
const sm = sessionManagerRef.current;
|
|
1127
|
+
if (sm) {
|
|
1128
|
+
void sm.create(props.cwd, currentProvider, currentModel).then((s) => {
|
|
1129
|
+
sessionPathRef.current = s.path;
|
|
1130
|
+
log("INFO", "tasks", "New session for task", { path: s.path });
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
// Inject completion instruction so the agent marks the task done
|
|
1134
|
+
const shortId = taskId.slice(0, 8);
|
|
1135
|
+
const completionHint = `\n\n---\nWhen you have fully completed this task, call the tasks tool to mark it done:\n` +
|
|
1136
|
+
`tasks({ action: "done", id: "${shortId}" })`;
|
|
1137
|
+
const fullPrompt = prompt + completionHint;
|
|
1138
|
+
// Show the short title in the TUI, but send the full prompt to the agent
|
|
1139
|
+
const taskItem = { kind: "task", title, id: getId() };
|
|
1140
|
+
setLastUserMessage(title);
|
|
1141
|
+
setDoneStatus(null);
|
|
1142
|
+
setLiveItems([taskItem]);
|
|
1143
|
+
void (async () => {
|
|
1144
|
+
try {
|
|
1145
|
+
await agentLoop.run(fullPrompt);
|
|
1146
|
+
}
|
|
1147
|
+
catch (err) {
|
|
1148
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1149
|
+
log("ERROR", "error", msg);
|
|
1150
|
+
const isAbort = msg.includes("aborted") || msg.includes("abort");
|
|
1151
|
+
setLiveItems((prev) => [
|
|
1152
|
+
...prev,
|
|
1153
|
+
isAbort
|
|
1154
|
+
? { kind: "info", text: "Request was stopped.", id: getId() }
|
|
1155
|
+
: { kind: "error", message: msg, id: getId() },
|
|
1156
|
+
]);
|
|
1157
|
+
// Stop run-all if a task errors
|
|
1158
|
+
setRunAllTasks(false);
|
|
1159
|
+
}
|
|
1160
|
+
})();
|
|
1161
|
+
}, [props.cwd, stdout, agentLoop, currentProvider, currentModel]);
|
|
1162
|
+
// Keep refs in sync for access from stale closures (onDone)
|
|
1163
|
+
startTaskRef.current = startTask;
|
|
1164
|
+
useEffect(() => {
|
|
1165
|
+
runAllTasksRef.current = runAllTasks;
|
|
1166
|
+
}, [runAllTasks]);
|
|
1167
|
+
const isTaskView = overlay === "tasks";
|
|
1168
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: isTaskView ? [] : history, style: { width: "100%" }, children: (item) => (_jsx(Box, { flexDirection: "column", paddingRight: 1, children: renderItem(item) }, item.id)) }, `${resizeKey}-${staticKey}`), isTaskView ? (_jsx(TaskOverlay, { cwd: props.cwd, agentRunning: agentLoop.isRunning, onClose: () => {
|
|
1169
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1170
|
+
setTaskCount(getTaskCount(props.cwd));
|
|
1171
|
+
setStaticKey((k) => k + 1);
|
|
1172
|
+
setOverlay(null);
|
|
1173
|
+
}, onWorkOnTask: (title, prompt, taskId) => {
|
|
1174
|
+
setOverlay(null);
|
|
1175
|
+
startTask(title, prompt, taskId);
|
|
1176
|
+
}, onRunAllTasks: () => {
|
|
1177
|
+
setOverlay(null);
|
|
1178
|
+
setRunAllTasks(true);
|
|
1179
|
+
const next = getNextPendingTask(props.cwd);
|
|
1180
|
+
if (next) {
|
|
1181
|
+
markTaskInProgress(props.cwd, next.id);
|
|
1182
|
+
startTask(next.title, next.prompt, next.id);
|
|
1183
|
+
}
|
|
1184
|
+
} })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, paddingRight: 1, children: [liveItems.map((item) => renderItem(item)), _jsx(StreamingArea, { isRunning: agentLoop.isRunning, streamingText: agentLoop.streamingText, streamingThinking: agentLoop.streamingThinking, showThinking: props.showThinking, thinkingMs: agentLoop.thinkingMs })] }), agentLoop.isRunning && agentLoop.activityPhase !== "idle" ? (_jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: agentLoop.activityPhase === "thinking"
|
|
1185
|
+
? THINKING_BORDER_COLORS[thinkingBorderFrame]
|
|
1186
|
+
: "transparent", paddingLeft: 1, paddingRight: 1, children: _jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, tokenEstimate: agentLoop.streamedTokenEstimate, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name) }) })) : (doneStatus && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.success, children: ["✻ ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] }) }))), _jsx(InputArea, { onSubmit: handleSubmit, onAbort: handleAbort, disabled: agentLoop.isRunning, isActive: !taskBarFocused && !overlay, onDownAtEnd: handleFocusTaskBar, onShiftTab: handleToggleThinking, onToggleTasks: () => {
|
|
1187
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1188
|
+
setOverlay("tasks");
|
|
1189
|
+
}, cwd: props.cwd, commands: allCommands }), overlay === "model" ? (_jsx(ModelSelector, { onSelect: handleModelSelect, onCancel: () => setOverlay(null), loggedInProviders: props.loggedInProviders ?? [currentProvider], currentModel: currentModel, currentProvider: currentProvider })) : (_jsx(Footer, { model: currentModel, tokensIn: agentLoop.contextUsed, cwd: props.cwd, gitBranch: gitBranch, thinkingEnabled: thinkingEnabled })), bgTasks.length > 0 && (_jsx(BackgroundTasksBar, { tasks: bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, onExpand: handleTaskBarExpand, onCollapse: handleTaskBarCollapse, onKill: handleTaskKill, onExit: handleTaskBarExit, onNavigate: handleTaskNavigate }))] }))] }));
|
|
1190
|
+
}
|
|
1191
|
+
//# sourceMappingURL=App.js.map
|