@qduc/term2 0.1.3 → 0.1.5
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/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +5 -0
- package/dist/agent.js.map +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +2 -2
- package/dist/app.js.map +1 -1
- 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/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/components/BottomArea.d.ts +2 -0
- package/dist/components/BottomArea.d.ts.map +1 -1
- package/dist/components/BottomArea.js +2 -2
- package/dist/components/BottomArea.js.map +1 -1
- package/dist/components/ChatMessage.js +1 -1
- package/dist/components/ChatMessage.js.map +1 -1
- package/dist/components/InputBox.js +1 -1
- package/dist/components/InputBox.js.map +1 -1
- package/dist/components/MarkdownRenderer.js +1 -1
- package/dist/components/MarkdownRenderer.js.map +1 -1
- 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/ModelSelectionMenu.d.ts.map +1 -1
- package/dist/components/ModelSelectionMenu.error-tabs.test.d.ts +2 -0
- package/dist/components/ModelSelectionMenu.error-tabs.test.d.ts.map +1 -0
- package/dist/components/ModelSelectionMenu.error-tabs.test.js +18 -0
- package/dist/components/ModelSelectionMenu.error-tabs.test.js.map +1 -0
- package/dist/components/ModelSelectionMenu.js +24 -15
- package/dist/components/ModelSelectionMenu.js.map +1 -1
- package/dist/components/SettingsSelectionMenu.js +1 -1
- package/dist/components/SettingsSelectionMenu.js.map +1 -1
- package/dist/components/SlashCommandMenu.js +2 -2
- package/dist/components/SlashCommandMenu.js.map +1 -1
- package/dist/components/StatusBar.d.ts +2 -0
- package/dist/components/StatusBar.d.ts.map +1 -1
- package/dist/components/StatusBar.js +44 -39
- package/dist/components/StatusBar.js.map +1 -1
- package/dist/components/StatusBar.test.d.ts +2 -0
- package/dist/components/StatusBar.test.d.ts.map +1 -0
- package/dist/components/StatusBar.test.js +19 -0
- package/dist/components/StatusBar.test.js.map +1 -0
- package/dist/components/TextInput.d.ts.map +1 -1
- package/dist/components/TextInput.js +1 -2
- package/dist/components/TextInput.js.map +1 -1
- package/dist/components/TextInput.test.js.map +1 -1
- 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 +2 -0
- package/dist/hooks/use-conversation.d.ts.map +1 -1
- package/dist/hooks/use-conversation.js +79 -493
- package/dist/hooks/use-conversation.js.map +1 -1
- package/dist/hooks/use-model-selection.d.ts.map +1 -1
- package/dist/hooks/use-model-selection.js +1 -25
- package/dist/hooks/use-model-selection.js.map +1 -1
- 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.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-settings-completion.d.ts.map +1 -1
- package/dist/hooks/use-settings-completion.js +2 -0
- package/dist/hooks/use-settings-completion.js.map +1 -1
- package/dist/hooks/use-settings-value-completion.d.ts.map +1 -1
- package/dist/hooks/use-settings-value-completion.js +1 -0
- package/dist/hooks/use-settings-value-completion.js.map +1 -1
- package/dist/lib/editor-impl.test.d.ts +2 -0
- package/dist/lib/editor-impl.test.d.ts.map +1 -0
- package/dist/lib/editor-impl.test.js +188 -0
- package/dist/lib/editor-impl.test.js.map +1 -0
- package/dist/lib/openai-agent-client.d.ts.map +1 -1
- package/dist/lib/openai-agent-client.js +12 -4
- package/dist/lib/openai-agent-client.js.map +1 -1
- package/dist/lib/openai-agent-client.public-methods.test.d.ts +2 -0
- package/dist/lib/openai-agent-client.public-methods.test.d.ts.map +1 -0
- package/dist/lib/openai-agent-client.public-methods.test.js +188 -0
- package/dist/lib/openai-agent-client.public-methods.test.js.map +1 -0
- package/dist/prompts/default.md.bak +77 -0
- package/dist/prompts/simple-mentor.md +0 -8
- package/dist/providers/github-copilot/converters.d.ts +45 -0
- package/dist/providers/github-copilot/converters.d.ts.map +1 -0
- package/dist/providers/github-copilot/converters.js +118 -0
- package/dist/providers/github-copilot/converters.js.map +1 -0
- package/dist/providers/github-copilot/converters.test.d.ts +2 -0
- package/dist/providers/github-copilot/converters.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/converters.test.js +162 -0
- package/dist/providers/github-copilot/converters.test.js.map +1 -0
- package/dist/providers/github-copilot/github-copilot.provider.d.ts +2 -0
- package/dist/providers/github-copilot/github-copilot.provider.d.ts.map +1 -0
- package/dist/providers/github-copilot/github-copilot.provider.js +75 -0
- package/dist/providers/github-copilot/github-copilot.provider.js.map +1 -0
- package/dist/providers/github-copilot/github-copilot.provider.test.d.ts +2 -0
- package/dist/providers/github-copilot/github-copilot.provider.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/github-copilot.provider.test.js +26 -0
- package/dist/providers/github-copilot/github-copilot.provider.test.js.map +1 -0
- package/dist/providers/github-copilot/index.d.ts +4 -0
- package/dist/providers/github-copilot/index.d.ts.map +1 -0
- package/dist/providers/github-copilot/index.js +4 -0
- package/dist/providers/github-copilot/index.js.map +1 -0
- package/dist/providers/github-copilot/model-direct.d.ts +34 -0
- package/dist/providers/github-copilot/model-direct.d.ts.map +1 -0
- package/dist/providers/github-copilot/model-direct.js +443 -0
- package/dist/providers/github-copilot/model-direct.js.map +1 -0
- package/dist/providers/github-copilot/model.d.ts +24 -0
- package/dist/providers/github-copilot/model.d.ts.map +1 -0
- package/dist/providers/github-copilot/model.delta.test.d.ts +2 -0
- package/dist/providers/github-copilot/model.delta.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/model.delta.test.js +15 -0
- package/dist/providers/github-copilot/model.delta.test.js.map +1 -0
- package/dist/providers/github-copilot/model.js +581 -0
- package/dist/providers/github-copilot/model.js.map +1 -0
- package/dist/providers/github-copilot/provider.d.ts +20 -0
- package/dist/providers/github-copilot/provider.d.ts.map +1 -0
- package/dist/providers/github-copilot/provider.js +30 -0
- package/dist/providers/github-copilot/provider.js.map +1 -0
- package/dist/providers/github-copilot/provider.test.d.ts +2 -0
- package/dist/providers/github-copilot/provider.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/provider.test.js +52 -0
- package/dist/providers/github-copilot/provider.test.js.map +1 -0
- package/dist/providers/github-copilot/utils.d.ts +20 -0
- package/dist/providers/github-copilot/utils.d.ts.map +1 -0
- package/dist/providers/github-copilot/utils.js +142 -0
- package/dist/providers/github-copilot/utils.js.map +1 -0
- package/dist/providers/github-copilot/utils.test.d.ts +2 -0
- package/dist/providers/github-copilot/utils.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/utils.test.js +21 -0
- package/dist/providers/github-copilot/utils.test.js.map +1 -0
- package/dist/providers/openai-compatible/reasoning-content.test.js.map +1 -1
- package/dist/providers/openrouter/converters.d.ts.map +1 -1
- package/dist/providers/openrouter/converters.js +64 -46
- package/dist/providers/openrouter/converters.js.map +1 -1
- package/dist/providers/openrouter/converters.test.js +13 -12
- package/dist/providers/openrouter/converters.test.js.map +1 -1
- package/dist/providers/openrouter/merge-messages.test.d.ts +2 -0
- package/dist/providers/openrouter/merge-messages.test.d.ts.map +1 -0
- package/dist/providers/openrouter/merge-messages.test.js +83 -0
- package/dist/providers/openrouter/merge-messages.test.js.map +1 -0
- package/dist/providers/openrouter/reasoning-content.test.js.map +1 -1
- 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.test.js.map +1 -1
- package/dist/reproduce_issue.test.d.ts +2 -0
- package/dist/reproduce_issue.test.d.ts.map +1 -0
- package/dist/reproduce_issue.test.js +31 -0
- package/dist/reproduce_issue.test.js.map +1 -0
- package/dist/services/conversation-events.d.ts +3 -0
- package/dist/services/conversation-events.d.ts.map +1 -1
- package/dist/services/conversation-service.test.js +183 -149
- package/dist/services/conversation-session.d.ts +2 -0
- package/dist/services/conversation-session.d.ts.map +1 -1
- package/dist/services/conversation-session.js +151 -28
- package/dist/services/conversation-session.js.map +1 -1
- package/dist/services/conversation-session.usage.test.d.ts +2 -0
- package/dist/services/conversation-session.usage.test.d.ts.map +1 -0
- package/dist/services/conversation-session.usage.test.js +59 -0
- package/dist/services/conversation-session.usage.test.js.map +1 -0
- package/dist/services/settings-service.d.ts +9 -3
- package/dist/services/settings-service.d.ts.map +1 -1
- package/dist/services/settings-service.js +14 -0
- package/dist/services/settings-service.js.map +1 -1
- 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/edit-healing.d.ts +24 -0
- package/dist/tools/edit-healing.d.ts.map +1 -0
- package/dist/tools/edit-healing.js +230 -0
- package/dist/tools/edit-healing.js.map +1 -0
- package/dist/tools/edit-healing.test.d.ts +2 -0
- package/dist/tools/edit-healing.test.d.ts.map +1 -0
- package/dist/tools/edit-healing.test.js +34 -0
- package/dist/tools/edit-healing.test.js.map +1 -0
- package/dist/tools/find-files.d.ts +1 -0
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js +92 -8
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/find-files.test.js +45 -0
- package/dist/tools/find-files.test.js.map +1 -1
- package/dist/tools/search-replace.d.ts +2 -0
- package/dist/tools/search-replace.d.ts.map +1 -1
- package/dist/tools/search-replace.js +207 -23
- package/dist/tools/search-replace.js.map +1 -1
- package/dist/tools/search-replace.test.js +223 -2
- package/dist/tools/search-replace.test.js.map +1 -1
- package/dist/tools/web-fetch.d.ts +20 -0
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +87 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-fetch.test.d.ts +2 -0
- package/dist/tools/web-fetch.test.d.ts.map +1 -0
- package/dist/tools/web-fetch.test.js +94 -0
- package/dist/tools/web-fetch.test.js.map +1 -0
- package/dist/utils/command-safety/index.d.ts +2 -2
- package/dist/utils/command-safety/index.d.ts.map +1 -1
- package/dist/utils/command-safety/index.js +7 -6
- package/dist/utils/command-safety/index.js.map +1 -1
- package/dist/utils/command-safety.d.ts.map +1 -1
- package/dist/utils/command-safety.find.test.js +19 -21
- package/dist/utils/command-safety.find.test.js.map +1 -1
- package/dist/utils/command-safety.js +364 -10
- package/dist/utils/command-safety.js.map +1 -1
- package/dist/utils/conversation-event-handler.d.ts +63 -0
- package/dist/utils/conversation-event-handler.d.ts.map +1 -0
- package/dist/utils/conversation-event-handler.js +132 -0
- package/dist/utils/conversation-event-handler.js.map +1 -0
- package/dist/utils/conversation-event-handler.test.d.ts +2 -0
- package/dist/utils/conversation-event-handler.test.d.ts.map +1 -0
- package/dist/utils/conversation-event-handler.test.js +281 -0
- package/dist/utils/conversation-event-handler.test.js.map +1 -0
- package/dist/utils/conversation-utils.d.ts +41 -0
- package/dist/utils/conversation-utils.d.ts.map +1 -0
- package/dist/utils/conversation-utils.js +109 -0
- package/dist/utils/conversation-utils.js.map +1 -0
- package/dist/utils/conversation-utils.test.d.ts +2 -0
- package/dist/utils/conversation-utils.test.d.ts.map +1 -0
- package/dist/utils/conversation-utils.test.js +190 -0
- package/dist/utils/conversation-utils.test.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 +51 -58
- package/dist/utils/ink-render-options.d.ts +9 -0
- package/dist/utils/ink-render-options.d.ts.map +1 -0
- package/dist/utils/ink-render-options.js +8 -0
- package/dist/utils/ink-render-options.js.map +1 -0
- package/dist/utils/message-utils.d.ts +17 -0
- package/dist/utils/message-utils.d.ts.map +1 -0
- package/dist/utils/message-utils.js +52 -0
- package/dist/utils/message-utils.js.map +1 -0
- package/dist/utils/message-utils.test.d.ts +2 -0
- package/dist/utils/message-utils.test.d.ts.map +1 -0
- package/dist/utils/message-utils.test.js +48 -0
- package/dist/utils/message-utils.test.js.map +1 -0
- package/dist/utils/token-usage.d.ts +16 -0
- package/dist/utils/token-usage.d.ts.map +1 -0
- package/dist/utils/token-usage.js +110 -0
- package/dist/utils/token-usage.js.map +1 -0
- package/dist/utils/token-usage.test.d.ts +2 -0
- package/dist/utils/token-usage.test.d.ts.map +1 -0
- package/dist/utils/token-usage.test.js +38 -0
- package/dist/utils/token-usage.test.js.map +1 -0
- package/dist/utils/trim-tool-output.d.ts +2 -0
- package/dist/utils/trim-tool-output.d.ts.map +1 -0
- package/dist/utils/trim-tool-output.js +52 -0
- package/dist/utils/trim-tool-output.js.map +1 -0
- package/package.json +10 -3
- package/readme.md +22 -1
- package/dist/agent.lite-mode.test.d.ts +0 -2
- package/dist/agent.lite-mode.test.d.ts.map +0 -1
- package/dist/agent.lite-mode.test.js +0 -39
- package/dist/agent.lite-mode.test.js.map +0 -1
- package/dist/debug_ask_mentor.d.ts +0 -2
- package/dist/debug_ask_mentor.d.ts.map +0 -1
- package/dist/debug_ask_mentor.js +0 -73
- package/dist/debug_ask_mentor.js.map +0 -1
- package/dist/modes/companion/command-index.d.ts +0 -26
- package/dist/modes/companion/command-index.d.ts.map +0 -1
- package/dist/modes/companion/command-index.js +0 -50
- package/dist/modes/companion/command-index.js.map +0 -1
- package/dist/modes/companion/command-index.test.d.ts +0 -2
- package/dist/modes/companion/command-index.test.d.ts.map +0 -1
- package/dist/modes/companion/command-index.test.js +0 -86
- package/dist/modes/companion/command-index.test.js.map +0 -1
- package/dist/modes/companion/companion-app.d.ts +0 -12
- package/dist/modes/companion/companion-app.d.ts.map +0 -1
- package/dist/modes/companion/companion-app.js +0 -297
- package/dist/modes/companion/companion-app.js.map +0 -1
- package/dist/modes/companion/companion-session.d.ts +0 -63
- package/dist/modes/companion/companion-session.d.ts.map +0 -1
- package/dist/modes/companion/companion-session.js +0 -146
- package/dist/modes/companion/companion-session.js.map +0 -1
- package/dist/modes/companion/companion-session.test.d.ts +0 -2
- package/dist/modes/companion/companion-session.test.d.ts.map +0 -1
- package/dist/modes/companion/companion-session.test.js +0 -28
- package/dist/modes/companion/companion-session.test.js.map +0 -1
- package/dist/modes/companion/components/status-bar.d.ts +0 -13
- package/dist/modes/companion/components/status-bar.d.ts.map +0 -1
- package/dist/modes/companion/components/status-bar.js +0 -26
- package/dist/modes/companion/components/status-bar.js.map +0 -1
- package/dist/modes/companion/context-buffer.d.ts +0 -65
- package/dist/modes/companion/context-buffer.d.ts.map +0 -1
- package/dist/modes/companion/context-buffer.js +0 -156
- package/dist/modes/companion/context-buffer.js.map +0 -1
- package/dist/modes/companion/context-buffer.test.d.ts +0 -2
- package/dist/modes/companion/context-buffer.test.d.ts.map +0 -1
- package/dist/modes/companion/context-buffer.test.js +0 -154
- package/dist/modes/companion/context-buffer.test.js.map +0 -1
- package/dist/modes/companion/event-detector.d.ts +0 -46
- package/dist/modes/companion/event-detector.d.ts.map +0 -1
- package/dist/modes/companion/event-detector.js +0 -169
- package/dist/modes/companion/event-detector.js.map +0 -1
- package/dist/modes/companion/event-detector.test.d.ts +0 -2
- package/dist/modes/companion/event-detector.test.d.ts.map +0 -1
- package/dist/modes/companion/event-detector.test.js +0 -121
- package/dist/modes/companion/event-detector.test.js.map +0 -1
- package/dist/modes/companion/index.d.ts +0 -33
- package/dist/modes/companion/index.d.ts.map +0 -1
- package/dist/modes/companion/index.js +0 -21
- package/dist/modes/companion/index.js.map +0 -1
- package/dist/modes/companion/input-key-mapper.d.ts +0 -3
- package/dist/modes/companion/input-key-mapper.d.ts.map +0 -1
- package/dist/modes/companion/input-key-mapper.js +0 -31
- package/dist/modes/companion/input-key-mapper.js.map +0 -1
- package/dist/modes/companion/input-key-mapper.test.d.ts +0 -2
- package/dist/modes/companion/input-key-mapper.test.d.ts.map +0 -1
- package/dist/modes/companion/input-key-mapper.test.js +0 -26
- package/dist/modes/companion/input-key-mapper.test.js.map +0 -1
- package/dist/modes/companion/input-parser.d.ts +0 -53
- package/dist/modes/companion/input-parser.d.ts.map +0 -1
- package/dist/modes/companion/input-parser.js +0 -114
- package/dist/modes/companion/input-parser.js.map +0 -1
- package/dist/modes/companion/input-parser.test.d.ts +0 -2
- package/dist/modes/companion/input-parser.test.d.ts.map +0 -1
- package/dist/modes/companion/input-parser.test.js +0 -123
- package/dist/modes/companion/input-parser.test.js.map +0 -1
- package/dist/modes/companion/mode-manager.d.ts +0 -41
- package/dist/modes/companion/mode-manager.d.ts.map +0 -1
- package/dist/modes/companion/mode-manager.js +0 -56
- package/dist/modes/companion/mode-manager.js.map +0 -1
- package/dist/modes/companion/mode-manager.test.d.ts +0 -2
- package/dist/modes/companion/mode-manager.test.d.ts.map +0 -1
- package/dist/modes/companion/mode-manager.test.js +0 -65
- package/dist/modes/companion/mode-manager.test.js.map +0 -1
- package/dist/modes/companion/output-classifier.d.ts +0 -15
- package/dist/modes/companion/output-classifier.d.ts.map +0 -1
- package/dist/modes/companion/output-classifier.js +0 -77
- package/dist/modes/companion/output-classifier.js.map +0 -1
- package/dist/modes/companion/output-classifier.test.d.ts +0 -2
- package/dist/modes/companion/output-classifier.test.d.ts.map +0 -1
- package/dist/modes/companion/output-classifier.test.js +0 -133
- package/dist/modes/companion/output-classifier.test.js.map +0 -1
- package/dist/modes/companion/pty-wrapper.d.ts +0 -46
- package/dist/modes/companion/pty-wrapper.d.ts.map +0 -1
- package/dist/modes/companion/pty-wrapper.js +0 -143
- package/dist/modes/companion/pty-wrapper.js.map +0 -1
- package/dist/modes/companion/safety-classifier.d.ts +0 -31
- package/dist/modes/companion/safety-classifier.d.ts.map +0 -1
- package/dist/modes/companion/safety-classifier.js +0 -140
- package/dist/modes/companion/safety-classifier.js.map +0 -1
- package/dist/modes/companion/safety-classifier.test.d.ts +0 -2
- package/dist/modes/companion/safety-classifier.test.d.ts.map +0 -1
- package/dist/modes/companion/safety-classifier.test.js +0 -151
- package/dist/modes/companion/safety-classifier.test.js.map +0 -1
- package/dist/modes/companion/summarizer.d.ts +0 -24
- package/dist/modes/companion/summarizer.d.ts.map +0 -1
- package/dist/modes/companion/summarizer.js +0 -132
- package/dist/modes/companion/summarizer.js.map +0 -1
- package/dist/modes/companion/terminal-history.d.ts +0 -27
- package/dist/modes/companion/terminal-history.d.ts.map +0 -1
- package/dist/modes/companion/terminal-history.js +0 -142
- package/dist/modes/companion/terminal-history.js.map +0 -1
- package/dist/services/check_mock.d.ts +0 -2
- package/dist/services/check_mock.d.ts.map +0 -1
- package/dist/services/check_mock.js +0 -22
- package/dist/services/check_mock.js.map +0 -1
- package/dist/services/conversation-service.test.d.ts +0 -2
- package/dist/services/conversation-service.test.d.ts.map +0 -1
- package/dist/services/conversation-service.test.js.map +0 -1
- package/dist/utils/command-safety.devnull.test.d.ts +0 -2
- package/dist/utils/command-safety.devnull.test.d.ts.map +0 -1
- package/dist/utils/command-safety.devnull.test.js +0 -13
- package/dist/utils/command-safety.devnull.test.js.map +0 -1
- package/dist/utils/extract-command-messages.test.d.ts +0 -2
- package/dist/utils/extract-command-messages.test.d.ts.map +0 -1
- package/dist/utils/extract-command-messages.test.js.map +0 -1
|
@@ -2,6 +2,8 @@ import { useCallback, useRef, useState } from 'react';
|
|
|
2
2
|
import { isAbortLikeError } from '../utils/error-helpers.js';
|
|
3
3
|
import { createStreamingUpdateCoordinator } from '../utils/streaming-updater.js';
|
|
4
4
|
import { appendMessagesCapped } from '../utils/message-buffer.js';
|
|
5
|
+
import { createStreamingState, enhanceApiKeyError, isMaxTurnsError, } from '../utils/conversation-utils.js';
|
|
6
|
+
import { createConversationEventHandler } from '../utils/conversation-event-handler.js';
|
|
5
7
|
const LIVE_RESPONSE_THROTTLE_MS = 150;
|
|
6
8
|
const REASONING_RESPONSE_THROTTLE_MS = 200;
|
|
7
9
|
const MAX_MESSAGE_COUNT = 300;
|
|
@@ -33,6 +35,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
33
35
|
const [pendingApproval, setPendingApproval] = useState(null);
|
|
34
36
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
35
37
|
const [liveResponse, setLiveResponse] = useState(null);
|
|
38
|
+
const [lastUsage, setLastUsage] = useState(null);
|
|
36
39
|
const approvedContextRef = useRef(null);
|
|
37
40
|
const createLiveResponseUpdater = useCallback((liveMessageId) => createStreamingUpdateCoordinator((text) => {
|
|
38
41
|
setLiveResponse(prev => prev && prev.id === liveMessageId
|
|
@@ -161,6 +164,9 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
161
164
|
});
|
|
162
165
|
setWaitingForApproval(false);
|
|
163
166
|
setPendingApproval(null);
|
|
167
|
+
if (result.usage) {
|
|
168
|
+
setLastUsage(result.usage);
|
|
169
|
+
}
|
|
164
170
|
}, [annotateCommandMessage, appendMessages, trimMessages]);
|
|
165
171
|
const sendUserMessage = useCallback(async (value) => {
|
|
166
172
|
if (!value.trim()) {
|
|
@@ -180,16 +186,12 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
180
186
|
text: '',
|
|
181
187
|
});
|
|
182
188
|
const liveResponseUpdater = createLiveResponseUpdater(liveMessageId);
|
|
183
|
-
//
|
|
184
|
-
|
|
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
|
+
// Create streaming state object for this message send
|
|
190
|
+
const streamingState = createStreamingState();
|
|
189
191
|
const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
|
|
190
192
|
setMessages(prev => {
|
|
191
|
-
if (currentReasoningMessageId !== null) {
|
|
192
|
-
const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
|
|
193
|
+
if (streamingState.currentReasoningMessageId !== null) {
|
|
194
|
+
const index = prev.findIndex(msg => msg.id === streamingState.currentReasoningMessageId);
|
|
193
195
|
if (index === -1)
|
|
194
196
|
return prev;
|
|
195
197
|
const current = prev[index];
|
|
@@ -201,7 +203,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
201
203
|
return trimMessages(next);
|
|
202
204
|
}
|
|
203
205
|
const newId = Date.now();
|
|
204
|
-
currentReasoningMessageId = newId;
|
|
206
|
+
streamingState.currentReasoningMessageId = newId;
|
|
205
207
|
return trimMessages([
|
|
206
208
|
...prev,
|
|
207
209
|
{
|
|
@@ -212,175 +214,33 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
212
214
|
]);
|
|
213
215
|
});
|
|
214
216
|
}, REASONING_RESPONSE_THROTTLE_MS);
|
|
215
|
-
// Create event
|
|
216
|
-
|
|
217
|
+
// Create event handler using extracted factory
|
|
218
|
+
const baseEventHandler = createConversationEventHandler({
|
|
219
|
+
liveResponseUpdater,
|
|
220
|
+
reasoningUpdater,
|
|
221
|
+
appendMessages,
|
|
222
|
+
setMessages,
|
|
223
|
+
setLiveResponse,
|
|
224
|
+
trimMessages,
|
|
225
|
+
annotateCommandMessage,
|
|
226
|
+
}, streamingState);
|
|
217
227
|
const applyConversationEvent = (event) => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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;
|
|
228
|
+
if (event.type === 'final') {
|
|
229
|
+
if (event.usage) {
|
|
230
|
+
loggingService.debug('UI received final usage (sendUserMessage)', { usage: event.usage });
|
|
231
|
+
setLastUsage(event.usage);
|
|
365
232
|
}
|
|
366
|
-
|
|
367
|
-
|
|
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;
|
|
233
|
+
else {
|
|
234
|
+
loggingService.debug('UI final event has no usage (sendUserMessage)');
|
|
374
235
|
}
|
|
375
|
-
default:
|
|
376
|
-
return;
|
|
377
236
|
}
|
|
237
|
+
baseEventHandler(event);
|
|
378
238
|
};
|
|
379
239
|
try {
|
|
380
240
|
const result = await conversationService.sendMessage(value, {
|
|
381
241
|
onEvent: applyConversationEvent,
|
|
382
242
|
});
|
|
383
|
-
applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
|
|
243
|
+
applyServiceResult(result, streamingState.accumulatedText, streamingState.accumulatedReasoningText, streamingState.textWasFlushed);
|
|
384
244
|
}
|
|
385
245
|
catch (error) {
|
|
386
246
|
loggingService.error('Error in sendUserMessage', {
|
|
@@ -393,19 +253,9 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
393
253
|
// The finally block will handle cleanup
|
|
394
254
|
return;
|
|
395
255
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
if (errorMessage
|
|
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) {
|
|
256
|
+
const rawErrorMessage = error instanceof Error ? error.message : String(error);
|
|
257
|
+
const errorMessage = enhanceApiKeyError(rawErrorMessage);
|
|
258
|
+
if (isMaxTurnsError(errorMessage)) {
|
|
409
259
|
// Create an approval prompt for max turns continuation
|
|
410
260
|
setPendingApproval({
|
|
411
261
|
agentName: 'System',
|
|
@@ -470,16 +320,12 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
470
320
|
text: '',
|
|
471
321
|
});
|
|
472
322
|
const liveResponseUpdater = createLiveResponseUpdater(liveMessageId);
|
|
473
|
-
//
|
|
474
|
-
|
|
475
|
-
let accumulatedReasoningText = '';
|
|
476
|
-
let flushedReasoningLength = 0;
|
|
477
|
-
let textWasFlushed = false;
|
|
478
|
-
let currentReasoningMessageId = null;
|
|
323
|
+
// Create streaming state object for max turns continuation
|
|
324
|
+
const streamingState = createStreamingState();
|
|
479
325
|
const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
|
|
480
326
|
setMessages(prev => {
|
|
481
|
-
if (currentReasoningMessageId !== null) {
|
|
482
|
-
const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
|
|
327
|
+
if (streamingState.currentReasoningMessageId !== null) {
|
|
328
|
+
const index = prev.findIndex(msg => msg.id === streamingState.currentReasoningMessageId);
|
|
483
329
|
if (index === -1)
|
|
484
330
|
return prev;
|
|
485
331
|
const current = prev[index];
|
|
@@ -494,7 +340,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
494
340
|
return trimMessages(next);
|
|
495
341
|
}
|
|
496
342
|
const newId = Date.now();
|
|
497
|
-
currentReasoningMessageId = newId;
|
|
343
|
+
streamingState.currentReasoningMessageId = newId;
|
|
498
344
|
return trimMessages([
|
|
499
345
|
...prev,
|
|
500
346
|
{
|
|
@@ -505,154 +351,27 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
505
351
|
]);
|
|
506
352
|
});
|
|
507
353
|
}, REASONING_RESPONSE_THROTTLE_MS);
|
|
508
|
-
//
|
|
354
|
+
// Create event handler using extracted factory
|
|
355
|
+
const baseEventHandler = createConversationEventHandler({
|
|
356
|
+
liveResponseUpdater,
|
|
357
|
+
reasoningUpdater,
|
|
358
|
+
appendMessages,
|
|
359
|
+
setMessages,
|
|
360
|
+
setLiveResponse,
|
|
361
|
+
trimMessages,
|
|
362
|
+
annotateCommandMessage,
|
|
363
|
+
}, streamingState);
|
|
509
364
|
const applyConversationEvent = (event) => {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
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;
|
|
365
|
+
if (event.type === 'final') {
|
|
366
|
+
if (event.usage) {
|
|
367
|
+
loggingService.debug('UI received final usage (maxTurnsContinuation)', { usage: event.usage });
|
|
368
|
+
setLastUsage(event.usage);
|
|
643
369
|
}
|
|
644
|
-
|
|
645
|
-
|
|
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;
|
|
370
|
+
else {
|
|
371
|
+
loggingService.debug('UI final event has no usage (maxTurnsContinuation)');
|
|
652
372
|
}
|
|
653
|
-
default:
|
|
654
|
-
return;
|
|
655
373
|
}
|
|
374
|
+
baseEventHandler(event);
|
|
656
375
|
};
|
|
657
376
|
try {
|
|
658
377
|
// Send a continuation message to resume work
|
|
@@ -660,7 +379,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
660
379
|
const result = await conversationService.sendMessage(continuationMessage, {
|
|
661
380
|
onEvent: applyConversationEvent,
|
|
662
381
|
});
|
|
663
|
-
applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
|
|
382
|
+
applyServiceResult(result, streamingState.accumulatedText, streamingState.accumulatedReasoningText, streamingState.textWasFlushed);
|
|
664
383
|
}
|
|
665
384
|
catch (error) {
|
|
666
385
|
loggingService.error('Error in continuation after max turns', {
|
|
@@ -704,16 +423,12 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
704
423
|
text: '',
|
|
705
424
|
});
|
|
706
425
|
const liveResponseUpdater = createLiveResponseUpdater(liveMessageId);
|
|
707
|
-
//
|
|
708
|
-
|
|
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
|
|
426
|
+
// Create streaming state object for this approval decision
|
|
427
|
+
const streamingState = createStreamingState();
|
|
713
428
|
const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
|
|
714
429
|
setMessages(prev => {
|
|
715
|
-
if (currentReasoningMessageId !== null) {
|
|
716
|
-
const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
|
|
430
|
+
if (streamingState.currentReasoningMessageId !== null) {
|
|
431
|
+
const index = prev.findIndex(msg => msg.id === streamingState.currentReasoningMessageId);
|
|
717
432
|
if (index === -1)
|
|
718
433
|
return prev;
|
|
719
434
|
const current = prev[index];
|
|
@@ -725,7 +440,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
725
440
|
return trimMessages(next);
|
|
726
441
|
}
|
|
727
442
|
const newId = Date.now();
|
|
728
|
-
currentReasoningMessageId = newId;
|
|
443
|
+
streamingState.currentReasoningMessageId = newId;
|
|
729
444
|
return trimMessages([
|
|
730
445
|
...prev,
|
|
731
446
|
{
|
|
@@ -736,163 +451,33 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
736
451
|
]);
|
|
737
452
|
});
|
|
738
453
|
}, REASONING_RESPONSE_THROTTLE_MS);
|
|
739
|
-
// Create event
|
|
740
|
-
|
|
454
|
+
// Create event handler using extracted factory
|
|
455
|
+
const baseEventHandler = createConversationEventHandler({
|
|
456
|
+
liveResponseUpdater,
|
|
457
|
+
reasoningUpdater,
|
|
458
|
+
appendMessages,
|
|
459
|
+
setMessages,
|
|
460
|
+
setLiveResponse,
|
|
461
|
+
trimMessages,
|
|
462
|
+
annotateCommandMessage,
|
|
463
|
+
}, streamingState);
|
|
741
464
|
const applyConversationEvent = (event) => {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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;
|
|
465
|
+
if (event.type === 'final') {
|
|
466
|
+
if (event.usage) {
|
|
467
|
+
loggingService.debug('UI received final usage (approvalDecision)', { usage: event.usage });
|
|
468
|
+
setLastUsage(event.usage);
|
|
877
469
|
}
|
|
878
|
-
|
|
879
|
-
|
|
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;
|
|
470
|
+
else {
|
|
471
|
+
loggingService.debug('UI final event has no usage (approvalDecision)');
|
|
886
472
|
}
|
|
887
|
-
default:
|
|
888
|
-
return;
|
|
889
473
|
}
|
|
474
|
+
baseEventHandler(event);
|
|
890
475
|
};
|
|
891
476
|
try {
|
|
892
477
|
const result = await conversationService.handleApprovalDecision(answer, rejectionReason, {
|
|
893
478
|
onEvent: applyConversationEvent,
|
|
894
479
|
});
|
|
895
|
-
applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
|
|
480
|
+
applyServiceResult(result, streamingState.accumulatedText, streamingState.accumulatedReasoningText, streamingState.textWasFlushed);
|
|
896
481
|
}
|
|
897
482
|
catch (error) {
|
|
898
483
|
loggingService.error('Error in handleApprovalDecision', {
|
|
@@ -998,6 +583,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
|
|
|
998
583
|
return {
|
|
999
584
|
messages,
|
|
1000
585
|
liveResponse,
|
|
586
|
+
lastUsage,
|
|
1001
587
|
pendingApproval,
|
|
1002
588
|
waitingForApproval,
|
|
1003
589
|
waitingForRejectionReason,
|