From 374e9de71934c41ce2c19c796e35a19234b240ec Mon Sep 17 00:00:00 2001 From: Haile <118998054+blackmammoth@users.noreply.github.com> Date: Thu, 28 May 2026 11:50:41 +0300 Subject: [PATCH] feat: add opencode support (#762) * feat: add opencode support * fix: stabilize opencode session startup * fix: /models * fix: improveUI for commands * fix: format commands.js * feat: load models through provider adapters Provider model selection had outgrown a single hardcoded service. The old service mixed shared caching with provider catalogs and CLI lookup details. That made stale model lists more likely as providers changed on separate schedules. Move model discovery behind each provider so lookup lives next to the integration. The shared service now focuses on provider resolution, caching, persistence, and dedupe. Return cache metadata and add bypassCache because model availability changes outside the app. The UI and /models command can show freshness and let users force a provider refresh. Surface model descriptions while keeping fallback catalogs for unavailable CLIs or SDKs. * feat(models): resolve active session models through provider adapters The model inventory command was showing a mix of catalog defaults and composer-local state instead of the model that is actually active for a real provider session. That made /models, /cost, and /status misleading once a session had already started, especially for providers whose effective runtime model can differ from the optimistic model value held in the UI. Introduce an explicit getCurrentActiveModel() contract on IProviderModels so model resolution lives next to each provider's catalog logic and uses the provider-native source of truth: - Claude reads the init event from a resumed stream-json run - Codex reads model from ~/.codex/config.toml - Cursor reads lastUsedModel from the chat store.db - OpenCode reads the persisted session model from opencode.db - Gemini intentionally returns its default because the CLI does not provide a reliable active-session lookup Keep the returned shape intentionally minimal ({ model }). The goal is to expose only what downstream command consumers need and avoid leaking provider-specific metadata into a shared transport shape that would create extra UI coupling and future cleanup cost. Also make command behavior session-aware: when there is no concrete session id, do not spawn provider processes or inspect provider session storage just to answer /models, /cost, or /status. In a new-session view the correct answer is simply the provider default, and doing more work there adds latency and unnecessary side effects for no user value. As part of this, centralize two supporting concerns: - add a shared helper for building the default current-model result from a provider catalog so fallbacks stay aligned with DEFAULT - move leaf-directory validation into shared utils so Cursor session readers and model lookup code enforce the same path-safety rule Tests were expanded to cover both the new service delegation path and the sessionless command behavior, while keeping cache-sensitive tests isolated from persisted host cache state. Why this change: - command output should reflect the model actually driving a session - new-session views should stay fast and side-effect free - provider-specific active-model lookup should not be scattered across routes or UI code - fallback behavior should be explicit, consistent, and limited to the provider default when no true active model can be resolved * feat: support session-scoped model overrides Model selection was acting like a provider-level preference. That made resumed sessions drift back to a default or request-time model. Users expect /models changes made inside a conversation to affect that session. Store explicit session choices in app-owned ~/.cloudcli state. This avoids editing provider transcripts or native provider config. Resolve the effective model before launching each provider runtime. Claude, Cursor, Codex, Gemini, and OpenCode now honor stored resume choices. Expose a backend active-model change endpoint for existing sessions. The models modal can now distinguish default changes from session overrides. It also shows when a selected model will apply on the next response. For Claude, stop probing active model state by resuming with a dummy prompt. Read the indexed JSONL transcript from the end instead. This preserves provider history while honoring /model stdout or model fields. Add service tests for adapter delegation and resume-model precedence. The tests keep cache state, override state, and requested fallback separate. * feat: make command modal more compact * fix: preserve opencode session creation events OpenCode emits the real session id asynchronously on its first JSON output. The runner registered that id from a helper that could not see the spawned process because the process reference was scoped inside the model-resolution callback. That ReferenceError was swallowed by the generic JSON parse fallback, so the client never received session_created. Without that event, a new OpenCode chat stayed on / and the assistant stream was not attached to the new session view. Keep the process reference in the outer spawn scope so registration can update the active-process map and websocket writer as soon as OpenCode announces the session id. Split JSON parsing from event processing so malformed non-JSON output can still stream as raw text, while registration or adapter failures are surfaced as real errors instead of being hidden as assistant content. Add a fake opencode executable regression test to lock in the expected lifecycle ordering: session_created must be sent before live assistant messages, and the same session id must carry through stream_end and complete. * fix: clarify model refresh and onboarding providers OpenCode is now a supported chat provider, but first-run onboarding still only offered Claude, Cursor, Codex, and Gemini. That made OpenCode harder to discover and forced users to finish setup before finding the provider in settings or chat. Adding it to onboarding keeps first-run setup aligned with the providers the application already supports elsewhere. The model refresh control was also doing too much visual work. In the new chat model picker, the previous Hard Refresh label looked like the dialog heading, which made the primary task unclear. Users open that dialog to choose a model; refreshing catalogs is only a secondary maintenance action for stale cached provider model lists. Rename and reposition the refresh affordance so the model picker reads as a model picker first. The copy now explains why catalogs are cached, when a refresh is useful, and that the refresh checks every provider. The /models modal gets the same clarification so both model-selection surfaces describe the cache behavior consistently. * fix: format opencode model catalog labels OpenCode returns provider-prefixed ids directly from the CLI. Passing those ids through as labels made the model picker hard to scan: users saw values like anthropic/claude-3-5-sonnet-20241022 or lowercased, hyphen-split text instead of readable model names. Keep the exact OpenCode id as the option value because that is what the CLI expects, but derive a presentation label for the frontend. The formatter is intentionally generic rather than a catalog of known providers. It handles common identifier structure such as provider/model, hyphen-delimited words, v-prefixed versions, adjacent numeric version tokens, and 8-digit date suffixes. This keeps OpenCode usable as its model list expands across many upstream providers without requiring code changes for every new provider or model family. The description keeps the raw provider-prefixed id visible so users can still confirm the precise model being selected. * feat: add more fallback models for cursor * docs: move model catalog out of shared The model catalog is no longer a frontend/backend runtime contract. Keeping it under shared made ownership misleading. It implied the catalog was application code shared by runtime consumers, even though it now only supports README links and public API documentation. Move the catalog into public so it lives beside the docs surfaces that need it. This gives the API docs a stable, served module and gives README readers a linkable source without suggesting frontend or backend runtime dependency. Render the API docs model list from the exported provider registry instead of a hardcoded Claude/Cursor/Codex subset. That keeps Gemini and OpenCode visible and makes future provider documentation changes flow through one docs-specific file. Update README links, provider maintenance notes, and package files so published artifacts include the standalone docs page and model catalog without relying on the old shared path. * fix: simplify empty-state model selector Keep the provider empty state focused on the setup action users need there: choosing a model. The refresh control, cache timestamp, and refresh explanation made the dialog feel like a cache-management surface. That extra action is out of place in the empty state, where the goal is to start a chat with the selected provider and model. Remove the refresh-specific UI from ProviderSelectionEmptyState and drop the now-unused refresh/cache props from the ChatMessagesPane pass-through. Refresh behavior remains available in the dedicated command result flow. --- README.de.md | 2 +- README.ko.md | 2 +- README.md | 2 +- README.ru.md | 2 +- README.tr.md | 2 +- README.zh-CN.md | 2 +- package.json | 2 + public/api-docs.html | 13 +- public/modelConstants.js | 841 ++++++++++++++++++ redirect-package/README.md | 2 +- server/claude-sdk.js | 16 +- server/cursor-cli.js | 9 +- server/gemini-cli.js | 8 +- server/index.js | 22 + server/modules/database/index.ts | 1 + .../services/project-management.service.ts | 2 + .../projects-with-sessions-fetch.service.ts | 8 +- server/modules/providers/README.md | 14 +- .../list/claude/claude-models.provider.ts | 230 +++++ .../providers/list/claude/claude.provider.ts | 3 + .../list/codex/codex-models.provider.ts | 125 +++ .../list/codex/codex-skills.provider.ts | 60 +- .../providers/list/codex/codex.provider.ts | 3 + .../list/cursor/cursor-models.provider.ts | 283 ++++++ .../list/cursor/cursor-sessions.provider.ts | 27 +- .../providers/list/cursor/cursor.provider.ts | 3 + .../list/gemini/gemini-models.provider.ts | 42 + .../providers/list/gemini/gemini.provider.ts | 3 + .../list/opencode/opencode-auth.provider.ts | 111 +++ .../list/opencode/opencode-mcp.provider.ts | 228 +++++ .../list/opencode/opencode-models.provider.ts | 339 +++++++ .../opencode-session-synchronizer.provider.ts | 157 ++++ .../opencode/opencode-sessions.provider.ts | 463 ++++++++++ .../list/opencode/opencode-skills.provider.ts | 78 ++ .../list/opencode/opencode.provider.ts | 27 + server/modules/providers/provider.registry.ts | 2 + server/modules/providers/provider.routes.ts | 64 +- .../services/provider-models.service.ts | 325 +++++++ .../services/session-synchronizer.service.ts | 1 + .../services/sessions-watcher.service.ts | 8 + .../shared/base/abstract.provider.ts | 2 + server/modules/providers/tests/mcp.test.ts | 92 +- .../providers/tests/opencode-models.test.ts | 73 ++ .../providers/tests/opencode-sessions.test.ts | 321 +++++++ .../tests/provider-models.service.test.ts | 318 +++++++ server/modules/providers/tests/skills.test.ts | 66 ++ .../services/chat-websocket.service.ts | 16 +- .../services/shell-websocket.service.ts | 9 + server/openai-codex.js | 9 +- server/opencode-cli.js | 263 ++++++ server/opencode-cli.test.js | 95 ++ server/routes/agent.js | 30 +- server/routes/commands.js | 449 +++++----- server/routes/cursor.js | 4 +- server/routes/tests/commands.test.js | 82 ++ server/shared/interfaces.ts | 45 + server/shared/types.ts | 89 +- server/shared/utils.ts | 384 ++++++++ shared/modelConstants.js | 107 --- .../chat/hooks/useChatComposerState.ts | 202 +++-- .../chat/hooks/useChatProviderState.ts | 279 +++++- .../chat/hooks/useChatRealtimeHandlers.ts | 53 +- .../chat/hooks/useChatSessionState.ts | 14 +- src/components/chat/view/ChatInterface.tsx | 40 +- .../view/subcomponents/ChatMessagesPane.tsx | 21 +- .../chat/view/subcomponents/ClaudeStatus.tsx | 3 +- .../view/subcomponents/CommandResultModal.tsx | 731 +++++++++++++++ .../view/subcomponents/MessageComponent.tsx | 14 +- .../ProviderSelectionEmptyState.tsx | 96 +- .../sources/useSessionsSource.ts | 2 + .../llm-logo-provider/OpenCodeLogo.tsx | 25 + .../llm-logo-provider/SessionProviderLogo.tsx | 5 + src/components/mcp/constants.ts | 5 + .../subcomponents/AgentConnectionsStep.tsx | 8 + src/components/provider-auth/types.ts | 4 +- .../provider-auth/view/ProviderLoginModal.tsx | 5 + .../settings/constants/constants.ts | 2 +- .../tabs/agents-settings/AgentListItem.tsx | 11 +- .../agents-settings/AgentsSettingsTab.tsx | 7 +- .../sections/AgentSelectorSection.tsx | 4 +- .../sections/content/AccountContent.tsx | 15 +- src/components/sidebar/types/types.ts | 1 + src/components/sidebar/utils/utils.ts | 8 +- src/hooks/useProjectsState.ts | 30 +- src/i18n/locales/en/chat.json | 4 +- src/i18n/locales/en/settings.json | 6 +- src/types/app.ts | 20 +- 87 files changed, 7024 insertions(+), 577 deletions(-) create mode 100644 public/modelConstants.js create mode 100644 server/modules/providers/list/claude/claude-models.provider.ts create mode 100644 server/modules/providers/list/codex/codex-models.provider.ts create mode 100644 server/modules/providers/list/cursor/cursor-models.provider.ts create mode 100644 server/modules/providers/list/gemini/gemini-models.provider.ts create mode 100644 server/modules/providers/list/opencode/opencode-auth.provider.ts create mode 100644 server/modules/providers/list/opencode/opencode-mcp.provider.ts create mode 100644 server/modules/providers/list/opencode/opencode-models.provider.ts create mode 100644 server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts create mode 100644 server/modules/providers/list/opencode/opencode-sessions.provider.ts create mode 100644 server/modules/providers/list/opencode/opencode-skills.provider.ts create mode 100644 server/modules/providers/list/opencode/opencode.provider.ts create mode 100644 server/modules/providers/services/provider-models.service.ts create mode 100644 server/modules/providers/tests/opencode-models.test.ts create mode 100644 server/modules/providers/tests/opencode-sessions.test.ts create mode 100644 server/modules/providers/tests/provider-models.service.test.ts create mode 100644 server/opencode-cli.js create mode 100644 server/opencode-cli.test.js create mode 100644 server/routes/tests/commands.test.js delete mode 100644 shared/modelConstants.js create mode 100644 src/components/chat/view/subcomponents/CommandResultModal.tsx create mode 100644 src/components/llm-logo-provider/OpenCodeLogo.tsx diff --git a/README.de.md b/README.de.md index c98af089..9f8a429b 100644 --- a/README.de.md +++ b/README.de.md @@ -62,7 +62,7 @@ - **Sitzungsverwaltung** – Gespräche fortsetzen, mehrere Sitzungen verwalten und Verlauf nachverfolgen - **Plugin-System** – CloudCLI mit eigenen Plugins erweitern – neue Tabs, Backend-Dienste und Integrationen hinzufügen. [Eigenes Plugin erstellen →](https://github.com/cloudcli-ai/cloudcli-plugin-starter) - **TaskMaster AI Integration** *(Optional)* – Erweitertes Projektmanagement mit KI-gestützter Aufgabenplanung, PRD-Parsing und Workflow-Automatisierung -- **Modell-Kompatibilität** – Funktioniert mit Claude, GPT und Gemini (vollständige Liste unterstützter Modelle in [`shared/modelConstants.js`](shared/modelConstants.js)) +- **Modell-Kompatibilität** – Funktioniert mit Claude, GPT und Gemini (vollständige Liste unterstützter Modelle in [`public/modelConstants.js`](public/modelConstants.js)) ## Schnellstart diff --git a/README.ko.md b/README.ko.md index b5cf5a98..41e142bf 100644 --- a/README.ko.md +++ b/README.ko.md @@ -60,7 +60,7 @@ - **세션 관리** - 대화를 재개하고, 여러 세션을 관리하며 기록을 추적 - **플러그인 시스템** - 커스텀 탭, 백엔드 서비스, 통합을 추가하여 CloudCLI 확장. [직접 빌드 →](https://github.com/cloudcli-ai/cloudcli-plugin-starter) - **TaskMaster AI 통합** *(선택사항)* - AI 중심의 작업 계획, PRD 파싱, 워크플로 자동화를 통한 고급 프로젝트 관리 -- **모델 호환성** - Claude, GPT, Gemini 모델 계열에서 작동 (`shared/modelConstants.js`에서 전체 지원 모델 확인) +- **모델 호환성** - Claude, GPT, Gemini 모델 계열에서 작동 (`public/modelConstants.js`에서 전체 지원 모델 확인) ## 빠른 시작 diff --git a/README.md b/README.md index ae95b030..bb60c9e0 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ - **Session Management** - Resume conversations, manage multiple sessions, and track history - **Plugin System** - Extend CloudCLI with custom plugins — add new tabs, backend services, and integrations. [Build your own →](https://github.com/cloudcli-ai/cloudcli-plugin-starter) - **TaskMaster AI Integration** *(Optional)* - Advanced project management with AI-powered task planning, PRD parsing, and workflow automation -- **Model Compatibility** - Works with Claude, GPT, and Gemini model families (see [`shared/modelConstants.js`](shared/modelConstants.js) for the full list of supported models) +- **Model Compatibility** - Works with Claude, GPT, and Gemini model families (see [`public/modelConstants.js`](public/modelConstants.js) for the full list of supported models) ## Quick Start diff --git a/README.ru.md b/README.ru.md index c197676a..97d8074f 100644 --- a/README.ru.md +++ b/README.ru.md @@ -62,7 +62,7 @@ - **Управление сессиями** - возобновляйте диалоги, управляйте несколькими сессиями и отслеживайте историю - **Система плагинов** - расширяйте CloudCLI кастомными плагинами — добавляйте новые вкладки, бэкенд-сервисы и интеграции. [Создать свой →](https://github.com/cloudcli-ai/cloudcli-plugin-starter) - **Интеграция с TaskMaster AI** *(опционально)* - продвинутое управление проектами с планированием задач на базе AI, разбором PRD и автоматизацией workflow -- **Совместимость с моделями** - работает с семействами моделей Claude, GPT и Gemini (см. [`shared/modelConstants.js`](shared/modelConstants.js) для полного списка поддерживаемых моделей) +- **Совместимость с моделями** - работает с семействами моделей Claude, GPT и Gemini (см. [`public/modelConstants.js`](public/modelConstants.js) для полного списка поддерживаемых моделей) ## Быстрый старт diff --git a/README.tr.md b/README.tr.md index a0bcdf58..c84770e5 100644 --- a/README.tr.md +++ b/README.tr.md @@ -62,7 +62,7 @@ - **Oturum Yönetimi** — Konuşmalara devam et, birden fazla oturumu yönet ve geçmişi takip et - **Eklenti Sistemi** — CloudCLI'ı özel eklentilerle genişlet: yeni sekmeler, arka uç servisleri ve entegrasyonlar ekle. [Kendi eklentini yaz →](https://github.com/cloudcli-ai/cloudcli-plugin-starter) - **TaskMaster AI Entegrasyonu** *(İsteğe Bağlı)* — AI destekli görev planlama, PRD ayrıştırma ve iş akışı otomasyonu ile gelişmiş proje yönetimi -- **Model Uyumluluğu** — Claude, GPT ve Gemini model aileleriyle çalışır (desteklenen tüm modeller için [`shared/modelConstants.js`](shared/modelConstants.js) dosyasına bak) +- **Model Uyumluluğu** — Claude, GPT ve Gemini model aileleriyle çalışır (desteklenen tüm modeller için [`public/modelConstants.js`](public/modelConstants.js) dosyasına bak) ## Hızlı Başlangıç diff --git a/README.zh-CN.md b/README.zh-CN.md index 3e6ced3f..2ee656e8 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -60,7 +60,7 @@ - **会话管理** - 恢复对话、管理多个会话并跟踪历史记录 - **插件系统** - 通过自定义选项卡、后端服务与集成扩展 CloudCLI。 [开始构建 →](https://github.com/cloudcli-ai/cloudcli-plugin-starter) - **TaskMaster AI 集成** *(可选)* - 结合 AI 任务规划、PRD 分析与工作流自动化,实现高级项目管理 -- **模型兼容性** - 支持 Claude、GPT、Gemini 模型家族(完整支持列表见 [`shared/modelConstants.js`](shared/modelConstants.js)) +- **模型兼容性** - 支持 Claude、GPT、Gemini 模型家族(完整支持列表见 [`public/modelConstants.js`](public/modelConstants.js)) ## 快速开始 diff --git a/package.json b/package.json index 8f7bd266..626d0e3b 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "files": [ "server/", "shared/", + "public/api-docs.html", + "public/modelConstants.js", "dist/", "dist-server/", "scripts/", diff --git a/public/api-docs.html b/public/api-docs.html index 1d86cf4c..0103b5ca 100644 --- a/public/api-docs.html +++ b/public/api-docs.html @@ -822,7 +822,7 @@ data: {"type":"done"}