@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
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useState, useEffect, useRef, useCallback } from "react";
|
|
2
|
+
import { useStdout } from "ink";
|
|
3
|
+
/**
|
|
4
|
+
* Returns { columns, rows, resizeKey } and forces a React re-render whenever
|
|
5
|
+
* the terminal is resized.
|
|
6
|
+
*
|
|
7
|
+
* `columns` and `rows` update immediately on every resize event so layout
|
|
8
|
+
* stays responsive while the user drags.
|
|
9
|
+
*
|
|
10
|
+
* `resizeKey` increments once after resize events settle (300ms debounce).
|
|
11
|
+
* Use it as a React `key` on the root content wrapper to force a full
|
|
12
|
+
* remount — this is the only reliable way to make Ink re-render <Static>
|
|
13
|
+
* content that was already printed to scrollback and got corrupted by
|
|
14
|
+
* terminal text reflow. Debounces 300ms then clears screen+scrollback
|
|
15
|
+
* and remounts.
|
|
16
|
+
*/
|
|
17
|
+
export function useTerminalSize() {
|
|
18
|
+
const { stdout } = useStdout();
|
|
19
|
+
const [size, setSize] = useState({
|
|
20
|
+
columns: stdout?.columns ?? 80,
|
|
21
|
+
rows: stdout?.rows ?? 24,
|
|
22
|
+
});
|
|
23
|
+
const [resizeKey, setResizeKey] = useState(0);
|
|
24
|
+
const debounceRef = useRef(null);
|
|
25
|
+
const onResize = useCallback(() => {
|
|
26
|
+
if (!stdout)
|
|
27
|
+
return;
|
|
28
|
+
// Update dimensions immediately for responsive layout
|
|
29
|
+
setSize({ columns: stdout.columns ?? 80, rows: stdout.rows ?? 24 });
|
|
30
|
+
// Debounce the resizeKey bump — only fires after the user stops dragging
|
|
31
|
+
if (debounceRef.current)
|
|
32
|
+
clearTimeout(debounceRef.current);
|
|
33
|
+
debounceRef.current = setTimeout(() => {
|
|
34
|
+
// Clear visible screen + scrollback to remove deformed ghost renders
|
|
35
|
+
// left behind by Ink re-rendering at different terminal widths during
|
|
36
|
+
// a resize drag.
|
|
37
|
+
stdout.write("\x1b[2J" + // clear visible screen
|
|
38
|
+
"\x1b[3J" + // clear scrollback buffer
|
|
39
|
+
"\x1b[H");
|
|
40
|
+
setResizeKey((k) => k + 1);
|
|
41
|
+
}, 300);
|
|
42
|
+
}, [stdout]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!stdout)
|
|
45
|
+
return;
|
|
46
|
+
stdout.on("resize", onResize);
|
|
47
|
+
return () => {
|
|
48
|
+
stdout.off("resize", onResize);
|
|
49
|
+
if (debounceRef.current)
|
|
50
|
+
clearTimeout(debounceRef.current);
|
|
51
|
+
};
|
|
52
|
+
}, [stdout, onResize]);
|
|
53
|
+
return { ...size, resizeKey };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=useTerminalSize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTerminalSize.js","sourceRoot":"","sources":["../../../src/ui/hooks/useTerminalSize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAEhC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAC/B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC;QAC/B,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE;QAC9B,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE;KACzB,CAAC,CAAC;IACH,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,MAAM,CAAuC,IAAI,CAAC,CAAC;IAEvE,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;QAChC,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,sDAAsD;QACtD,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;QAEpE,yEAAyE;QACzE,IAAI,WAAW,CAAC,OAAO;YAAE,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC3D,WAAW,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACpC,qEAAqE;YACrE,sEAAsE;YACtE,iBAAiB;YACjB,MAAM,CAAC,KAAK,CACV,SAAS,GAAG,uBAAuB;gBACjC,SAAS,GAAG,0BAA0B;gBACtC,QAAQ,CACX,CAAC;YACF,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7B,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC9B,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC/B,IAAI,WAAW,CAAC,OAAO;gBAAE,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC7D,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEvB,OAAO,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTerminalTitle.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useTerminalTitle.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAqBvD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAqB/E"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useStdout } from "ink";
|
|
3
|
+
import { SPINNER_FRAMES, SPINNER_INTERVAL } from "../spinner-frames.js";
|
|
4
|
+
import { useAnimationTick, deriveFrame } from "../components/AnimationContext.js";
|
|
5
|
+
function getTitleText(phase, isRunning) {
|
|
6
|
+
if (!isRunning)
|
|
7
|
+
return "MR Agentix";
|
|
8
|
+
switch (phase) {
|
|
9
|
+
case "thinking":
|
|
10
|
+
return "Thinking...";
|
|
11
|
+
case "generating":
|
|
12
|
+
return "Generating...";
|
|
13
|
+
case "tools":
|
|
14
|
+
return "Running tools...";
|
|
15
|
+
case "waiting":
|
|
16
|
+
return "Thinking...";
|
|
17
|
+
default:
|
|
18
|
+
return "MR Agentix";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function useTerminalTitle(phase, isRunning) {
|
|
22
|
+
const { stdout } = useStdout();
|
|
23
|
+
// Derive spinner frame from global animation tick — no independent timer
|
|
24
|
+
const tick = useAnimationTick();
|
|
25
|
+
const spinnerFrame = isRunning ? deriveFrame(tick, SPINNER_INTERVAL, SPINNER_FRAMES.length) : 0;
|
|
26
|
+
// Write terminal title
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!stdout)
|
|
29
|
+
return;
|
|
30
|
+
const text = getTitleText(phase, isRunning);
|
|
31
|
+
const title = isRunning ? `${SPINNER_FRAMES[spinnerFrame]} ${text}` : text;
|
|
32
|
+
stdout.write(`\x1b]0;${title}\x1b\\`);
|
|
33
|
+
}, [stdout, phase, isRunning, spinnerFrame]);
|
|
34
|
+
// Reset title on unmount
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
return () => {
|
|
37
|
+
stdout?.write(`\x1b]0;MR Agentix\x1b\\`);
|
|
38
|
+
};
|
|
39
|
+
}, [stdout]);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=useTerminalTitle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTerminalTitle.js","sourceRoot":"","sources":["../../../src/ui/hooks/useTerminalTitle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAGhC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAElF,SAAS,YAAY,CAAC,KAAoB,EAAE,SAAkB;IAC5D,IAAI,CAAC,SAAS;QAAE,OAAO,YAAY,CAAC;IACpC,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,UAAU;YACb,OAAO,aAAa,CAAC;QACvB,KAAK,YAAY;YACf,OAAO,eAAe,CAAC;QACzB,KAAK,OAAO;YACV,OAAO,kBAAkB,CAAC;QAC5B,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC;QACvB;YACE,OAAO,YAAY,CAAC;IACxB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAoB,EAAE,SAAkB;IACvE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAE/B,yEAAyE;IACzE,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAC;IAChC,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,gBAAgB,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhG,uBAAuB;IACvB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3E,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC;IACxC,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;IAE7C,yBAAyB;IACzB,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,GAAG,EAAE;YACV,MAAM,EAAE,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC3C,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure functions for flushing completed live items to Static history.
|
|
3
|
+
*
|
|
4
|
+
* During an agent run, items (text blocks, tool calls, etc.) accumulate in
|
|
5
|
+
* the live area. Ink re-renders ALL live items on every state change, so if
|
|
6
|
+
* they grow unbounded the live area becomes very tall, making Ink's cursor
|
|
7
|
+
* math expensive and causing visible jank.
|
|
8
|
+
*
|
|
9
|
+
* These functions determine which items can be safely moved to Static history
|
|
10
|
+
* (where Ink writes them once and never re-renders them).
|
|
11
|
+
*/
|
|
12
|
+
/** Minimal item shape needed for flush logic. */
|
|
13
|
+
export interface FlushableItem {
|
|
14
|
+
kind: string;
|
|
15
|
+
id: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Max history items kept in React state. Ink's <Static> renders items once
|
|
19
|
+
* to stdout, so pruned items remain visible in terminal scrollback — we just
|
|
20
|
+
* release the JS object references to avoid unbounded memory growth.
|
|
21
|
+
*/
|
|
22
|
+
export declare const MAX_HISTORY_ITEMS = 200;
|
|
23
|
+
/**
|
|
24
|
+
* Prune history to keep at most MAX_HISTORY_ITEMS. Oldest items are dropped
|
|
25
|
+
* first; they remain visible in terminal scrollback.
|
|
26
|
+
*/
|
|
27
|
+
export declare function pruneHistory<T>(items: T[]): T[];
|
|
28
|
+
/**
|
|
29
|
+
* Trim large payload data from items that have been flushed to Static history.
|
|
30
|
+
* Ink already rendered them to the terminal — we only keep a truncated version
|
|
31
|
+
* to prevent multi-GB memory retention from tool results, server tool data, and
|
|
32
|
+
* sub-agent results.
|
|
33
|
+
*
|
|
34
|
+
* Works with any item shape via duck-typing: truncates `result` strings,
|
|
35
|
+
* clears `input`/`data` fields, and trims sub-agent result strings.
|
|
36
|
+
*/
|
|
37
|
+
export declare function trimFlushedItems<T extends FlushableItem>(items: T[]): T[];
|
|
38
|
+
/**
|
|
39
|
+
* Called when `onTurnText` fires (end of each LLM turn that produced text).
|
|
40
|
+
* All previous items are guaranteed complete — tool calls from this turn
|
|
41
|
+
* finished before `turn_end` fired, and `onTurnText` fires inside `turn_end`.
|
|
42
|
+
*
|
|
43
|
+
* Returns the items to flush to history. The caller should then set liveItems
|
|
44
|
+
* to contain only the new text item.
|
|
45
|
+
*/
|
|
46
|
+
export declare function flushOnTurnText<T extends FlushableItem>(liveItems: T[]): T[];
|
|
47
|
+
/**
|
|
48
|
+
* Called when `onTurnEnd` fires with a tool_use stop reason (LLM responded
|
|
49
|
+
* with only tool calls, no text). Flushes all items IF none are still pending
|
|
50
|
+
* (no `tool_start` without a corresponding `tool_done`).
|
|
51
|
+
*
|
|
52
|
+
* Returns { flushed, remaining } — flushed items go to history, remaining
|
|
53
|
+
* items stay in liveItems.
|
|
54
|
+
*/
|
|
55
|
+
export declare function flushOnTurnEnd<T extends FlushableItem>(liveItems: T[], stopReason: string): {
|
|
56
|
+
flushed: T[];
|
|
57
|
+
remaining: T[];
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=live-item-flush.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-item-flush.d.ts","sourceRoot":"","sources":["../../src/ui/live-item-flush.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAUrC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAG/C;AAQD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,aAAa,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CA0DzE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,aAAa,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE5E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,EACpD,SAAS,EAAE,CAAC,EAAE,EACd,UAAU,EAAE,MAAM,GACjB;IAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAAC,SAAS,EAAE,CAAC,EAAE,CAAA;CAAE,CAoBlC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure functions for flushing completed live items to Static history.
|
|
3
|
+
*
|
|
4
|
+
* During an agent run, items (text blocks, tool calls, etc.) accumulate in
|
|
5
|
+
* the live area. Ink re-renders ALL live items on every state change, so if
|
|
6
|
+
* they grow unbounded the live area becomes very tall, making Ink's cursor
|
|
7
|
+
* math expensive and causing visible jank.
|
|
8
|
+
*
|
|
9
|
+
* These functions determine which items can be safely moved to Static history
|
|
10
|
+
* (where Ink writes them once and never re-renders them).
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Max history items kept in React state. Ink's <Static> renders items once
|
|
14
|
+
* to stdout, so pruned items remain visible in terminal scrollback — we just
|
|
15
|
+
* release the JS object references to avoid unbounded memory growth.
|
|
16
|
+
*/
|
|
17
|
+
export const MAX_HISTORY_ITEMS = 200;
|
|
18
|
+
/**
|
|
19
|
+
* Max characters to keep in a tool result string after the item has been
|
|
20
|
+
* flushed to Static history. Ink already rendered the full result, so we
|
|
21
|
+
* only need enough for potential re-renders (which shouldn't happen for
|
|
22
|
+
* Static items, but keep a small buffer for safety).
|
|
23
|
+
*/
|
|
24
|
+
const MAX_RESULT_CHARS_IN_HISTORY = 2_000;
|
|
25
|
+
/**
|
|
26
|
+
* Prune history to keep at most MAX_HISTORY_ITEMS. Oldest items are dropped
|
|
27
|
+
* first; they remain visible in terminal scrollback.
|
|
28
|
+
*/
|
|
29
|
+
export function pruneHistory(items) {
|
|
30
|
+
if (items.length <= MAX_HISTORY_ITEMS)
|
|
31
|
+
return items;
|
|
32
|
+
return items.slice(items.length - MAX_HISTORY_ITEMS);
|
|
33
|
+
}
|
|
34
|
+
/** Truncate a string if it exceeds the history limit. */
|
|
35
|
+
function truncateResult(s) {
|
|
36
|
+
if (s.length <= MAX_RESULT_CHARS_IN_HISTORY)
|
|
37
|
+
return s;
|
|
38
|
+
return s.slice(0, MAX_RESULT_CHARS_IN_HISTORY) + "\n… (truncated)";
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Trim large payload data from items that have been flushed to Static history.
|
|
42
|
+
* Ink already rendered them to the terminal — we only keep a truncated version
|
|
43
|
+
* to prevent multi-GB memory retention from tool results, server tool data, and
|
|
44
|
+
* sub-agent results.
|
|
45
|
+
*
|
|
46
|
+
* Works with any item shape via duck-typing: truncates `result` strings,
|
|
47
|
+
* clears `input`/`data` fields, and trims sub-agent result strings.
|
|
48
|
+
*/
|
|
49
|
+
export function trimFlushedItems(items) {
|
|
50
|
+
return items.map((item) => {
|
|
51
|
+
const rec = item;
|
|
52
|
+
const patches = {};
|
|
53
|
+
let changed = false;
|
|
54
|
+
// Truncate tool result strings (ToolDoneItem, SubAgentInfo, etc.)
|
|
55
|
+
if (typeof rec.result === "string" && rec.result.length > MAX_RESULT_CHARS_IN_HISTORY) {
|
|
56
|
+
patches.result = truncateResult(rec.result);
|
|
57
|
+
changed = true;
|
|
58
|
+
}
|
|
59
|
+
// Clear server tool input/data — potentially large JSON payloads
|
|
60
|
+
if (rec.input !== undefined && rec.kind === "server_tool_done") {
|
|
61
|
+
patches.input = undefined;
|
|
62
|
+
changed = true;
|
|
63
|
+
}
|
|
64
|
+
if (rec.data !== undefined && rec.kind === "server_tool_done") {
|
|
65
|
+
patches.data = undefined;
|
|
66
|
+
changed = true;
|
|
67
|
+
}
|
|
68
|
+
// Trim tool group results
|
|
69
|
+
if (rec.kind === "tool_group" && Array.isArray(rec.tools)) {
|
|
70
|
+
const tools = rec.tools;
|
|
71
|
+
let toolsChanged = false;
|
|
72
|
+
const trimmedTools = tools.map((t) => {
|
|
73
|
+
if (typeof t.result === "string" && t.result.length > MAX_RESULT_CHARS_IN_HISTORY) {
|
|
74
|
+
toolsChanged = true;
|
|
75
|
+
return { ...t, result: truncateResult(t.result) };
|
|
76
|
+
}
|
|
77
|
+
return t;
|
|
78
|
+
});
|
|
79
|
+
if (toolsChanged) {
|
|
80
|
+
patches.tools = trimmedTools;
|
|
81
|
+
changed = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Trim sub-agent group results
|
|
85
|
+
if (rec.kind === "subagent_group" && Array.isArray(rec.agents)) {
|
|
86
|
+
const agents = rec.agents;
|
|
87
|
+
let agentsChanged = false;
|
|
88
|
+
const trimmedAgents = agents.map((a) => {
|
|
89
|
+
if (typeof a.result === "string" && a.result.length > MAX_RESULT_CHARS_IN_HISTORY) {
|
|
90
|
+
agentsChanged = true;
|
|
91
|
+
return { ...a, result: truncateResult(a.result) };
|
|
92
|
+
}
|
|
93
|
+
return a;
|
|
94
|
+
});
|
|
95
|
+
if (agentsChanged) {
|
|
96
|
+
patches.agents = trimmedAgents;
|
|
97
|
+
changed = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return changed ? { ...item, ...patches } : item;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Called when `onTurnText` fires (end of each LLM turn that produced text).
|
|
105
|
+
* All previous items are guaranteed complete — tool calls from this turn
|
|
106
|
+
* finished before `turn_end` fired, and `onTurnText` fires inside `turn_end`.
|
|
107
|
+
*
|
|
108
|
+
* Returns the items to flush to history. The caller should then set liveItems
|
|
109
|
+
* to contain only the new text item.
|
|
110
|
+
*/
|
|
111
|
+
export function flushOnTurnText(liveItems) {
|
|
112
|
+
return liveItems;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Called when `onTurnEnd` fires with a tool_use stop reason (LLM responded
|
|
116
|
+
* with only tool calls, no text). Flushes all items IF none are still pending
|
|
117
|
+
* (no `tool_start` without a corresponding `tool_done`).
|
|
118
|
+
*
|
|
119
|
+
* Returns { flushed, remaining } — flushed items go to history, remaining
|
|
120
|
+
* items stay in liveItems.
|
|
121
|
+
*/
|
|
122
|
+
export function flushOnTurnEnd(liveItems, stopReason) {
|
|
123
|
+
if (stopReason !== "tool_use") {
|
|
124
|
+
return { flushed: [], remaining: liveItems };
|
|
125
|
+
}
|
|
126
|
+
const hasPendingToolStart = liveItems.some((item) => item.kind === "tool_start" ||
|
|
127
|
+
item.kind === "server_tool_start" ||
|
|
128
|
+
(item.kind === "tool_group" &&
|
|
129
|
+
(item.tools ?? []).some((t) => t.status === "running")));
|
|
130
|
+
if (hasPendingToolStart || liveItems.length === 0) {
|
|
131
|
+
return { flushed: [], remaining: liveItems };
|
|
132
|
+
}
|
|
133
|
+
return { flushed: liveItems, remaining: [] };
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=live-item-flush.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-item-flush.js","sourceRoot":"","sources":["../../src/ui/live-item-flush.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-item-flush.test.d.ts","sourceRoot":"","sources":["../../src/ui/live-item-flush.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { pruneHistory, flushOnTurnText, flushOnTurnEnd, MAX_HISTORY_ITEMS, } from "./live-item-flush.js";
|
|
3
|
+
// ── Test helpers ──────────────────────────────────────────
|
|
4
|
+
let idCounter = 0;
|
|
5
|
+
function id() {
|
|
6
|
+
return `test-${++idCounter}`;
|
|
7
|
+
}
|
|
8
|
+
function userItem() {
|
|
9
|
+
return { kind: "user", id: id() };
|
|
10
|
+
}
|
|
11
|
+
function assistantItem() {
|
|
12
|
+
return { kind: "assistant", id: id() };
|
|
13
|
+
}
|
|
14
|
+
function toolStart(toolCallId) {
|
|
15
|
+
return { kind: "tool_start", id: id(), toolCallId: toolCallId ?? id() };
|
|
16
|
+
}
|
|
17
|
+
function toolDone() {
|
|
18
|
+
return { kind: "tool_done", id: id() };
|
|
19
|
+
}
|
|
20
|
+
function subagentGroup() {
|
|
21
|
+
return { kind: "subagent_group", id: id() };
|
|
22
|
+
}
|
|
23
|
+
function errorItem() {
|
|
24
|
+
return { kind: "error", id: id() };
|
|
25
|
+
}
|
|
26
|
+
// ── pruneHistory ──────────────────────────────────────────
|
|
27
|
+
describe("pruneHistory", () => {
|
|
28
|
+
it("returns items unchanged when under the limit", () => {
|
|
29
|
+
const items = [userItem(), assistantItem(), toolDone()];
|
|
30
|
+
const result = pruneHistory(items);
|
|
31
|
+
expect(result).toBe(items); // same reference, no copy
|
|
32
|
+
});
|
|
33
|
+
it("returns items unchanged at exactly the limit", () => {
|
|
34
|
+
const items = Array.from({ length: MAX_HISTORY_ITEMS }, () => userItem());
|
|
35
|
+
const result = pruneHistory(items);
|
|
36
|
+
expect(result).toBe(items);
|
|
37
|
+
});
|
|
38
|
+
it("drops oldest items when over the limit", () => {
|
|
39
|
+
const items = Array.from({ length: MAX_HISTORY_ITEMS + 50 }, (_, i) => ({
|
|
40
|
+
kind: "user",
|
|
41
|
+
id: `item-${i}`,
|
|
42
|
+
}));
|
|
43
|
+
const result = pruneHistory(items);
|
|
44
|
+
expect(result).toHaveLength(MAX_HISTORY_ITEMS);
|
|
45
|
+
// Should keep the LAST 500 items (newest)
|
|
46
|
+
expect(result[0].id).toBe("item-50");
|
|
47
|
+
expect(result[result.length - 1].id).toBe(`item-${MAX_HISTORY_ITEMS + 49}`);
|
|
48
|
+
});
|
|
49
|
+
it("handles empty array", () => {
|
|
50
|
+
const result = pruneHistory([]);
|
|
51
|
+
expect(result).toEqual([]);
|
|
52
|
+
});
|
|
53
|
+
it("handles single item", () => {
|
|
54
|
+
const items = [userItem()];
|
|
55
|
+
const result = pruneHistory(items);
|
|
56
|
+
expect(result).toBe(items);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
// ── flushOnTurnText ───────────────────────────────────────
|
|
60
|
+
describe("flushOnTurnText", () => {
|
|
61
|
+
it("returns all items for flushing when liveItems is non-empty", () => {
|
|
62
|
+
const items = [assistantItem(), toolDone(), toolDone()];
|
|
63
|
+
const flushed = flushOnTurnText(items);
|
|
64
|
+
expect(flushed).toBe(items);
|
|
65
|
+
expect(flushed).toHaveLength(3);
|
|
66
|
+
});
|
|
67
|
+
it("returns empty array when liveItems is empty", () => {
|
|
68
|
+
const flushed = flushOnTurnText([]);
|
|
69
|
+
expect(flushed).toEqual([]);
|
|
70
|
+
});
|
|
71
|
+
it("flushes tool_start items (they are from a completed turn)", () => {
|
|
72
|
+
// When onTurnText fires, even stale tool_start items from the same turn
|
|
73
|
+
// are complete (tool_end already replaced them in the normal flow, but
|
|
74
|
+
// if somehow a tool_start remains, the turn is still over).
|
|
75
|
+
const items = [assistantItem(), toolStart(), toolDone()];
|
|
76
|
+
const flushed = flushOnTurnText(items);
|
|
77
|
+
expect(flushed).toHaveLength(3);
|
|
78
|
+
});
|
|
79
|
+
it("flushes mixed item types from multi-turn accumulation", () => {
|
|
80
|
+
// Simulates items accumulated across multiple turns before flush was added
|
|
81
|
+
const items = [
|
|
82
|
+
userItem(),
|
|
83
|
+
assistantItem(),
|
|
84
|
+
toolDone(),
|
|
85
|
+
toolDone(),
|
|
86
|
+
assistantItem(),
|
|
87
|
+
toolDone(),
|
|
88
|
+
toolDone(),
|
|
89
|
+
toolDone(),
|
|
90
|
+
assistantItem(),
|
|
91
|
+
];
|
|
92
|
+
const flushed = flushOnTurnText(items);
|
|
93
|
+
expect(flushed).toHaveLength(9);
|
|
94
|
+
});
|
|
95
|
+
it("flushes subagent group items", () => {
|
|
96
|
+
const items = [assistantItem(), subagentGroup(), toolDone()];
|
|
97
|
+
const flushed = flushOnTurnText(items);
|
|
98
|
+
expect(flushed).toHaveLength(3);
|
|
99
|
+
});
|
|
100
|
+
it("flushes error items", () => {
|
|
101
|
+
const items = [errorItem(), assistantItem()];
|
|
102
|
+
const flushed = flushOnTurnText(items);
|
|
103
|
+
expect(flushed).toHaveLength(2);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
// ── flushOnTurnEnd ────────────────────────────────────────
|
|
107
|
+
describe("flushOnTurnEnd", () => {
|
|
108
|
+
describe("with tool_use stop reason", () => {
|
|
109
|
+
it("flushes all items when no pending tool_start exists", () => {
|
|
110
|
+
const items = [assistantItem(), toolDone(), toolDone()];
|
|
111
|
+
const { flushed, remaining } = flushOnTurnEnd(items, "tool_use");
|
|
112
|
+
expect(flushed).toBe(items);
|
|
113
|
+
expect(flushed).toHaveLength(3);
|
|
114
|
+
expect(remaining).toEqual([]);
|
|
115
|
+
});
|
|
116
|
+
it("does NOT flush when a tool_start is still pending", () => {
|
|
117
|
+
const items = [assistantItem(), toolStart(), toolDone()];
|
|
118
|
+
const { flushed, remaining } = flushOnTurnEnd(items, "tool_use");
|
|
119
|
+
expect(flushed).toEqual([]);
|
|
120
|
+
expect(remaining).toBe(items);
|
|
121
|
+
expect(remaining).toHaveLength(3);
|
|
122
|
+
});
|
|
123
|
+
it("does NOT flush when liveItems is empty", () => {
|
|
124
|
+
const { flushed, remaining } = flushOnTurnEnd([], "tool_use");
|
|
125
|
+
expect(flushed).toEqual([]);
|
|
126
|
+
expect(remaining).toEqual([]);
|
|
127
|
+
});
|
|
128
|
+
it("does NOT flush when only tool_start items exist (all pending)", () => {
|
|
129
|
+
const items = [toolStart(), toolStart()];
|
|
130
|
+
const { flushed, remaining } = flushOnTurnEnd(items, "tool_use");
|
|
131
|
+
expect(flushed).toEqual([]);
|
|
132
|
+
expect(remaining).toBe(items);
|
|
133
|
+
});
|
|
134
|
+
it("flushes when items include subagent groups with no pending tool_start", () => {
|
|
135
|
+
const items = [subagentGroup(), toolDone()];
|
|
136
|
+
const { flushed, remaining } = flushOnTurnEnd(items, "tool_use");
|
|
137
|
+
expect(flushed).toHaveLength(2);
|
|
138
|
+
expect(remaining).toEqual([]);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
describe("with non-tool_use stop reasons", () => {
|
|
142
|
+
it("does NOT flush for end_turn stop reason", () => {
|
|
143
|
+
const items = [assistantItem(), toolDone()];
|
|
144
|
+
const { flushed, remaining } = flushOnTurnEnd(items, "end_turn");
|
|
145
|
+
expect(flushed).toEqual([]);
|
|
146
|
+
expect(remaining).toBe(items);
|
|
147
|
+
});
|
|
148
|
+
it("does NOT flush for stop_sequence stop reason", () => {
|
|
149
|
+
const items = [assistantItem()];
|
|
150
|
+
const { flushed, remaining } = flushOnTurnEnd(items, "stop_sequence");
|
|
151
|
+
expect(flushed).toEqual([]);
|
|
152
|
+
expect(remaining).toBe(items);
|
|
153
|
+
});
|
|
154
|
+
it("does NOT flush for max_tokens stop reason", () => {
|
|
155
|
+
const items = [assistantItem(), toolDone(), toolDone()];
|
|
156
|
+
const { flushed, remaining } = flushOnTurnEnd(items, "max_tokens");
|
|
157
|
+
expect(flushed).toEqual([]);
|
|
158
|
+
expect(remaining).toBe(items);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
// ── Integration: simulated agent run scenarios ────────────
|
|
163
|
+
describe("flush integration scenarios", () => {
|
|
164
|
+
/**
|
|
165
|
+
* Simulates the state management that App.tsx does with setLiveItems
|
|
166
|
+
* and setHistory, using the extracted pure functions.
|
|
167
|
+
*/
|
|
168
|
+
function simulateRun(turns) {
|
|
169
|
+
let liveItems = [];
|
|
170
|
+
let history = [];
|
|
171
|
+
for (const turn of turns) {
|
|
172
|
+
// Simulate tool calls happening before turn_end
|
|
173
|
+
for (let i = 0; i < (turn.tools ?? 0); i++) {
|
|
174
|
+
liveItems = [...liveItems, toolStart()];
|
|
175
|
+
// Tool completes (replaces tool_start with tool_done in real code)
|
|
176
|
+
liveItems = liveItems.map((item) => item.kind === "tool_start" && liveItems.indexOf(item) === liveItems.length - 1
|
|
177
|
+
? toolDone()
|
|
178
|
+
: item);
|
|
179
|
+
}
|
|
180
|
+
// onTurnEnd fires first
|
|
181
|
+
const turnEndResult = flushOnTurnEnd(liveItems, turn.stopReason);
|
|
182
|
+
if (turnEndResult.flushed.length > 0) {
|
|
183
|
+
history = pruneHistory([...history, ...turnEndResult.flushed]);
|
|
184
|
+
}
|
|
185
|
+
liveItems = turnEndResult.remaining;
|
|
186
|
+
// onTurnText fires second (only if text exists)
|
|
187
|
+
if (turn.text) {
|
|
188
|
+
const flushed = flushOnTurnText(liveItems);
|
|
189
|
+
if (flushed.length > 0) {
|
|
190
|
+
history = pruneHistory([...history, ...flushed]);
|
|
191
|
+
}
|
|
192
|
+
liveItems = [assistantItem()]; // new text item
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return { liveItems, history };
|
|
196
|
+
}
|
|
197
|
+
it("scenario: simple text-only conversation stays bounded", () => {
|
|
198
|
+
const { liveItems, history } = simulateRun([
|
|
199
|
+
{ text: "Hello!", stopReason: "end_turn" },
|
|
200
|
+
{ text: "How can I help?", stopReason: "end_turn" },
|
|
201
|
+
{ text: "Sure thing.", stopReason: "end_turn" },
|
|
202
|
+
]);
|
|
203
|
+
// Only the last text item should be live
|
|
204
|
+
expect(liveItems).toHaveLength(1);
|
|
205
|
+
expect(liveItems[0].kind).toBe("assistant");
|
|
206
|
+
// Previous text items moved to history
|
|
207
|
+
expect(history).toHaveLength(2);
|
|
208
|
+
});
|
|
209
|
+
it("scenario: text + tools cycle stays bounded", () => {
|
|
210
|
+
const { liveItems, history } = simulateRun([
|
|
211
|
+
{ text: "Let me check.", tools: 3, stopReason: "end_turn" },
|
|
212
|
+
{ text: "Found it.", tools: 2, stopReason: "end_turn" },
|
|
213
|
+
{ text: "Done.", stopReason: "end_turn" },
|
|
214
|
+
]);
|
|
215
|
+
expect(liveItems).toHaveLength(1);
|
|
216
|
+
// 3 tool_done + 1 assistant + 2 tool_done + 1 assistant = 7
|
|
217
|
+
expect(history).toHaveLength(7);
|
|
218
|
+
});
|
|
219
|
+
it("scenario: tool-only turns flush correctly", () => {
|
|
220
|
+
const { liveItems, history } = simulateRun([
|
|
221
|
+
{ tools: 3, stopReason: "tool_use" }, // tool-only, flushed by onTurnEnd
|
|
222
|
+
{ tools: 2, stopReason: "tool_use" }, // tool-only, flushed by onTurnEnd
|
|
223
|
+
{ text: "All done.", stopReason: "end_turn" },
|
|
224
|
+
]);
|
|
225
|
+
expect(liveItems).toHaveLength(1);
|
|
226
|
+
expect(liveItems[0].kind).toBe("assistant");
|
|
227
|
+
expect(history).toHaveLength(5); // 3 + 2 tool_done items
|
|
228
|
+
});
|
|
229
|
+
it("scenario: many turns — liveItems never exceeds current turn size", () => {
|
|
230
|
+
const turns = Array.from({ length: 50 }, (_, i) => ({
|
|
231
|
+
text: `Turn ${i}`,
|
|
232
|
+
tools: 5,
|
|
233
|
+
stopReason: "end_turn",
|
|
234
|
+
}));
|
|
235
|
+
const { liveItems } = simulateRun(turns);
|
|
236
|
+
// Only the last text item should remain live
|
|
237
|
+
expect(liveItems).toHaveLength(1);
|
|
238
|
+
});
|
|
239
|
+
it("scenario: alternating text and tool-only turns", () => {
|
|
240
|
+
const { liveItems, history } = simulateRun([
|
|
241
|
+
{ text: "Searching...", tools: 2, stopReason: "end_turn" },
|
|
242
|
+
{ tools: 3, stopReason: "tool_use" }, // tool-only
|
|
243
|
+
{ text: "Found results.", tools: 1, stopReason: "end_turn" },
|
|
244
|
+
{ tools: 4, stopReason: "tool_use" }, // tool-only
|
|
245
|
+
{ text: "Complete.", stopReason: "end_turn" },
|
|
246
|
+
]);
|
|
247
|
+
expect(liveItems).toHaveLength(1);
|
|
248
|
+
// History: (2 tools + 1 text) + (3 tools) + (1 tool + 1 text) + (4 tools) = 12
|
|
249
|
+
expect(history).toHaveLength(12);
|
|
250
|
+
});
|
|
251
|
+
it("scenario: stress test — 100 turns with 10 tools each", () => {
|
|
252
|
+
const turns = Array.from({ length: 100 }, () => ({
|
|
253
|
+
text: "Working...",
|
|
254
|
+
tools: 10,
|
|
255
|
+
stopReason: "end_turn",
|
|
256
|
+
}));
|
|
257
|
+
const { liveItems, history } = simulateRun(turns);
|
|
258
|
+
expect(liveItems).toHaveLength(1);
|
|
259
|
+
// Total items: 100 turns * (10 tools + 1 text) - 1 (last text is live) = 1099
|
|
260
|
+
// But pruneHistory caps at 500
|
|
261
|
+
expect(history).toHaveLength(MAX_HISTORY_ITEMS);
|
|
262
|
+
});
|
|
263
|
+
it("REGRESSION: liveItems retained after agent finishes, flushed on next submit", () => {
|
|
264
|
+
// After the agent finishes its last turn, the final AssistantItem stays
|
|
265
|
+
// in liveItems (NOT moved to Static via useEffect). This prevents Ink
|
|
266
|
+
// cursor-math glitches that caused text to get cut off during the
|
|
267
|
+
// live→Static transition. Items are flushed when the user submits the
|
|
268
|
+
// next message (simulated here by flushing before adding a new userItem).
|
|
269
|
+
let liveItems = [];
|
|
270
|
+
let history = [];
|
|
271
|
+
// Agent produces a response (turn 1)
|
|
272
|
+
const flushed1 = flushOnTurnText(liveItems);
|
|
273
|
+
if (flushed1.length > 0)
|
|
274
|
+
history = pruneHistory([...history, ...flushed1]);
|
|
275
|
+
liveItems = [assistantItem()];
|
|
276
|
+
// Agent finishes — liveItems NOT cleared (no useEffect flush)
|
|
277
|
+
// The final response stays in liveItems
|
|
278
|
+
expect(liveItems).toHaveLength(1);
|
|
279
|
+
expect(liveItems[0].kind).toBe("assistant");
|
|
280
|
+
// User submits next message — NOW flush to Static
|
|
281
|
+
if (liveItems.length > 0) {
|
|
282
|
+
history = pruneHistory([...history, ...liveItems]);
|
|
283
|
+
}
|
|
284
|
+
liveItems = [userItem()];
|
|
285
|
+
// Previous AssistantItem is now in history
|
|
286
|
+
expect(history).toHaveLength(1);
|
|
287
|
+
expect(history[0].kind).toBe("assistant");
|
|
288
|
+
// New user message is in liveItems
|
|
289
|
+
expect(liveItems).toHaveLength(1);
|
|
290
|
+
expect(liveItems[0].kind).toBe("user");
|
|
291
|
+
});
|
|
292
|
+
it("REGRESSION: without flush, liveItems would grow unbounded", () => {
|
|
293
|
+
// This test documents the old behavior — liveItems would contain
|
|
294
|
+
// ALL items from ALL turns. With 20 turns of 5 tools each, that's
|
|
295
|
+
// 20 * (5 + 1) = 120 items re-rendered on every timer tick.
|
|
296
|
+
// With the fix, liveItems is always at most 1 item (current text).
|
|
297
|
+
const turns = Array.from({ length: 20 }, () => ({
|
|
298
|
+
text: "Working...",
|
|
299
|
+
tools: 5,
|
|
300
|
+
stopReason: "end_turn",
|
|
301
|
+
}));
|
|
302
|
+
const { liveItems } = simulateRun(turns);
|
|
303
|
+
// The whole point: liveItems MUST be bounded
|
|
304
|
+
expect(liveItems.length).toBeLessThanOrEqual(1);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
//# sourceMappingURL=live-item-flush.test.js.map
|