@myrialabs/clopen 0.0.1
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/.env.example +6 -0
- package/.github/workflows/release.yml +60 -0
- package/.github/workflows/test.yml +40 -0
- package/CONTRIBUTING.md +499 -0
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/backend/index.ts +156 -0
- package/backend/lib/chat/helpers.ts +42 -0
- package/backend/lib/chat/index.ts +2 -0
- package/backend/lib/chat/stream-manager.ts +1126 -0
- package/backend/lib/database/README.md +77 -0
- package/backend/lib/database/index.ts +119 -0
- package/backend/lib/database/migrations/001_create_projects_table.ts +31 -0
- package/backend/lib/database/migrations/002_create_chat_sessions_table.ts +33 -0
- package/backend/lib/database/migrations/003_create_messages_table.ts +32 -0
- package/backend/lib/database/migrations/004_create_prompt_templates_table.ts +34 -0
- package/backend/lib/database/migrations/005_create_settings_table.ts +24 -0
- package/backend/lib/database/migrations/006_add_user_to_messages.ts +58 -0
- package/backend/lib/database/migrations/007_create_stream_states_table.ts +41 -0
- package/backend/lib/database/migrations/008_create_message_snapshots_table.ts +62 -0
- package/backend/lib/database/migrations/009_add_delta_snapshot_fields.ts +41 -0
- package/backend/lib/database/migrations/010_add_soft_delete_and_branch_support.ts +70 -0
- package/backend/lib/database/migrations/011_git_like_commit_graph.ts +156 -0
- package/backend/lib/database/migrations/012_add_file_change_statistics.ts +41 -0
- package/backend/lib/database/migrations/013_checkpoint_tree_state.ts +118 -0
- package/backend/lib/database/migrations/014_add_engine_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/015_add_model_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/016_create_user_projects_table.ts +34 -0
- package/backend/lib/database/migrations/017_add_current_session_to_user_projects.ts +32 -0
- package/backend/lib/database/migrations/018_create_claude_accounts_table.ts +24 -0
- package/backend/lib/database/migrations/019_add_claude_account_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/020_add_snapshot_tree_hash.ts +32 -0
- package/backend/lib/database/migrations/021_drop_prompt_templates_table.ts +33 -0
- package/backend/lib/database/migrations/index.ts +154 -0
- package/backend/lib/database/queries/checkpoint-queries.ts +87 -0
- package/backend/lib/database/queries/engine-queries.ts +75 -0
- package/backend/lib/database/queries/index.ts +9 -0
- package/backend/lib/database/queries/message-queries.ts +472 -0
- package/backend/lib/database/queries/project-queries.ts +117 -0
- package/backend/lib/database/queries/session-queries.ts +271 -0
- package/backend/lib/database/queries/settings-queries.ts +34 -0
- package/backend/lib/database/queries/snapshot-queries.ts +326 -0
- package/backend/lib/database/queries/utils-queries.ts +59 -0
- package/backend/lib/database/seeders/index.ts +13 -0
- package/backend/lib/database/seeders/settings_seeder.ts +84 -0
- package/backend/lib/database/utils/connection.ts +174 -0
- package/backend/lib/database/utils/index.ts +4 -0
- package/backend/lib/database/utils/migration-runner.ts +118 -0
- package/backend/lib/database/utils/seeder-runner.ts +121 -0
- package/backend/lib/engine/adapters/claude/environment.ts +164 -0
- package/backend/lib/engine/adapters/claude/error-handler.ts +60 -0
- package/backend/lib/engine/adapters/claude/index.ts +1 -0
- package/backend/lib/engine/adapters/claude/path-utils.ts +38 -0
- package/backend/lib/engine/adapters/claude/stream.ts +177 -0
- package/backend/lib/engine/adapters/opencode/index.ts +2 -0
- package/backend/lib/engine/adapters/opencode/message-converter.ts +862 -0
- package/backend/lib/engine/adapters/opencode/server.ts +104 -0
- package/backend/lib/engine/adapters/opencode/stream.ts +755 -0
- package/backend/lib/engine/index.ts +196 -0
- package/backend/lib/engine/types.ts +58 -0
- package/backend/lib/files/file-operations.ts +478 -0
- package/backend/lib/files/file-reading.ts +308 -0
- package/backend/lib/files/file-watcher.ts +383 -0
- package/backend/lib/files/path-browsing.ts +382 -0
- package/backend/lib/git/git-executor.ts +88 -0
- package/backend/lib/git/git-parser.ts +411 -0
- package/backend/lib/git/git-service.ts +505 -0
- package/backend/lib/mcp/README.md +1144 -0
- package/backend/lib/mcp/config.ts +316 -0
- package/backend/lib/mcp/index.ts +35 -0
- package/backend/lib/mcp/project-context.ts +236 -0
- package/backend/lib/mcp/servers/browser-automation/actions.ts +156 -0
- package/backend/lib/mcp/servers/browser-automation/browser.ts +419 -0
- package/backend/lib/mcp/servers/browser-automation/index.ts +791 -0
- package/backend/lib/mcp/servers/browser-automation/inspection.ts +501 -0
- package/backend/lib/mcp/servers/helper.ts +143 -0
- package/backend/lib/mcp/servers/index.ts +45 -0
- package/backend/lib/mcp/servers/weather/get-temperature.ts +56 -0
- package/backend/lib/mcp/servers/weather/index.ts +31 -0
- package/backend/lib/mcp/stdio-server.ts +103 -0
- package/backend/lib/mcp/types.ts +65 -0
- package/backend/lib/preview/browser/browser-audio-capture.ts +86 -0
- package/backend/lib/preview/browser/browser-console-manager.ts +263 -0
- package/backend/lib/preview/browser/browser-dialog-handler.ts +222 -0
- package/backend/lib/preview/browser/browser-interaction-handler.ts +421 -0
- package/backend/lib/preview/browser/browser-mcp-control.ts +415 -0
- package/backend/lib/preview/browser/browser-native-ui-handler.ts +512 -0
- package/backend/lib/preview/browser/browser-navigation-tracker.ts +104 -0
- package/backend/lib/preview/browser/browser-pool.ts +357 -0
- package/backend/lib/preview/browser/browser-preview-service.ts +882 -0
- package/backend/lib/preview/browser/browser-tab-manager.ts +935 -0
- package/backend/lib/preview/browser/browser-video-capture.ts +695 -0
- package/backend/lib/preview/browser/scripts/audio-stream.ts +292 -0
- package/backend/lib/preview/browser/scripts/cursor-tracking.ts +85 -0
- package/backend/lib/preview/browser/scripts/video-stream.ts +438 -0
- package/backend/lib/preview/browser/types.ts +359 -0
- package/backend/lib/preview/index.ts +24 -0
- package/backend/lib/project/index.ts +2 -0
- package/backend/lib/project/status-manager.ts +182 -0
- package/backend/lib/shared/index.ts +2 -0
- package/backend/lib/shared/port-utils.ts +25 -0
- package/backend/lib/shared/process-manager.ts +281 -0
- package/backend/lib/snapshot/blob-store.ts +227 -0
- package/backend/lib/snapshot/gitignore.ts +307 -0
- package/backend/lib/snapshot/helpers.ts +397 -0
- package/backend/lib/snapshot/snapshot-service.ts +483 -0
- package/backend/lib/terminal/helpers.ts +14 -0
- package/backend/lib/terminal/index.ts +8 -0
- package/backend/lib/terminal/pty-manager.ts +4 -0
- package/backend/lib/terminal/pty-session-manager.ts +387 -0
- package/backend/lib/terminal/shell-utils.ts +313 -0
- package/backend/lib/terminal/stream-manager.ts +293 -0
- package/backend/lib/tunnel/global-tunnel-manager.ts +243 -0
- package/backend/lib/tunnel/project-tunnel-manager.ts +311 -0
- package/backend/lib/user/helpers.ts +87 -0
- package/backend/lib/utils/ws.ts +944 -0
- package/backend/lib/vite-dev.ts +353 -0
- package/backend/middleware/cors.ts +15 -0
- package/backend/middleware/error-handler.ts +49 -0
- package/backend/middleware/logger.ts +9 -0
- package/backend/types/api.ts +24 -0
- package/backend/ws/README.md +1505 -0
- package/backend/ws/chat/background.ts +198 -0
- package/backend/ws/chat/index.ts +21 -0
- package/backend/ws/chat/stream.ts +707 -0
- package/backend/ws/engine/claude/accounts.ts +401 -0
- package/backend/ws/engine/claude/index.ts +13 -0
- package/backend/ws/engine/claude/status.ts +43 -0
- package/backend/ws/engine/index.ts +14 -0
- package/backend/ws/engine/opencode/index.ts +11 -0
- package/backend/ws/engine/opencode/status.ts +30 -0
- package/backend/ws/engine/utils.ts +36 -0
- package/backend/ws/files/index.ts +30 -0
- package/backend/ws/files/read.ts +189 -0
- package/backend/ws/files/search.ts +453 -0
- package/backend/ws/files/watch.ts +124 -0
- package/backend/ws/files/write.ts +143 -0
- package/backend/ws/git/branch.ts +106 -0
- package/backend/ws/git/commit.ts +39 -0
- package/backend/ws/git/conflict.ts +68 -0
- package/backend/ws/git/diff.ts +69 -0
- package/backend/ws/git/index.ts +24 -0
- package/backend/ws/git/log.ts +41 -0
- package/backend/ws/git/remote.ts +214 -0
- package/backend/ws/git/staging.ts +84 -0
- package/backend/ws/git/status.ts +90 -0
- package/backend/ws/index.ts +69 -0
- package/backend/ws/mcp/index.ts +61 -0
- package/backend/ws/messages/crud.ts +74 -0
- package/backend/ws/messages/index.ts +14 -0
- package/backend/ws/preview/browser/cleanup.ts +129 -0
- package/backend/ws/preview/browser/console.ts +114 -0
- package/backend/ws/preview/browser/interact.ts +513 -0
- package/backend/ws/preview/browser/mcp.ts +129 -0
- package/backend/ws/preview/browser/native-ui.ts +235 -0
- package/backend/ws/preview/browser/stats.ts +55 -0
- package/backend/ws/preview/browser/tab-info.ts +126 -0
- package/backend/ws/preview/browser/tab.ts +166 -0
- package/backend/ws/preview/browser/webcodecs.ts +293 -0
- package/backend/ws/preview/index.ts +146 -0
- package/backend/ws/projects/crud.ts +113 -0
- package/backend/ws/projects/index.ts +25 -0
- package/backend/ws/projects/presence.ts +46 -0
- package/backend/ws/projects/status.ts +116 -0
- package/backend/ws/sessions/crud.ts +327 -0
- package/backend/ws/sessions/index.ts +33 -0
- package/backend/ws/settings/crud.ts +112 -0
- package/backend/ws/settings/index.ts +14 -0
- package/backend/ws/snapshot/index.ts +17 -0
- package/backend/ws/snapshot/restore.ts +173 -0
- package/backend/ws/snapshot/timeline.ts +141 -0
- package/backend/ws/system/index.ts +14 -0
- package/backend/ws/system/operations.ts +49 -0
- package/backend/ws/terminal/index.ts +40 -0
- package/backend/ws/terminal/persistence.ts +153 -0
- package/backend/ws/terminal/session.ts +382 -0
- package/backend/ws/terminal/stream.ts +79 -0
- package/backend/ws/tunnel/index.ts +14 -0
- package/backend/ws/tunnel/operations.ts +91 -0
- package/backend/ws/types.ts +20 -0
- package/backend/ws/user/crud.ts +156 -0
- package/backend/ws/user/index.ts +14 -0
- package/bin/clopen.ts +307 -0
- package/bun.lock +1352 -0
- package/frontend/App.svelte +34 -0
- package/frontend/app.css +313 -0
- package/frontend/lib/app-environment.ts +10 -0
- package/frontend/lib/components/chat/ChatInterface.svelte +407 -0
- package/frontend/lib/components/chat/formatters/ErrorMessage.svelte +57 -0
- package/frontend/lib/components/chat/formatters/MessageFormatter.svelte +224 -0
- package/frontend/lib/components/chat/formatters/TextMessage.svelte +395 -0
- package/frontend/lib/components/chat/formatters/Tools.svelte +70 -0
- package/frontend/lib/components/chat/formatters/index.ts +3 -0
- package/frontend/lib/components/chat/input/ChatInput.svelte +421 -0
- package/frontend/lib/components/chat/input/components/ChatInputActions.svelte +78 -0
- package/frontend/lib/components/chat/input/components/DragDropOverlay.svelte +30 -0
- package/frontend/lib/components/chat/input/components/EditModeIndicator.svelte +33 -0
- package/frontend/lib/components/chat/input/components/EngineModelPicker.svelte +619 -0
- package/frontend/lib/components/chat/input/components/FileAttachmentPreview.svelte +48 -0
- package/frontend/lib/components/chat/input/components/LoadingIndicator.svelte +31 -0
- package/frontend/lib/components/chat/input/composables/use-animations.svelte.ts +201 -0
- package/frontend/lib/components/chat/input/composables/use-chat-actions.svelte.ts +148 -0
- package/frontend/lib/components/chat/input/composables/use-file-handling.svelte.ts +216 -0
- package/frontend/lib/components/chat/input/composables/use-input-state.svelte.ts +357 -0
- package/frontend/lib/components/chat/input/composables/use-textarea-resize.svelte.ts +57 -0
- package/frontend/lib/components/chat/message/ChatMessage.svelte +478 -0
- package/frontend/lib/components/chat/message/ChatMessages.svelte +541 -0
- package/frontend/lib/components/chat/message/DateSeparator.svelte +86 -0
- package/frontend/lib/components/chat/message/MessageBubble.svelte +86 -0
- package/frontend/lib/components/chat/message/MessageHeader.svelte +157 -0
- package/frontend/lib/components/chat/modal/DebugModal.svelte +59 -0
- package/frontend/lib/components/chat/modal/TokenUsageModal.svelte +124 -0
- package/frontend/lib/components/chat/shared/index.ts +2 -0
- package/frontend/lib/components/chat/shared/utils.ts +116 -0
- package/frontend/lib/components/chat/tools/BashOutputTool.svelte +35 -0
- package/frontend/lib/components/chat/tools/BashTool.svelte +46 -0
- package/frontend/lib/components/chat/tools/CustomMcpTool.svelte +139 -0
- package/frontend/lib/components/chat/tools/EditTool.svelte +48 -0
- package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +32 -0
- package/frontend/lib/components/chat/tools/GlobTool.svelte +51 -0
- package/frontend/lib/components/chat/tools/GrepTool.svelte +90 -0
- package/frontend/lib/components/chat/tools/KillShellTool.svelte +26 -0
- package/frontend/lib/components/chat/tools/ListMcpResourcesTool.svelte +31 -0
- package/frontend/lib/components/chat/tools/NotebookEditTool.svelte +38 -0
- package/frontend/lib/components/chat/tools/ReadMcpResourceTool.svelte +34 -0
- package/frontend/lib/components/chat/tools/ReadTool.svelte +41 -0
- package/frontend/lib/components/chat/tools/TaskTool.svelte +64 -0
- package/frontend/lib/components/chat/tools/TodoWriteTool.svelte +75 -0
- package/frontend/lib/components/chat/tools/WebFetchTool.svelte +35 -0
- package/frontend/lib/components/chat/tools/WebSearchTool.svelte +84 -0
- package/frontend/lib/components/chat/tools/WriteTool.svelte +33 -0
- package/frontend/lib/components/chat/tools/components/CodeBlock.svelte +79 -0
- package/frontend/lib/components/chat/tools/components/DiffBlock.svelte +408 -0
- package/frontend/lib/components/chat/tools/components/FileHeader.svelte +45 -0
- package/frontend/lib/components/chat/tools/components/InfoLine.svelte +19 -0
- package/frontend/lib/components/chat/tools/components/StatsBadges.svelte +27 -0
- package/frontend/lib/components/chat/tools/components/TerminalCommand.svelte +54 -0
- package/frontend/lib/components/chat/tools/components/index.ts +7 -0
- package/frontend/lib/components/chat/tools/index.ts +26 -0
- package/frontend/lib/components/chat/widgets/FloatingTodoList.svelte +249 -0
- package/frontend/lib/components/chat/widgets/TokenUsage.svelte +78 -0
- package/frontend/lib/components/checkpoint/TimelineModal.svelte +391 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineEdge.svelte +26 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineGraph.svelte +87 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineNode.svelte +108 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineVersionGroup.svelte +59 -0
- package/frontend/lib/components/checkpoint/timeline/animation.ts +168 -0
- package/frontend/lib/components/checkpoint/timeline/config.ts +44 -0
- package/frontend/lib/components/checkpoint/timeline/graph-builder.ts +304 -0
- package/frontend/lib/components/checkpoint/timeline/types.ts +65 -0
- package/frontend/lib/components/checkpoint/timeline/utils.ts +53 -0
- package/frontend/lib/components/common/Alert.svelte +139 -0
- package/frontend/lib/components/common/AvatarBubble.svelte +56 -0
- package/frontend/lib/components/common/Button.svelte +71 -0
- package/frontend/lib/components/common/Card.svelte +102 -0
- package/frontend/lib/components/common/Checkbox.svelte +48 -0
- package/frontend/lib/components/common/Dialog.svelte +249 -0
- package/frontend/lib/components/common/FolderBrowser.svelte +843 -0
- package/frontend/lib/components/common/Icon.svelte +58 -0
- package/frontend/lib/components/common/Input.svelte +72 -0
- package/frontend/lib/components/common/Lightbox.svelte +233 -0
- package/frontend/lib/components/common/LoadingScreen.svelte +52 -0
- package/frontend/lib/components/common/LoadingSpinner.svelte +48 -0
- package/frontend/lib/components/common/Modal.svelte +177 -0
- package/frontend/lib/components/common/ModalProvider.svelte +28 -0
- package/frontend/lib/components/common/ModelSelector.svelte +110 -0
- package/frontend/lib/components/common/MonacoEditor.svelte +569 -0
- package/frontend/lib/components/common/NotificationToast.svelte +113 -0
- package/frontend/lib/components/common/PageTemplate.svelte +76 -0
- package/frontend/lib/components/common/ProjectUserAvatars.svelte +79 -0
- package/frontend/lib/components/common/Select.svelte +98 -0
- package/frontend/lib/components/common/Textarea.svelte +80 -0
- package/frontend/lib/components/common/ThemeToggle.svelte +44 -0
- package/frontend/lib/components/common/lucide-icons.ts +1642 -0
- package/frontend/lib/components/common/material-icons.ts +1082 -0
- package/frontend/lib/components/common/xterm/XTerm.svelte +796 -0
- package/frontend/lib/components/common/xterm/index.ts +16 -0
- package/frontend/lib/components/common/xterm/terminal-config.ts +68 -0
- package/frontend/lib/components/common/xterm/types.ts +31 -0
- package/frontend/lib/components/common/xterm/xterm-service.ts +353 -0
- package/frontend/lib/components/files/FileNode.svelte +384 -0
- package/frontend/lib/components/files/FileTree.svelte +681 -0
- package/frontend/lib/components/files/FileViewer.svelte +728 -0
- package/frontend/lib/components/files/SearchResults.svelte +303 -0
- package/frontend/lib/components/git/BranchManager.svelte +458 -0
- package/frontend/lib/components/git/ChangesSection.svelte +107 -0
- package/frontend/lib/components/git/CommitForm.svelte +76 -0
- package/frontend/lib/components/git/ConflictResolver.svelte +158 -0
- package/frontend/lib/components/git/DiffViewer.svelte +364 -0
- package/frontend/lib/components/git/FileChangeItem.svelte +97 -0
- package/frontend/lib/components/git/GitButton.svelte +33 -0
- package/frontend/lib/components/git/GitLog.svelte +361 -0
- package/frontend/lib/components/git/GitModal.svelte +80 -0
- package/frontend/lib/components/history/HistoryModal.svelte +563 -0
- package/frontend/lib/components/history/HistoryView.svelte +615 -0
- package/frontend/lib/components/index.ts +34 -0
- package/frontend/lib/components/preview/browser/BrowserPreview.svelte +549 -0
- package/frontend/lib/components/preview/browser/components/Canvas.svelte +1058 -0
- package/frontend/lib/components/preview/browser/components/ConsolePanel.svelte +757 -0
- package/frontend/lib/components/preview/browser/components/Container.svelte +450 -0
- package/frontend/lib/components/preview/browser/components/ContextMenu.svelte +236 -0
- package/frontend/lib/components/preview/browser/components/SelectDropdown.svelte +224 -0
- package/frontend/lib/components/preview/browser/components/Toolbar.svelte +339 -0
- package/frontend/lib/components/preview/browser/components/VirtualCursor.svelte +36 -0
- package/frontend/lib/components/preview/browser/core/cleanup.svelte.ts +155 -0
- package/frontend/lib/components/preview/browser/core/coordinator.svelte.ts +837 -0
- package/frontend/lib/components/preview/browser/core/interactions.svelte.ts +113 -0
- package/frontend/lib/components/preview/browser/core/mcp-handlers.svelte.ts +296 -0
- package/frontend/lib/components/preview/browser/core/native-ui-handlers.svelte.ts +391 -0
- package/frontend/lib/components/preview/browser/core/stream-handler.svelte.ts +231 -0
- package/frontend/lib/components/preview/browser/core/tab-manager.svelte.ts +210 -0
- package/frontend/lib/components/preview/browser/core/tab-operations.svelte.ts +239 -0
- package/frontend/lib/components/preview/index.ts +2 -0
- package/frontend/lib/components/settings/SettingsModal.svelte +235 -0
- package/frontend/lib/components/settings/SettingsView.svelte +36 -0
- package/frontend/lib/components/settings/appearance/AppearanceSettings.svelte +51 -0
- package/frontend/lib/components/settings/appearance/LayoutPresetSettings.svelte +160 -0
- package/frontend/lib/components/settings/appearance/LayoutPreview.svelte +76 -0
- package/frontend/lib/components/settings/engines/AIEnginesSettings.svelte +917 -0
- package/frontend/lib/components/settings/general/AdvancedSettings.svelte +187 -0
- package/frontend/lib/components/settings/general/DataManagementSettings.svelte +203 -0
- package/frontend/lib/components/settings/general/GeneralSettings.svelte +10 -0
- package/frontend/lib/components/settings/model/ModelSettings.svelte +357 -0
- package/frontend/lib/components/settings/notifications/NotificationSettings.svelte +205 -0
- package/frontend/lib/components/settings/user/UserSettings.svelte +197 -0
- package/frontend/lib/components/terminal/Terminal.svelte +368 -0
- package/frontend/lib/components/terminal/TerminalTabs.svelte +87 -0
- package/frontend/lib/components/terminal/TerminalView.svelte +55 -0
- package/frontend/lib/components/tunnel/TunnelActive.svelte +142 -0
- package/frontend/lib/components/tunnel/TunnelButton.svelte +54 -0
- package/frontend/lib/components/tunnel/TunnelInactive.svelte +284 -0
- package/frontend/lib/components/tunnel/TunnelModal.svelte +47 -0
- package/frontend/lib/components/tunnel/TunnelQRCode.svelte +49 -0
- package/frontend/lib/components/workspace/DesktopNavigator.svelte +382 -0
- package/frontend/lib/components/workspace/MobileNavigator.svelte +403 -0
- package/frontend/lib/components/workspace/PanelContainer.svelte +100 -0
- package/frontend/lib/components/workspace/PanelHeader.svelte +505 -0
- package/frontend/lib/components/workspace/ViewMenu.svelte +162 -0
- package/frontend/lib/components/workspace/WorkspaceLayout.svelte +169 -0
- package/frontend/lib/components/workspace/layout/DesktopLayout.svelte +15 -0
- package/frontend/lib/components/workspace/layout/MobileLayout.svelte +17 -0
- package/frontend/lib/components/workspace/layout/split-pane/Container.svelte +42 -0
- package/frontend/lib/components/workspace/layout/split-pane/Handle.svelte +85 -0
- package/frontend/lib/components/workspace/layout/split-pane/Layout.svelte +37 -0
- package/frontend/lib/components/workspace/panels/ChatPanel.svelte +274 -0
- package/frontend/lib/components/workspace/panels/FilesPanel.svelte +1261 -0
- package/frontend/lib/components/workspace/panels/GitPanel.svelte +1560 -0
- package/frontend/lib/components/workspace/panels/PreviewPanel.svelte +150 -0
- package/frontend/lib/components/workspace/panels/TerminalPanel.svelte +73 -0
- package/frontend/lib/constants/preview.ts +45 -0
- package/frontend/lib/services/chat/chat.service.ts +704 -0
- package/frontend/lib/services/chat/index.ts +7 -0
- package/frontend/lib/services/notification/global-stream-monitor.ts +86 -0
- package/frontend/lib/services/notification/index.ts +8 -0
- package/frontend/lib/services/notification/push.service.ts +144 -0
- package/frontend/lib/services/notification/sound.service.ts +127 -0
- package/frontend/lib/services/preview/browser/browser-console.service.ts +61 -0
- package/frontend/lib/services/preview/browser/browser-webcodecs.service.ts +1499 -0
- package/frontend/lib/services/preview/browser/mcp-integration.svelte.ts +67 -0
- package/frontend/lib/services/preview/index.ts +23 -0
- package/frontend/lib/services/project/index.ts +8 -0
- package/frontend/lib/services/project/status.service.ts +159 -0
- package/frontend/lib/services/snapshot/snapshot.service.ts +47 -0
- package/frontend/lib/services/terminal/background/index.ts +130 -0
- package/frontend/lib/services/terminal/background/session-restore.ts +274 -0
- package/frontend/lib/services/terminal/background/stream-manager.ts +286 -0
- package/frontend/lib/services/terminal/index.ts +14 -0
- package/frontend/lib/services/terminal/persistence.service.ts +260 -0
- package/frontend/lib/services/terminal/project.service.ts +953 -0
- package/frontend/lib/services/terminal/session.service.ts +364 -0
- package/frontend/lib/services/terminal/terminal.service.ts +369 -0
- package/frontend/lib/stores/core/app.svelte.ts +117 -0
- package/frontend/lib/stores/core/files.svelte.ts +73 -0
- package/frontend/lib/stores/core/presence.svelte.ts +48 -0
- package/frontend/lib/stores/core/projects.svelte.ts +317 -0
- package/frontend/lib/stores/core/sessions.svelte.ts +383 -0
- package/frontend/lib/stores/features/claude-accounts.svelte.ts +58 -0
- package/frontend/lib/stores/features/models.svelte.ts +89 -0
- package/frontend/lib/stores/features/settings.svelte.ts +87 -0
- package/frontend/lib/stores/features/terminal.svelte.ts +701 -0
- package/frontend/lib/stores/features/tunnel.svelte.ts +161 -0
- package/frontend/lib/stores/features/user.svelte.ts +96 -0
- package/frontend/lib/stores/ui/chat-input.svelte.ts +57 -0
- package/frontend/lib/stores/ui/chat-model.svelte.ts +61 -0
- package/frontend/lib/stores/ui/dialog.svelte.ts +59 -0
- package/frontend/lib/stores/ui/edit-mode.svelte.ts +214 -0
- package/frontend/lib/stores/ui/notification.svelte.ts +166 -0
- package/frontend/lib/stores/ui/settings-modal.svelte.ts +88 -0
- package/frontend/lib/stores/ui/theme.svelte.ts +179 -0
- package/frontend/lib/stores/ui/workspace.svelte.ts +754 -0
- package/frontend/lib/types/native-ui.ts +73 -0
- package/frontend/lib/utils/chat/date-separator.ts +39 -0
- package/frontend/lib/utils/chat/message-grouper.ts +219 -0
- package/frontend/lib/utils/chat/message-processor.ts +135 -0
- package/frontend/lib/utils/chat/tool-handler.ts +161 -0
- package/frontend/lib/utils/chat/virtual-scroll.svelte.ts +142 -0
- package/frontend/lib/utils/click-outside.ts +20 -0
- package/frontend/lib/utils/context-manager.ts +257 -0
- package/frontend/lib/utils/file-icon-mappings.ts +769 -0
- package/frontend/lib/utils/folder-icon-mappings.ts +1030 -0
- package/frontend/lib/utils/git-status.ts +68 -0
- package/frontend/lib/utils/platform.ts +113 -0
- package/frontend/lib/utils/port-check.ts +65 -0
- package/frontend/lib/utils/terminalFormatter.ts +207 -0
- package/frontend/lib/utils/theme.ts +6 -0
- package/frontend/lib/utils/tree-visualizer.ts +320 -0
- package/frontend/lib/utils/ws.ts +44 -0
- package/frontend/main.ts +13 -0
- package/index.html +70 -0
- package/package.json +111 -0
- package/scripts/generate-icons.ts +87 -0
- package/scripts/pre-publish-check.sh +142 -0
- package/scripts/setup-hooks.sh +134 -0
- package/scripts/validate-branch-name.sh +47 -0
- package/scripts/validate-commit-msg.sh +42 -0
- package/shared/constants/engines.ts +134 -0
- package/shared/types/database/connection.ts +16 -0
- package/shared/types/database/index.ts +6 -0
- package/shared/types/database/schema.ts +141 -0
- package/shared/types/engine/index.ts +45 -0
- package/shared/types/filesystem/index.ts +22 -0
- package/shared/types/git.ts +171 -0
- package/shared/types/messaging/index.ts +239 -0
- package/shared/types/messaging/tool.ts +526 -0
- package/shared/types/network/api.ts +18 -0
- package/shared/types/network/index.ts +5 -0
- package/shared/types/stores/app.ts +23 -0
- package/shared/types/stores/dialog.ts +21 -0
- package/shared/types/stores/index.ts +3 -0
- package/shared/types/stores/settings.ts +15 -0
- package/shared/types/terminal/index.ts +44 -0
- package/shared/types/ui/components.ts +61 -0
- package/shared/types/ui/icons.ts +23 -0
- package/shared/types/ui/index.ts +22 -0
- package/shared/types/ui/notifications.ts +14 -0
- package/shared/types/ui/theme.ts +12 -0
- package/shared/types/websocket/index.ts +43 -0
- package/shared/types/window.d.ts +13 -0
- package/shared/utils/anonymous-user.ts +168 -0
- package/shared/utils/async.ts +10 -0
- package/shared/utils/diff-calculator.ts +184 -0
- package/shared/utils/file-type-detection.ts +166 -0
- package/shared/utils/logger.ts +158 -0
- package/shared/utils/message-formatter.ts +79 -0
- package/shared/utils/path.ts +47 -0
- package/shared/utils/ws-client.ts +768 -0
- package/shared/utils/ws-server.ts +660 -0
- package/static/audio/notification.ogg +0 -0
- package/static/favicon.svg +8 -0
- package/static/fonts/dm-sans/dm-sans-italic-latin-ext.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-italic-latin.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-normal-latin-ext.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-normal-latin.woff2 +0 -0
- package/static/fonts/dm-sans.css +96 -0
- package/svelte.config.js +20 -0
- package/vite.config.ts +33 -0
|
@@ -0,0 +1,862 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Open Code → Claude SDK Message Converter
|
|
3
|
+
*
|
|
4
|
+
* Converts Open Code SDK messages and parts into Claude SDK format (SDKMessage)
|
|
5
|
+
* with type-safe tool input normalization (camelCase → snake_case).
|
|
6
|
+
*
|
|
7
|
+
* Content blocks are split into separate messages:
|
|
8
|
+
* - Consecutive text blocks → one message
|
|
9
|
+
* - Each tool_use block → its own message
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { SDKMessage, EngineSDKMessage } from '$shared/types/messaging';
|
|
13
|
+
import type { ToolResult } from '$shared/types/messaging/tool';
|
|
14
|
+
import { resolveOpenCodeToolName } from '../../../mcp';
|
|
15
|
+
import type { Message as OCMessage, Part, ToolPart, AssistantMessage } from '@opencode-ai/sdk';
|
|
16
|
+
import type {
|
|
17
|
+
BashInput,
|
|
18
|
+
FileReadInput,
|
|
19
|
+
FileEditInput,
|
|
20
|
+
FileWriteInput,
|
|
21
|
+
GlobInput,
|
|
22
|
+
GrepInput,
|
|
23
|
+
WebFetchInput,
|
|
24
|
+
WebSearchInput,
|
|
25
|
+
AgentInput,
|
|
26
|
+
NotebookEditInput,
|
|
27
|
+
KillShellInput,
|
|
28
|
+
ListMcpResourcesInput,
|
|
29
|
+
ReadMcpResourceInput,
|
|
30
|
+
TaskOutputInput,
|
|
31
|
+
TodoWriteInput,
|
|
32
|
+
ExitPlanModeInput,
|
|
33
|
+
} from '@anthropic-ai/claude-agent-sdk/sdk-tools';
|
|
34
|
+
|
|
35
|
+
// ============================================================
|
|
36
|
+
// Types
|
|
37
|
+
// ============================================================
|
|
38
|
+
|
|
39
|
+
/** Raw tool input from OpenCode SDK (ToolPart.state.input) */
|
|
40
|
+
type OCToolInput = ToolPart['state']['input'];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolve tool input from ToolPart.
|
|
44
|
+
* Pending tools may have empty input ({}) while data is in state.raw.
|
|
45
|
+
* This ensures we always get the actual input even during progressive rendering.
|
|
46
|
+
*/
|
|
47
|
+
export function getToolInput(toolPart: ToolPart): OCToolInput {
|
|
48
|
+
const input = toolPart.state.input;
|
|
49
|
+
// If input already has data, use it directly
|
|
50
|
+
if (Object.keys(input).length > 0) return input;
|
|
51
|
+
|
|
52
|
+
// Pending state has a raw JSON string — try parsing it
|
|
53
|
+
if (toolPart.state.status === 'pending') {
|
|
54
|
+
const raw = toolPart.state.raw;
|
|
55
|
+
if (raw) {
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(raw);
|
|
58
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
59
|
+
return parsed as OCToolInput;
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
// Incomplete JSON during streaming — will get full input in next update
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return input;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** All Claude Code tool input types */
|
|
71
|
+
type ClaudeToolInput =
|
|
72
|
+
| BashInput | FileReadInput | FileEditInput | FileWriteInput
|
|
73
|
+
| GlobInput | GrepInput | WebFetchInput | WebSearchInput
|
|
74
|
+
| AgentInput | NotebookEditInput | KillShellInput
|
|
75
|
+
| ListMcpResourcesInput | ReadMcpResourceInput
|
|
76
|
+
| TaskOutputInput | TodoWriteInput | ExitPlanModeInput;
|
|
77
|
+
|
|
78
|
+
/** Text content block */
|
|
79
|
+
interface TextContentBlock {
|
|
80
|
+
type: 'text';
|
|
81
|
+
text: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Tool use content block with optional result */
|
|
85
|
+
interface ToolUseContentBlock {
|
|
86
|
+
type: 'tool_use';
|
|
87
|
+
id: string;
|
|
88
|
+
name: string;
|
|
89
|
+
input: ClaudeToolInput;
|
|
90
|
+
$result?: ToolResult;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type ContentBlock = TextContentBlock | ToolUseContentBlock;
|
|
94
|
+
|
|
95
|
+
// ============================================================
|
|
96
|
+
// Tool Name Mapping
|
|
97
|
+
// ============================================================
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* OpenCode tool names → Claude Code tool names
|
|
101
|
+
*
|
|
102
|
+
* OpenCode tool names (from Go source):
|
|
103
|
+
* bash, view, edit, write, glob, grep, fetch, ls, patch, diagnostics, sourcegraph
|
|
104
|
+
*/
|
|
105
|
+
const TOOL_NAME_MAP: Record<string, string> = {
|
|
106
|
+
'bash': 'Bash',
|
|
107
|
+
'view': 'Read',
|
|
108
|
+
'read': 'Read',
|
|
109
|
+
'write': 'Write',
|
|
110
|
+
'edit': 'Edit',
|
|
111
|
+
'glob': 'Glob',
|
|
112
|
+
'grep': 'Grep',
|
|
113
|
+
'fetch': 'WebFetch',
|
|
114
|
+
'web_fetch': 'WebFetch',
|
|
115
|
+
'webfetch': 'WebFetch',
|
|
116
|
+
'web_search': 'WebSearch',
|
|
117
|
+
'websearch': 'WebSearch',
|
|
118
|
+
'task': 'Task',
|
|
119
|
+
'todo_write': 'TodoWrite',
|
|
120
|
+
'todowrite': 'TodoWrite',
|
|
121
|
+
'todoread': 'TodoWrite',
|
|
122
|
+
'notebook_edit': 'NotebookEdit',
|
|
123
|
+
'notebookedit': 'NotebookEdit',
|
|
124
|
+
'exit_plan_mode': 'ExitPlanMode',
|
|
125
|
+
'exitplanmode': 'ExitPlanMode',
|
|
126
|
+
'kill_shell': 'KillShell',
|
|
127
|
+
'killshell': 'KillShell',
|
|
128
|
+
'list_mcp_resources': 'ListMcpResources',
|
|
129
|
+
'read_mcp_resource': 'ReadMcpResource',
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/** Map Open Code tool name to Claude Code tool name for UI rendering */
|
|
133
|
+
export function mapToolName(openCodeToolName: string): string {
|
|
134
|
+
// Check if this is a custom MCP tool (resolves via single source in backend/lib/mcp)
|
|
135
|
+
const mcpName = resolveOpenCodeToolName(openCodeToolName);
|
|
136
|
+
if (mcpName) return mcpName;
|
|
137
|
+
|
|
138
|
+
const lower = openCodeToolName.toLowerCase();
|
|
139
|
+
return TOOL_NAME_MAP[lower] || TOOL_NAME_MAP[openCodeToolName] || openCodeToolName;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ============================================================
|
|
143
|
+
// Tool Input Normalizer Helpers
|
|
144
|
+
// ============================================================
|
|
145
|
+
|
|
146
|
+
/** Get string value, checking both snake_case and camelCase keys */
|
|
147
|
+
function str(raw: OCToolInput, snakeCase: string, camelCase: string, fallback = ''): string {
|
|
148
|
+
const val = raw[snakeCase] ?? raw[camelCase];
|
|
149
|
+
return val != null ? String(val) : fallback;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Get optional number from either key variant */
|
|
153
|
+
function optNum(raw: OCToolInput, snakeCase: string, camelCase: string): number | undefined {
|
|
154
|
+
const val = raw[snakeCase] ?? raw[camelCase];
|
|
155
|
+
return val != null ? Number(val) : undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Get optional boolean from either key variant */
|
|
159
|
+
function optBool(raw: OCToolInput, snakeCase: string, camelCase: string): boolean | undefined {
|
|
160
|
+
const val = raw[snakeCase] ?? raw[camelCase];
|
|
161
|
+
return val != null ? Boolean(val) : undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Get optional string from either key variant */
|
|
165
|
+
function optStr(raw: OCToolInput, snakeCase: string, camelCase: string): string | undefined {
|
|
166
|
+
const val = raw[snakeCase] ?? raw[camelCase];
|
|
167
|
+
return val != null ? String(val) : undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Convert camelCase string to snake_case */
|
|
171
|
+
function camelToSnake(s: string): string {
|
|
172
|
+
return s.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ============================================================
|
|
176
|
+
// Per-Tool Input Normalizers
|
|
177
|
+
// ============================================================
|
|
178
|
+
|
|
179
|
+
function normalizeReadInput(raw: OCToolInput): FileReadInput {
|
|
180
|
+
const result: FileReadInput = {
|
|
181
|
+
file_path: str(raw, 'file_path', 'filePath'),
|
|
182
|
+
};
|
|
183
|
+
const offset = optNum(raw, 'offset', 'offset');
|
|
184
|
+
if (offset != null) result.offset = offset;
|
|
185
|
+
const limit = optNum(raw, 'limit', 'limit');
|
|
186
|
+
if (limit != null) result.limit = limit;
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function normalizeEditInput(raw: OCToolInput): FileEditInput {
|
|
191
|
+
const result: FileEditInput = {
|
|
192
|
+
file_path: str(raw, 'file_path', 'filePath'),
|
|
193
|
+
old_string: str(raw, 'old_string', 'oldString'),
|
|
194
|
+
new_string: str(raw, 'new_string', 'newString'),
|
|
195
|
+
};
|
|
196
|
+
const replaceAll = optBool(raw, 'replace_all', 'replaceAll');
|
|
197
|
+
if (replaceAll != null) result.replace_all = replaceAll;
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function normalizeWriteInput(raw: OCToolInput): FileWriteInput {
|
|
202
|
+
return {
|
|
203
|
+
file_path: str(raw, 'file_path', 'filePath'),
|
|
204
|
+
content: str(raw, 'content', 'content'),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function normalizeBashInput(raw: OCToolInput): BashInput {
|
|
209
|
+
const result: BashInput = {
|
|
210
|
+
command: str(raw, 'command', 'command'),
|
|
211
|
+
};
|
|
212
|
+
const timeout = optNum(raw, 'timeout', 'timeout');
|
|
213
|
+
if (timeout != null) result.timeout = timeout;
|
|
214
|
+
const description = optStr(raw, 'description', 'description');
|
|
215
|
+
if (description != null) result.description = description;
|
|
216
|
+
const runInBackground = optBool(raw, 'run_in_background', 'runInBackground');
|
|
217
|
+
if (runInBackground != null) result.run_in_background = runInBackground;
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function normalizeGlobInput(raw: OCToolInput): GlobInput {
|
|
222
|
+
const result: GlobInput = {
|
|
223
|
+
pattern: str(raw, 'pattern', 'pattern'),
|
|
224
|
+
};
|
|
225
|
+
const path = optStr(raw, 'path', 'path');
|
|
226
|
+
if (path != null) result.path = path;
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function normalizeGrepInput(raw: OCToolInput): GrepInput {
|
|
231
|
+
const result: GrepInput = {
|
|
232
|
+
pattern: str(raw, 'pattern', 'pattern'),
|
|
233
|
+
};
|
|
234
|
+
const path = optStr(raw, 'path', 'path');
|
|
235
|
+
if (path != null) result.path = path;
|
|
236
|
+
// OpenCode uses 'include' for file filter, Claude Code uses 'glob'
|
|
237
|
+
const glob = optStr(raw, 'glob', 'glob') ?? optStr(raw, 'include', 'include');
|
|
238
|
+
if (glob != null) result.glob = glob;
|
|
239
|
+
const outputMode = optStr(raw, 'output_mode', 'outputMode') as GrepInput['output_mode'];
|
|
240
|
+
if (outputMode != null) result.output_mode = outputMode;
|
|
241
|
+
const type = optStr(raw, 'type', 'type');
|
|
242
|
+
if (type != null) result.type = type;
|
|
243
|
+
const headLimit = optNum(raw, 'head_limit', 'headLimit');
|
|
244
|
+
if (headLimit != null) result.head_limit = headLimit;
|
|
245
|
+
const offset = optNum(raw, 'offset', 'offset');
|
|
246
|
+
if (offset != null) result.offset = offset;
|
|
247
|
+
const multiline = optBool(raw, 'multiline', 'multiline');
|
|
248
|
+
if (multiline != null) result.multiline = multiline;
|
|
249
|
+
const caseInsensitive = optBool(raw, '-i', '-i');
|
|
250
|
+
if (caseInsensitive != null) result['-i'] = caseInsensitive;
|
|
251
|
+
const beforeContext = optNum(raw, '-B', '-B');
|
|
252
|
+
if (beforeContext != null) result['-B'] = beforeContext;
|
|
253
|
+
const afterContext = optNum(raw, '-A', '-A');
|
|
254
|
+
if (afterContext != null) result['-A'] = afterContext;
|
|
255
|
+
const context = optNum(raw, '-C', '-C');
|
|
256
|
+
if (context != null) result['-C'] = context;
|
|
257
|
+
const lineNumbers = optBool(raw, '-n', '-n');
|
|
258
|
+
if (lineNumbers != null) result['-n'] = lineNumbers;
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function normalizeWebFetchInput(raw: OCToolInput): WebFetchInput {
|
|
263
|
+
return {
|
|
264
|
+
url: str(raw, 'url', 'url'),
|
|
265
|
+
// OpenCode uses 'format', Claude Code uses 'prompt'
|
|
266
|
+
prompt: str(raw, 'prompt', 'prompt') || str(raw, 'format', 'format'),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function normalizeWebSearchInput(raw: OCToolInput): WebSearchInput {
|
|
271
|
+
const result: WebSearchInput = {
|
|
272
|
+
query: str(raw, 'query', 'query'),
|
|
273
|
+
};
|
|
274
|
+
const allowedDomains = raw.allowed_domains ?? raw.allowedDomains;
|
|
275
|
+
if (Array.isArray(allowedDomains) && allowedDomains.length) {
|
|
276
|
+
result.allowed_domains = allowedDomains as string[];
|
|
277
|
+
}
|
|
278
|
+
const blockedDomains = raw.blocked_domains ?? raw.blockedDomains;
|
|
279
|
+
if (Array.isArray(blockedDomains) && blockedDomains.length) {
|
|
280
|
+
result.blocked_domains = blockedDomains as string[];
|
|
281
|
+
}
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function normalizeAgentInput(raw: OCToolInput): AgentInput {
|
|
286
|
+
const result: AgentInput = {
|
|
287
|
+
description: str(raw, 'description', 'description'),
|
|
288
|
+
prompt: str(raw, 'prompt', 'prompt'),
|
|
289
|
+
subagent_type: str(raw, 'subagent_type', 'subagentType'),
|
|
290
|
+
};
|
|
291
|
+
const model = optStr(raw, 'model', 'model') as AgentInput['model'];
|
|
292
|
+
if (model != null) result.model = model;
|
|
293
|
+
const resume = optStr(raw, 'resume', 'resume');
|
|
294
|
+
if (resume != null) result.resume = resume;
|
|
295
|
+
const runInBackground = optBool(raw, 'run_in_background', 'runInBackground');
|
|
296
|
+
if (runInBackground != null) result.run_in_background = runInBackground;
|
|
297
|
+
const maxTurns = optNum(raw, 'max_turns', 'maxTurns');
|
|
298
|
+
if (maxTurns != null) result.max_turns = maxTurns;
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function normalizeNotebookEditInput(raw: OCToolInput): NotebookEditInput {
|
|
303
|
+
const result: NotebookEditInput = {
|
|
304
|
+
notebook_path: str(raw, 'notebook_path', 'notebookPath'),
|
|
305
|
+
new_source: str(raw, 'new_source', 'newSource'),
|
|
306
|
+
};
|
|
307
|
+
const cellId = optStr(raw, 'cell_id', 'cellId');
|
|
308
|
+
if (cellId != null) result.cell_id = cellId;
|
|
309
|
+
const cellType = optStr(raw, 'cell_type', 'cellType') as NotebookEditInput['cell_type'];
|
|
310
|
+
if (cellType != null) result.cell_type = cellType;
|
|
311
|
+
const editMode = optStr(raw, 'edit_mode', 'editMode') as NotebookEditInput['edit_mode'];
|
|
312
|
+
if (editMode != null) result.edit_mode = editMode;
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function normalizeKillShellInput(raw: OCToolInput): KillShellInput {
|
|
317
|
+
return {
|
|
318
|
+
shell_id: str(raw, 'shell_id', 'shellId'),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function normalizeListMcpResourcesInput(raw: OCToolInput): ListMcpResourcesInput {
|
|
323
|
+
const result: ListMcpResourcesInput = {};
|
|
324
|
+
const server = optStr(raw, 'server', 'server');
|
|
325
|
+
if (server != null) result.server = server;
|
|
326
|
+
return result;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function normalizeReadMcpResourceInput(raw: OCToolInput): ReadMcpResourceInput {
|
|
330
|
+
return {
|
|
331
|
+
server: str(raw, 'server', 'server'),
|
|
332
|
+
uri: str(raw, 'uri', 'uri'),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function normalizeTaskOutputInput(raw: OCToolInput): TaskOutputInput {
|
|
337
|
+
return {
|
|
338
|
+
task_id: str(raw, 'task_id', 'taskId'),
|
|
339
|
+
block: (raw.block ?? true) as boolean,
|
|
340
|
+
timeout: (raw.timeout ?? 30000) as number,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function normalizeTodoWriteInput(raw: OCToolInput): TodoWriteInput {
|
|
345
|
+
return {
|
|
346
|
+
todos: (raw.todos ?? []) as TodoWriteInput['todos'],
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function normalizeExitPlanModeInput(raw: OCToolInput): ExitPlanModeInput {
|
|
351
|
+
const result: ExitPlanModeInput = {};
|
|
352
|
+
const allowedPrompts = raw.allowedPrompts ?? raw.allowed_prompts;
|
|
353
|
+
if (allowedPrompts != null) result.allowedPrompts = allowedPrompts as ExitPlanModeInput['allowedPrompts'];
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ============================================================
|
|
358
|
+
// Normalizer Dispatcher
|
|
359
|
+
// ============================================================
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Normalize OpenCode tool input → Claude Code tool input format.
|
|
363
|
+
* Handles camelCase → snake_case conversion and field name differences.
|
|
364
|
+
*/
|
|
365
|
+
function normalizeToolInput(claudeToolName: string, raw: OCToolInput): ClaudeToolInput {
|
|
366
|
+
// Custom MCP tools (mcp__*) — pass input through as-is
|
|
367
|
+
if (claudeToolName.startsWith('mcp__')) {
|
|
368
|
+
return raw as ClaudeToolInput;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
switch (claudeToolName) {
|
|
372
|
+
case 'Read': return normalizeReadInput(raw);
|
|
373
|
+
case 'Edit': return normalizeEditInput(raw);
|
|
374
|
+
case 'Write': return normalizeWriteInput(raw);
|
|
375
|
+
case 'Bash': return normalizeBashInput(raw);
|
|
376
|
+
case 'Glob': return normalizeGlobInput(raw);
|
|
377
|
+
case 'Grep': return normalizeGrepInput(raw);
|
|
378
|
+
case 'WebFetch': return normalizeWebFetchInput(raw);
|
|
379
|
+
case 'WebSearch': return normalizeWebSearchInput(raw);
|
|
380
|
+
case 'Task': return normalizeAgentInput(raw);
|
|
381
|
+
case 'NotebookEdit': return normalizeNotebookEditInput(raw);
|
|
382
|
+
case 'KillShell': return normalizeKillShellInput(raw);
|
|
383
|
+
case 'ListMcpResources': return normalizeListMcpResourcesInput(raw);
|
|
384
|
+
case 'ReadMcpResource': return normalizeReadMcpResourceInput(raw);
|
|
385
|
+
case 'TaskOutput': return normalizeTaskOutputInput(raw);
|
|
386
|
+
case 'TodoWrite': return normalizeTodoWriteInput(raw);
|
|
387
|
+
case 'ExitPlanMode': return normalizeExitPlanModeInput(raw);
|
|
388
|
+
default: {
|
|
389
|
+
// Unknown tool: generic camelCase → snake_case key normalization
|
|
390
|
+
const normalized: Record<string, string | number | boolean> = {};
|
|
391
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
392
|
+
normalized[camelToSnake(key)] = value as string | number | boolean;
|
|
393
|
+
}
|
|
394
|
+
return normalized as ClaudeToolInput;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ============================================================
|
|
400
|
+
// Stop Reason Mapping
|
|
401
|
+
// ============================================================
|
|
402
|
+
|
|
403
|
+
/** Map OpenCode finish reason → Claude Code stop_reason */
|
|
404
|
+
function mapStopReason(finish: string | undefined): string | null {
|
|
405
|
+
switch (finish) {
|
|
406
|
+
case 'tool-calls': return 'tool_use';
|
|
407
|
+
case 'stop': return 'end_turn';
|
|
408
|
+
case 'length': return 'max_tokens';
|
|
409
|
+
default: return finish || 'end_turn';
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ============================================================
|
|
414
|
+
// Message Builder Helper
|
|
415
|
+
// ============================================================
|
|
416
|
+
|
|
417
|
+
interface AssistantMessageParams {
|
|
418
|
+
content: ContentBlock[];
|
|
419
|
+
ocMessage: OCMessage;
|
|
420
|
+
modelId: string;
|
|
421
|
+
usage?: {
|
|
422
|
+
input_tokens: number;
|
|
423
|
+
output_tokens: number;
|
|
424
|
+
cache_creation_input_tokens: number;
|
|
425
|
+
cache_read_input_tokens: number;
|
|
426
|
+
};
|
|
427
|
+
sessionId: string;
|
|
428
|
+
stopReason: string | null;
|
|
429
|
+
uuid: string;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** Build a single SDKAssistantMessage from content blocks */
|
|
433
|
+
function buildAssistantSDKMessage(params: AssistantMessageParams): SDKMessage {
|
|
434
|
+
return {
|
|
435
|
+
type: 'assistant',
|
|
436
|
+
message: {
|
|
437
|
+
model: params.modelId,
|
|
438
|
+
id: params.uuid,
|
|
439
|
+
type: 'message',
|
|
440
|
+
role: 'assistant',
|
|
441
|
+
content: params.content,
|
|
442
|
+
stop_reason: params.stopReason,
|
|
443
|
+
stop_sequence: null,
|
|
444
|
+
...(params.usage && { usage: params.usage }),
|
|
445
|
+
context_management: null
|
|
446
|
+
},
|
|
447
|
+
parent_tool_use_id: null,
|
|
448
|
+
session_id: params.sessionId,
|
|
449
|
+
uuid: params.uuid
|
|
450
|
+
} as unknown as SDKMessage;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// ============================================================
|
|
454
|
+
// Public Converters
|
|
455
|
+
// ============================================================
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Convert Open Code user message → SDKMessage (user type)
|
|
459
|
+
*/
|
|
460
|
+
export function convertUserMessage(
|
|
461
|
+
ocMessage: OCMessage,
|
|
462
|
+
ocParts: Part[],
|
|
463
|
+
sessionId: string
|
|
464
|
+
): SDKMessage {
|
|
465
|
+
const textBlocks: TextContentBlock[] = [];
|
|
466
|
+
|
|
467
|
+
for (const part of ocParts) {
|
|
468
|
+
if (part.type === 'text') {
|
|
469
|
+
textBlocks.push({ type: 'text', text: part.text || '' });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (textBlocks.length === 0) {
|
|
474
|
+
textBlocks.push({ type: 'text', text: '' });
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return {
|
|
478
|
+
type: 'user',
|
|
479
|
+
uuid: ocMessage.id || crypto.randomUUID(),
|
|
480
|
+
session_id: sessionId,
|
|
481
|
+
parent_tool_use_id: null,
|
|
482
|
+
message: {
|
|
483
|
+
role: 'user',
|
|
484
|
+
content: textBlocks.length === 1
|
|
485
|
+
? textBlocks[0].text
|
|
486
|
+
: textBlocks
|
|
487
|
+
}
|
|
488
|
+
} as unknown as SDKMessage;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Convert Open Code assistant message + parts → SDKMessage[] (split by content type)
|
|
493
|
+
*
|
|
494
|
+
* Splits content into separate messages:
|
|
495
|
+
* - Consecutive text blocks → one message
|
|
496
|
+
* - Each tool_use block → its own message
|
|
497
|
+
*
|
|
498
|
+
* This matches Claude Code's UI behavior where each tool call is a separate bubble.
|
|
499
|
+
*/
|
|
500
|
+
export function convertAssistantMessages(
|
|
501
|
+
ocMessage: OCMessage,
|
|
502
|
+
ocParts: Part[],
|
|
503
|
+
sessionId: string
|
|
504
|
+
): SDKMessage[] {
|
|
505
|
+
// 1. Build typed content blocks from parts
|
|
506
|
+
const allBlocks: ContentBlock[] = [];
|
|
507
|
+
|
|
508
|
+
for (const part of ocParts) {
|
|
509
|
+
if (part.type === 'text') {
|
|
510
|
+
allBlocks.push({ type: 'text', text: part.text || '' });
|
|
511
|
+
} else if (part.type === 'tool') {
|
|
512
|
+
const toolPart = part as ToolPart;
|
|
513
|
+
const claudeName = mapToolName(toolPart.tool || 'unknown');
|
|
514
|
+
const resolvedInput = getToolInput(toolPart);
|
|
515
|
+
const normalizedInput = normalizeToolInput(claudeName, resolvedInput);
|
|
516
|
+
|
|
517
|
+
const block: ToolUseContentBlock = {
|
|
518
|
+
type: 'tool_use',
|
|
519
|
+
id: toolPart.callID || toolPart.id || crypto.randomUUID(),
|
|
520
|
+
name: claudeName,
|
|
521
|
+
input: normalizedInput,
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
if (toolPart.state.status === 'completed') {
|
|
525
|
+
block.$result = {
|
|
526
|
+
type: 'tool_result',
|
|
527
|
+
tool_use_id: block.id,
|
|
528
|
+
content: toolPart.state.output || '',
|
|
529
|
+
};
|
|
530
|
+
} else if (toolPart.state.status === 'error') {
|
|
531
|
+
block.$result = {
|
|
532
|
+
type: 'tool_result',
|
|
533
|
+
tool_use_id: block.id,
|
|
534
|
+
content: toolPart.state.error || 'Tool execution failed',
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
allBlocks.push(block);
|
|
539
|
+
}
|
|
540
|
+
// Skip: reasoning, step-start, step-finish, snapshot, patch, agent, retry, compaction
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// 2. Split into groups: consecutive text → one group, each tool_use → its own group
|
|
544
|
+
const groups: ContentBlock[][] = [];
|
|
545
|
+
let currentTextGroup: TextContentBlock[] = [];
|
|
546
|
+
|
|
547
|
+
for (const block of allBlocks) {
|
|
548
|
+
if (block.type === 'text') {
|
|
549
|
+
currentTextGroup.push(block);
|
|
550
|
+
} else {
|
|
551
|
+
// Flush accumulated text blocks as a group
|
|
552
|
+
if (currentTextGroup.length > 0) {
|
|
553
|
+
groups.push([...currentTextGroup]);
|
|
554
|
+
currentTextGroup = [];
|
|
555
|
+
}
|
|
556
|
+
// Each tool_use is its own group
|
|
557
|
+
groups.push([block]);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
// Flush remaining text
|
|
561
|
+
if (currentTextGroup.length > 0) {
|
|
562
|
+
groups.push([...currentTextGroup]);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// If no content at all, add an empty text block
|
|
566
|
+
if (groups.length === 0) {
|
|
567
|
+
groups.push([{ type: 'text', text: '' }]);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// 3. Build SDKMessages from groups
|
|
571
|
+
const assistantMsg = ocMessage.role === 'assistant' ? ocMessage as AssistantMessage : null;
|
|
572
|
+
const modelId = assistantMsg ? `${assistantMsg.providerID}/${assistantMsg.modelID}` : '';
|
|
573
|
+
const mappedStop = mapStopReason(assistantMsg?.finish);
|
|
574
|
+
const usage = assistantMsg?.tokens ? {
|
|
575
|
+
input_tokens: assistantMsg.tokens.input || 0,
|
|
576
|
+
output_tokens: assistantMsg.tokens.output || 0,
|
|
577
|
+
cache_creation_input_tokens: assistantMsg.tokens.cache?.write || 0,
|
|
578
|
+
cache_read_input_tokens: assistantMsg.tokens.cache?.read || 0,
|
|
579
|
+
} : undefined;
|
|
580
|
+
|
|
581
|
+
const messages: SDKMessage[] = [];
|
|
582
|
+
const baseUuid = ocMessage.id || crypto.randomUUID();
|
|
583
|
+
|
|
584
|
+
for (let i = 0; i < groups.length; i++) {
|
|
585
|
+
const isLast = i === groups.length - 1;
|
|
586
|
+
const group = groups[i];
|
|
587
|
+
const hasToolUse = group.some(b => b.type === 'tool_use');
|
|
588
|
+
|
|
589
|
+
// Determine stop_reason for this split
|
|
590
|
+
let stopReason: string | null;
|
|
591
|
+
if (isLast) {
|
|
592
|
+
stopReason = mappedStop;
|
|
593
|
+
} else if (hasToolUse) {
|
|
594
|
+
stopReason = 'tool_use';
|
|
595
|
+
} else {
|
|
596
|
+
stopReason = null;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
messages.push(buildAssistantSDKMessage({
|
|
600
|
+
content: group,
|
|
601
|
+
ocMessage,
|
|
602
|
+
modelId,
|
|
603
|
+
// Only the last message carries usage (to avoid double-counting)
|
|
604
|
+
usage: isLast ? usage : undefined,
|
|
605
|
+
sessionId,
|
|
606
|
+
stopReason,
|
|
607
|
+
uuid: i === 0 ? baseUuid : crypto.randomUUID(),
|
|
608
|
+
}));
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return messages;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Convert Open Code result/completion → SDKResultMessage
|
|
616
|
+
*/
|
|
617
|
+
export function convertResultMessage(
|
|
618
|
+
ocMessage: OCMessage,
|
|
619
|
+
sessionId: string
|
|
620
|
+
): SDKMessage {
|
|
621
|
+
const assistantMsg = ocMessage.role === 'assistant' ? ocMessage as AssistantMessage : null;
|
|
622
|
+
|
|
623
|
+
return {
|
|
624
|
+
type: 'result',
|
|
625
|
+
subtype: assistantMsg?.error ? 'error_during_execution' : 'success',
|
|
626
|
+
uuid: crypto.randomUUID(),
|
|
627
|
+
session_id: sessionId,
|
|
628
|
+
duration_ms: assistantMsg?.time?.completed
|
|
629
|
+
? (assistantMsg.time.completed - assistantMsg.time.created) * 1000
|
|
630
|
+
: 0,
|
|
631
|
+
duration_api_ms: 0,
|
|
632
|
+
is_error: !!assistantMsg?.error,
|
|
633
|
+
num_turns: 1,
|
|
634
|
+
total_cost_usd: assistantMsg?.cost || 0,
|
|
635
|
+
usage: {
|
|
636
|
+
input_tokens: assistantMsg?.tokens?.input || 0,
|
|
637
|
+
output_tokens: assistantMsg?.tokens?.output || 0,
|
|
638
|
+
cache_creation_input_tokens: assistantMsg?.tokens?.cache?.write || 0,
|
|
639
|
+
cache_read_input_tokens: assistantMsg?.tokens?.cache?.read || 0
|
|
640
|
+
},
|
|
641
|
+
...(assistantMsg?.error
|
|
642
|
+
? { errors: [JSON.stringify(assistantMsg.error)] }
|
|
643
|
+
: { result: '' }
|
|
644
|
+
)
|
|
645
|
+
} as unknown as SDKMessage;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Convert Open Code system/init event → SDKSystemMessage
|
|
650
|
+
*/
|
|
651
|
+
export function convertSystemInitMessage(sessionId: string, model: string): SDKMessage {
|
|
652
|
+
return {
|
|
653
|
+
type: 'system',
|
|
654
|
+
subtype: 'init',
|
|
655
|
+
uuid: crypto.randomUUID(),
|
|
656
|
+
session_id: sessionId,
|
|
657
|
+
tools: [],
|
|
658
|
+
mcp_servers: [],
|
|
659
|
+
model,
|
|
660
|
+
permissionMode: 'bypassPermissions',
|
|
661
|
+
cwd: process.cwd(),
|
|
662
|
+
apiKeySource: 'user',
|
|
663
|
+
slash_commands: [],
|
|
664
|
+
skills: [],
|
|
665
|
+
plugins: [],
|
|
666
|
+
claude_code_version: 'opencode',
|
|
667
|
+
output_style: 'text'
|
|
668
|
+
} as unknown as SDKMessage;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Convert Open Code text delta → SDKPartialAssistantMessage
|
|
673
|
+
*/
|
|
674
|
+
export function convertPartialTextDelta(
|
|
675
|
+
text: string,
|
|
676
|
+
sessionId: string,
|
|
677
|
+
parentToolUseId: string | null = null
|
|
678
|
+
): SDKMessage {
|
|
679
|
+
return {
|
|
680
|
+
type: 'stream_event',
|
|
681
|
+
event: {
|
|
682
|
+
type: 'content_block_delta',
|
|
683
|
+
index: 0,
|
|
684
|
+
delta: { type: 'text_delta', text }
|
|
685
|
+
},
|
|
686
|
+
parent_tool_use_id: parentToolUseId,
|
|
687
|
+
uuid: crypto.randomUUID(),
|
|
688
|
+
session_id: sessionId
|
|
689
|
+
} as unknown as SDKMessage;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Convert Open Code stream start → SDKPartialAssistantMessage
|
|
694
|
+
*/
|
|
695
|
+
export function convertStreamStart(sessionId: string): SDKMessage {
|
|
696
|
+
return {
|
|
697
|
+
type: 'stream_event',
|
|
698
|
+
event: { type: 'message_start', message: { role: 'assistant', content: [] } },
|
|
699
|
+
parent_tool_use_id: null,
|
|
700
|
+
uuid: crypto.randomUUID(),
|
|
701
|
+
session_id: sessionId
|
|
702
|
+
} as unknown as SDKMessage;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Convert Open Code stream stop → SDKPartialAssistantMessage
|
|
707
|
+
*/
|
|
708
|
+
export function convertStreamStop(sessionId: string): SDKMessage {
|
|
709
|
+
return {
|
|
710
|
+
type: 'stream_event',
|
|
711
|
+
event: { type: 'message_stop' },
|
|
712
|
+
parent_tool_use_id: null,
|
|
713
|
+
uuid: crypto.randomUUID(),
|
|
714
|
+
session_id: sessionId
|
|
715
|
+
} as unknown as SDKMessage;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Convert a single tool part → assistant message with tool_use only (no $result).
|
|
720
|
+
* Used for progressive tool rendering — tool appears immediately in UI
|
|
721
|
+
* before the tool finishes executing.
|
|
722
|
+
*/
|
|
723
|
+
export function convertToolUseOnly(
|
|
724
|
+
toolPart: ToolPart,
|
|
725
|
+
ocMessage: OCMessage,
|
|
726
|
+
sessionId: string,
|
|
727
|
+
): SDKMessage {
|
|
728
|
+
const claudeName = mapToolName(toolPart.tool || 'unknown');
|
|
729
|
+
const resolvedInput = getToolInput(toolPart);
|
|
730
|
+
const normalizedInput = normalizeToolInput(claudeName, resolvedInput);
|
|
731
|
+
const toolUseId = toolPart.callID || toolPart.id || crypto.randomUUID();
|
|
732
|
+
|
|
733
|
+
const assistantMsg = ocMessage.role === 'assistant' ? ocMessage as AssistantMessage : null;
|
|
734
|
+
const modelId = assistantMsg ? `${assistantMsg.providerID}/${assistantMsg.modelID}` : '';
|
|
735
|
+
|
|
736
|
+
return buildAssistantSDKMessage({
|
|
737
|
+
content: [{
|
|
738
|
+
type: 'tool_use',
|
|
739
|
+
id: toolUseId,
|
|
740
|
+
name: claudeName,
|
|
741
|
+
input: normalizedInput,
|
|
742
|
+
}],
|
|
743
|
+
ocMessage,
|
|
744
|
+
modelId,
|
|
745
|
+
sessionId,
|
|
746
|
+
stopReason: 'tool_use',
|
|
747
|
+
uuid: crypto.randomUUID(),
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Convert reasoning text → assistant message with metadata.reasoning flag.
|
|
753
|
+
* Displayed as separate "Reasoning" bubble in UI.
|
|
754
|
+
*/
|
|
755
|
+
export function convertReasoningMessage(
|
|
756
|
+
reasoningText: string,
|
|
757
|
+
ocMessage: OCMessage,
|
|
758
|
+
sessionId: string,
|
|
759
|
+
): EngineSDKMessage {
|
|
760
|
+
const assistantMsg = ocMessage.role === 'assistant' ? ocMessage as AssistantMessage : null;
|
|
761
|
+
const modelId = assistantMsg ? `${assistantMsg.providerID}/${assistantMsg.modelID}` : '';
|
|
762
|
+
|
|
763
|
+
return {
|
|
764
|
+
...buildAssistantSDKMessage({
|
|
765
|
+
content: [{ type: 'text', text: reasoningText }],
|
|
766
|
+
ocMessage,
|
|
767
|
+
modelId,
|
|
768
|
+
sessionId,
|
|
769
|
+
stopReason: null,
|
|
770
|
+
uuid: crypto.randomUUID(),
|
|
771
|
+
}),
|
|
772
|
+
metadata: { reasoning: true },
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Convert reasoning delta → stream event with metadata.reasoning flag.
|
|
778
|
+
*/
|
|
779
|
+
export function convertPartialReasoningDelta(
|
|
780
|
+
text: string,
|
|
781
|
+
sessionId: string,
|
|
782
|
+
): EngineSDKMessage {
|
|
783
|
+
// Synthetic stream event — partial SDK structure by design
|
|
784
|
+
return {
|
|
785
|
+
type: 'stream_event',
|
|
786
|
+
event: {
|
|
787
|
+
type: 'content_block_delta',
|
|
788
|
+
index: 0,
|
|
789
|
+
delta: { type: 'text_delta', text }
|
|
790
|
+
},
|
|
791
|
+
metadata: { reasoning: true },
|
|
792
|
+
parent_tool_use_id: null,
|
|
793
|
+
uuid: crypto.randomUUID(),
|
|
794
|
+
session_id: sessionId
|
|
795
|
+
} as unknown as EngineSDKMessage;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Convert reasoning stream start → stream event with metadata.reasoning flag.
|
|
800
|
+
*/
|
|
801
|
+
export function convertReasoningStreamStart(sessionId: string): EngineSDKMessage {
|
|
802
|
+
// Synthetic stream event — partial SDK structure by design
|
|
803
|
+
return {
|
|
804
|
+
type: 'stream_event',
|
|
805
|
+
event: { type: 'message_start', message: { role: 'assistant', content: [] } },
|
|
806
|
+
metadata: { reasoning: true },
|
|
807
|
+
parent_tool_use_id: null,
|
|
808
|
+
uuid: crypto.randomUUID(),
|
|
809
|
+
session_id: sessionId
|
|
810
|
+
} as unknown as EngineSDKMessage;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Convert reasoning stream stop → stream event with metadata.reasoning flag.
|
|
815
|
+
*/
|
|
816
|
+
export function convertReasoningStreamStop(sessionId: string): EngineSDKMessage {
|
|
817
|
+
// Synthetic stream event — partial SDK structure by design
|
|
818
|
+
return {
|
|
819
|
+
type: 'stream_event',
|
|
820
|
+
event: { type: 'message_stop' },
|
|
821
|
+
metadata: { reasoning: true },
|
|
822
|
+
parent_tool_use_id: null,
|
|
823
|
+
uuid: crypto.randomUUID(),
|
|
824
|
+
session_id: sessionId
|
|
825
|
+
} as unknown as EngineSDKMessage;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Convert a completed/errored tool part → user message with tool_result.
|
|
830
|
+
* Sent after the tool finishes executing, matching Claude Code's pattern
|
|
831
|
+
* where tool_result arrives as a separate user message.
|
|
832
|
+
*/
|
|
833
|
+
export function convertToolResultOnly(
|
|
834
|
+
toolPart: ToolPart,
|
|
835
|
+
sessionId: string,
|
|
836
|
+
): SDKMessage {
|
|
837
|
+
const toolUseId = toolPart.callID || toolPart.id || crypto.randomUUID();
|
|
838
|
+
|
|
839
|
+
let content: string;
|
|
840
|
+
if (toolPart.state.status === 'completed') {
|
|
841
|
+
content = toolPart.state.output || '';
|
|
842
|
+
} else if (toolPart.state.status === 'error') {
|
|
843
|
+
content = toolPart.state.error || 'Tool execution failed';
|
|
844
|
+
} else {
|
|
845
|
+
content = '';
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
return {
|
|
849
|
+
type: 'user',
|
|
850
|
+
uuid: crypto.randomUUID(),
|
|
851
|
+
session_id: sessionId,
|
|
852
|
+
parent_tool_use_id: null,
|
|
853
|
+
message: {
|
|
854
|
+
role: 'user',
|
|
855
|
+
content: [{
|
|
856
|
+
type: 'tool_result',
|
|
857
|
+
tool_use_id: toolUseId,
|
|
858
|
+
content
|
|
859
|
+
}]
|
|
860
|
+
}
|
|
861
|
+
} as unknown as SDKMessage;
|
|
862
|
+
}
|