@qduc/term2 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/agent.d.ts +19 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +143 -0
- package/dist/agent.js.map +1 -0
- package/dist/app.d.ts +22 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +403 -0
- package/dist/app.js.map +1 -0
- package/dist/app.model-command-feedback.test.d.ts +2 -0
- package/dist/app.model-command-feedback.test.d.ts.map +1 -0
- package/dist/app.model-command-feedback.test.js +19 -0
- package/dist/app.model-command-feedback.test.js.map +1 -0
- package/dist/app.parseInput.test.d.ts +2 -0
- package/dist/app.parseInput.test.d.ts.map +1 -0
- package/dist/app.parseInput.test.js +97 -0
- package/dist/app.parseInput.test.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +241 -0
- package/dist/cli.js.map +1 -0
- package/dist/components/ApprovalPrompt.d.ts +10 -0
- package/dist/components/ApprovalPrompt.d.ts.map +1 -0
- package/dist/components/ApprovalPrompt.js +163 -0
- package/dist/components/ApprovalPrompt.js.map +1 -0
- package/dist/components/Banner.d.ts +9 -0
- package/dist/components/Banner.d.ts.map +1 -0
- package/dist/components/Banner.js +86 -0
- package/dist/components/Banner.js.map +1 -0
- package/dist/components/BottomArea.d.ts +33 -0
- package/dist/components/BottomArea.d.ts.map +1 -0
- package/dist/components/BottomArea.js +31 -0
- package/dist/components/BottomArea.js.map +1 -0
- package/dist/components/BottomArea.test.d.ts +2 -0
- package/dist/components/BottomArea.test.d.ts.map +1 -0
- package/dist/components/BottomArea.test.js +73 -0
- package/dist/components/BottomArea.test.js.map +1 -0
- package/dist/components/ChatMessage.d.ts +7 -0
- package/dist/components/ChatMessage.d.ts.map +1 -0
- package/dist/components/ChatMessage.js +10 -0
- package/dist/components/ChatMessage.js.map +1 -0
- package/dist/components/CommandMessage.d.ts +15 -0
- package/dist/components/CommandMessage.d.ts.map +1 -0
- package/dist/components/CommandMessage.js +188 -0
- package/dist/components/CommandMessage.js.map +1 -0
- package/dist/components/CommandMessage.test.d.ts +2 -0
- package/dist/components/CommandMessage.test.d.ts.map +1 -0
- package/dist/components/CommandMessage.test.js +35 -0
- package/dist/components/CommandMessage.test.js.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +27 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.js +77 -0
- package/dist/components/ErrorBoundary.js.map +1 -0
- package/dist/components/ErrorBoundary.test.d.ts +2 -0
- package/dist/components/ErrorBoundary.test.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.test.js +32 -0
- package/dist/components/ErrorBoundary.test.js.map +1 -0
- package/dist/components/Input/PopupManager.d.ts +42 -0
- package/dist/components/Input/PopupManager.d.ts.map +1 -0
- package/dist/components/Input/PopupManager.js +13 -0
- package/dist/components/Input/PopupManager.js.map +1 -0
- package/dist/components/InputBox.d.ts +18 -0
- package/dist/components/InputBox.d.ts.map +1 -0
- package/dist/components/InputBox.js +384 -0
- package/dist/components/InputBox.js.map +1 -0
- package/dist/components/InputBox.menu-logic.test.d.ts +2 -0
- package/dist/components/InputBox.menu-logic.test.d.ts.map +1 -0
- package/dist/components/InputBox.menu-logic.test.js +151 -0
- package/dist/components/InputBox.menu-logic.test.js.map +1 -0
- package/dist/components/InputBox.test.d.ts +2 -0
- package/dist/components/InputBox.test.d.ts.map +1 -0
- package/dist/components/InputBox.test.js +91 -0
- package/dist/components/InputBox.test.js.map +1 -0
- package/dist/components/LiveResponse.d.ts +13 -0
- package/dist/components/LiveResponse.d.ts.map +1 -0
- package/dist/components/LiveResponse.js +16 -0
- package/dist/components/LiveResponse.js.map +1 -0
- package/dist/components/MarkdownRenderer.d.ts +8 -0
- package/dist/components/MarkdownRenderer.d.ts.map +1 -0
- package/dist/components/MarkdownRenderer.js +225 -0
- package/dist/components/MarkdownRenderer.js.map +1 -0
- package/dist/components/MentorMode.test.d.ts +2 -0
- package/dist/components/MentorMode.test.d.ts.map +1 -0
- package/dist/components/MentorMode.test.js.map +1 -0
- package/dist/components/MessageList.d.ts +7 -0
- package/dist/components/MessageList.d.ts.map +1 -0
- package/dist/components/MessageList.js +29 -0
- package/dist/components/MessageList.js.map +1 -0
- package/dist/components/MessageList.test.d.ts +2 -0
- package/dist/components/MessageList.test.d.ts.map +1 -0
- package/dist/components/MessageList.test.js +15 -0
- package/dist/components/MessageList.test.js.map +1 -0
- package/dist/components/ModelSelectionMenu.d.ts +18 -0
- package/dist/components/ModelSelectionMenu.d.ts.map +1 -0
- package/dist/components/ModelSelectionMenu.js +91 -0
- package/dist/components/ModelSelectionMenu.js.map +1 -0
- package/dist/components/ModelSelectionMenu.test.d.ts +2 -0
- package/dist/components/ModelSelectionMenu.test.d.ts.map +1 -0
- package/dist/components/ModelSelectionMenu.test.js +83 -0
- package/dist/components/ModelSelectionMenu.test.js.map +1 -0
- package/dist/components/PathSelectionMenu.d.ts +12 -0
- package/dist/components/PathSelectionMenu.d.ts.map +1 -0
- package/dist/components/PathSelectionMenu.js +42 -0
- package/dist/components/PathSelectionMenu.js.map +1 -0
- package/dist/components/SettingsSelectionMenu.d.ts +9 -0
- package/dist/components/SettingsSelectionMenu.d.ts.map +1 -0
- package/dist/components/SettingsSelectionMenu.js +21 -0
- package/dist/components/SettingsSelectionMenu.js.map +1 -0
- package/dist/components/SlashCommandMenu.d.ts +15 -0
- package/dist/components/SlashCommandMenu.d.ts.map +1 -0
- package/dist/components/SlashCommandMenu.js +20 -0
- package/dist/components/SlashCommandMenu.js.map +1 -0
- package/dist/components/StatusBar.d.ts +11 -0
- package/dist/components/StatusBar.d.ts.map +1 -0
- package/dist/components/StatusBar.js +59 -0
- package/dist/components/StatusBar.js.map +1 -0
- package/dist/components/TextInput.d.ts +42 -0
- package/dist/components/TextInput.d.ts.map +1 -0
- package/dist/components/TextInput.js +397 -0
- package/dist/components/TextInput.js.map +1 -0
- package/dist/components/TextInput.test.d.ts +2 -0
- package/dist/components/TextInput.test.d.ts.map +1 -0
- package/dist/components/TextInput.test.js +75 -0
- package/dist/components/TextInput.test.js.map +1 -0
- package/dist/context/InputContext.d.ts +31 -0
- package/dist/context/InputContext.d.ts.map +1 -0
- package/dist/context/InputContext.js +36 -0
- package/dist/context/InputContext.js.map +1 -0
- package/dist/context/InputContext.stability.test.d.ts +2 -0
- package/dist/context/InputContext.stability.test.d.ts.map +1 -0
- package/dist/context/InputContext.stability.test.js +28 -0
- package/dist/context/InputContext.stability.test.js.map +1 -0
- package/dist/context/InputContext.test.d.ts +2 -0
- package/dist/context/InputContext.test.d.ts.map +1 -0
- package/dist/context/InputContext.test.js +168 -0
- package/dist/context/InputContext.test.js.map +1 -0
- package/dist/debug-schema.d.ts +2 -0
- package/dist/debug-schema.d.ts.map +1 -0
- package/dist/debug-schema.js +22 -0
- package/dist/debug-schema.js.map +1 -0
- package/dist/hooks/use-conversation.d.ts +78 -0
- package/dist/hooks/use-conversation.d.ts.map +1 -0
- package/dist/hooks/use-conversation.js +1017 -0
- package/dist/hooks/use-conversation.js.map +1 -0
- package/dist/hooks/use-input-history.d.ts +16 -0
- package/dist/hooks/use-input-history.d.ts.map +1 -0
- package/dist/hooks/use-input-history.js +71 -0
- package/dist/hooks/use-input-history.js.map +1 -0
- package/dist/hooks/use-model-selection.d.ts +27 -0
- package/dist/hooks/use-model-selection.d.ts.map +1 -0
- package/dist/hooks/use-model-selection.js +187 -0
- package/dist/hooks/use-model-selection.js.map +1 -0
- package/dist/hooks/use-model-selection.test.d.ts +2 -0
- package/dist/hooks/use-model-selection.test.d.ts.map +1 -0
- package/dist/hooks/use-model-selection.test.js +28 -0
- package/dist/hooks/use-model-selection.test.js.map +1 -0
- package/dist/hooks/use-path-completion.d.ts +22 -0
- package/dist/hooks/use-path-completion.d.ts.map +1 -0
- package/dist/hooks/use-path-completion.js +153 -0
- package/dist/hooks/use-path-completion.js.map +1 -0
- package/dist/hooks/use-path-completion.test.d.ts +2 -0
- package/dist/hooks/use-path-completion.test.d.ts.map +1 -0
- package/dist/hooks/use-path-completion.test.js +29 -0
- package/dist/hooks/use-path-completion.test.js.map +1 -0
- package/dist/hooks/use-setting.d.ts +7 -0
- package/dist/hooks/use-setting.d.ts.map +1 -0
- package/dist/hooks/use-setting.js +35 -0
- package/dist/hooks/use-setting.js.map +1 -0
- package/dist/hooks/use-settings-completion.d.ts +23 -0
- package/dist/hooks/use-settings-completion.d.ts.map +1 -0
- package/dist/hooks/use-settings-completion.js +164 -0
- package/dist/hooks/use-settings-completion.js.map +1 -0
- package/dist/hooks/use-settings-completion.test.d.ts +2 -0
- package/dist/hooks/use-settings-completion.test.d.ts.map +1 -0
- package/dist/hooks/use-settings-completion.test.js +334 -0
- package/dist/hooks/use-settings-completion.test.js.map +1 -0
- package/dist/hooks/use-slash-commands.d.ts +21 -0
- package/dist/hooks/use-slash-commands.d.ts.map +1 -0
- package/dist/hooks/use-slash-commands.js +87 -0
- package/dist/hooks/use-slash-commands.js.map +1 -0
- package/dist/hooks/use-slash-commands.test.d.ts +2 -0
- package/dist/hooks/use-slash-commands.test.d.ts.map +1 -0
- package/dist/hooks/use-slash-commands.test.js +246 -0
- package/dist/hooks/use-slash-commands.test.js.map +1 -0
- package/dist/lib/editor-impl.d.ts +23 -0
- package/dist/lib/editor-impl.d.ts.map +1 -0
- package/dist/lib/editor-impl.js +235 -0
- package/dist/lib/editor-impl.js.map +1 -0
- package/dist/lib/openai-agent-client.chat.test.d.ts +2 -0
- package/dist/lib/openai-agent-client.chat.test.d.ts.map +1 -0
- package/dist/lib/openai-agent-client.chat.test.js +68 -0
- package/dist/lib/openai-agent-client.chat.test.js.map +1 -0
- package/dist/lib/openai-agent-client.d.ts +48 -0
- package/dist/lib/openai-agent-client.d.ts.map +1 -0
- package/dist/lib/openai-agent-client.js +653 -0
- package/dist/lib/openai-agent-client.js.map +1 -0
- package/dist/lib/openai-agent-client.test.d.ts +2 -0
- package/dist/lib/openai-agent-client.test.d.ts.map +1 -0
- package/dist/lib/openai-agent-client.test.js +181 -0
- package/dist/lib/openai-agent-client.test.js.map +1 -0
- package/dist/lib/shell.d.ts +7 -0
- package/dist/lib/shell.d.ts.map +1 -0
- package/dist/lib/shell.js +56 -0
- package/dist/lib/shell.js.map +1 -0
- package/dist/lib/tool-invoke.d.ts +4 -0
- package/dist/lib/tool-invoke.d.ts.map +1 -0
- package/dist/lib/tool-invoke.js +26 -0
- package/dist/lib/tool-invoke.js.map +1 -0
- package/dist/lib/tool-invoke.test.d.ts +2 -0
- package/dist/lib/tool-invoke.test.d.ts.map +1 -0
- package/dist/lib/tool-invoke.test.js +19 -0
- package/dist/lib/tool-invoke.test.js.map +1 -0
- package/dist/no-singleton-imports.test.d.ts +2 -0
- package/dist/no-singleton-imports.test.d.ts.map +1 -0
- package/dist/no-singleton-imports.test.js +30 -0
- package/dist/no-singleton-imports.test.js.map +1 -0
- package/dist/prompts/anthropic.md +79 -0
- package/dist/prompts/codex.md +97 -0
- package/dist/prompts/default.md +77 -0
- package/dist/prompts/default.md.bak +77 -0
- package/dist/prompts/gpt-5.md +318 -0
- package/dist/prompts/lite.md +29 -0
- package/dist/prompts/simple-mentor.md +207 -0
- package/dist/prompts/simple.md +189 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai-compatible/api.d.ts +17 -0
- package/dist/providers/openai-compatible/api.d.ts.map +1 -0
- package/dist/providers/openai-compatible/api.js +58 -0
- package/dist/providers/openai-compatible/api.js.map +1 -0
- package/dist/providers/openai-compatible/model.d.ts +17 -0
- package/dist/providers/openai-compatible/model.d.ts.map +1 -0
- package/dist/providers/openai-compatible/model.js +435 -0
- package/dist/providers/openai-compatible/model.js.map +1 -0
- package/dist/providers/openai-compatible/provider.d.ts +22 -0
- package/dist/providers/openai-compatible/provider.d.ts.map +1 -0
- package/dist/providers/openai-compatible/provider.js +43 -0
- package/dist/providers/openai-compatible/provider.js.map +1 -0
- package/dist/providers/openai-compatible/utils.d.ts +3 -0
- package/dist/providers/openai-compatible/utils.d.ts.map +1 -0
- package/dist/providers/openai-compatible/utils.js +11 -0
- package/dist/providers/openai-compatible/utils.js.map +1 -0
- package/dist/providers/openai-compatible.provider.d.ts +8 -0
- package/dist/providers/openai-compatible.provider.d.ts.map +1 -0
- package/dist/providers/openai-compatible.provider.js +71 -0
- package/dist/providers/openai-compatible.provider.js.map +1 -0
- package/dist/providers/openai.provider.d.ts +2 -0
- package/dist/providers/openai.provider.d.ts.map +1 -0
- package/dist/providers/openai.provider.js +36 -0
- package/dist/providers/openai.provider.js.map +1 -0
- package/dist/providers/openrouter/api.d.ts +39 -0
- package/dist/providers/openrouter/api.d.ts.map +1 -0
- package/dist/providers/openrouter/api.js +172 -0
- package/dist/providers/openrouter/api.js.map +1 -0
- package/dist/providers/openrouter/converters.d.ts +8 -0
- package/dist/providers/openrouter/converters.d.ts.map +1 -0
- package/dist/providers/openrouter/converters.js +382 -0
- package/dist/providers/openrouter/converters.js.map +1 -0
- package/dist/providers/openrouter/converters.test.d.ts +2 -0
- package/dist/providers/openrouter/converters.test.d.ts.map +1 -0
- package/dist/providers/openrouter/converters.test.js +158 -0
- package/dist/providers/openrouter/converters.test.js.map +1 -0
- package/dist/providers/openrouter/index.d.ts +4 -0
- package/dist/providers/openrouter/index.d.ts.map +1 -0
- package/dist/providers/openrouter/index.js +4 -0
- package/dist/providers/openrouter/index.js.map +1 -0
- package/dist/providers/openrouter/model.d.ts +14 -0
- package/dist/providers/openrouter/model.d.ts.map +1 -0
- package/dist/providers/openrouter/model.js +485 -0
- package/dist/providers/openrouter/model.js.map +1 -0
- package/dist/providers/openrouter/provider.d.ts +15 -0
- package/dist/providers/openrouter/provider.d.ts.map +1 -0
- package/dist/providers/openrouter/provider.js +21 -0
- package/dist/providers/openrouter/provider.js.map +1 -0
- package/dist/providers/openrouter/utils.d.ts +10 -0
- package/dist/providers/openrouter/utils.d.ts.map +1 -0
- package/dist/providers/openrouter/utils.js +27 -0
- package/dist/providers/openrouter/utils.js.map +1 -0
- package/dist/providers/openrouter.api.retry.test.d.ts +2 -0
- package/dist/providers/openrouter.api.retry.test.d.ts.map +1 -0
- package/dist/providers/openrouter.api.retry.test.js +148 -0
- package/dist/providers/openrouter.api.retry.test.js.map +1 -0
- package/dist/providers/openrouter.d.ts +2 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.history.test.d.ts +2 -0
- package/dist/providers/openrouter.history.test.d.ts.map +1 -0
- package/dist/providers/openrouter.history.test.js +533 -0
- package/dist/providers/openrouter.history.test.js.map +1 -0
- package/dist/providers/openrouter.js +4 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/openrouter.provider.createRunner.test.d.ts +2 -0
- package/dist/providers/openrouter.provider.createRunner.test.d.ts.map +1 -0
- package/dist/providers/openrouter.provider.createRunner.test.js +23 -0
- package/dist/providers/openrouter.provider.createRunner.test.js.map +1 -0
- package/dist/providers/openrouter.provider.d.ts +2 -0
- package/dist/providers/openrouter.provider.d.ts.map +1 -0
- package/dist/providers/openrouter.provider.js +56 -0
- package/dist/providers/openrouter.provider.js.map +1 -0
- package/dist/providers/openrouter.test.d.ts +2 -0
- package/dist/providers/openrouter.test.d.ts.map +1 -0
- package/dist/providers/openrouter.test.js +1382 -0
- package/dist/providers/openrouter.test.js.map +1 -0
- package/dist/providers/registry.d.ts +65 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +44 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/registry.test.d.ts +2 -0
- package/dist/providers/registry.test.d.ts.map +1 -0
- package/dist/providers/registry.test.js +76 -0
- package/dist/providers/registry.test.js.map +1 -0
- package/dist/providers/web-search/index.d.ts +8 -0
- package/dist/providers/web-search/index.d.ts.map +1 -0
- package/dist/providers/web-search/index.js +9 -0
- package/dist/providers/web-search/index.js.map +1 -0
- package/dist/providers/web-search/registry.d.ts +35 -0
- package/dist/providers/web-search/registry.d.ts.map +1 -0
- package/dist/providers/web-search/registry.js +56 -0
- package/dist/providers/web-search/registry.js.map +1 -0
- package/dist/providers/web-search/registry.test.d.ts +2 -0
- package/dist/providers/web-search/registry.test.d.ts.map +1 -0
- package/dist/providers/web-search/registry.test.js +105 -0
- package/dist/providers/web-search/registry.test.js.map +1 -0
- package/dist/providers/web-search/tavily.provider.d.ts +15 -0
- package/dist/providers/web-search/tavily.provider.d.ts.map +1 -0
- package/dist/providers/web-search/tavily.provider.js +69 -0
- package/dist/providers/web-search/tavily.provider.js.map +1 -0
- package/dist/providers/web-search/tavily.provider.test.d.ts +2 -0
- package/dist/providers/web-search/tavily.provider.test.d.ts.map +1 -0
- package/dist/providers/web-search/tavily.provider.test.js +67 -0
- package/dist/providers/web-search/tavily.provider.test.js.map +1 -0
- package/dist/providers/web-search/types.d.ts +55 -0
- package/dist/providers/web-search/types.d.ts.map +1 -0
- package/dist/providers/web-search/types.js +6 -0
- package/dist/providers/web-search/types.js.map +1 -0
- package/dist/safety-checker.js +57 -0
- package/dist/services/conversation-events.d.ts +76 -0
- package/dist/services/conversation-events.d.ts.map +1 -0
- package/dist/services/conversation-events.js +2 -0
- package/dist/services/conversation-events.js.map +1 -0
- package/dist/services/conversation-service.d.ts +31 -0
- package/dist/services/conversation-service.d.ts.map +1 -0
- package/dist/services/conversation-service.js +46 -0
- package/dist/services/conversation-service.js.map +1 -0
- package/dist/services/conversation-service.test.js +190 -0
- package/dist/services/conversation-session.d.ts +99 -0
- package/dist/services/conversation-session.d.ts.map +1 -0
- package/dist/services/conversation-session.js +978 -0
- package/dist/services/conversation-session.js.map +1 -0
- package/dist/services/conversation-store.d.ts +24 -0
- package/dist/services/conversation-store.d.ts.map +1 -0
- package/dist/services/conversation-store.js +216 -0
- package/dist/services/conversation-store.js.map +1 -0
- package/dist/services/conversation-store.test.d.ts +2 -0
- package/dist/services/conversation-store.test.d.ts.map +1 -0
- package/dist/services/conversation-store.test.js +167 -0
- package/dist/services/conversation-store.test.js.map +1 -0
- package/dist/services/execution-context.d.ts +10 -0
- package/dist/services/execution-context.d.ts.map +1 -0
- package/dist/services/execution-context.js +22 -0
- package/dist/services/execution-context.js.map +1 -0
- package/dist/services/execution-context.test.d.ts +2 -0
- package/dist/services/execution-context.test.d.ts.map +1 -0
- package/dist/services/execution-context.test.js +49 -0
- package/dist/services/execution-context.test.js.map +1 -0
- package/dist/services/file-service.d.ts +12 -0
- package/dist/services/file-service.d.ts.map +1 -0
- package/dist/services/file-service.js +90 -0
- package/dist/services/file-service.js.map +1 -0
- package/dist/services/history-service.d.ts +39 -0
- package/dist/services/history-service.d.ts.map +1 -0
- package/dist/services/history-service.js +152 -0
- package/dist/services/history-service.js.map +1 -0
- package/dist/services/logging-service.d.ts +75 -0
- package/dist/services/logging-service.d.ts.map +1 -0
- package/dist/services/logging-service.js +343 -0
- package/dist/services/logging-service.js.map +1 -0
- package/dist/services/model-service.d.ts +15 -0
- package/dist/services/model-service.d.ts.map +1 -0
- package/dist/services/model-service.js +46 -0
- package/dist/services/model-service.js.map +1 -0
- package/dist/services/model-service.test.d.ts +2 -0
- package/dist/services/model-service.test.d.ts.map +1 -0
- package/dist/services/model-service.test.js +128 -0
- package/dist/services/model-service.test.js.map +1 -0
- package/dist/services/service-interfaces.d.ts +33 -0
- package/dist/services/service-interfaces.d.ts.map +1 -0
- package/dist/services/service-interfaces.js +2 -0
- package/dist/services/service-interfaces.js.map +1 -0
- package/dist/services/settings-service.d.ts +316 -0
- package/dist/services/settings-service.d.ts.map +1 -0
- package/dist/services/settings-service.js +1128 -0
- package/dist/services/settings-service.js.map +1 -0
- package/dist/services/settings-service.mock.d.ts +20 -0
- package/dist/services/settings-service.mock.d.ts.map +1 -0
- package/dist/services/settings-service.mock.js +55 -0
- package/dist/services/settings-service.mock.js.map +1 -0
- package/dist/services/singleton-deprecation.test.d.ts +2 -0
- package/dist/services/singleton-deprecation.test.d.ts.map +1 -0
- package/dist/services/singleton-deprecation.test.js +59 -0
- package/dist/services/singleton-deprecation.test.js.map +1 -0
- package/dist/services/ssh-service.d.ts +32 -0
- package/dist/services/ssh-service.d.ts.map +1 -0
- package/dist/services/ssh-service.js +119 -0
- package/dist/services/ssh-service.js.map +1 -0
- package/dist/services/ssh-service.test.d.ts +2 -0
- package/dist/services/ssh-service.test.d.ts.map +1 -0
- package/dist/services/ssh-service.test.js +269 -0
- package/dist/services/ssh-service.test.js.map +1 -0
- package/dist/test-search-tool.d.ts +2 -0
- package/dist/test-search-tool.d.ts.map +1 -0
- package/dist/test-search-tool.js +36 -0
- package/dist/test-search-tool.js.map +1 -0
- package/dist/tools/apply-patch.d.ts +28 -0
- package/dist/tools/apply-patch.d.ts.map +1 -0
- package/dist/tools/apply-patch.js +399 -0
- package/dist/tools/apply-patch.js.map +1 -0
- package/dist/tools/apply-patch.test.d.ts +2 -0
- package/dist/tools/apply-patch.test.d.ts.map +1 -0
- package/dist/tools/apply-patch.test.js +155 -0
- package/dist/tools/apply-patch.test.js.map +1 -0
- package/dist/tools/ask-mentor.d.ts +11 -0
- package/dist/tools/ask-mentor.d.ts.map +1 -0
- package/dist/tools/ask-mentor.js +52 -0
- package/dist/tools/ask-mentor.js.map +1 -0
- package/dist/tools/ask-mentor.test.d.ts +2 -0
- package/dist/tools/ask-mentor.test.d.ts.map +1 -0
- package/dist/tools/ask-mentor.test.js +47 -0
- package/dist/tools/ask-mentor.test.js.map +1 -0
- package/dist/tools/bash.d.ts +10 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +55 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/find-files.d.ts +15 -0
- package/dist/tools/find-files.d.ts.map +1 -0
- package/dist/tools/find-files.js +179 -0
- package/dist/tools/find-files.js.map +1 -0
- package/dist/tools/find-files.test.d.ts +2 -0
- package/dist/tools/find-files.test.d.ts.map +1 -0
- package/dist/tools/find-files.test.js +131 -0
- package/dist/tools/find-files.test.js.map +1 -0
- package/dist/tools/format-helpers.d.ts +34 -0
- package/dist/tools/format-helpers.d.ts.map +1 -0
- package/dist/tools/format-helpers.js +131 -0
- package/dist/tools/format-helpers.js.map +1 -0
- package/dist/tools/grep.d.ts +16 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +211 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/read-file.d.ts +15 -0
- package/dist/tools/read-file.d.ts.map +1 -0
- package/dist/tools/read-file.js +114 -0
- package/dist/tools/read-file.js.map +1 -0
- package/dist/tools/read-file.test.d.ts +2 -0
- package/dist/tools/read-file.test.d.ts.map +1 -0
- package/dist/tools/read-file.test.js +122 -0
- package/dist/tools/read-file.test.js.map +1 -0
- package/dist/tools/search-replace.d.ts +19 -0
- package/dist/tools/search-replace.d.ts.map +1 -0
- package/dist/tools/search-replace.js +411 -0
- package/dist/tools/search-replace.js.map +1 -0
- package/dist/tools/search-replace.test.d.ts +2 -0
- package/dist/tools/search-replace.test.d.ts.map +1 -0
- package/dist/tools/search-replace.test.js +302 -0
- package/dist/tools/search-replace.test.js.map +1 -0
- package/dist/tools/search.d.ts +15 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +143 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/shell.d.ts +19 -0
- package/dist/tools/shell.d.ts.map +1 -0
- package/dist/tools/shell.js +278 -0
- package/dist/tools/shell.js.map +1 -0
- package/dist/tools/tool-execution-context.d.ts +7 -0
- package/dist/tools/tool-execution-context.d.ts.map +1 -0
- package/dist/tools/tool-execution-context.js +7 -0
- package/dist/tools/tool-execution-context.js.map +1 -0
- package/dist/tools/types.d.ts +30 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/utils.d.ts +12 -0
- package/dist/tools/utils.d.ts.map +1 -0
- package/dist/tools/utils.js +19 -0
- package/dist/tools/utils.js.map +1 -0
- package/dist/tools/web-search.d.ts +29 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +106 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/web-search.test.d.ts +2 -0
- package/dist/tools/web-search.test.d.ts.map +1 -0
- package/dist/tools/web-search.test.js +176 -0
- package/dist/tools/web-search.test.js.map +1 -0
- package/dist/utils/command-logger.d.ts +11 -0
- package/dist/utils/command-logger.d.ts.map +1 -0
- package/dist/utils/command-logger.js +34 -0
- package/dist/utils/command-logger.js.map +1 -0
- package/dist/utils/command-safety/constants.d.ts +21 -0
- package/dist/utils/command-safety/constants.d.ts.map +1 -0
- package/dist/utils/command-safety/constants.js +245 -0
- package/dist/utils/command-safety/constants.js.map +1 -0
- package/dist/utils/command-safety/find-helpers.d.ts +15 -0
- package/dist/utils/command-safety/find-helpers.d.ts.map +1 -0
- package/dist/utils/command-safety/find-helpers.js +218 -0
- package/dist/utils/command-safety/find-helpers.js.map +1 -0
- package/dist/utils/command-safety/handlers/find-handler.d.ts +6 -0
- package/dist/utils/command-safety/handlers/find-handler.d.ts.map +1 -0
- package/dist/utils/command-safety/handlers/find-handler.js +113 -0
- package/dist/utils/command-safety/handlers/find-handler.js.map +1 -0
- package/dist/utils/command-safety/handlers/git-handler.d.ts +6 -0
- package/dist/utils/command-safety/handlers/git-handler.d.ts.map +1 -0
- package/dist/utils/command-safety/handlers/git-handler.js +68 -0
- package/dist/utils/command-safety/handlers/git-handler.js.map +1 -0
- package/dist/utils/command-safety/handlers/index.d.ts +13 -0
- package/dist/utils/command-safety/handlers/index.d.ts.map +1 -0
- package/dist/utils/command-safety/handlers/index.js +20 -0
- package/dist/utils/command-safety/handlers/index.js.map +1 -0
- package/dist/utils/command-safety/handlers/sed-handler.d.ts +6 -0
- package/dist/utils/command-safety/handlers/sed-handler.d.ts.map +1 -0
- package/dist/utils/command-safety/handlers/sed-handler.js +94 -0
- package/dist/utils/command-safety/handlers/sed-handler.js.map +1 -0
- package/dist/utils/command-safety/handlers/types.d.ts +36 -0
- package/dist/utils/command-safety/handlers/types.d.ts.map +1 -0
- package/dist/utils/command-safety/handlers/types.js +2 -0
- package/dist/utils/command-safety/handlers/types.js.map +1 -0
- package/dist/utils/command-safety/index.d.ts +14 -0
- package/dist/utils/command-safety/index.d.ts.map +1 -0
- package/dist/utils/command-safety/index.js +183 -0
- package/dist/utils/command-safety/index.js.map +1 -0
- package/dist/utils/command-safety/path-analysis.d.ts +4 -0
- package/dist/utils/command-safety/path-analysis.d.ts.map +1 -0
- package/dist/utils/command-safety/path-analysis.js +153 -0
- package/dist/utils/command-safety/path-analysis.js.map +1 -0
- package/dist/utils/command-safety/utils.d.ts +2 -0
- package/dist/utils/command-safety/utils.d.ts.map +1 -0
- package/dist/utils/command-safety/utils.js +22 -0
- package/dist/utils/command-safety/utils.js.map +1 -0
- package/dist/utils/command-safety.d.ts +21 -0
- package/dist/utils/command-safety.d.ts.map +1 -0
- package/dist/utils/command-safety.find.test.d.ts +2 -0
- package/dist/utils/command-safety.find.test.d.ts.map +1 -0
- package/dist/utils/command-safety.find.test.js +342 -0
- package/dist/utils/command-safety.find.test.js.map +1 -0
- package/dist/utils/command-safety.js +702 -0
- package/dist/utils/command-safety.js.map +1 -0
- package/dist/utils/command-safety.path.test.d.ts +2 -0
- package/dist/utils/command-safety.path.test.d.ts.map +1 -0
- package/dist/utils/command-safety.path.test.js +360 -0
- package/dist/utils/command-safety.path.test.js.map +1 -0
- package/dist/utils/diff.d.ts +2 -0
- package/dist/utils/diff.d.ts.map +1 -0
- package/dist/utils/diff.js +44 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/utils/diff.test.d.ts +2 -0
- package/dist/utils/diff.test.d.ts.map +1 -0
- package/dist/utils/diff.test.js +85 -0
- package/dist/utils/diff.test.js.map +1 -0
- package/dist/utils/error-helpers.d.ts +6 -0
- package/dist/utils/error-helpers.d.ts.map +1 -0
- package/dist/utils/error-helpers.js +46 -0
- package/dist/utils/error-helpers.js.map +1 -0
- package/dist/utils/error-helpers.test.d.ts +2 -0
- package/dist/utils/error-helpers.test.d.ts.map +1 -0
- package/dist/utils/error-helpers.test.js +152 -0
- package/dist/utils/error-helpers.test.js.map +1 -0
- package/dist/utils/execute-shell.d.ts +15 -0
- package/dist/utils/execute-shell.d.ts.map +1 -0
- package/dist/utils/execute-shell.js +34 -0
- package/dist/utils/execute-shell.js.map +1 -0
- package/dist/utils/execute-shell.test.d.ts +2 -0
- package/dist/utils/execute-shell.test.d.ts.map +1 -0
- package/dist/utils/execute-shell.test.js +20 -0
- package/dist/utils/execute-shell.test.js.map +1 -0
- package/dist/utils/extract-command-messages.d.ts +5 -0
- package/dist/utils/extract-command-messages.d.ts.map +1 -0
- package/dist/utils/extract-command-messages.js +140 -0
- package/dist/utils/extract-command-messages.js.map +1 -0
- package/dist/utils/extract-command-messages.repro.test.d.ts +2 -0
- package/dist/utils/extract-command-messages.repro.test.d.ts.map +1 -0
- package/dist/utils/extract-command-messages.repro.test.js +31 -0
- package/dist/utils/extract-command-messages.repro.test.js.map +1 -0
- package/dist/utils/extract-command-messages.test.js +57 -0
- package/dist/utils/message-buffer.d.ts +2 -0
- package/dist/utils/message-buffer.d.ts.map +1 -0
- package/dist/utils/message-buffer.js +15 -0
- package/dist/utils/message-buffer.js.map +1 -0
- package/dist/utils/message-buffer.test.d.ts +2 -0
- package/dist/utils/message-buffer.test.d.ts.map +1 -0
- package/dist/utils/message-buffer.test.js +17 -0
- package/dist/utils/message-buffer.test.js.map +1 -0
- package/dist/utils/output-trim.d.ts +31 -0
- package/dist/utils/output-trim.d.ts.map +1 -0
- package/dist/utils/output-trim.js +71 -0
- package/dist/utils/output-trim.js.map +1 -0
- package/dist/utils/provider-credentials.d.ts +10 -0
- package/dist/utils/provider-credentials.d.ts.map +1 -0
- package/dist/utils/provider-credentials.js +22 -0
- package/dist/utils/provider-credentials.js.map +1 -0
- package/dist/utils/settings-command.d.ts +13 -0
- package/dist/utils/settings-command.d.ts.map +1 -0
- package/dist/utils/settings-command.js +173 -0
- package/dist/utils/settings-command.js.map +1 -0
- package/dist/utils/ssh-config-parser.d.ts +21 -0
- package/dist/utils/ssh-config-parser.d.ts.map +1 -0
- package/dist/utils/ssh-config-parser.js +89 -0
- package/dist/utils/ssh-config-parser.js.map +1 -0
- package/dist/utils/ssh-config-parser.test.d.ts +2 -0
- package/dist/utils/ssh-config-parser.test.d.ts.map +1 -0
- package/dist/utils/ssh-config-parser.test.js +153 -0
- package/dist/utils/ssh-config-parser.test.js.map +1 -0
- package/dist/utils/streaming-updater.d.ts +7 -0
- package/dist/utils/streaming-updater.d.ts.map +1 -0
- package/dist/utils/streaming-updater.js +41 -0
- package/dist/utils/streaming-updater.js.map +1 -0
- package/dist/utils/throttle.d.ts +7 -0
- package/dist/utils/throttle.d.ts.map +1 -0
- package/dist/utils/throttle.js +49 -0
- package/dist/utils/throttle.js.map +1 -0
- package/package.json +108 -0
- package/readme.md +428 -0
|
@@ -0,0 +1,1017 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from 'react';
|
|
2
|
+
import { isAbortLikeError } from '../utils/error-helpers.js';
|
|
3
|
+
import { createStreamingUpdateCoordinator } from '../utils/streaming-updater.js';
|
|
4
|
+
import { appendMessagesCapped } from '../utils/message-buffer.js';
|
|
5
|
+
const LIVE_RESPONSE_THROTTLE_MS = 150;
|
|
6
|
+
const REASONING_RESPONSE_THROTTLE_MS = 200;
|
|
7
|
+
const MAX_MESSAGE_COUNT = 300;
|
|
8
|
+
export const filterPendingCommandMessagesForApproval = (messages, approval) => {
|
|
9
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
10
|
+
return messages ?? [];
|
|
11
|
+
}
|
|
12
|
+
const callId = approval?.callId ? String(approval.callId) : undefined;
|
|
13
|
+
const toolName = approval?.toolName;
|
|
14
|
+
if (!callId && !toolName) {
|
|
15
|
+
return messages;
|
|
16
|
+
}
|
|
17
|
+
return messages.filter(msg => {
|
|
18
|
+
if (!msg || msg.sender !== 'command') {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (msg.status !== 'pending' && msg.status !== 'running') {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
const matchesCallId = callId && msg.callId && String(msg.callId) === callId;
|
|
25
|
+
const matchesToolName = !callId && toolName && msg.toolName === toolName;
|
|
26
|
+
return !(matchesCallId || matchesToolName);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
export const useConversation = ({ conversationService, loggingService, }) => {
|
|
30
|
+
const [messages, setMessages] = useState([]);
|
|
31
|
+
const [waitingForApproval, setWaitingForApproval] = useState(false);
|
|
32
|
+
const [waitingForRejectionReason, setWaitingForRejectionReason] = useState(false);
|
|
33
|
+
const [pendingApproval, setPendingApproval] = useState(null);
|
|
34
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
35
|
+
const [liveResponse, setLiveResponse] = useState(null);
|
|
36
|
+
const approvedContextRef = useRef(null);
|
|
37
|
+
const createLiveResponseUpdater = useCallback((liveMessageId) => createStreamingUpdateCoordinator((text) => {
|
|
38
|
+
setLiveResponse(prev => prev && prev.id === liveMessageId
|
|
39
|
+
? { ...prev, text }
|
|
40
|
+
: {
|
|
41
|
+
id: liveMessageId,
|
|
42
|
+
sender: 'bot',
|
|
43
|
+
text,
|
|
44
|
+
});
|
|
45
|
+
}, LIVE_RESPONSE_THROTTLE_MS), []);
|
|
46
|
+
const trimMessages = useCallback((list) => appendMessagesCapped(list, [], MAX_MESSAGE_COUNT), []);
|
|
47
|
+
const appendMessages = useCallback((additions) => {
|
|
48
|
+
if (!additions.length)
|
|
49
|
+
return;
|
|
50
|
+
setMessages(prev => trimMessages([...prev, ...additions]));
|
|
51
|
+
}, [trimMessages]);
|
|
52
|
+
// Helper to log events with deduplication
|
|
53
|
+
// const createEventLogger = () => {
|
|
54
|
+
// let lastEventType: string | null = null;
|
|
55
|
+
// let eventCount = 0;
|
|
56
|
+
// let eventSequence: string[] = [];
|
|
57
|
+
//
|
|
58
|
+
// const logDeduplicated = (eventType: string) => {
|
|
59
|
+
// if (eventType !== lastEventType) {
|
|
60
|
+
// if (lastEventType !== null) {
|
|
61
|
+
// loggingService.debug('Conversation event sequence', {
|
|
62
|
+
// event: lastEventType,
|
|
63
|
+
// count: eventCount,
|
|
64
|
+
// sequenceLength: eventSequence.length,
|
|
65
|
+
// sequence: eventSequence,
|
|
66
|
+
// });
|
|
67
|
+
// }
|
|
68
|
+
// lastEventType = eventType;
|
|
69
|
+
// eventCount = 1;
|
|
70
|
+
// if (!eventSequence.includes(eventType)) {
|
|
71
|
+
// eventSequence.push(eventType);
|
|
72
|
+
// }
|
|
73
|
+
// } else {
|
|
74
|
+
// eventCount++;
|
|
75
|
+
// }
|
|
76
|
+
// };
|
|
77
|
+
//
|
|
78
|
+
// const flush = () => {
|
|
79
|
+
// if (lastEventType !== null) {
|
|
80
|
+
// loggingService.debug('Conversation event sequence final', {
|
|
81
|
+
// event: lastEventType,
|
|
82
|
+
// count: eventCount,
|
|
83
|
+
// sequenceLength: eventSequence.length,
|
|
84
|
+
// sequence: eventSequence,
|
|
85
|
+
// });
|
|
86
|
+
// lastEventType = null;
|
|
87
|
+
// eventCount = 0;
|
|
88
|
+
// eventSequence = [];
|
|
89
|
+
// }
|
|
90
|
+
// };
|
|
91
|
+
//
|
|
92
|
+
// return {logDeduplicated, flush};
|
|
93
|
+
// };
|
|
94
|
+
const annotateCommandMessage = useCallback((cmdMsg) => {
|
|
95
|
+
const approvalContext = approvedContextRef.current;
|
|
96
|
+
if (!approvalContext || cmdMsg.toolName !== 'search_replace') {
|
|
97
|
+
return cmdMsg;
|
|
98
|
+
}
|
|
99
|
+
const matchesCallId = approvalContext.callId &&
|
|
100
|
+
cmdMsg.callId &&
|
|
101
|
+
approvalContext.callId === cmdMsg.callId;
|
|
102
|
+
const matchesToolName = !approvalContext.callId &&
|
|
103
|
+
approvalContext.toolName &&
|
|
104
|
+
approvalContext.toolName === cmdMsg.toolName;
|
|
105
|
+
if (!matchesCallId && !matchesToolName) {
|
|
106
|
+
return cmdMsg;
|
|
107
|
+
}
|
|
108
|
+
if (matchesToolName && !approvalContext.callId) {
|
|
109
|
+
approvedContextRef.current = null;
|
|
110
|
+
}
|
|
111
|
+
return { ...cmdMsg, hadApproval: true };
|
|
112
|
+
}, []);
|
|
113
|
+
const applyServiceResult = useCallback((result, remainingText, remainingReasoningText, textWasFlushed) => {
|
|
114
|
+
if (!result) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (result.type === 'approval_required') {
|
|
118
|
+
// Flush reasoning and text separately before showing approval prompt
|
|
119
|
+
const messagesToAdd = [];
|
|
120
|
+
if (remainingReasoningText?.trim() && !textWasFlushed) {
|
|
121
|
+
// Ensure reasoning is captured if not already streamed (edge case)
|
|
122
|
+
// But with new logic, it should be in messages.
|
|
123
|
+
// We'll leave this empty or remove it as reasoning is handled via stream
|
|
124
|
+
}
|
|
125
|
+
if (remainingText?.trim() && !textWasFlushed) {
|
|
126
|
+
const textMessage = {
|
|
127
|
+
id: Date.now() + 1,
|
|
128
|
+
sender: 'bot',
|
|
129
|
+
text: remainingText,
|
|
130
|
+
};
|
|
131
|
+
messagesToAdd.push(textMessage);
|
|
132
|
+
}
|
|
133
|
+
appendMessages(messagesToAdd);
|
|
134
|
+
// If a tool call requires approval, we show it in the approval prompt.
|
|
135
|
+
// Don't also show the transient pending/running command message.
|
|
136
|
+
setMessages(prev => trimMessages(filterPendingCommandMessagesForApproval(prev, result.approval)));
|
|
137
|
+
setPendingApproval(result.approval);
|
|
138
|
+
// Set waiting state AFTER adding approval message to ensure proper render order
|
|
139
|
+
setWaitingForApproval(true);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// If text was already flushed before command messages, don't add it again
|
|
143
|
+
// Only add final text if there's new text after the commands
|
|
144
|
+
const shouldAddBotMessage = !textWasFlushed || remainingText?.trim();
|
|
145
|
+
const finalText = remainingText?.trim()
|
|
146
|
+
? remainingText
|
|
147
|
+
: result.finalText;
|
|
148
|
+
setMessages(prev => {
|
|
149
|
+
const messagesToAdd = [];
|
|
150
|
+
const annotatedCommands = result.commandMessages.map(annotateCommandMessage);
|
|
151
|
+
let next = [...prev, ...messagesToAdd, ...annotatedCommands];
|
|
152
|
+
if (shouldAddBotMessage && finalText) {
|
|
153
|
+
const botMessage = {
|
|
154
|
+
id: Date.now() + 1,
|
|
155
|
+
sender: 'bot',
|
|
156
|
+
text: finalText,
|
|
157
|
+
};
|
|
158
|
+
next = [...next, botMessage];
|
|
159
|
+
}
|
|
160
|
+
return trimMessages(next);
|
|
161
|
+
});
|
|
162
|
+
setWaitingForApproval(false);
|
|
163
|
+
setPendingApproval(null);
|
|
164
|
+
}, [annotateCommandMessage, appendMessages, trimMessages]);
|
|
165
|
+
const sendUserMessage = useCallback(async (value) => {
|
|
166
|
+
if (!value.trim()) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const userMessage = {
|
|
170
|
+
id: Date.now(),
|
|
171
|
+
sender: 'user',
|
|
172
|
+
text: value,
|
|
173
|
+
};
|
|
174
|
+
appendMessages([userMessage]);
|
|
175
|
+
setIsProcessing(true);
|
|
176
|
+
const liveMessageId = Date.now();
|
|
177
|
+
setLiveResponse({
|
|
178
|
+
id: liveMessageId,
|
|
179
|
+
sender: 'bot',
|
|
180
|
+
text: '',
|
|
181
|
+
});
|
|
182
|
+
const liveResponseUpdater = createLiveResponseUpdater(liveMessageId);
|
|
183
|
+
// Track accumulated text so we can flush it before command messages
|
|
184
|
+
let accumulatedText = '';
|
|
185
|
+
let accumulatedReasoningText = '';
|
|
186
|
+
let flushedReasoningLength = 0; // Track how much reasoning has been flushed
|
|
187
|
+
let textWasFlushed = false;
|
|
188
|
+
let currentReasoningMessageId = null; // Track current reasoning message ID
|
|
189
|
+
const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
|
|
190
|
+
setMessages(prev => {
|
|
191
|
+
if (currentReasoningMessageId !== null) {
|
|
192
|
+
const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
|
|
193
|
+
if (index === -1)
|
|
194
|
+
return prev;
|
|
195
|
+
const current = prev[index];
|
|
196
|
+
if (current.sender !== 'reasoning') {
|
|
197
|
+
return prev;
|
|
198
|
+
}
|
|
199
|
+
const next = prev.slice();
|
|
200
|
+
next[index] = { ...current, text: newReasoningText };
|
|
201
|
+
return trimMessages(next);
|
|
202
|
+
}
|
|
203
|
+
const newId = Date.now();
|
|
204
|
+
currentReasoningMessageId = newId;
|
|
205
|
+
return trimMessages([
|
|
206
|
+
...prev,
|
|
207
|
+
{
|
|
208
|
+
id: newId,
|
|
209
|
+
sender: 'reasoning',
|
|
210
|
+
text: newReasoningText,
|
|
211
|
+
},
|
|
212
|
+
]);
|
|
213
|
+
});
|
|
214
|
+
}, REASONING_RESPONSE_THROTTLE_MS);
|
|
215
|
+
// Create event logger with deduplication for this message send
|
|
216
|
+
// const {logDeduplicated, flush: flushLog} = createEventLogger();
|
|
217
|
+
const applyConversationEvent = (event) => {
|
|
218
|
+
switch (event.type) {
|
|
219
|
+
case 'text_delta': {
|
|
220
|
+
// logDeduplicated('text_delta');
|
|
221
|
+
accumulatedText += event.delta;
|
|
222
|
+
liveResponseUpdater.push(accumulatedText);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
case 'reasoning_delta': {
|
|
226
|
+
// logDeduplicated('reasoning_delta');
|
|
227
|
+
const fullReasoningText = event.fullText ?? '';
|
|
228
|
+
// Only show reasoning text after what was already flushed
|
|
229
|
+
const newReasoningText = fullReasoningText.slice(flushedReasoningLength);
|
|
230
|
+
accumulatedReasoningText = newReasoningText;
|
|
231
|
+
if (!newReasoningText.trim())
|
|
232
|
+
return;
|
|
233
|
+
reasoningUpdater.push(newReasoningText);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
case 'tool_started': {
|
|
237
|
+
// Flush reasoning state
|
|
238
|
+
if (accumulatedReasoningText.trim()) {
|
|
239
|
+
reasoningUpdater.flush();
|
|
240
|
+
flushedReasoningLength += accumulatedReasoningText.length;
|
|
241
|
+
accumulatedReasoningText = '';
|
|
242
|
+
currentReasoningMessageId = null;
|
|
243
|
+
}
|
|
244
|
+
// Flush any accumulated text before showing the tool call
|
|
245
|
+
if (accumulatedText.trim()) {
|
|
246
|
+
const textMessage = {
|
|
247
|
+
id: Date.now() + 1,
|
|
248
|
+
sender: 'bot',
|
|
249
|
+
text: accumulatedText,
|
|
250
|
+
};
|
|
251
|
+
appendMessages([textMessage]);
|
|
252
|
+
accumulatedText = '';
|
|
253
|
+
textWasFlushed = true;
|
|
254
|
+
liveResponseUpdater.cancel();
|
|
255
|
+
setLiveResponse(null);
|
|
256
|
+
}
|
|
257
|
+
// Emit a "pending" command message when tool starts running
|
|
258
|
+
// This provides immediate UI feedback before output is available
|
|
259
|
+
const { toolCallId, toolName, arguments: rawArgs } = event;
|
|
260
|
+
// tool_started.arguments may be either an object or a JSON string
|
|
261
|
+
// depending on provider. Normalize so we can render params.
|
|
262
|
+
const args = (() => {
|
|
263
|
+
if (typeof rawArgs !== 'string') {
|
|
264
|
+
return rawArgs;
|
|
265
|
+
}
|
|
266
|
+
const trimmed = rawArgs.trim();
|
|
267
|
+
if (!trimmed) {
|
|
268
|
+
return rawArgs;
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
return JSON.parse(trimmed);
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return rawArgs;
|
|
275
|
+
}
|
|
276
|
+
})();
|
|
277
|
+
// Create a command string from the tool info
|
|
278
|
+
const command = (() => {
|
|
279
|
+
if (toolName === 'shell') {
|
|
280
|
+
const cmd = args?.command ?? args?.commands;
|
|
281
|
+
if (typeof cmd === 'string' && cmd.trim()) {
|
|
282
|
+
return cmd;
|
|
283
|
+
}
|
|
284
|
+
if (Array.isArray(cmd) && cmd.length > 0) {
|
|
285
|
+
return cmd.join('\n');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (toolName === 'grep' && args?.pattern) {
|
|
289
|
+
return `grep "${args.pattern}" ${args.path ?? '.'}`;
|
|
290
|
+
}
|
|
291
|
+
if (toolName === 'search_replace') {
|
|
292
|
+
return `search_replace "${args.search_content ?? ''}" → "${args.replace_content ?? ''}" ${args.path ?? ''}`;
|
|
293
|
+
}
|
|
294
|
+
if (toolName === 'apply_patch') {
|
|
295
|
+
return `apply_patch ${args?.type ?? 'unknown'} ${args?.path ?? ''}`;
|
|
296
|
+
}
|
|
297
|
+
if (toolName === 'ask_mentor') {
|
|
298
|
+
return `ask_mentor: ${args?.question ?? ''}`;
|
|
299
|
+
}
|
|
300
|
+
return `${toolName ?? 'unknown_tool'}`;
|
|
301
|
+
})();
|
|
302
|
+
const pendingMessage = {
|
|
303
|
+
id: toolCallId ?? String(Date.now()),
|
|
304
|
+
sender: 'command',
|
|
305
|
+
status: 'running',
|
|
306
|
+
command,
|
|
307
|
+
output: '',
|
|
308
|
+
callId: toolCallId,
|
|
309
|
+
toolName,
|
|
310
|
+
toolArgs: args,
|
|
311
|
+
};
|
|
312
|
+
appendMessages([pendingMessage]);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
case 'command_message': {
|
|
316
|
+
// logDeduplicated('command_message');
|
|
317
|
+
const cmdMsg = event.message;
|
|
318
|
+
const annotated = annotateCommandMessage(cmdMsg);
|
|
319
|
+
// Before adding command message, flush reasoning and text separately
|
|
320
|
+
// This preserves the order: reasoning -> command -> response text
|
|
321
|
+
const messagesToAdd = [];
|
|
322
|
+
if (accumulatedReasoningText.trim()) {
|
|
323
|
+
reasoningUpdater.flush();
|
|
324
|
+
// Reasoning is already in messages via stream updates.
|
|
325
|
+
// We just need to track what we've "flushed" (sealed) so next reasoning chunks start fresh.
|
|
326
|
+
flushedReasoningLength +=
|
|
327
|
+
accumulatedReasoningText.length;
|
|
328
|
+
accumulatedReasoningText = '';
|
|
329
|
+
currentReasoningMessageId = null; // Reset for potential post-command reasoning
|
|
330
|
+
}
|
|
331
|
+
if (accumulatedText.trim()) {
|
|
332
|
+
const textMessage = {
|
|
333
|
+
id: Date.now() + 1,
|
|
334
|
+
sender: 'bot',
|
|
335
|
+
text: accumulatedText,
|
|
336
|
+
};
|
|
337
|
+
messagesToAdd.push(textMessage);
|
|
338
|
+
accumulatedText = '';
|
|
339
|
+
textWasFlushed = true;
|
|
340
|
+
}
|
|
341
|
+
if (messagesToAdd.length > 0) {
|
|
342
|
+
appendMessages(messagesToAdd);
|
|
343
|
+
// Clear live response since we've committed the text
|
|
344
|
+
liveResponseUpdater.cancel();
|
|
345
|
+
setLiveResponse(null);
|
|
346
|
+
}
|
|
347
|
+
// Replace pending message with completed one, or add new if not found
|
|
348
|
+
setMessages(prev => {
|
|
349
|
+
// Try to find the pending message by callId
|
|
350
|
+
const pendingIndex = annotated.callId
|
|
351
|
+
? prev.findIndex(msg => msg.sender === 'command' &&
|
|
352
|
+
msg.callId === annotated.callId &&
|
|
353
|
+
msg.status === 'running')
|
|
354
|
+
: -1;
|
|
355
|
+
if (pendingIndex !== -1) {
|
|
356
|
+
// Replace the pending message with the completed one
|
|
357
|
+
const next = [...prev];
|
|
358
|
+
next[pendingIndex] = annotated;
|
|
359
|
+
return trimMessages(next);
|
|
360
|
+
}
|
|
361
|
+
// If no pending message found, append the completed one
|
|
362
|
+
return trimMessages([...prev, annotated]);
|
|
363
|
+
});
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
case 'retry': {
|
|
367
|
+
const systemMessage = {
|
|
368
|
+
id: Date.now(),
|
|
369
|
+
sender: 'system',
|
|
370
|
+
text: `Tool hallucination detected (${event.toolName}). Retrying... (Attempt ${event.attempt}/${event.maxRetries})`,
|
|
371
|
+
};
|
|
372
|
+
setMessages(prev => [...prev, systemMessage]);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
default:
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
try {
|
|
380
|
+
const result = await conversationService.sendMessage(value, {
|
|
381
|
+
onEvent: applyConversationEvent,
|
|
382
|
+
});
|
|
383
|
+
applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
loggingService.error('Error in sendUserMessage', {
|
|
387
|
+
error: error instanceof Error ? error.message : String(error),
|
|
388
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
389
|
+
});
|
|
390
|
+
// Don't show error messages for user-initiated aborts
|
|
391
|
+
if (isAbortLikeError(error)) {
|
|
392
|
+
loggingService.debug('Suppressing abort error in sendUserMessage');
|
|
393
|
+
// The finally block will handle cleanup
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
let errorMessage = error instanceof Error ? error.message : String(error);
|
|
397
|
+
// Enhance error messages for common issues
|
|
398
|
+
if (errorMessage.includes('OPENAI_API_KEY') ||
|
|
399
|
+
(errorMessage.includes('401') &&
|
|
400
|
+
errorMessage.toLowerCase().includes('unauthorized'))) {
|
|
401
|
+
errorMessage =
|
|
402
|
+
'OpenAI API key is not configured or invalid. Please set the OPENAI_API_KEY environment variable. ' +
|
|
403
|
+
'Get your API key from: https://platform.openai.com/api-keys';
|
|
404
|
+
}
|
|
405
|
+
// Check if this is a max turns exceeded error
|
|
406
|
+
const isMaxTurnsError = errorMessage.includes('Max turns') &&
|
|
407
|
+
errorMessage.includes('exceeded');
|
|
408
|
+
if (isMaxTurnsError) {
|
|
409
|
+
// Create an approval prompt for max turns continuation
|
|
410
|
+
setPendingApproval({
|
|
411
|
+
agentName: 'System',
|
|
412
|
+
toolName: 'max_turns_exceeded',
|
|
413
|
+
argumentsText: errorMessage,
|
|
414
|
+
rawInterruption: null,
|
|
415
|
+
isMaxTurnsPrompt: true,
|
|
416
|
+
});
|
|
417
|
+
setWaitingForApproval(true);
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
// For other errors, just show the error message
|
|
421
|
+
const botErrorMessage = {
|
|
422
|
+
id: Date.now(),
|
|
423
|
+
sender: 'bot',
|
|
424
|
+
text: `Error: ${errorMessage}`,
|
|
425
|
+
};
|
|
426
|
+
appendMessages([botErrorMessage]);
|
|
427
|
+
// Reset approval state on error to allow user to continue
|
|
428
|
+
setWaitingForApproval(false);
|
|
429
|
+
setPendingApproval(null);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
finally {
|
|
433
|
+
loggingService.debug('sendUserMessage finally block - resetting state');
|
|
434
|
+
// flushLog();
|
|
435
|
+
reasoningUpdater.flush();
|
|
436
|
+
liveResponseUpdater.cancel();
|
|
437
|
+
setLiveResponse(null);
|
|
438
|
+
setIsProcessing(false);
|
|
439
|
+
// Don't reset waitingForApproval here - it's set by applyServiceResult
|
|
440
|
+
// and should only be cleared by handleApprovalDecision or stopProcessing
|
|
441
|
+
}
|
|
442
|
+
}, [conversationService, applyServiceResult, appendMessages, trimMessages, loggingService, createLiveResponseUpdater]);
|
|
443
|
+
const handleApprovalDecision = useCallback(async (answer, rejectionReason) => {
|
|
444
|
+
if (!waitingForApproval || !pendingApproval) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
// Check if this is a max turns exceeded prompt
|
|
448
|
+
const isMaxTurnsPrompt = pendingApproval.isMaxTurnsPrompt;
|
|
449
|
+
if (answer === 'y' &&
|
|
450
|
+
pendingApproval.toolName === 'search_replace') {
|
|
451
|
+
approvedContextRef.current = {
|
|
452
|
+
callId: pendingApproval.callId,
|
|
453
|
+
toolName: pendingApproval.toolName,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
setPendingApproval(null);
|
|
457
|
+
setWaitingForApproval(false);
|
|
458
|
+
// Handle "n" answer for max turns - return to input
|
|
459
|
+
if (isMaxTurnsPrompt && answer === 'n') {
|
|
460
|
+
setIsProcessing(false);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
// Handle "y" answer for max turns - continue execution automatically
|
|
464
|
+
if (isMaxTurnsPrompt && answer === 'y') {
|
|
465
|
+
setIsProcessing(true);
|
|
466
|
+
const liveMessageId = Date.now();
|
|
467
|
+
setLiveResponse({
|
|
468
|
+
id: liveMessageId,
|
|
469
|
+
sender: 'bot',
|
|
470
|
+
text: '',
|
|
471
|
+
});
|
|
472
|
+
const liveResponseUpdater = createLiveResponseUpdater(liveMessageId);
|
|
473
|
+
// Track accumulated text so we can flush it before command messages
|
|
474
|
+
let accumulatedText = '';
|
|
475
|
+
let accumulatedReasoningText = '';
|
|
476
|
+
let flushedReasoningLength = 0;
|
|
477
|
+
let textWasFlushed = false;
|
|
478
|
+
let currentReasoningMessageId = null;
|
|
479
|
+
const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
|
|
480
|
+
setMessages(prev => {
|
|
481
|
+
if (currentReasoningMessageId !== null) {
|
|
482
|
+
const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
|
|
483
|
+
if (index === -1)
|
|
484
|
+
return prev;
|
|
485
|
+
const current = prev[index];
|
|
486
|
+
if (current.sender !== 'reasoning') {
|
|
487
|
+
return prev;
|
|
488
|
+
}
|
|
489
|
+
const next = prev.slice();
|
|
490
|
+
next[index] = {
|
|
491
|
+
...current,
|
|
492
|
+
text: newReasoningText,
|
|
493
|
+
};
|
|
494
|
+
return trimMessages(next);
|
|
495
|
+
}
|
|
496
|
+
const newId = Date.now();
|
|
497
|
+
currentReasoningMessageId = newId;
|
|
498
|
+
return trimMessages([
|
|
499
|
+
...prev,
|
|
500
|
+
{
|
|
501
|
+
id: newId,
|
|
502
|
+
sender: 'reasoning',
|
|
503
|
+
text: newReasoningText,
|
|
504
|
+
},
|
|
505
|
+
]);
|
|
506
|
+
});
|
|
507
|
+
}, REASONING_RESPONSE_THROTTLE_MS);
|
|
508
|
+
// const {logDeduplicated, flush: flushLog} = createEventLogger();
|
|
509
|
+
const applyConversationEvent = (event) => {
|
|
510
|
+
switch (event.type) {
|
|
511
|
+
case 'text_delta': {
|
|
512
|
+
// logDeduplicated('text_delta');
|
|
513
|
+
accumulatedText += event.delta;
|
|
514
|
+
liveResponseUpdater.push(accumulatedText);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
case 'reasoning_delta': {
|
|
518
|
+
// logDeduplicated('reasoning_delta');
|
|
519
|
+
const fullReasoningText = event.fullText ?? '';
|
|
520
|
+
const newReasoningText = fullReasoningText.slice(flushedReasoningLength);
|
|
521
|
+
accumulatedReasoningText = newReasoningText;
|
|
522
|
+
if (!newReasoningText.trim())
|
|
523
|
+
return;
|
|
524
|
+
reasoningUpdater.push(newReasoningText);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
case 'tool_started': {
|
|
528
|
+
// Flush reasoning state
|
|
529
|
+
if (accumulatedReasoningText.trim()) {
|
|
530
|
+
reasoningUpdater.flush();
|
|
531
|
+
flushedReasoningLength += accumulatedReasoningText.length;
|
|
532
|
+
accumulatedReasoningText = '';
|
|
533
|
+
currentReasoningMessageId = null;
|
|
534
|
+
}
|
|
535
|
+
// Flush any accumulated text before showing the tool call
|
|
536
|
+
if (accumulatedText.trim()) {
|
|
537
|
+
const textMessage = {
|
|
538
|
+
id: Date.now() + 1,
|
|
539
|
+
sender: 'bot',
|
|
540
|
+
text: accumulatedText,
|
|
541
|
+
};
|
|
542
|
+
appendMessages([textMessage]);
|
|
543
|
+
accumulatedText = '';
|
|
544
|
+
textWasFlushed = true;
|
|
545
|
+
liveResponseUpdater.cancel();
|
|
546
|
+
setLiveResponse(null);
|
|
547
|
+
}
|
|
548
|
+
const { toolCallId, toolName, arguments: rawArgs } = event;
|
|
549
|
+
const args = (() => {
|
|
550
|
+
if (typeof rawArgs !== 'string') {
|
|
551
|
+
return rawArgs;
|
|
552
|
+
}
|
|
553
|
+
const trimmed = rawArgs.trim();
|
|
554
|
+
if (!trimmed) {
|
|
555
|
+
return rawArgs;
|
|
556
|
+
}
|
|
557
|
+
try {
|
|
558
|
+
return JSON.parse(trimmed);
|
|
559
|
+
}
|
|
560
|
+
catch {
|
|
561
|
+
return rawArgs;
|
|
562
|
+
}
|
|
563
|
+
})();
|
|
564
|
+
const command = (() => {
|
|
565
|
+
if (toolName === 'shell') {
|
|
566
|
+
const cmd = args?.command ?? args?.commands;
|
|
567
|
+
if (typeof cmd === 'string' && cmd.trim()) {
|
|
568
|
+
return cmd;
|
|
569
|
+
}
|
|
570
|
+
if (Array.isArray(cmd) && cmd.length > 0) {
|
|
571
|
+
return cmd.join('\n');
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (toolName === 'grep' && args?.pattern) {
|
|
575
|
+
return `grep "${args.pattern}" ${args.path ?? '.'}`;
|
|
576
|
+
}
|
|
577
|
+
if (toolName === 'search_replace') {
|
|
578
|
+
return `search_replace "${args.search_content ?? ''}" → "${args.replace_content ?? ''}" ${args.path ?? ''}`;
|
|
579
|
+
}
|
|
580
|
+
if (toolName === 'apply_patch') {
|
|
581
|
+
return `apply_patch ${args?.type ?? 'unknown'} ${args?.path ?? ''}`;
|
|
582
|
+
}
|
|
583
|
+
if (toolName === 'ask_mentor') {
|
|
584
|
+
return `ask_mentor: ${args?.question ?? ''}`;
|
|
585
|
+
}
|
|
586
|
+
return `${toolName ?? 'unknown_tool'}`;
|
|
587
|
+
})();
|
|
588
|
+
const pendingMessage = {
|
|
589
|
+
id: toolCallId ?? String(Date.now()),
|
|
590
|
+
sender: 'command',
|
|
591
|
+
status: 'running',
|
|
592
|
+
command,
|
|
593
|
+
output: '',
|
|
594
|
+
callId: toolCallId,
|
|
595
|
+
toolName,
|
|
596
|
+
toolArgs: args,
|
|
597
|
+
};
|
|
598
|
+
appendMessages([pendingMessage]);
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
case 'command_message': {
|
|
602
|
+
// logDeduplicated('command_message');
|
|
603
|
+
const cmdMsg = event.message;
|
|
604
|
+
const annotated = annotateCommandMessage(cmdMsg);
|
|
605
|
+
const messagesToAdd = [];
|
|
606
|
+
if (accumulatedReasoningText.trim()) {
|
|
607
|
+
reasoningUpdater.flush();
|
|
608
|
+
flushedReasoningLength +=
|
|
609
|
+
accumulatedReasoningText.length;
|
|
610
|
+
accumulatedReasoningText = '';
|
|
611
|
+
currentReasoningMessageId = null;
|
|
612
|
+
}
|
|
613
|
+
if (accumulatedText.trim()) {
|
|
614
|
+
const textMessage = {
|
|
615
|
+
id: Date.now() + 1,
|
|
616
|
+
sender: 'bot',
|
|
617
|
+
text: accumulatedText,
|
|
618
|
+
};
|
|
619
|
+
messagesToAdd.push(textMessage);
|
|
620
|
+
accumulatedText = '';
|
|
621
|
+
textWasFlushed = true;
|
|
622
|
+
}
|
|
623
|
+
if (messagesToAdd.length > 0) {
|
|
624
|
+
appendMessages(messagesToAdd);
|
|
625
|
+
liveResponseUpdater.cancel();
|
|
626
|
+
setLiveResponse(null);
|
|
627
|
+
}
|
|
628
|
+
// Replace pending message with completed one, or add new if not found
|
|
629
|
+
setMessages(prev => {
|
|
630
|
+
const pendingIndex = annotated.callId
|
|
631
|
+
? prev.findIndex(msg => msg.sender === 'command' &&
|
|
632
|
+
msg.callId === annotated.callId &&
|
|
633
|
+
msg.status === 'running')
|
|
634
|
+
: -1;
|
|
635
|
+
if (pendingIndex !== -1) {
|
|
636
|
+
const next = [...prev];
|
|
637
|
+
next[pendingIndex] = annotated;
|
|
638
|
+
return trimMessages(next);
|
|
639
|
+
}
|
|
640
|
+
return trimMessages([...prev, annotated]);
|
|
641
|
+
});
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
case 'retry': {
|
|
645
|
+
const systemMessage = {
|
|
646
|
+
id: Date.now(),
|
|
647
|
+
sender: 'system',
|
|
648
|
+
text: `Tool hallucination detected (${event.toolName}). Retrying... (Attempt ${event.attempt}/${event.maxRetries})`,
|
|
649
|
+
};
|
|
650
|
+
setMessages(prev => [...prev, systemMessage]);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
default:
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
try {
|
|
658
|
+
// Send a continuation message to resume work
|
|
659
|
+
const continuationMessage = 'Please continue with your previous task.';
|
|
660
|
+
const result = await conversationService.sendMessage(continuationMessage, {
|
|
661
|
+
onEvent: applyConversationEvent,
|
|
662
|
+
});
|
|
663
|
+
applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
|
|
664
|
+
}
|
|
665
|
+
catch (error) {
|
|
666
|
+
loggingService.error('Error in continuation after max turns', {
|
|
667
|
+
error: error instanceof Error
|
|
668
|
+
? error.message
|
|
669
|
+
: String(error),
|
|
670
|
+
stack: error instanceof Error
|
|
671
|
+
? error.stack
|
|
672
|
+
: undefined,
|
|
673
|
+
});
|
|
674
|
+
// Don't show error messages for user-initiated aborts
|
|
675
|
+
if (isAbortLikeError(error)) {
|
|
676
|
+
loggingService.debug('Suppressing abort error in max turns continuation');
|
|
677
|
+
// The finally block will handle cleanup
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
681
|
+
const botErrorMessage = {
|
|
682
|
+
id: Date.now(),
|
|
683
|
+
sender: 'bot',
|
|
684
|
+
text: `Error: ${errorMessage}`,
|
|
685
|
+
};
|
|
686
|
+
appendMessages([botErrorMessage]);
|
|
687
|
+
setWaitingForApproval(false);
|
|
688
|
+
setPendingApproval(null);
|
|
689
|
+
}
|
|
690
|
+
finally {
|
|
691
|
+
// flushLog();
|
|
692
|
+
reasoningUpdater.flush();
|
|
693
|
+
liveResponseUpdater.cancel();
|
|
694
|
+
setLiveResponse(null);
|
|
695
|
+
setIsProcessing(false);
|
|
696
|
+
}
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
setIsProcessing(true);
|
|
700
|
+
const liveMessageId = Date.now();
|
|
701
|
+
setLiveResponse({
|
|
702
|
+
id: liveMessageId,
|
|
703
|
+
sender: 'bot',
|
|
704
|
+
text: '',
|
|
705
|
+
});
|
|
706
|
+
const liveResponseUpdater = createLiveResponseUpdater(liveMessageId);
|
|
707
|
+
// Track accumulated text so we can flush it before command messages
|
|
708
|
+
let accumulatedText = '';
|
|
709
|
+
let accumulatedReasoningText = '';
|
|
710
|
+
let flushedReasoningLength = 0; // Track how much reasoning has been flushed
|
|
711
|
+
let textWasFlushed = false;
|
|
712
|
+
let currentReasoningMessageId = null; // Track current reasoning message ID
|
|
713
|
+
const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
|
|
714
|
+
setMessages(prev => {
|
|
715
|
+
if (currentReasoningMessageId !== null) {
|
|
716
|
+
const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
|
|
717
|
+
if (index === -1)
|
|
718
|
+
return prev;
|
|
719
|
+
const current = prev[index];
|
|
720
|
+
if (current.sender !== 'reasoning') {
|
|
721
|
+
return prev;
|
|
722
|
+
}
|
|
723
|
+
const next = prev.slice();
|
|
724
|
+
next[index] = { ...current, text: newReasoningText };
|
|
725
|
+
return trimMessages(next);
|
|
726
|
+
}
|
|
727
|
+
const newId = Date.now();
|
|
728
|
+
currentReasoningMessageId = newId;
|
|
729
|
+
return trimMessages([
|
|
730
|
+
...prev,
|
|
731
|
+
{
|
|
732
|
+
id: newId,
|
|
733
|
+
sender: 'reasoning',
|
|
734
|
+
text: newReasoningText,
|
|
735
|
+
},
|
|
736
|
+
]);
|
|
737
|
+
});
|
|
738
|
+
}, REASONING_RESPONSE_THROTTLE_MS);
|
|
739
|
+
// Create event logger with deduplication for this approval decision
|
|
740
|
+
// const {logDeduplicated, flush: flushLog} = createEventLogger();
|
|
741
|
+
const applyConversationEvent = (event) => {
|
|
742
|
+
switch (event.type) {
|
|
743
|
+
case 'text_delta': {
|
|
744
|
+
// logDeduplicated('text_delta');
|
|
745
|
+
accumulatedText += event.delta;
|
|
746
|
+
liveResponseUpdater.push(accumulatedText);
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
case 'reasoning_delta': {
|
|
750
|
+
// logDeduplicated('reasoning_delta');
|
|
751
|
+
const fullReasoningText = event.fullText ?? '';
|
|
752
|
+
const newReasoningText = fullReasoningText.slice(flushedReasoningLength);
|
|
753
|
+
accumulatedReasoningText = newReasoningText;
|
|
754
|
+
if (!newReasoningText.trim())
|
|
755
|
+
return;
|
|
756
|
+
reasoningUpdater.push(newReasoningText);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
case 'tool_started': {
|
|
760
|
+
// Flush reasoning state
|
|
761
|
+
if (accumulatedReasoningText.trim()) {
|
|
762
|
+
reasoningUpdater.flush();
|
|
763
|
+
flushedReasoningLength += accumulatedReasoningText.length;
|
|
764
|
+
accumulatedReasoningText = '';
|
|
765
|
+
currentReasoningMessageId = null;
|
|
766
|
+
}
|
|
767
|
+
// Flush any accumulated text before showing the tool call
|
|
768
|
+
if (accumulatedText.trim()) {
|
|
769
|
+
const textMessage = {
|
|
770
|
+
id: Date.now() + 1,
|
|
771
|
+
sender: 'bot',
|
|
772
|
+
text: accumulatedText,
|
|
773
|
+
};
|
|
774
|
+
appendMessages([textMessage]);
|
|
775
|
+
accumulatedText = '';
|
|
776
|
+
textWasFlushed = true;
|
|
777
|
+
liveResponseUpdater.cancel();
|
|
778
|
+
setLiveResponse(null);
|
|
779
|
+
}
|
|
780
|
+
const { toolCallId, toolName, arguments: rawArgs } = event;
|
|
781
|
+
// tool_started.arguments may be either an object or a JSON string
|
|
782
|
+
// depending on provider. Normalize so we can render params.
|
|
783
|
+
const args = (() => {
|
|
784
|
+
if (typeof rawArgs !== 'string') {
|
|
785
|
+
return rawArgs;
|
|
786
|
+
}
|
|
787
|
+
const trimmed = rawArgs.trim();
|
|
788
|
+
if (!trimmed) {
|
|
789
|
+
return rawArgs;
|
|
790
|
+
}
|
|
791
|
+
try {
|
|
792
|
+
return JSON.parse(trimmed);
|
|
793
|
+
}
|
|
794
|
+
catch {
|
|
795
|
+
return rawArgs;
|
|
796
|
+
}
|
|
797
|
+
})();
|
|
798
|
+
const command = (() => {
|
|
799
|
+
if (toolName === 'shell') {
|
|
800
|
+
const cmd = args?.command ?? args?.commands;
|
|
801
|
+
if (typeof cmd === 'string' && cmd.trim()) {
|
|
802
|
+
return cmd;
|
|
803
|
+
}
|
|
804
|
+
if (Array.isArray(cmd) && cmd.length > 0) {
|
|
805
|
+
return cmd.join('\n');
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
if (toolName === 'grep' && args?.pattern) {
|
|
809
|
+
return `grep "${args.pattern}" ${args.path ?? '.'}`;
|
|
810
|
+
}
|
|
811
|
+
if (toolName === 'search_replace') {
|
|
812
|
+
return `search_replace "${args.search_content ?? ''}" → "${args.replace_content ?? ''}" ${args.path ?? ''}`;
|
|
813
|
+
}
|
|
814
|
+
if (toolName === 'apply_patch') {
|
|
815
|
+
return `apply_patch ${args?.type ?? 'unknown'} ${args?.path ?? ''}`;
|
|
816
|
+
}
|
|
817
|
+
if (toolName === 'ask_mentor') {
|
|
818
|
+
return `ask_mentor: ${args?.question ?? ''}`;
|
|
819
|
+
}
|
|
820
|
+
return `${toolName ?? 'unknown_tool'}`;
|
|
821
|
+
})();
|
|
822
|
+
const pendingMessage = {
|
|
823
|
+
id: toolCallId ?? String(Date.now()),
|
|
824
|
+
sender: 'command',
|
|
825
|
+
status: 'running',
|
|
826
|
+
command,
|
|
827
|
+
output: '',
|
|
828
|
+
callId: toolCallId,
|
|
829
|
+
toolName,
|
|
830
|
+
toolArgs: args,
|
|
831
|
+
};
|
|
832
|
+
appendMessages([pendingMessage]);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
case 'command_message': {
|
|
836
|
+
// logDeduplicated('command_message');
|
|
837
|
+
const cmdMsg = event.message;
|
|
838
|
+
const annotated = annotateCommandMessage(cmdMsg);
|
|
839
|
+
const messagesToAdd = [];
|
|
840
|
+
if (accumulatedReasoningText.trim()) {
|
|
841
|
+
reasoningUpdater.flush();
|
|
842
|
+
flushedReasoningLength +=
|
|
843
|
+
accumulatedReasoningText.length;
|
|
844
|
+
accumulatedReasoningText = '';
|
|
845
|
+
currentReasoningMessageId = null;
|
|
846
|
+
}
|
|
847
|
+
if (accumulatedText.trim()) {
|
|
848
|
+
const textMessage = {
|
|
849
|
+
id: Date.now() + 1,
|
|
850
|
+
sender: 'bot',
|
|
851
|
+
text: accumulatedText,
|
|
852
|
+
};
|
|
853
|
+
messagesToAdd.push(textMessage);
|
|
854
|
+
accumulatedText = '';
|
|
855
|
+
textWasFlushed = true;
|
|
856
|
+
}
|
|
857
|
+
if (messagesToAdd.length > 0) {
|
|
858
|
+
appendMessages(messagesToAdd);
|
|
859
|
+
liveResponseUpdater.cancel();
|
|
860
|
+
setLiveResponse(null);
|
|
861
|
+
}
|
|
862
|
+
// Replace pending message with completed one, or add new if not found
|
|
863
|
+
setMessages(prev => {
|
|
864
|
+
const pendingIndex = annotated.callId
|
|
865
|
+
? prev.findIndex(msg => msg.sender === 'command' &&
|
|
866
|
+
msg.callId === annotated.callId &&
|
|
867
|
+
msg.status === 'running')
|
|
868
|
+
: -1;
|
|
869
|
+
if (pendingIndex !== -1) {
|
|
870
|
+
const next = [...prev];
|
|
871
|
+
next[pendingIndex] = annotated;
|
|
872
|
+
return trimMessages(next);
|
|
873
|
+
}
|
|
874
|
+
return trimMessages([...prev, annotated]);
|
|
875
|
+
});
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
case 'retry': {
|
|
879
|
+
const systemMessage = {
|
|
880
|
+
id: Date.now(),
|
|
881
|
+
sender: 'system',
|
|
882
|
+
text: `Tool hallucination detected (${event.toolName}). Retrying... (Attempt ${event.attempt}/${event.maxRetries})`,
|
|
883
|
+
};
|
|
884
|
+
setMessages(prev => [...prev, systemMessage]);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
default:
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
try {
|
|
892
|
+
const result = await conversationService.handleApprovalDecision(answer, rejectionReason, {
|
|
893
|
+
onEvent: applyConversationEvent,
|
|
894
|
+
});
|
|
895
|
+
applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
|
|
896
|
+
}
|
|
897
|
+
catch (error) {
|
|
898
|
+
loggingService.error('Error in handleApprovalDecision', {
|
|
899
|
+
error: error instanceof Error ? error.message : String(error),
|
|
900
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
901
|
+
});
|
|
902
|
+
// Don't show error messages for user-initiated aborts
|
|
903
|
+
if (isAbortLikeError(error)) {
|
|
904
|
+
loggingService.debug('Suppressing abort error in handleApprovalDecision');
|
|
905
|
+
// The finally block will handle cleanup
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
909
|
+
const botErrorMessage = {
|
|
910
|
+
id: Date.now(),
|
|
911
|
+
sender: 'bot',
|
|
912
|
+
text: `Error: ${errorMessage}`,
|
|
913
|
+
};
|
|
914
|
+
appendMessages([botErrorMessage]);
|
|
915
|
+
// Reset approval state on error to allow user to continue
|
|
916
|
+
setWaitingForApproval(false);
|
|
917
|
+
setPendingApproval(null);
|
|
918
|
+
}
|
|
919
|
+
finally {
|
|
920
|
+
loggingService.debug('handleApprovalDecision finally block - resetting state');
|
|
921
|
+
// flushLog();
|
|
922
|
+
reasoningUpdater.flush();
|
|
923
|
+
liveResponseUpdater.cancel();
|
|
924
|
+
setLiveResponse(null);
|
|
925
|
+
setIsProcessing(false);
|
|
926
|
+
// Don't reset approval state here - if the result is another approval_required,
|
|
927
|
+
// applyServiceResult will set waitingForApproval=true, but this finally block
|
|
928
|
+
// would immediately clear it, causing the input box to reappear
|
|
929
|
+
}
|
|
930
|
+
}, [
|
|
931
|
+
applyServiceResult,
|
|
932
|
+
conversationService,
|
|
933
|
+
waitingForApproval,
|
|
934
|
+
pendingApproval,
|
|
935
|
+
appendMessages,
|
|
936
|
+
trimMessages,
|
|
937
|
+
loggingService,
|
|
938
|
+
createLiveResponseUpdater,
|
|
939
|
+
]);
|
|
940
|
+
const clearConversation = useCallback(() => {
|
|
941
|
+
conversationService.reset();
|
|
942
|
+
setMessages([]);
|
|
943
|
+
setWaitingForApproval(false);
|
|
944
|
+
setPendingApproval(null);
|
|
945
|
+
approvedContextRef.current = null;
|
|
946
|
+
setIsProcessing(false);
|
|
947
|
+
setLiveResponse(null);
|
|
948
|
+
}, [conversationService]);
|
|
949
|
+
const stopProcessing = useCallback(() => {
|
|
950
|
+
conversationService.abort();
|
|
951
|
+
setWaitingForApproval(false);
|
|
952
|
+
setWaitingForRejectionReason(false);
|
|
953
|
+
setPendingApproval(null);
|
|
954
|
+
approvedContextRef.current = null;
|
|
955
|
+
setIsProcessing(false);
|
|
956
|
+
setLiveResponse(null);
|
|
957
|
+
}, [conversationService]);
|
|
958
|
+
const setModel = useCallback((model) => {
|
|
959
|
+
conversationService.setModel(model);
|
|
960
|
+
}, [conversationService]);
|
|
961
|
+
const setReasoningEffort = useCallback((effort) => {
|
|
962
|
+
conversationService.setReasoningEffort?.(effort);
|
|
963
|
+
}, [conversationService]);
|
|
964
|
+
const setTemperature = useCallback((temperature) => {
|
|
965
|
+
conversationService.setTemperature?.(temperature);
|
|
966
|
+
}, [conversationService]);
|
|
967
|
+
const addSystemMessage = useCallback((text) => {
|
|
968
|
+
appendMessages([
|
|
969
|
+
{
|
|
970
|
+
id: Date.now(),
|
|
971
|
+
sender: 'system',
|
|
972
|
+
text,
|
|
973
|
+
},
|
|
974
|
+
]);
|
|
975
|
+
}, [appendMessages]);
|
|
976
|
+
const addShellMessage = useCallback((command, output, exitCode, timedOut) => {
|
|
977
|
+
const success = !timedOut && exitCode === 0;
|
|
978
|
+
const failureReason = timedOut
|
|
979
|
+
? 'timeout'
|
|
980
|
+
: exitCode == null
|
|
981
|
+
? 'error'
|
|
982
|
+
: exitCode !== 0
|
|
983
|
+
? `exit ${exitCode}`
|
|
984
|
+
: undefined;
|
|
985
|
+
appendMessages([
|
|
986
|
+
{
|
|
987
|
+
id: String(Date.now()),
|
|
988
|
+
sender: 'command',
|
|
989
|
+
status: success ? 'completed' : 'failed',
|
|
990
|
+
command,
|
|
991
|
+
output,
|
|
992
|
+
success,
|
|
993
|
+
failureReason,
|
|
994
|
+
toolName: 'shell',
|
|
995
|
+
},
|
|
996
|
+
]);
|
|
997
|
+
}, [appendMessages]);
|
|
998
|
+
return {
|
|
999
|
+
messages,
|
|
1000
|
+
liveResponse,
|
|
1001
|
+
pendingApproval,
|
|
1002
|
+
waitingForApproval,
|
|
1003
|
+
waitingForRejectionReason,
|
|
1004
|
+
setWaitingForRejectionReason,
|
|
1005
|
+
isProcessing,
|
|
1006
|
+
sendUserMessage,
|
|
1007
|
+
handleApprovalDecision,
|
|
1008
|
+
clearConversation,
|
|
1009
|
+
stopProcessing,
|
|
1010
|
+
setModel,
|
|
1011
|
+
setReasoningEffort,
|
|
1012
|
+
setTemperature,
|
|
1013
|
+
addSystemMessage,
|
|
1014
|
+
addShellMessage,
|
|
1015
|
+
};
|
|
1016
|
+
};
|
|
1017
|
+
//# sourceMappingURL=use-conversation.js.map
|