Преглед на файлове

Merge branch 'dev' into nxl/background-subagents

Shoubhit Dash преди 3 месеца
родител
ревизия
b26fe0d357
променени са 100 файла, в които са добавени 1429 реда и са изтрити 1727 реда
  1. 5 0
      .github/VOUCHED.td
  2. 6 0
      .github/workflows/deploy.yml
  3. 8 1
      .github/workflows/publish.yml
  4. 2 2
      .github/workflows/review.yml
  5. 0 899
      .opencode/agent/translator.md
  6. 14 0
      .opencode/command/translate.md
  7. 27 10
      .opencode/skills/effect/SKILL.md
  8. 2 2
      .opencode/tool/github-triage.ts
  9. 238 106
      bun.lock
  10. 24 0
      infra/console.ts
  11. 4 4
      nix/hashes.json
  12. 1 1
      nix/opencode.nix
  13. 8 5
      package.json
  14. 4 2
      packages/app/package.json
  15. 23 7
      packages/app/src/app.tsx
  16. 1 1
      packages/app/src/components/dialog-edit-project.tsx
  17. 1 1
      packages/app/src/components/dialog-fork.tsx
  18. 1 1
      packages/app/src/components/dialog-select-directory.tsx
  19. 2 2
      packages/app/src/components/dialog-select-file.tsx
  20. 7 55
      packages/app/src/components/dialog-select-mcp.tsx
  21. 1 1
      packages/app/src/components/prompt-input/build-request-parts.ts
  22. 1 1
      packages/app/src/components/prompt-input/context-items.tsx
  23. 1 1
      packages/app/src/components/prompt-input/slash-popover.tsx
  24. 1 1
      packages/app/src/components/prompt-input/submit.test.ts
  25. 2 2
      packages/app/src/components/prompt-input/submit.ts
  26. 2 2
      packages/app/src/components/session/session-context-tab.tsx
  27. 1 1
      packages/app/src/components/session/session-header.tsx
  28. 1 1
      packages/app/src/components/session/session-new-view.tsx
  29. 1 1
      packages/app/src/components/session/session-sortable-tab.tsx
  30. 124 66
      packages/app/src/components/settings-general.tsx
  31. 4 45
      packages/app/src/components/status-popover-body.tsx
  32. 1 1
      packages/app/src/context/file.tsx
  33. 120 101
      packages/app/src/context/global-sync.tsx
  34. 99 107
      packages/app/src/context/global-sync/bootstrap.ts
  35. 2 0
      packages/app/src/context/global-sync/child-store.test.ts
  36. 101 50
      packages/app/src/context/global-sync/child-store.ts
  37. 1 1
      packages/app/src/context/global-sync/event-reducer.ts
  38. 46 0
      packages/app/src/context/global-sync/queue.test.ts
  39. 9 6
      packages/app/src/context/global-sync/queue.ts
  40. 18 1
      packages/app/src/context/global-sync/utils.test.ts
  41. 1 0
      packages/app/src/context/global-sync/utils.ts
  42. 8 1
      packages/app/src/context/layout.tsx
  43. 2 2
      packages/app/src/context/local.tsx
  44. 2 2
      packages/app/src/context/notification.tsx
  45. 1 1
      packages/app/src/context/permission-auto-respond.test.ts
  46. 1 1
      packages/app/src/context/permission-auto-respond.ts
  47. 1 1
      packages/app/src/context/prompt.tsx
  48. 2 2
      packages/app/src/context/sync.tsx
  49. 20 0
      packages/app/src/entry.tsx
  50. 4 0
      packages/app/src/env.d.ts
  51. 2 2
      packages/app/src/i18n/ar.ts
  52. 2 2
      packages/app/src/i18n/br.ts
  53. 2 2
      packages/app/src/i18n/bs.ts
  54. 2 2
      packages/app/src/i18n/da.ts
  55. 2 2
      packages/app/src/i18n/de.ts
  56. 7 2
      packages/app/src/i18n/en.ts
  57. 2 2
      packages/app/src/i18n/es.ts
  58. 2 2
      packages/app/src/i18n/fr.ts
  59. 2 2
      packages/app/src/i18n/ja.ts
  60. 2 2
      packages/app/src/i18n/ko.ts
  61. 2 2
      packages/app/src/i18n/no.ts
  62. 2 2
      packages/app/src/i18n/pl.ts
  63. 2 2
      packages/app/src/i18n/ru.ts
  64. 2 2
      packages/app/src/i18n/th.ts
  65. 2 2
      packages/app/src/i18n/tr.ts
  66. 2 2
      packages/app/src/i18n/zh.ts
  67. 2 2
      packages/app/src/i18n/zht.ts
  68. 1 1
      packages/app/src/pages/directory-layout.tsx
  69. 20 2
      packages/app/src/pages/error.tsx
  70. 1 1
      packages/app/src/pages/home.tsx
  71. 37 43
      packages/app/src/pages/layout.tsx
  72. 8 8
      packages/app/src/pages/layout/helpers.test.ts
  73. 6 13
      packages/app/src/pages/layout/helpers.ts
  74. 5 4
      packages/app/src/pages/layout/sidebar-items.tsx
  75. 1 1
      packages/app/src/pages/layout/sidebar-project.tsx
  76. 5 4
      packages/app/src/pages/layout/sidebar-workspace.tsx
  77. 1 1
      packages/app/src/pages/session.tsx
  78. 1 1
      packages/app/src/pages/session/file-tabs.tsx
  79. 2 2
      packages/app/src/pages/session/message-timeline.tsx
  80. 1 1
      packages/app/src/pages/session/use-session-commands.tsx
  81. 1 1
      packages/app/src/utils/base64.ts
  82. 24 0
      packages/app/src/utils/path-key.ts
  83. 52 0
      packages/app/src/utils/persist.test.ts
  84. 194 59
      packages/app/src/utils/persist.ts
  85. 20 2
      packages/app/vite.config.ts
  86. 1 1
      packages/console/app/package.json
  87. 3 0
      packages/console/app/src/component/icon.tsx
  88. 2 2
      packages/console/app/src/config.ts
  89. 4 4
      packages/console/app/src/i18n/ar.ts
  90. 4 4
      packages/console/app/src/i18n/br.ts
  91. 4 4
      packages/console/app/src/i18n/da.ts
  92. 4 4
      packages/console/app/src/i18n/de.ts
  93. 4 4
      packages/console/app/src/i18n/en.ts
  94. 4 4
      packages/console/app/src/i18n/es.ts
  95. 4 4
      packages/console/app/src/i18n/fr.ts
  96. 4 4
      packages/console/app/src/i18n/it.ts
  97. 4 4
      packages/console/app/src/i18n/ja.ts
  98. 4 4
      packages/console/app/src/i18n/ko.ts
  99. 4 4
      packages/console/app/src/i18n/no.ts
  100. 4 4
      packages/console/app/src/i18n/pl.ts

+ 5 - 0
.github/VOUCHED.td

@@ -12,9 +12,13 @@ adamdotdevin
 ariane-emory
 ariane-emory
 -atharvau AI review spamming literally every PR
 -atharvau AI review spamming literally every PR
 -borealbytes
 -borealbytes
+-carycooper777
 -danieljoshuanazareth
 -danieljoshuanazareth
 -danieljoshuanazareth
 -danieljoshuanazareth
+-davidbernat looks to be a clawdbot that spams team and sends super weird emails, doesnt appear to be a real person
+dmtrkovalenko
 edemaine
 edemaine
+fahreddinozcan
 -florianleibert
 -florianleibert
 fwang
 fwang
 iamdavidhill
 iamdavidhill
@@ -31,5 +35,6 @@ rubdos
 shantur
 shantur
 simonklee
 simonklee
 -spider-yamet clawdbot/llm psychosis, spam pinging the team
 -spider-yamet clawdbot/llm psychosis, spam pinging the team
+-terisuke
 thdxr
 thdxr
 -toastythebot
 -toastythebot

+ 6 - 0
.github/workflows/deploy.yml

@@ -36,3 +36,9 @@ jobs:
           PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }}
           PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }}
           PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }}
           PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }}
           STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }}
           STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }}
+          SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
+          SENTRY_ORG: ${{ vars.SENTRY_ORG }}
+          SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }}
+          SENTRY_RELEASE: web@${{ github.sha }}
+          VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }}
+          VITE_SENTRY_RELEASE: web@${{ github.sha }}

+ 8 - 1
.github/workflows/publish.yml

@@ -88,7 +88,7 @@ jobs:
       - name: Build
       - name: Build
         id: build
         id: build
         run: |
         run: |
-          ./packages/opencode/script/build.ts
+          ./packages/opencode/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }}
         env:
         env:
           OPENCODE_VERSION: ${{ needs.version.outputs.version }}
           OPENCODE_VERSION: ${{ needs.version.outputs.version }}
           OPENCODE_RELEASE: ${{ needs.version.outputs.release }}
           OPENCODE_RELEASE: ${{ needs.version.outputs.release }}
@@ -494,6 +494,13 @@ jobs:
         working-directory: packages/desktop-electron
         working-directory: packages/desktop-electron
         env:
         env:
           OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
           OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
+          SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
+          SENTRY_ORG: ${{ vars.SENTRY_ORG }}
+          SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }}
+          SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }}
+          VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }}
+          VITE_SENTRY_ENVIRONMENT: ${{ (github.ref_name == 'beta' && 'beta') || 'production' }}
+          VITE_SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }}
 
 
       - name: Package and publish
       - name: Package and publish
         if: needs.version.outputs.release
         if: needs.version.outputs.release

+ 2 - 2
.github/workflows/review.yml

@@ -45,13 +45,13 @@ jobs:
 
 
       - name: Check PR guidelines compliance
       - name: Check PR guidelines compliance
         env:
         env:
-          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+          OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           OPENCODE_PERMISSION: '{ "bash": {  "*": "deny", "gh*": "allow", "gh pr review*": "deny" } }'
           OPENCODE_PERMISSION: '{ "bash": {  "*": "deny", "gh*": "allow", "gh pr review*": "deny" } }'
           PR_TITLE: ${{ steps.pr-details.outputs.title }}
           PR_TITLE: ${{ steps.pr-details.outputs.title }}
         run: |
         run: |
           PR_BODY=$(jq -r .body pr_data.json)
           PR_BODY=$(jq -r .body pr_data.json)
-          opencode run -m anthropic/claude-opus-4-5 "A new pull request has been created: '${PR_TITLE}'
+          opencode run -m opencode/gpt-5.5 --variant medium "A new pull request has been created: '${PR_TITLE}'
 
 
           <pr-number>
           <pr-number>
           ${{ steps.pr-number.outputs.number }}
           ${{ steps.pr-number.outputs.number }}

+ 0 - 899
.opencode/agent/translator.md

@@ -1,899 +0,0 @@
----
-description: Translate content for a specified locale while preserving technical terms
-mode: subagent
-model: opencode/gpt-5.4
----
-
-You are a professional translator and localization specialist.
-
-Translate the user's content into the requested target locale (language + region, e.g. fr-FR, de-DE).
-
-Requirements:
-
-- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure).
-- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks.
-- Also preserve every term listed in the Do-Not-Translate glossary below.
-- Also apply locale-specific guidance from `.opencode/glossary/<locale>.md` when available (for example, `zh-cn.md`).
-- Do not modify fenced code blocks.
-- Output ONLY the translation (no commentary).
-
-If the target locale is missing, ask the user to provide it.
-If no locale-specific glossary exists, use the global glossary only.
-
----
-
-# Locale-Specific Glossaries
-
-When a locale glossary exists, use it to:
-
-- Apply preferred wording for recurring UI/docs terms in that locale
-- Preserve locale-specific do-not-translate terms and casing decisions
-- Prefer natural phrasing over literal translation when the locale file calls it out
-- If the repo uses a locale alias slug, apply that file too (for example, `pt-BR` maps to `br.md` in this repo)
-
-Locale guidance does not override code/command preservation rules or the global Do-Not-Translate glossary below.
-
----
-
-# Do-Not-Translate Terms (OpenCode Docs)
-
-Generated from: `packages/web/src/content/docs/*.mdx` (default English docs)
-Generated on: 2026-02-10
-
-Use this as a translation QA checklist / glossary. Preserve listed terms exactly (spelling, casing, punctuation).
-
-General rules (verbatim, even if not listed below):
-
-- Anything inside inline code (single backticks) or fenced code blocks (triple backticks)
-- MDX/JS code in docs: `import ... from "..."`, component tags, identifiers
-- CLI commands, flags, config keys/values, file paths, URLs/domains, and env vars
-
-## Proper nouns and product names
-
-Additional (not reliably captured via link text):
-
-```text
-Astro
-Bun
-Chocolatey
-Cursor
-Docker
-Git
-GitHub Actions
-GitLab CI
-GNOME Terminal
-Homebrew
-Mise
-Neovim
-Node.js
-npm
-Obsidian
-opencode
-opencode-ai
-Paru
-pnpm
-ripgrep
-Scoop
-SST
-Starlight
-Visual Studio Code
-VS Code
-VSCodium
-Windsurf
-Windows Terminal
-Yarn
-Zellij
-Zed
-anomalyco
-```
-
-Extracted from link labels in the English docs (review and prune as desired):
-
-```text
-@openspoon/subtask2
-302.AI console
-ACP progress report
-Agent Client Protocol
-Agent Skills
-Agentic
-AGENTS.md
-AI SDK
-Alacritty
-Anthropic
-Anthropic's Data Policies
-Atom One
-Avante.nvim
-Ayu
-Azure AI Foundry
-Azure portal
-Baseten
-built-in GITHUB_TOKEN
-Bun.$
-Catppuccin
-Cerebras console
-ChatGPT Plus or Pro
-Cloudflare dashboard
-CodeCompanion.nvim
-CodeNomad
-Configuring Adapters: Environment Variables
-Context7 MCP server
-Cortecs console
-Deep Infra dashboard
-DeepSeek console
-Duo Agent Platform
-Everforest
-Fireworks AI console
-Firmware dashboard
-Ghostty
-GitLab CLI agents docs
-GitLab docs
-GitLab User Settings > Access Tokens
-Granular Rules (Object Syntax)
-Grep by Vercel
-Groq console
-Gruvbox
-Helicone
-Helicone documentation
-Helicone Header Directory
-Helicone's Model Directory
-Hugging Face Inference Providers
-Hugging Face settings
-install WSL
-IO.NET console
-JetBrains IDE
-Kanagawa
-Kitty
-MiniMax API Console
-Models.dev
-Moonshot AI console
-Nebius Token Factory console
-Nord
-OAuth
-Ollama integration docs
-OpenAI's Data Policies
-OpenChamber
-OpenCode
-OpenCode config
-OpenCode Config
-OpenCode TUI with the opencode theme
-OpenCode Web - Active Session
-OpenCode Web - New Session
-OpenCode Web - See Servers
-OpenCode Zen
-OpenCode-Obsidian
-OpenRouter dashboard
-OpenWork
-OVHcloud panel
-Pro+ subscription
-SAP BTP Cockpit
-Scaleway Console IAM settings
-Scaleway Generative APIs
-SDK documentation
-Sentry MCP server
-shell API
-Together AI console
-Tokyonight
-Unified Billing
-Venice AI console
-Vercel dashboard
-WezTerm
-Windows Subsystem for Linux (WSL)
-WSL
-WSL (Windows Subsystem for Linux)
-WSL extension
-xAI console
-Z.AI API console
-Zed
-ZenMux dashboard
-Zod
-```
-
-## Acronyms and initialisms
-
-```text
-ACP
-AGENTS
-AI
-AI21
-ANSI
-API
-AST
-AWS
-BTP
-CD
-CDN
-CI
-CLI
-CMD
-CORS
-DEBUG
-EKS
-ERROR
-FAQ
-GLM
-GNOME
-GPT
-HTML
-HTTP
-HTTPS
-IAM
-ID
-IDE
-INFO
-IO
-IP
-IRSA
-JS
-JSON
-JSONC
-K2
-LLM
-LM
-LSP
-M2
-MCP
-MR
-NET
-NPM
-NTLM
-OIDC
-OS
-PAT
-PATH
-PHP
-PR
-PTY
-README
-RFC
-RPC
-SAP
-SDK
-SKILL
-SSE
-SSO
-TS
-TTY
-TUI
-UI
-URL
-US
-UX
-VCS
-VPC
-VPN
-VS
-WARN
-WSL
-X11
-YAML
-```
-
-## Code identifiers used in prose (CamelCase, mixedCase)
-
-```text
-apiKey
-AppleScript
-AssistantMessage
-baseURL
-BurntSushi
-ChatGPT
-ClangFormat
-CodeCompanion
-CodeNomad
-DeepSeek
-DefaultV2
-FileContent
-FileDiff
-FileNode
-fineGrained
-FormatterStatus
-GitHub
-GitLab
-iTerm2
-JavaScript
-JetBrains
-macOS
-mDNS
-MiniMax
-NeuralNomadsAI
-NickvanDyke
-NoeFabris
-OpenAI
-OpenAPI
-OpenChamber
-OpenCode
-OpenRouter
-OpenTUI
-OpenWork
-ownUserPermissions
-PowerShell
-ProviderAuthAuthorization
-ProviderAuthMethod
-ProviderInitError
-SessionStatus
-TabItem
-tokenType
-ToolIDs
-ToolList
-TypeScript
-typesUrl
-UserMessage
-VcsInfo
-WebView2
-WezTerm
-xAI
-ZenMux
-```
-
-## OpenCode CLI commands (as shown in docs)
-
-```text
-opencode
-opencode [project]
-opencode /path/to/project
-opencode acp
-opencode agent [command]
-opencode agent create
-opencode agent list
-opencode attach [url]
-opencode attach http://10.20.30.40:4096
-opencode attach http://localhost:4096
-opencode auth [command]
-opencode auth list
-opencode auth login
-opencode auth logout
-opencode auth ls
-opencode export [sessionID]
-opencode github [command]
-opencode github install
-opencode github run
-opencode import <file>
-opencode import https://opncd.ai/s/abc123
-opencode import session.json
-opencode mcp [command]
-opencode mcp add
-opencode mcp auth [name]
-opencode mcp auth list
-opencode mcp auth ls
-opencode mcp auth my-oauth-server
-opencode mcp auth sentry
-opencode mcp debug <name>
-opencode mcp debug my-oauth-server
-opencode mcp list
-opencode mcp logout [name]
-opencode mcp logout my-oauth-server
-opencode mcp ls
-opencode models --refresh
-opencode models [provider]
-opencode models anthropic
-opencode run [message..]
-opencode run Explain the use of context in Go
-opencode serve
-opencode serve --cors http://localhost:5173 --cors https://app.example.com
-opencode serve --hostname 0.0.0.0 --port 4096
-opencode serve [--port <number>] [--hostname <string>] [--cors <origin>]
-opencode session [command]
-opencode session list
-opencode session delete <sessionID>
-opencode stats
-opencode uninstall
-opencode upgrade
-opencode upgrade [target]
-opencode upgrade v0.1.48
-opencode web
-opencode web --cors https://example.com
-opencode web --hostname 0.0.0.0
-opencode web --mdns
-opencode web --mdns --mdns-domain myproject.local
-opencode web --port 4096
-opencode web --port 4096 --hostname 0.0.0.0
-opencode.server.close()
-```
-
-## Slash commands and routes
-
-```text
-/agent
-/auth/:id
-/clear
-/command
-/config
-/config/providers
-/connect
-/continue
-/doc
-/editor
-/event
-/experimental/tool?provider=<p>&model=<m>
-/experimental/tool/ids
-/export
-/file?path=<path>
-/file/content?path=<p>
-/file/status
-/find?pattern=<pat>
-/find/file
-/find/file?query=<q>
-/find/symbol?query=<q>
-/formatter
-/global/event
-/global/health
-/help
-/init
-/instance/dispose
-/log
-/lsp
-/mcp
-/mnt/
-/mnt/c/
-/mnt/d/
-/models
-/oc
-/opencode
-/path
-/project
-/project/current
-/provider
-/provider/{id}/oauth/authorize
-/provider/{id}/oauth/callback
-/provider/auth
-/q
-/quit
-/redo
-/resume
-/session
-/session/:id
-/session/:id/abort
-/session/:id/children
-/session/:id/command
-/session/:id/diff
-/session/:id/fork
-/session/:id/init
-/session/:id/message
-/session/:id/message/:messageID
-/session/:id/permissions/:permissionID
-/session/:id/prompt_async
-/session/:id/revert
-/session/:id/share
-/session/:id/shell
-/session/:id/summarize
-/session/:id/todo
-/session/:id/unrevert
-/session/status
-/share
-/summarize
-/theme
-/tui
-/tui/append-prompt
-/tui/clear-prompt
-/tui/control/next
-/tui/control/response
-/tui/execute-command
-/tui/open-help
-/tui/open-models
-/tui/open-sessions
-/tui/open-themes
-/tui/show-toast
-/tui/submit-prompt
-/undo
-/Users/username
-/Users/username/projects/*
-/vcs
-```
-
-## CLI flags and short options
-
-```text
---agent
---attach
---command
---continue
---cors
---cwd
---days
---dir
---dry-run
---event
---file
---force
---fork
---format
---help
---hostname
---hostname 0.0.0.0
---keep-config
---keep-data
---log-level
---max-count
---mdns
---mdns-domain
---method
---model
---models
---port
---print-logs
---project
---prompt
---refresh
---session
---share
---title
---token
---tools
---verbose
---version
---wait
-
--c
--d
--f
--h
--m
--n
--s
--v
-```
-
-## Environment variables
-
-```text
-AI_API_URL
-AI_FLOW_CONTEXT
-AI_FLOW_EVENT
-AI_FLOW_INPUT
-AICORE_DEPLOYMENT_ID
-AICORE_RESOURCE_GROUP
-AICORE_SERVICE_KEY
-ANTHROPIC_API_KEY
-AWS_ACCESS_KEY_ID
-AWS_BEARER_TOKEN_BEDROCK
-AWS_PROFILE
-AWS_REGION
-AWS_ROLE_ARN
-AWS_SECRET_ACCESS_KEY
-AWS_WEB_IDENTITY_TOKEN_FILE
-AZURE_COGNITIVE_SERVICES_RESOURCE_NAME
-AZURE_RESOURCE_NAME
-CI_PROJECT_DIR
-CI_SERVER_FQDN
-CI_WORKLOAD_REF
-CLOUDFLARE_ACCOUNT_ID
-CLOUDFLARE_API_TOKEN
-CLOUDFLARE_GATEWAY_ID
-CONTEXT7_API_KEY
-GITHUB_TOKEN
-GITLAB_AI_GATEWAY_URL
-GITLAB_HOST
-GITLAB_INSTANCE_URL
-GITLAB_OAUTH_CLIENT_ID
-GITLAB_TOKEN
-GITLAB_TOKEN_OPENCODE
-GOOGLE_APPLICATION_CREDENTIALS
-GOOGLE_CLOUD_PROJECT
-HTTP_PROXY
-HTTPS_PROXY
-K2_
-MY_API_KEY
-MY_ENV_VAR
-MY_MCP_CLIENT_ID
-MY_MCP_CLIENT_SECRET
-NO_PROXY
-NODE_ENV
-NODE_EXTRA_CA_CERTS
-NPM_AUTH_TOKEN
-OC_ALLOW_WAYLAND
-OPENCODE_API_KEY
-OPENCODE_AUTH_JSON
-OPENCODE_AUTO_SHARE
-OPENCODE_CLIENT
-OPENCODE_CONFIG
-OPENCODE_CONFIG_CONTENT
-OPENCODE_CONFIG_DIR
-OPENCODE_DISABLE_AUTOCOMPACT
-OPENCODE_DISABLE_AUTOUPDATE
-OPENCODE_DISABLE_CLAUDE_CODE
-OPENCODE_DISABLE_CLAUDE_CODE_PROMPT
-OPENCODE_DISABLE_CLAUDE_CODE_SKILLS
-OPENCODE_DISABLE_DEFAULT_PLUGINS
-OPENCODE_DISABLE_LSP_DOWNLOAD
-OPENCODE_DISABLE_MODELS_FETCH
-OPENCODE_DISABLE_PRUNE
-OPENCODE_DISABLE_TERMINAL_TITLE
-OPENCODE_ENABLE_EXA
-OPENCODE_ENABLE_EXPERIMENTAL_MODELS
-OPENCODE_EXPERIMENTAL
-OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS
-OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT
-OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER
-OPENCODE_EXPERIMENTAL_EXA
-OPENCODE_EXPERIMENTAL_FILEWATCHER
-OPENCODE_EXPERIMENTAL_ICON_DISCOVERY
-OPENCODE_EXPERIMENTAL_LSP_TOOL
-OPENCODE_EXPERIMENTAL_LSP_TY
-OPENCODE_EXPERIMENTAL_MARKDOWN
-OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX
-OPENCODE_EXPERIMENTAL_OXFMT
-OPENCODE_EXPERIMENTAL_PLAN_MODE
-OPENCODE_ENABLE_QUESTION_TOOL
-OPENCODE_FAKE_VCS
-OPENCODE_GIT_BASH_PATH
-OPENCODE_MODEL
-OPENCODE_MODELS_URL
-OPENCODE_PERMISSION
-OPENCODE_PORT
-OPENCODE_SERVER_PASSWORD
-OPENCODE_SERVER_USERNAME
-PROJECT_ROOT
-RESOURCE_NAME
-RUST_LOG
-VARIABLE_NAME
-VERTEX_LOCATION
-XDG_CONFIG_HOME
-```
-
-## Package/module identifiers
-
-```text
-../../../config.mjs
-@astrojs/starlight/components
-@opencode-ai/plugin
-@opencode-ai/sdk
-path
-shescape
-zod
-
-@
-@ai-sdk/anthropic
-@ai-sdk/cerebras
-@ai-sdk/google
-@ai-sdk/openai
-@ai-sdk/openai-compatible
-@File#L37-42
-@modelcontextprotocol/server-everything
-@opencode
-```
-
-## GitHub owner/repo slugs referenced in docs
-
-```text
-24601/opencode-zellij-namer
-angristan/opencode-wakatime
-anomalyco/opencode
-apps/opencode-agent
-athal7/opencode-devcontainers
-awesome-opencode/awesome-opencode
-backnotprop/plannotator
-ben-vargas/ai-sdk-provider-opencode-sdk
-btriapitsyn/openchamber
-BurntSushi/ripgrep
-Cluster444/agentic
-code-yeongyu/oh-my-opencode
-darrenhinde/opencode-agents
-different-ai/opencode-scheduler
-different-ai/openwork
-features/copilot
-folke/tokyonight.nvim
-franlol/opencode-md-table-formatter
-ggml-org/llama.cpp
-ghoulr/opencode-websearch-cited.git
-H2Shami/opencode-helicone-session
-hosenur/portal
-jamesmurdza/daytona
-jenslys/opencode-gemini-auth
-JRedeker/opencode-morph-fast-apply
-JRedeker/opencode-shell-strategy
-kdcokenny/ocx
-kdcokenny/opencode-background-agents
-kdcokenny/opencode-notify
-kdcokenny/opencode-workspace
-kdcokenny/opencode-worktree
-login/device
-mohak34/opencode-notifier
-morhetz/gruvbox
-mtymek/opencode-obsidian
-NeuralNomadsAI/CodeNomad
-nick-vi/opencode-type-inject
-NickvanDyke/opencode.nvim
-NoeFabris/opencode-antigravity-auth
-nordtheme/nord
-numman-ali/opencode-openai-codex-auth
-olimorris/codecompanion.nvim
-panta82/opencode-notificator
-rebelot/kanagawa.nvim
-remorses/kimaki
-sainnhe/everforest
-shekohex/opencode-google-antigravity-auth
-shekohex/opencode-pty.git
-spoons-and-mirrors/subtask2
-sudo-tee/opencode.nvim
-supermemoryai/opencode-supermemory
-Tarquinen/opencode-dynamic-context-pruning
-Th3Whit3Wolf/one-nvim
-upstash/context7
-vtemian/micode
-vtemian/octto
-yetone/avante.nvim
-zenobi-us/opencode-plugin-template
-zenobi-us/opencode-skillful
-```
-
-## Paths, filenames, globs, and URLs
-
-```text
-./.opencode/themes/*.json
-./<project-slug>/storage/
-./config/#custom-directory
-./global/storage/
-.agents/skills/*/SKILL.md
-.agents/skills/<name>/SKILL.md
-.clang-format
-.claude
-.claude/skills
-.claude/skills/*/SKILL.md
-.claude/skills/<name>/SKILL.md
-.env
-.github/workflows/opencode.yml
-.gitignore
-.gitlab-ci.yml
-.ignore
-.NET SDK
-.npmrc
-.ocamlformat
-.opencode
-.opencode/
-.opencode/agents/
-.opencode/commands/
-.opencode/commands/test.md
-.opencode/modes/
-.opencode/plans/*.md
-.opencode/plugins/
-.opencode/skills/<name>/SKILL.md
-.opencode/skills/git-release/SKILL.md
-.opencode/tools/
-.well-known/opencode
-{ type: "raw" \| "patch", content: string }
-{file:path/to/file}
-**/*.js
-%USERPROFILE%/intelephense/license.txt
-%USERPROFILE%\.cache\opencode
-%USERPROFILE%\.config\opencode\opencode.jsonc
-%USERPROFILE%\.config\opencode\plugins
-%USERPROFILE%\.local\share\opencode
-%USERPROFILE%\.local\share\opencode\log
-<project-root>/.opencode/themes/*.json
-<providerId>/<modelId>
-<your-project>/.opencode/plugins/
-~
-~/...
-~/.agents/skills/*/SKILL.md
-~/.agents/skills/<name>/SKILL.md
-~/.aws/credentials
-~/.bashrc
-~/.cache/opencode
-~/.cache/opencode/node_modules/
-~/.claude/CLAUDE.md
-~/.claude/skills/
-~/.claude/skills/*/SKILL.md
-~/.claude/skills/<name>/SKILL.md
-~/.config/opencode
-~/.config/opencode/AGENTS.md
-~/.config/opencode/agents/
-~/.config/opencode/commands/
-~/.config/opencode/modes/
-~/.config/opencode/opencode.json
-~/.config/opencode/opencode.jsonc
-~/.config/opencode/plugins/
-~/.config/opencode/skills/*/SKILL.md
-~/.config/opencode/skills/<name>/SKILL.md
-~/.config/opencode/themes/*.json
-~/.config/opencode/tools/
-~/.config/zed/settings.json
-~/.local/share
-~/.local/share/opencode/
-~/.local/share/opencode/auth.json
-~/.local/share/opencode/log/
-~/.local/share/opencode/mcp-auth.json
-~/.local/share/opencode/opencode.jsonc
-~/.npmrc
-~/.zshrc
-~/code/
-~/Library/Application Support
-~/projects/*
-~/projects/personal/
-${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts
-$HOME/intelephense/license.txt
-$HOME/projects/*
-$XDG_CONFIG_HOME/opencode/themes/*.json
-agent/
-agents/
-build/
-commands/
-dist/
-http://<wsl-ip>:4096
-http://127.0.0.1:8080/callback
-http://localhost:<port>
-http://localhost:4096
-http://localhost:4096/doc
-https://app.example.com
-https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/
-https://opencode.ai/zen/v1/chat/completions
-https://opencode.ai/zen/v1/messages
-https://opencode.ai/zen/v1/models/gemini-3-flash
-https://opencode.ai/zen/v1/models/gemini-3-pro
-https://opencode.ai/zen/v1/responses
-https://RESOURCE_NAME.openai.azure.com/
-laravel/pint
-log/
-model: "anthropic/claude-sonnet-4-5"
-modes/
-node_modules/
-openai/gpt-4.1
-opencode.ai/config.json
-opencode/<model-id>
-opencode/gpt-5.1-codex
-opencode/gpt-5.2-codex
-opencode/kimi-k2
-openrouter/google/gemini-2.5-flash
-opncd.ai/s/<share-id>
-packages/*/AGENTS.md
-plugins/
-project/
-provider_id/model_id
-provider/model
-provider/model-id
-rm -rf ~/.cache/opencode
-skills/
-skills/*/SKILL.md
-src/**/*.ts
-themes/
-tools/
-```
-
-## Keybind strings
-
-```text
-alt+b
-Alt+Ctrl+K
-alt+d
-alt+f
-Cmd+Esc
-Cmd+Option+K
-Cmd+Shift+Esc
-Cmd+Shift+G
-Cmd+Shift+P
-ctrl+a
-ctrl+b
-ctrl+d
-ctrl+e
-Ctrl+Esc
-ctrl+f
-ctrl+g
-ctrl+k
-Ctrl+Shift+Esc
-Ctrl+Shift+P
-ctrl+t
-ctrl+u
-ctrl+w
-ctrl+x
-DELETE
-Shift+Enter
-WIN+R
-```
-
-## Model ID strings referenced
-
-```text
-{env:OPENCODE_MODEL}
-anthropic/claude-3-5-sonnet-20241022
-anthropic/claude-haiku-4-20250514
-anthropic/claude-haiku-4-5
-anthropic/claude-sonnet-4-20250514
-anthropic/claude-sonnet-4-5
-gitlab/duo-chat-haiku-4-5
-lmstudio/google/gemma-3n-e4b
-openai/gpt-4.1
-openai/gpt-5
-opencode/gpt-5.1-codex
-opencode/gpt-5.2-codex
-opencode/kimi-k2
-openrouter/google/gemini-2.5-flash
-```

+ 14 - 0
.opencode/command/translate.md

@@ -0,0 +1,14 @@
+---
+description: translate English to other languages
+model: opencode/claude-opus-4-7
+---
+
+run git diff and translate changed english doc and UI copy files to other international languages. Translate all languages in parallel to save time.
+
+Requirements:
+
+- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure).
+- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks.
+- Also preserve every term listed in the Do-Not-Translate glossary below.
+- Also apply locale-specific guidance from `.opencode/glossary/<locale>.md` when available (for example, `zh-cn.md`).
+- Do not modify fenced code blocks.

+ 27 - 10
.opencode/skills/effect/SKILL.md

@@ -1,21 +1,38 @@
 ---
 ---
 name: effect
 name: effect
-description: Answer questions about the Effect framework
+description: Work with Effect v4 / effect-smol TypeScript code in this repo
 ---
 ---
 
 
 # Effect
 # Effect
 
 
-This codebase uses Effect, a framework for writing typescript.
+This codebase uses Effect for typed, composable TypeScript services, schemas, and workflows.
 
 
-## How to Answer Effect Questions
+## Source Of Truth
 
 
-1. Clone the Effect repository: `https://github.com/Effect-TS/effect-smol` to
-   `.opencode/references/effect-smol` in this project NOT the skill folder.
-2. Use the explore agent to search the codebase for answers about Effect patterns, APIs, and concepts
-3. Provide responses based on the actual Effect source code and documentation
+Use the current Effect v4 / effect-smol source, not memory or older Effect v2/v3 examples.
+
+1. If `.opencode/references/effect-smol` is missing, clone `https://github.com/Effect-TS/effect-smol` there. Do this in the project, not in the skill folder.
+2. Search `.opencode/references/effect-smol` for exact APIs, examples, tests, and naming patterns before answering or implementing Effect-specific code.
+3. Also inspect existing repo code for local house style before introducing new patterns.
+4. Prefer answers and implementations backed by specific source files or nearby repo examples.
 
 
 ## Guidelines
 ## Guidelines
 
 
-- Always use the explore agent with the cloned repository when answering Effect-related questions
-- Reference specific files and patterns found in the Effect codebase
-- Do not answer from memory - always verify against the source
+- Prefer current Effect v4 APIs and project-local patterns over old blog posts, examples, or package-memory guesses.
+- Use `Effect.gen(function* () { ... })` for multi-step workflows.
+- Use `Effect.fn("Name")` or `Effect.fnUntraced(...)` for named effects when adding reusable service methods or important workflows.
+- Prefer Effect `Schema` for API and domain data shapes. Use branded schemas for IDs and `Schema.TaggedErrorClass` for typed domain errors when modeling new error surfaces.
+- Keep HTTP handlers thin: decode input, read request context, call services, and map transport errors. Put business rules in services.
+- In Effect service code, prefer Effect-aware platform abstractions and dependencies over ad hoc promises where the surrounding code already does so.
+- Keep layer composition explicit. Avoid broad hidden provisioning that makes missing dependencies hard to see.
+- In tests, prefer the repo's existing Effect test helpers and live tests for filesystem, git, child process, locks, or timing behavior.
+- Do not introduce `any`, non-null assertions, unchecked casts, or older Effect APIs just to satisfy types.
+- Do not answer from memory. Verify against `.opencode/references/effect-smol` or nearby code first.
+
+## Testing Patterns
+
+- Use `testEffect(...)` from `packages/opencode/test/lib/effect.ts` for tests that exercise Effect services, layers, runtime context, scoped resources, or platform integrations.
+- Use `it.live(...)` for filesystem, git repositories, HTTP servers, sockets, child processes, locks, real time, and other live platform behavior.
+- Run tests from package directories such as `packages/opencode`; never run package tests from the repo root.
+- Prefer explicit test layers over ad hoc managed runtimes. Keep dependency provisioning visible in the test file.
+- Use scoped fixtures and finalizers for resources that must be cleaned up, including temporary directories, flags, databases, fibers, servers, and global state.

+ 2 - 2
.opencode/tool/github-triage.ts

@@ -3,8 +3,8 @@ import { tool } from "@opencode-ai/plugin"
 const TEAM = {
 const TEAM = {
   desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"],
   desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"],
   zen: ["fwang", "MrMushrooooom"],
   zen: ["fwang", "MrMushrooooom"],
-  tui: ["thdxr", "kommander", "rekram1-node"],
-  core: ["thdxr", "rekram1-node", "jlongster"],
+  tui: ["kommander", "rekram1-node", "simonklee"],
+  core: ["kitlangton", "rekram1-node", "jlongster"],
   docs: ["R44VC0RP"],
   docs: ["R44VC0RP"],
   windows: ["Hona"],
   windows: ["Hona"],
 } as const
 } as const

+ 238 - 106
bun.lock

@@ -29,12 +29,13 @@
     },
     },
     "packages/app": {
     "packages/app": {
       "name": "@opencode-ai/app",
       "name": "@opencode-ai/app",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@kobalte/core": "catalog:",
         "@kobalte/core": "catalog:",
+        "@opencode-ai/core": "workspace:*",
         "@opencode-ai/sdk": "workspace:*",
         "@opencode-ai/sdk": "workspace:*",
-        "@opencode-ai/shared": "workspace:*",
         "@opencode-ai/ui": "workspace:*",
         "@opencode-ai/ui": "workspace:*",
+        "@sentry/solid": "catalog:",
         "@shikijs/transformers": "3.9.2",
         "@shikijs/transformers": "3.9.2",
         "@solid-primitives/active-element": "2.1.3",
         "@solid-primitives/active-element": "2.1.3",
         "@solid-primitives/audio": "1.4.2",
         "@solid-primitives/audio": "1.4.2",
@@ -69,6 +70,7 @@
       "devDependencies": {
       "devDependencies": {
         "@happy-dom/global-registrator": "20.0.11",
         "@happy-dom/global-registrator": "20.0.11",
         "@playwright/test": "catalog:",
         "@playwright/test": "catalog:",
+        "@sentry/vite-plugin": "catalog:",
         "@tailwindcss/vite": "catalog:",
         "@tailwindcss/vite": "catalog:",
         "@tsconfig/bun": "1.0.9",
         "@tsconfig/bun": "1.0.9",
         "@types/bun": "catalog:",
         "@types/bun": "catalog:",
@@ -83,7 +85,7 @@
     },
     },
     "packages/console/app": {
     "packages/console/app": {
       "name": "@opencode-ai/console-app",
       "name": "@opencode-ai/console-app",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@cloudflare/vite-plugin": "1.15.2",
         "@cloudflare/vite-plugin": "1.15.2",
         "@ibm/plex": "6.4.1",
         "@ibm/plex": "6.4.1",
@@ -117,7 +119,7 @@
     },
     },
     "packages/console/core": {
     "packages/console/core": {
       "name": "@opencode-ai/console-core",
       "name": "@opencode-ai/console-core",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@aws-sdk/client-sts": "3.782.0",
         "@aws-sdk/client-sts": "3.782.0",
         "@jsx-email/render": "1.1.1",
         "@jsx-email/render": "1.1.1",
@@ -144,7 +146,7 @@
     },
     },
     "packages/console/function": {
     "packages/console/function": {
       "name": "@opencode-ai/console-function",
       "name": "@opencode-ai/console-function",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@ai-sdk/anthropic": "3.0.64",
         "@ai-sdk/anthropic": "3.0.64",
         "@ai-sdk/openai": "3.0.48",
         "@ai-sdk/openai": "3.0.48",
@@ -168,7 +170,7 @@
     },
     },
     "packages/console/mail": {
     "packages/console/mail": {
       "name": "@opencode-ai/console-mail",
       "name": "@opencode-ai/console-mail",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@jsx-email/all": "2.2.3",
         "@jsx-email/all": "2.2.3",
         "@jsx-email/cli": "1.4.3",
         "@jsx-email/cli": "1.4.3",
@@ -190,12 +192,47 @@
         "cloudflare": "5.2.0",
         "cloudflare": "5.2.0",
       },
       },
     },
     },
+    "packages/core": {
+      "name": "@opencode-ai/core",
+      "version": "1.14.31",
+      "bin": {
+        "opencode": "./bin/opencode",
+      },
+      "dependencies": {
+        "@effect/opentelemetry": "catalog:",
+        "@effect/platform-node": "catalog:",
+        "@npmcli/arborist": "9.4.0",
+        "@npmcli/config": "10.8.1",
+        "@opentelemetry/api": "1.9.0",
+        "@opentelemetry/context-async-hooks": "2.6.1",
+        "@opentelemetry/exporter-trace-otlp-http": "0.214.0",
+        "@opentelemetry/sdk-trace-base": "2.6.1",
+        "cross-spawn": "catalog:",
+        "effect": "catalog:",
+        "glob": "13.0.5",
+        "mime-types": "3.0.2",
+        "minimatch": "10.2.5",
+        "npm-package-arg": "13.0.2",
+        "semver": "^7.6.3",
+        "xdg-basedir": "5.1.0",
+        "zod": "catalog:",
+      },
+      "devDependencies": {
+        "@tsconfig/bun": "catalog:",
+        "@types/bun": "catalog:",
+        "@types/cross-spawn": "catalog:",
+        "@types/npm-package-arg": "6.1.4",
+        "@types/npmcli__arborist": "6.3.3",
+        "@types/semver": "catalog:",
+      },
+    },
     "packages/desktop": {
     "packages/desktop": {
       "name": "@opencode-ai/desktop",
       "name": "@opencode-ai/desktop",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@opencode-ai/app": "workspace:*",
         "@opencode-ai/app": "workspace:*",
         "@opencode-ai/ui": "workspace:*",
         "@opencode-ai/ui": "workspace:*",
+        "@sentry/solid": "catalog:",
         "@solid-primitives/i18n": "2.2.1",
         "@solid-primitives/i18n": "2.2.1",
         "@solid-primitives/storage": "catalog:",
         "@solid-primitives/storage": "catalog:",
         "@solidjs/meta": "catalog:",
         "@solidjs/meta": "catalog:",
@@ -216,6 +253,7 @@
       },
       },
       "devDependencies": {
       "devDependencies": {
         "@actions/artifact": "4.0.0",
         "@actions/artifact": "4.0.0",
+        "@sentry/vite-plugin": "catalog:",
         "@tauri-apps/cli": "^2",
         "@tauri-apps/cli": "^2",
         "@types/bun": "catalog:",
         "@types/bun": "catalog:",
         "@typescript/native-preview": "catalog:",
         "@typescript/native-preview": "catalog:",
@@ -225,7 +263,7 @@
     },
     },
     "packages/desktop-electron": {
     "packages/desktop-electron": {
       "name": "@opencode-ai/desktop-electron",
       "name": "@opencode-ai/desktop-electron",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "drizzle-orm": "catalog:",
         "drizzle-orm": "catalog:",
         "effect": "catalog:",
         "effect": "catalog:",
@@ -241,6 +279,8 @@
         "@lydell/node-pty": "catalog:",
         "@lydell/node-pty": "catalog:",
         "@opencode-ai/app": "workspace:*",
         "@opencode-ai/app": "workspace:*",
         "@opencode-ai/ui": "workspace:*",
         "@opencode-ai/ui": "workspace:*",
+        "@sentry/solid": "catalog:",
+        "@sentry/vite-plugin": "catalog:",
         "@solid-primitives/i18n": "2.2.1",
         "@solid-primitives/i18n": "2.2.1",
         "@solid-primitives/storage": "catalog:",
         "@solid-primitives/storage": "catalog:",
         "@solidjs/meta": "catalog:",
         "@solidjs/meta": "catalog:",
@@ -269,9 +309,9 @@
     },
     },
     "packages/enterprise": {
     "packages/enterprise": {
       "name": "@opencode-ai/enterprise",
       "name": "@opencode-ai/enterprise",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
-        "@opencode-ai/shared": "workspace:*",
+        "@opencode-ai/core": "workspace:*",
         "@opencode-ai/ui": "workspace:*",
         "@opencode-ai/ui": "workspace:*",
         "@pierre/diffs": "catalog:",
         "@pierre/diffs": "catalog:",
         "@solidjs/meta": "catalog:",
         "@solidjs/meta": "catalog:",
@@ -298,7 +338,7 @@
     },
     },
     "packages/function": {
     "packages/function": {
       "name": "@opencode-ai/function",
       "name": "@opencode-ai/function",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@octokit/auth-app": "8.0.1",
         "@octokit/auth-app": "8.0.1",
         "@octokit/rest": "catalog:",
         "@octokit/rest": "catalog:",
@@ -314,7 +354,7 @@
     },
     },
     "packages/opencode": {
     "packages/opencode": {
       "name": "opencode",
       "name": "opencode",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "bin": {
       "bin": {
         "opencode": "./bin/opencode",
         "opencode": "./bin/opencode",
       },
       },
@@ -353,22 +393,20 @@
         "@hono/zod-validator": "catalog:",
         "@hono/zod-validator": "catalog:",
         "@lydell/node-pty": "catalog:",
         "@lydell/node-pty": "catalog:",
         "@modelcontextprotocol/sdk": "1.27.1",
         "@modelcontextprotocol/sdk": "1.27.1",
-        "@npmcli/arborist": "9.4.0",
-        "@npmcli/config": "10.8.1",
         "@octokit/graphql": "9.0.2",
         "@octokit/graphql": "9.0.2",
         "@octokit/rest": "catalog:",
         "@octokit/rest": "catalog:",
         "@openauthjs/openauth": "catalog:",
         "@openauthjs/openauth": "catalog:",
         "@opencode-ai/plugin": "workspace:*",
         "@opencode-ai/plugin": "workspace:*",
         "@opencode-ai/script": "workspace:*",
         "@opencode-ai/script": "workspace:*",
         "@opencode-ai/sdk": "workspace:*",
         "@opencode-ai/sdk": "workspace:*",
-        "@openrouter/ai-sdk-provider": "2.5.1",
+        "@openrouter/ai-sdk-provider": "2.8.1",
         "@opentelemetry/api": "1.9.0",
         "@opentelemetry/api": "1.9.0",
         "@opentelemetry/context-async-hooks": "2.6.1",
         "@opentelemetry/context-async-hooks": "2.6.1",
         "@opentelemetry/exporter-trace-otlp-http": "0.214.0",
         "@opentelemetry/exporter-trace-otlp-http": "0.214.0",
         "@opentelemetry/sdk-trace-base": "2.6.1",
         "@opentelemetry/sdk-trace-base": "2.6.1",
         "@opentelemetry/sdk-trace-node": "2.6.1",
         "@opentelemetry/sdk-trace-node": "2.6.1",
-        "@opentui/core": "0.1.99",
-        "@opentui/solid": "0.1.99",
+        "@opentui/core": "catalog:",
+        "@opentui/solid": "catalog:",
         "@parcel/watcher": "2.5.1",
         "@parcel/watcher": "2.5.1",
         "@pierre/diffs": "catalog:",
         "@pierre/diffs": "catalog:",
         "@solid-primitives/event-bus": "1.1.2",
         "@solid-primitives/event-bus": "1.1.2",
@@ -403,7 +441,7 @@
         "open": "10.1.2",
         "open": "10.1.2",
         "opencode-gitlab-auth": "2.0.1",
         "opencode-gitlab-auth": "2.0.1",
         "opencode-poe-auth": "0.0.1",
         "opencode-poe-auth": "0.0.1",
-        "opentui-spinner": "0.0.6",
+        "opentui-spinner": "catalog:",
         "partial-json": "0.1.7",
         "partial-json": "0.1.7",
         "remeda": "catalog:",
         "remeda": "catalog:",
         "semver": "^7.6.3",
         "semver": "^7.6.3",
@@ -424,10 +462,9 @@
       },
       },
       "devDependencies": {
       "devDependencies": {
         "@babel/core": "7.28.4",
         "@babel/core": "7.28.4",
-        "@effect/language-service": "0.84.2",
         "@octokit/webhooks-types": "7.6.1",
         "@octokit/webhooks-types": "7.6.1",
+        "@opencode-ai/core": "workspace:*",
         "@opencode-ai/script": "workspace:*",
         "@opencode-ai/script": "workspace:*",
-        "@opencode-ai/shared": "workspace:*",
         "@parcel/watcher-darwin-arm64": "2.5.1",
         "@parcel/watcher-darwin-arm64": "2.5.1",
         "@parcel/watcher-darwin-x64": "2.5.1",
         "@parcel/watcher-darwin-x64": "2.5.1",
         "@parcel/watcher-linux-arm64-glibc": "2.5.1",
         "@parcel/watcher-linux-arm64-glibc": "2.5.1",
@@ -443,7 +480,6 @@
         "@types/cross-spawn": "catalog:",
         "@types/cross-spawn": "catalog:",
         "@types/mime-types": "3.0.1",
         "@types/mime-types": "3.0.1",
         "@types/npm-package-arg": "6.1.4",
         "@types/npm-package-arg": "6.1.4",
-        "@types/npmcli__arborist": "6.3.3",
         "@types/semver": "^7.5.8",
         "@types/semver": "^7.5.8",
         "@types/turndown": "5.0.5",
         "@types/turndown": "5.0.5",
         "@types/which": "3.0.4",
         "@types/which": "3.0.4",
@@ -451,6 +487,7 @@
         "@typescript/native-preview": "catalog:",
         "@typescript/native-preview": "catalog:",
         "drizzle-kit": "catalog:",
         "drizzle-kit": "catalog:",
         "drizzle-orm": "catalog:",
         "drizzle-orm": "catalog:",
+        "prettier": "3.6.2",
         "typescript": "catalog:",
         "typescript": "catalog:",
         "vscode-languageserver-types": "3.17.5",
         "vscode-languageserver-types": "3.17.5",
         "why-is-node-running": "3.2.2",
         "why-is-node-running": "3.2.2",
@@ -459,23 +496,23 @@
     },
     },
     "packages/plugin": {
     "packages/plugin": {
       "name": "@opencode-ai/plugin",
       "name": "@opencode-ai/plugin",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@opencode-ai/sdk": "workspace:*",
         "@opencode-ai/sdk": "workspace:*",
         "effect": "catalog:",
         "effect": "catalog:",
         "zod": "catalog:",
         "zod": "catalog:",
       },
       },
       "devDependencies": {
       "devDependencies": {
-        "@opentui/core": "0.1.99",
-        "@opentui/solid": "0.1.99",
+        "@opentui/core": "catalog:",
+        "@opentui/solid": "catalog:",
         "@tsconfig/node22": "catalog:",
         "@tsconfig/node22": "catalog:",
         "@types/node": "catalog:",
         "@types/node": "catalog:",
         "@typescript/native-preview": "catalog:",
         "@typescript/native-preview": "catalog:",
         "typescript": "catalog:",
         "typescript": "catalog:",
       },
       },
       "peerDependencies": {
       "peerDependencies": {
-        "@opentui/core": ">=0.1.99",
-        "@opentui/solid": ">=0.1.99",
+        "@opentui/core": ">=0.2.0",
+        "@opentui/solid": ">=0.2.0",
       },
       },
       "optionalPeers": [
       "optionalPeers": [
         "@opentui/core",
         "@opentui/core",
@@ -494,7 +531,7 @@
     },
     },
     "packages/sdk/js": {
     "packages/sdk/js": {
       "name": "@opencode-ai/sdk",
       "name": "@opencode-ai/sdk",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "cross-spawn": "catalog:",
         "cross-spawn": "catalog:",
       },
       },
@@ -507,33 +544,9 @@
         "typescript": "catalog:",
         "typescript": "catalog:",
       },
       },
     },
     },
-    "packages/shared": {
-      "name": "@opencode-ai/shared",
-      "version": "1.14.23",
-      "bin": {
-        "opencode": "./bin/opencode",
-      },
-      "dependencies": {
-        "@effect/platform-node": "catalog:",
-        "@npmcli/arborist": "catalog:",
-        "effect": "catalog:",
-        "glob": "13.0.5",
-        "mime-types": "3.0.2",
-        "minimatch": "10.2.5",
-        "semver": "catalog:",
-        "xdg-basedir": "5.1.0",
-        "zod": "catalog:",
-      },
-      "devDependencies": {
-        "@tsconfig/bun": "catalog:",
-        "@types/bun": "catalog:",
-        "@types/npmcli__arborist": "6.3.3",
-        "@types/semver": "catalog:",
-      },
-    },
     "packages/slack": {
     "packages/slack": {
       "name": "@opencode-ai/slack",
       "name": "@opencode-ai/slack",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@opencode-ai/sdk": "workspace:*",
         "@opencode-ai/sdk": "workspace:*",
         "@slack/bolt": "^3.17.1",
         "@slack/bolt": "^3.17.1",
@@ -568,11 +581,11 @@
     },
     },
     "packages/ui": {
     "packages/ui": {
       "name": "@opencode-ai/ui",
       "name": "@opencode-ai/ui",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@kobalte/core": "catalog:",
         "@kobalte/core": "catalog:",
+        "@opencode-ai/core": "workspace:*",
         "@opencode-ai/sdk": "workspace:*",
         "@opencode-ai/sdk": "workspace:*",
-        "@opencode-ai/shared": "workspace:*",
         "@pierre/diffs": "catalog:",
         "@pierre/diffs": "catalog:",
         "@shikijs/transformers": "3.9.2",
         "@shikijs/transformers": "3.9.2",
         "@solid-primitives/bounds": "0.1.3",
         "@solid-primitives/bounds": "0.1.3",
@@ -617,7 +630,7 @@
     },
     },
     "packages/web": {
     "packages/web": {
       "name": "@opencode-ai/web",
       "name": "@opencode-ai/web",
-      "version": "1.14.23",
+      "version": "1.14.31",
       "dependencies": {
       "dependencies": {
         "@astrojs/cloudflare": "12.6.3",
         "@astrojs/cloudflare": "12.6.3",
         "@astrojs/markdown-remark": "6.3.1",
         "@astrojs/markdown-remark": "6.3.1",
@@ -669,18 +682,20 @@
   },
   },
   "catalog": {
   "catalog": {
     "@cloudflare/workers-types": "4.20251008.0",
     "@cloudflare/workers-types": "4.20251008.0",
-    "@effect/opentelemetry": "4.0.0-beta.48",
-    "@effect/platform-node": "4.0.0-beta.48",
+    "@effect/opentelemetry": "4.0.0-beta.57",
+    "@effect/platform-node": "4.0.0-beta.57",
     "@hono/zod-validator": "0.4.2",
     "@hono/zod-validator": "0.4.2",
     "@kobalte/core": "0.13.11",
     "@kobalte/core": "0.13.11",
     "@lydell/node-pty": "1.2.0-beta.10",
     "@lydell/node-pty": "1.2.0-beta.10",
     "@npmcli/arborist": "9.4.0",
     "@npmcli/arborist": "9.4.0",
     "@octokit/rest": "22.0.0",
     "@octokit/rest": "22.0.0",
     "@openauthjs/openauth": "0.0.0-20250322224806",
     "@openauthjs/openauth": "0.0.0-20250322224806",
-    "@opentui/core": "0.1.99",
-    "@opentui/solid": "0.1.99",
+    "@opentui/core": "0.2.0",
+    "@opentui/solid": "0.2.0",
     "@pierre/diffs": "1.1.0-beta.18",
     "@pierre/diffs": "1.1.0-beta.18",
     "@playwright/test": "1.59.1",
     "@playwright/test": "1.59.1",
+    "@sentry/solid": "10.36.0",
+    "@sentry/vite-plugin": "4.6.0",
     "@solid-primitives/storage": "4.3.3",
     "@solid-primitives/storage": "4.3.3",
     "@solidjs/meta": "0.29.4",
     "@solidjs/meta": "0.29.4",
     "@solidjs/router": "0.15.4",
     "@solidjs/router": "0.15.4",
@@ -700,13 +715,14 @@
     "dompurify": "3.3.1",
     "dompurify": "3.3.1",
     "drizzle-kit": "1.0.0-beta.19-d95b7a4",
     "drizzle-kit": "1.0.0-beta.19-d95b7a4",
     "drizzle-orm": "1.0.0-beta.19-d95b7a4",
     "drizzle-orm": "1.0.0-beta.19-d95b7a4",
-    "effect": "4.0.0-beta.48",
+    "effect": "4.0.0-beta.57",
     "fuzzysort": "3.1.0",
     "fuzzysort": "3.1.0",
     "hono": "4.10.7",
     "hono": "4.10.7",
     "hono-openapi": "1.1.2",
     "hono-openapi": "1.1.2",
     "luxon": "3.6.1",
     "luxon": "3.6.1",
     "marked": "17.0.1",
     "marked": "17.0.1",
     "marked-shiki": "1.2.1",
     "marked-shiki": "1.2.1",
+    "opentui-spinner": "0.0.6",
     "remeda": "2.26.0",
     "remeda": "2.26.0",
     "remend": "1.3.0",
     "remend": "1.3.0",
     "semver": "7.7.4",
     "semver": "7.7.4",
@@ -1060,13 +1076,11 @@
 
 
     "@drizzle-team/brocli": ["@drizzle-team/brocli@0.11.0", "", {}, "sha512-hD3pekGiPg0WPCCGAZmusBBJsDqGUR66Y452YgQsZOnkdQ7ViEPKuyP4huUGEZQefp8g34RRodXYmJ2TbCH+tg=="],
     "@drizzle-team/brocli": ["@drizzle-team/brocli@0.11.0", "", {}, "sha512-hD3pekGiPg0WPCCGAZmusBBJsDqGUR66Y452YgQsZOnkdQ7ViEPKuyP4huUGEZQefp8g34RRodXYmJ2TbCH+tg=="],
 
 
-    "@effect/language-service": ["@effect/language-service@0.84.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-l04qNxpiA8rY5yXWckRPJ7Mk5MNerXuNymSFf+IdflfI5i8jgL1bpBNLuP6ijg7wgjdHc/KmTnCj2kT0SCntuA=="],
+    "@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.57", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^4.0.0-beta.57" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-gdjZPEP0QQg4qmI1vd+443kheeQZKytrjJIzCJncy6ZEpyk/SfrqeStLqLXdTRcms3IB0ls0vOV7KNq7YmBRVA=="],
 
 
-    "@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.48", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^4.0.0-beta.48" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-vHk/X1vgDrviGcOTHQqzm2D81TtyPE/C7Qdksg5eAdbGpnqL4Dm4lk6PzTReQ0pO1/avIvWqpxy315IURV0Ldw=="],
+    "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.57", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.57", "mime": "^4.1.0", "undici": "^8.0.2" }, "peerDependencies": { "effect": "^4.0.0-beta.57", "ioredis": "^5.7.0" } }, "sha512-la0xxPSAYOsY0d+uVxEBxok3jYB31iPQmIaZZRUj2SNWqcGGHJc6KorKtI8guqSLuv9FGZ255kBWXRbG6hMeeg=="],
 
 
-    "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.48", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.48", "mime": "^4.1.0", "undici": "^8.0.2" }, "peerDependencies": { "effect": "^4.0.0-beta.48", "ioredis": "^5.7.0" } }, "sha512-8J6H0k9rtbp9O1QvKOyOPRcCTJ8WrR7IzZLJtYFTZ4bXVEEMCTo84h0CRpi7ccpA9t7DLqotip0NeFgiBosNKQ=="],
-
-    "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.48", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.48" } }, "sha512-wlhcdDHyacydCgiWdM8JwtQkViQhZsC8uJZ9wMoZXYxlCTvqfdzLeWw4A1UVMoq7sS6/KR1aZVeFkUjrqonncQ=="],
+    "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.57", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.57" } }, "sha512-C976X6f+qHUtLSqcqImuCrjhAHnJV17NC2RvvybsAuDfkyIWU4MyiO2XwgiBeijeNupyr1M/KPKnyjtkNxV9Hw=="],
 
 
     "@electron/asar": ["@electron/asar@3.4.1", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA=="],
     "@electron/asar": ["@electron/asar@3.4.1", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA=="],
 
 
@@ -1552,6 +1566,8 @@
 
 
     "@opencode-ai/console-resource": ["@opencode-ai/console-resource@workspace:packages/console/resource"],
     "@opencode-ai/console-resource": ["@opencode-ai/console-resource@workspace:packages/console/resource"],
 
 
+    "@opencode-ai/core": ["@opencode-ai/core@workspace:packages/core"],
+
     "@opencode-ai/desktop": ["@opencode-ai/desktop@workspace:packages/desktop"],
     "@opencode-ai/desktop": ["@opencode-ai/desktop@workspace:packages/desktop"],
 
 
     "@opencode-ai/desktop-electron": ["@opencode-ai/desktop-electron@workspace:packages/desktop-electron"],
     "@opencode-ai/desktop-electron": ["@opencode-ai/desktop-electron@workspace:packages/desktop-electron"],
@@ -1566,8 +1582,6 @@
 
 
     "@opencode-ai/sdk": ["@opencode-ai/sdk@workspace:packages/sdk/js"],
     "@opencode-ai/sdk": ["@opencode-ai/sdk@workspace:packages/sdk/js"],
 
 
-    "@opencode-ai/shared": ["@opencode-ai/shared@workspace:packages/shared"],
-
     "@opencode-ai/slack": ["@opencode-ai/slack@workspace:packages/slack"],
     "@opencode-ai/slack": ["@opencode-ai/slack@workspace:packages/slack"],
 
 
     "@opencode-ai/storybook": ["@opencode-ai/storybook@workspace:packages/storybook"],
     "@opencode-ai/storybook": ["@opencode-ai/storybook@workspace:packages/storybook"],
@@ -1576,7 +1590,7 @@
 
 
     "@opencode-ai/web": ["@opencode-ai/web@workspace:packages/web"],
     "@opencode-ai/web": ["@opencode-ai/web@workspace:packages/web"],
 
 
-    "@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.5.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-r1fJL1Cb3gQDa2MpWH/sfx1BsEW0uzlRriJM6eihaKqbtKDmZoBisF32VcVaQYassighX7NGCkF68EsrZA43uQ=="],
+    "@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.8.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Y6j3yivgoEUf/kutD/k5GX/mzZfioRFoSx0gbQ+mIOzMaH/vJv1rCkztiuvlLw5xRYQil7oxHUZvmSfXqOx1NQ=="],
 
 
     "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
     "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
 
 
@@ -1592,7 +1606,7 @@
 
 
     "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/sdk-logs": "0.214.0", "@opentelemetry/sdk-metrics": "2.6.1", "@opentelemetry/sdk-trace-base": "2.6.1", "protobufjs": "^7.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w=="],
     "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/sdk-logs": "0.214.0", "@opentelemetry/sdk-metrics": "2.6.1", "@opentelemetry/sdk-trace-base": "2.6.1", "protobufjs": "^7.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w=="],
 
 
-    "@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="],
+    "@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
 
 
     "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA=="],
     "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA=="],
 
 
@@ -1604,21 +1618,21 @@
 
 
     "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
     "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
 
 
-    "@opentui/core": ["@opentui/core@0.1.99", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.99", "@opentui/core-darwin-x64": "0.1.99", "@opentui/core-linux-arm64": "0.1.99", "@opentui/core-linux-x64": "0.1.99", "@opentui/core-win32-arm64": "0.1.99", "@opentui/core-win32-x64": "0.1.99", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-I3+AEgGzqNWIpWX9g2WOscSPwtQDNOm4KlBjxBWCZjLxkF07u77heWXF7OiAdhKLtNUW6TFiyt6yznqAZPdG3A=="],
+    "@opentui/core": ["@opentui/core@0.2.0", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.2.0", "@opentui/core-darwin-x64": "0.2.0", "@opentui/core-linux-arm64": "0.2.0", "@opentui/core-linux-x64": "0.2.0", "@opentui/core-win32-arm64": "0.2.0", "@opentui/core-win32-x64": "0.2.0", "bun-webgpu": "0.1.7", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-7YOEqPUQmsgrOb9nmLEBlX8RVHPFy4HquK1C489DwfvvPTiws8nTbZ+webNQDWha7shgnYQK4Zo1EcOlpQ5+1Q=="],
 
 
-    "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.99", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bzVrqeX2vb5iWrc/ftOUOqeUY8XO+qSgoTwj5TXHuwagavgwD3Hpeyjx8+icnTTeM4pao0som1WR9xfye6/X5Q=="],
+    "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VVmKwth3hzsQPjAZ7WGJxmzuzx0uCtynd79JJDg26D7QRM9V5beVGbKwwU5SKsDlK74EyQoY85Mv9xFY5E4jrA=="],
 
 
-    "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.99", "", { "os": "darwin", "cpu": "x64" }, "sha512-VE4FrXBYpkxnvkqcCV1a8aN9jyyMJMihVW+V2NLCtp+4yQsj0AapG5TiUSN76XnmSZRptxDy5rBmEempeoIZbg=="],
+    "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-eX+WNdbSNr7Bozdq/MH6p1vXIALGt0SqBHR4YtWyTh6X7KDz9FTtJT3ylxMPqiVRUGBNAiWOxoqKGXW7JLQ0TA=="],
 
 
-    "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.99", "", { "os": "linux", "cpu": "arm64" }, "sha512-viXQsbpS7yHjYkl7+am32JdvG96QU9lvHh1UiZtpOxcNUUqiYmA2ZwZFPD2Bi54jNyj5l2hjH6YkD3DzE2FEWA=="],
+    "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ARZa+ywbN/OV7esT5ZdJMlQW3a4Pr56qLlEI/X65ik88C2sgmDze4Kf2FmqtvJ1hbv1YsMfLHH9MfhLl5twyHQ=="],
 
 
-    "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.99", "", { "os": "linux", "cpu": "x64" }, "sha512-WLoEFINOSp0tZSR9y4LUuGc7n4Y7H1wcpjUPzQ9vChkYDXrfZltEanzoDWbDcQ4kZQW5tHVC7LrZHpAsRLwFZg=="],
+    "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZjNxrD45P51cdbABoivVQLBakVYwDqAridJbHhkK6T/+EU7YsTrmAu9ae19N9ZGnrlKzLViQF8GOavNUNjAbhw=="],
 
 
-    "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.99", "", { "os": "win32", "cpu": "arm64" }, "sha512-yWMOLWCEO8HdrctU1dMkgZC8qGkiO4Dwr4/e11tTvVpRmYhDsP/IR89ZjEEtOwnKwFOFuB/MxvflqaEWVQ2g5Q=="],
+    "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.2.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-ImMjFPOWE8wcZQ2lUz1D418xonS/5EwnItUF1g5dbp1q9+A0vv2P3bxTenLwMqcYvG4wjO6gKT3n2QLnRd6qKg=="],
 
 
-    "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.99", "", { "os": "win32", "cpu": "x64" }, "sha512-aYRlsL2w8YRL6vPd7/hrqlNVkXU3QowWb01TOvAcHS8UAsXaGFUr47kSDyjxDi1wg1MzmVduCfsC7T3NoThV1w=="],
+    "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.2.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6yfYHTtJ4yzbl8kXCW3Pc4eWbZDYVw21GumwdNgkjJJ2JqQAQ861em0riEoucYAa5qPYYTiMUEw7X4Fv8lGwuQ=="],
 
 
-    "@opentui/solid": ["@opentui/solid@0.1.99", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.99", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-DrqqO4h2V88FmeIP2cErYkMU0ZK5MrUsZw3w6IzZpoXyyiL4/9qpWzUq+CXx+r16VP2iGxDJwGKUmtFAzUch2Q=="],
+    "@opentui/solid": ["@opentui/solid@0.2.0", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.2.0", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-kZR9i0FPAcVtomrPsKuSb+D9smooplo9zggFfU2vnnguNuQjGNbEmuJtxhCacy7ig9g3GomdNtQAzD4LiAY+3w=="],
 
 
     "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
     "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
 
 
@@ -1950,6 +1964,44 @@
 
 
     "@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="],
     "@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="],
 
 
+    "@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.36.0", "", { "dependencies": { "@sentry/core": "10.36.0" } }, "sha512-WILVR8HQBWOxbqLRuTxjzRCMIACGsDTo6jXvzA8rz6ezElElLmIrn3CFAswrESLqEEUa4CQHl5bLgSVJCRNweA=="],
+
+    "@sentry-internal/feedback": ["@sentry-internal/feedback@10.36.0", "", { "dependencies": { "@sentry/core": "10.36.0" } }, "sha512-zPjz7AbcxEyx8AHj8xvp28fYtPTPWU1XcNtymhAHJLS9CXOblqSC7W02Jxz6eo3eR1/pLyOo6kJBUjvLe9EoFA=="],
+
+    "@sentry-internal/replay": ["@sentry-internal/replay@10.36.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.36.0", "@sentry/core": "10.36.0" } }, "sha512-nLMkJgvHq+uCCrQKV2KgSdVHxTsmDk0r2hsAoTcKCbzUpXyW5UhCziMRS6ULjBlzt5sbxoIIplE25ZpmIEeNgg=="],
+
+    "@sentry-internal/replay-canvas": ["@sentry-internal/replay-canvas@10.36.0", "", { "dependencies": { "@sentry-internal/replay": "10.36.0", "@sentry/core": "10.36.0" } }, "sha512-DLGIwmT2LX+O6TyYPtOQL5GiTm2rN0taJPDJ/Lzg2KEJZrdd5sKkzTckhh2x+vr4JQyeaLmnb8M40Ch1hvG/vQ=="],
+
+    "@sentry/babel-plugin-component-annotate": ["@sentry/babel-plugin-component-annotate@4.6.0", "", {}, "sha512-3soTX50JPQQ51FSbb4qvNBf4z/yP7jTdn43vMTp9E4IxvJ9HKJR7OEuKkCMszrZmWsVABXl02msqO7QisePdiQ=="],
+
+    "@sentry/browser": ["@sentry/browser@10.36.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.36.0", "@sentry-internal/feedback": "10.36.0", "@sentry-internal/replay": "10.36.0", "@sentry-internal/replay-canvas": "10.36.0", "@sentry/core": "10.36.0" } }, "sha512-yHhXbgdGY1s+m8CdILC9U/II7gb6+s99S2Eh8VneEn/JG9wHc+UOzrQCeFN0phFP51QbLkjkiQbbanjT1HP8UQ=="],
+
+    "@sentry/bundler-plugin-core": ["@sentry/bundler-plugin-core@4.6.0", "", { "dependencies": { "@babel/core": "^7.18.5", "@sentry/babel-plugin-component-annotate": "4.6.0", "@sentry/cli": "^2.57.0", "dotenv": "^16.3.1", "find-up": "^5.0.0", "glob": "^9.3.2", "magic-string": "0.30.8", "unplugin": "1.0.1" } }, "sha512-Fub2XQqrS258jjS8qAxLLU1k1h5UCNJ76i8m4qZJJdogWWaF8t00KnnTyp9TEDJzrVD64tRXS8+HHENxmeUo3g=="],
+
+    "@sentry/cli": ["@sentry/cli@2.58.5", "", { "dependencies": { "https-proxy-agent": "^5.0.0", "node-fetch": "^2.6.7", "progress": "^2.0.3", "proxy-from-env": "^1.1.0", "which": "^2.0.2" }, "optionalDependencies": { "@sentry/cli-darwin": "2.58.5", "@sentry/cli-linux-arm": "2.58.5", "@sentry/cli-linux-arm64": "2.58.5", "@sentry/cli-linux-i686": "2.58.5", "@sentry/cli-linux-x64": "2.58.5", "@sentry/cli-win32-arm64": "2.58.5", "@sentry/cli-win32-i686": "2.58.5", "@sentry/cli-win32-x64": "2.58.5" }, "bin": { "sentry-cli": "bin/sentry-cli" } }, "sha512-tavJ7yGUZV+z3Ct2/ZB6mg339i08sAk6HDkgqmSRuQEu2iLS5sl9HIvuXfM6xjv8fwlgFOSy++WNABNAcGHUbg=="],
+
+    "@sentry/cli-darwin": ["@sentry/cli-darwin@2.58.5", "", { "os": "darwin" }, "sha512-lYrNzenZFJftfwSya7gwrHGxtE+Kob/e1sr9lmHMFOd4utDlmq0XFDllmdZAMf21fxcPRI1GL28ejZ3bId01fQ=="],
+
+    "@sentry/cli-linux-arm": ["@sentry/cli-linux-arm@2.58.5", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "arm" }, "sha512-KtHweSIomYL4WVDrBrYSYJricKAAzxUgX86kc6OnlikbyOhoK6Fy8Vs6vwd52P6dvWPjgrMpUYjW2M5pYXQDUw=="],
+
+    "@sentry/cli-linux-arm64": ["@sentry/cli-linux-arm64@2.58.5", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "arm64" }, "sha512-/4gywFeBqRB6tR/iGMRAJ3HRqY6Z7Yp4l8ZCbl0TDLAfHNxu7schEw4tSnm2/Hh9eNMiOVy4z58uzAWlZXAYBQ=="],
+
+    "@sentry/cli-linux-i686": ["@sentry/cli-linux-i686@2.58.5", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "ia32" }, "sha512-G7261dkmyxqlMdyvyP06b+RTIVzp1gZNgglj5UksxSouSUqRd/46W/2pQeOMPhloDYo9yLtCN2YFb3Mw4aUsWw=="],
+
+    "@sentry/cli-linux-x64": ["@sentry/cli-linux-x64@2.58.5", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "x64" }, "sha512-rP04494RSmt86xChkQ+ecBNRYSPbyXc4u0IA7R7N1pSLCyO74e5w5Al+LnAq35cMfVbZgz5Sm0iGLjyiUu4I1g=="],
+
+    "@sentry/cli-win32-arm64": ["@sentry/cli-win32-arm64@2.58.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-AOJ2nCXlQL1KBaCzv38m3i2VmSHNurUpm7xVKd6yAHX+ZoVBI8VT0EgvwmtJR2TY2N2hNCC7UrgRmdUsQ152bA=="],
+
+    "@sentry/cli-win32-i686": ["@sentry/cli-win32-i686@2.58.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-EsuboLSOnlrN7MMPJ1eFvfMDm+BnzOaSWl8eYhNo8W/BIrmNgpRUdBwnWn9Q2UOjJj5ZopukmsiMYtU/D7ml9g=="],
+
+    "@sentry/cli-win32-x64": ["@sentry/cli-win32-x64@2.58.5", "", { "os": "win32", "cpu": "x64" }, "sha512-IZf+XIMiQwj+5NzqbOQfywlOitmCV424Vtf9c+ep61AaVScUFD1TSrQbOcJJv5xGxhlxNOMNgMeZhdexdzrKZg=="],
+
+    "@sentry/core": ["@sentry/core@10.36.0", "", {}, "sha512-EYJjZvofI+D93eUsPLDIUV0zQocYqiBRyXS6CCV6dHz64P/Hob5NJQOwPa8/v6nD+UvJXvwsFfvXOHhYZhZJOQ=="],
+
+    "@sentry/solid": ["@sentry/solid@10.36.0", "", { "dependencies": { "@sentry/browser": "10.36.0", "@sentry/core": "10.36.0" }, "peerDependencies": { "@solidjs/router": "^0.13.4 || ^0.14.0 || ^0.15.0", "@tanstack/solid-router": "^1.132.27", "solid-js": "^1.8.4" }, "optionalPeers": ["@solidjs/router", "@tanstack/solid-router"] }, "sha512-AaDqz3JGBrQCm2YVqODVyJHwg7LRTNSJig9mjfProFyvkC7eUXQ/HBJrrhAD1Dct9ufmDH3G+f3/Ut9LgpItSg=="],
+
+    "@sentry/vite-plugin": ["@sentry/vite-plugin@4.6.0", "", { "dependencies": { "@sentry/bundler-plugin-core": "4.6.0", "unplugin": "1.0.1" } }, "sha512-fMR2d+EHwbzBa0S1fp45SNUTProxmyFBp+DeBWWQOSP9IU6AH6ea2rqrpMAnp/skkcdW4z4LSRrOEpMZ5rWXLw=="],
+
     "@shikijs/core": ["@shikijs/core@3.9.2", "", { "dependencies": { "@shikijs/types": "3.9.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3q/mzmw09B2B6PgFNeiaN8pkNOixWS726IHmJEpjDAcneDPMQmUg2cweT9cWXY4XcyQS3i6mOOUgQz9RRUP6HA=="],
     "@shikijs/core": ["@shikijs/core@3.9.2", "", { "dependencies": { "@shikijs/types": "3.9.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3q/mzmw09B2B6PgFNeiaN8pkNOixWS726IHmJEpjDAcneDPMQmUg2cweT9cWXY4XcyQS3i6mOOUgQz9RRUP6HA=="],
 
 
     "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg=="],
     "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg=="],
@@ -2722,15 +2774,15 @@
 
 
     "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
     "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
 
 
-    "bun-webgpu": ["bun-webgpu@0.1.5", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.5", "bun-webgpu-darwin-x64": "^0.1.5", "bun-webgpu-linux-x64": "^0.1.5", "bun-webgpu-win32-x64": "^0.1.5" } }, "sha512-91/K6S5whZKX7CWAm9AylhyKrLGRz6BUiiPiM/kXadSnD4rffljCD/q9cNFftm5YXhx4MvLqw33yEilxogJvwA=="],
+    "bun-webgpu": ["bun-webgpu@0.1.7", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.7", "bun-webgpu-darwin-x64": "^0.1.7", "bun-webgpu-linux-x64": "^0.1.7", "bun-webgpu-win32-x64": "^0.1.7" } }, "sha512-KUxUp+oQIf7pPBMD4Hv1TUu7DWaOZ4ciKulTk9to9+Uc8yHoYrMW7L2SJCJ4FHHkywgf/7aLRgRx0b7i6DvGIQ=="],
 
 
-    "bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lIsDkPzJzPl6yrB5CUOINJFPnTRv6fF/Q8J1mAr43ogSp86WZEg9XZKaT6f3EUJ+9ETogGoMnoj1q0AwHUTbAQ=="],
+    "bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mRrFFyHzPWjsTRidAZBRcu808CPQBOUL0P6b4nxLhp+XHcV/mbUHERZMgW9s58tsojQfSdzschiQa8q+JCgRWA=="],
 
 
-    "bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-uEddf5U7GvKIkM/BV18rUKtYHL6d0KeqBjNHwfqDH9QgEo9KVSKvJXS5I/sMefk5V5pIYE+8tQhtrREevhocng=="],
+    "bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-g0NXGNgvaVCSH/jCWWlfdiquOHkbUN6vP4zqzSkIxWKQeLnqm3oADcok7SO3yIgI7v5mKpRc/ks7NDEKNH+jNQ=="],
 
 
-    "bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-Y/f15j9r8ba0xUz+3lATtS74OE+PPzQXO7Do/1eCluJcuOlfa77kMjvBK/ShWnem3Y9xqi59pebTPOGRB+CaJA=="],
+    "bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.7", "", { "os": "linux", "cpu": "x64" }, "sha512-UEP7UZdEhx9otvkZczjsszL8ZVlrODANQvgl+C88/bNVmxDoFi7w1fWzGi1sZyakiETjmtFDq2/xCLhbSZxjqw=="],
 
 
-    "bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-MHSFAKqizISb+C5NfDrFe3g0Al5Njnu0j/A+oO2Q+bIWX+fUYjBSowiYE1ZXJx65KuryuB+tiM7Qh6cQbVvkEg=="],
+    "bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.7", "", { "os": "win32", "cpu": "x64" }, "sha512-KZktiFkBz6sN7PEm1NVdeaLP5Q5X/PlSHZqefY4nNuWtf0LNvh54NhZe7yVv/Plz/nGbv92b0KHMBY3ki/pp6g=="],
 
 
     "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
     "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
 
 
@@ -3026,7 +3078,7 @@
 
 
     "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
     "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
 
 
-    "effect": ["effect@4.0.0-beta.48", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw=="],
+    "effect": ["effect@4.0.0-beta.57", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-rg32VgXnLKaPRs9tbRDaZ5jxmzNY7ojXt85gSHGUTwdlbWH5Ik+OCUY2q14TXliygPGoHwCAvNWS4bQJOqf00g=="],
 
 
     "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
     "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
 
 
@@ -3234,7 +3286,7 @@
 
 
     "find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="],
     "find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="],
 
 
-    "find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
+    "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
 
 
     "finity": ["finity@0.5.4", "", {}, "sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA=="],
     "finity": ["finity@0.5.4", "", {}, "sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA=="],
 
 
@@ -3728,7 +3780,7 @@
 
 
     "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
     "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
 
 
-    "locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
+    "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
 
 
     "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
     "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
 
 
@@ -4132,7 +4184,7 @@
 
 
     "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="],
     "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="],
 
 
-    "p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
+    "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
 
 
     "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="],
     "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="],
 
 
@@ -4182,7 +4234,7 @@
 
 
     "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
     "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
 
 
-    "path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
+    "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
 
 
     "path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="],
     "path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="],
 
 
@@ -4942,7 +4994,7 @@
 
 
     "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
     "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
 
 
-    "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
+    "unplugin": ["unplugin@1.0.1", "", { "dependencies": { "acorn": "^8.8.1", "chokidar": "^3.5.3", "webpack-sources": "^3.2.3", "webpack-virtual-modules": "^0.5.0" } }, "sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA=="],
 
 
     "unstorage": ["unstorage@2.0.0-alpha.7", "", { "peerDependencies": { "@azure/app-configuration": "^1.11.0", "@azure/cosmos": "^4.9.1", "@azure/data-tables": "^13.3.2", "@azure/identity": "^4.13.0", "@azure/keyvault-secrets": "^4.10.0", "@azure/storage-blob": "^12.31.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.13.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.36.2", "@vercel/blob": ">=0.27.3", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "chokidar": "^4 || ^5", "db0": ">=0.3.4", "idb-keyval": "^6.2.2", "ioredis": "^5.9.3", "lru-cache": "^11.2.6", "mongodb": "^6 || ^7", "ofetch": "*", "uploadthing": "^7.7.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "chokidar", "db0", "idb-keyval", "ioredis", "lru-cache", "mongodb", "ofetch", "uploadthing"] }, "sha512-ELPztchk2zgFJnakyodVY3vJWGW9jy//keJ32IOJVGUMyaPydwcA1FtVvWqT0TNRch9H+cMNEGllfVFfScImog=="],
     "unstorage": ["unstorage@2.0.0-alpha.7", "", { "peerDependencies": { "@azure/app-configuration": "^1.11.0", "@azure/cosmos": "^4.9.1", "@azure/data-tables": "^13.3.2", "@azure/identity": "^4.13.0", "@azure/keyvault-secrets": "^4.10.0", "@azure/storage-blob": "^12.31.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.13.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.36.2", "@vercel/blob": ">=0.27.3", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "chokidar": "^4 || ^5", "db0": ">=0.3.4", "idb-keyval": "^6.2.2", "ioredis": "^5.9.3", "lru-cache": "^11.2.6", "mongodb": "^6 || ^7", "ofetch": "*", "uploadthing": "^7.7.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "chokidar", "db0", "idb-keyval", "ioredis", "lru-cache", "mongodb", "ofetch", "uploadthing"] }, "sha512-ELPztchk2zgFJnakyodVY3vJWGW9jy//keJ32IOJVGUMyaPydwcA1FtVvWqT0TNRch9H+cMNEGllfVFfScImog=="],
 
 
@@ -5050,7 +5102,9 @@
 
 
     "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
     "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
 
 
-    "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
+    "webpack-sources": ["webpack-sources@3.4.0", "", {}, "sha512-gHwIe1cgBvvfLeu1Yz/dcFpmHfKDVxxyqI+kzqmuxZED81z2ChxpyqPaWcNqigPywhaEke7AjSGga+kxY55gjQ=="],
+
+    "webpack-virtual-modules": ["webpack-virtual-modules@0.5.0", "", {}, "sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw=="],
 
 
     "whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="],
     "whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="],
 
 
@@ -5586,22 +5640,8 @@
 
 
     "@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
     "@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
 
 
-    "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
-
-    "@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
-
-    "@opentelemetry/resources/@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
-
-    "@opentelemetry/sdk-logs/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
-
-    "@opentelemetry/sdk-metrics/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
-
-    "@opentelemetry/sdk-trace-base/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
-
     "@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
     "@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
 
 
-    "@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
-
     "@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="],
     "@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="],
 
 
     "@pierre/diffs/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
     "@pierre/diffs/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
@@ -5616,6 +5656,16 @@
 
 
     "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
     "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
 
 
+    "@sentry/bundler-plugin-core/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
+
+    "@sentry/bundler-plugin-core/magic-string": ["magic-string@0.30.8", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ=="],
+
+    "@sentry/cli/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
+
+    "@sentry/cli/proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
+
+    "@sentry/cli/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
+
     "@shikijs/engine-javascript/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
     "@shikijs/engine-javascript/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
 
 
     "@shikijs/engine-oniguruma/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
     "@shikijs/engine-oniguruma/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
@@ -5666,6 +5716,12 @@
 
 
     "@solidjs/start/vite-plugin-solid": ["vite-plugin-solid@2.11.12", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-FgjPcx2OwX9h6f28jli7A4bG7PP3te8uyakE5iqsmpq3Jqi1TWLgSroC9N6cMfGRU2zXsl4Q6ISvTr2VL0QHpA=="],
     "@solidjs/start/vite-plugin-solid": ["vite-plugin-solid@2.11.12", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-FgjPcx2OwX9h6f28jli7A4bG7PP3te8uyakE5iqsmpq3Jqi1TWLgSroC9N6cMfGRU2zXsl4Q6ISvTr2VL0QHpA=="],
 
 
+    "@standard-community/standard-json/effect": ["effect@4.0.0-beta.48", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw=="],
+
+    "@standard-community/standard-openapi/effect": ["effect@4.0.0-beta.48", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw=="],
+
+    "@storybook/csf-plugin/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
+
     "@tailwindcss/oxide/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
     "@tailwindcss/oxide/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
 
 
     "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
     "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
@@ -5714,6 +5770,8 @@
 
 
     "ai-gateway-provider/@ai-sdk/xai": ["@ai-sdk/xai@3.0.75", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-V8UKK4fNpI9cnrtsZBvUp9O9J6Y9fTKBRoSLyEaNGPirACewixmLDbXsSgAeownPVWiWpK34bFysd+XouI5Ywg=="],
     "ai-gateway-provider/@ai-sdk/xai": ["@ai-sdk/xai@3.0.75", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-V8UKK4fNpI9cnrtsZBvUp9O9J6Y9fTKBRoSLyEaNGPirACewixmLDbXsSgAeownPVWiWpK34bFysd+XouI5Ywg=="],
 
 
+    "ai-gateway-provider/@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.5.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-r1fJL1Cb3gQDa2MpWH/sfx1BsEW0uzlRriJM6eihaKqbtKDmZoBisF32VcVaQYassighX7NGCkF68EsrZA43uQ=="],
+
     "ajv-keywords/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
     "ajv-keywords/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
 
 
     "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
     "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@@ -5848,8 +5906,6 @@
 
 
     "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
     "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
 
 
-    "find-up/path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
-
     "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
     "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
 
 
     "fs-extra/jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
     "fs-extra/jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
@@ -5948,6 +6004,10 @@
 
 
     "openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
     "openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
 
 
+    "opentui-spinner/@opentui/core": ["@opentui/core@0.1.105", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.105", "@opentui/core-darwin-x64": "0.1.105", "@opentui/core-linux-arm64": "0.1.105", "@opentui/core-linux-x64": "0.1.105", "@opentui/core-win32-arm64": "0.1.105", "@opentui/core-win32-x64": "0.1.105", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-vllSOOCW6VIThV/96GRLJ1IxIBuR+ci6FDvnPIAG4s7SJ/FW6zAkqDn1xrtBwwk/lM3QWjLqy8BZc+zwWvveJA=="],
+
+    "opentui-spinner/@opentui/solid": ["@opentui/solid@0.1.105", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.105", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-uxnaMP802sCI487pv/Hk9xdFdIj9mkg3eNliAqbqR0Shmd4phcjKEZvPRpijjmI99j4s9nul71jzF3h1oz31Nw=="],
+
     "ora/bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
     "ora/bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
 
 
     "ora/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
     "ora/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
@@ -5956,7 +6016,7 @@
 
 
     "ora/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
     "ora/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
 
 
-    "p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
+    "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
 
 
     "p-retry/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
     "p-retry/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
 
 
@@ -5968,6 +6028,8 @@
 
 
     "pixelmatch/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="],
     "pixelmatch/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="],
 
 
+    "pkg-dir/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
+
     "pkg-up/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="],
     "pkg-up/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="],
 
 
     "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
     "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
@@ -6064,6 +6126,10 @@
 
 
     "unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
     "unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
 
 
+    "unplugin/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
+
+    "unused-filename/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
+
     "uri-js/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
     "uri-js/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
 
 
     "venice-ai-sdk-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
     "venice-ai-sdk-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
@@ -6564,6 +6630,16 @@
 
 
     "@pierre/diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
     "@pierre/diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
 
 
+    "@sentry/bundler-plugin-core/glob/minimatch": ["minimatch@8.0.7", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg=="],
+
+    "@sentry/bundler-plugin-core/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
+
+    "@sentry/bundler-plugin-core/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
+
+    "@sentry/cli/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
+
+    "@sentry/cli/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
+
     "@slack/web-api/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
     "@slack/web-api/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
 
 
     "@slack/web-api/p-queue/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
     "@slack/web-api/p-queue/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
@@ -6582,6 +6658,12 @@
 
 
     "@solidjs/start/shiki/@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="],
     "@solidjs/start/shiki/@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="],
 
 
+    "@standard-community/standard-json/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
+
+    "@standard-community/standard-openapi/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
+
+    "@storybook/csf-plugin/unplugin/webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
+
     "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
     "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
 
 
     "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
     "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
@@ -6704,14 +6786,36 @@
 
 
     "opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
     "opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
 
 
+    "opentui-spinner/@opentui/core/@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.105", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1pIL7aer9amwj8EpYoMNtvavKetIe+nX8uBRmYsMQb+KvJoUAZUqENfRW+qHE5WrsOyxx8/QoyXTHw15GG5iLQ=="],
+
+    "opentui-spinner/@opentui/core/@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.105", "", { "os": "darwin", "cpu": "x64" }, "sha512-hLIRSWlK3gY2NRXJGWiTBiMYSmRDjOYFZF6WtUVXhY2SL3sp08dhmr/6dmAVH+3pKCsCipLEsrrcQX6SAihCTA=="],
+
+    "opentui-spinner/@opentui/core/@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.105", "", { "os": "linux", "cpu": "arm64" }, "sha512-jlRKfPkozTZEkHEePuCWYcTIUtPm+ieInAwGVqGmjbvqjxdVv1/W/Dt6LEZ/9jpRiOPd+FjXAfLe6wa/XWHr+w=="],
+
+    "opentui-spinner/@opentui/core/@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.105", "", { "os": "linux", "cpu": "x64" }, "sha512-kfWS1WMg6qHShmxZX9s1tZc/8JcXw6uyy2UtyTbJdRFExtXGH37oKHi8QK8iPL2ExCx4z7zqVnVJfO3X/Wh7lA=="],
+
+    "opentui-spinner/@opentui/core/@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.105", "", { "os": "win32", "cpu": "arm64" }, "sha512-UFx6A8OpBVbGWK6OAw4GqAqKZgIITJfSOd35pG9yDVKQouHN2OGc2HeeXrH2A4h42p40Xl6IfcqqfllkpC13Dg=="],
+
+    "opentui-spinner/@opentui/core/@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.105", "", { "os": "win32", "cpu": "x64" }, "sha512-f9FqqUmxehwhF+cgyazm0YT0v0BYTTCPzd6eztqhl74N3x/kC+jOOz2rdJDC/tTBo1JVsF64KupOnhIs6/Cogg=="],
+
+    "opentui-spinner/@opentui/core/bun-webgpu": ["bun-webgpu@0.1.5", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.5", "bun-webgpu-darwin-x64": "^0.1.5", "bun-webgpu-linux-x64": "^0.1.5", "bun-webgpu-win32-x64": "^0.1.5" } }, "sha512-91/K6S5whZKX7CWAm9AylhyKrLGRz6BUiiPiM/kXadSnD4rffljCD/q9cNFftm5YXhx4MvLqw33yEilxogJvwA=="],
+
+    "opentui-spinner/@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
+
+    "opentui-spinner/@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
+
     "ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
     "ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
 
 
     "ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
     "ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
 
 
     "ora/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
     "ora/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
 
 
+    "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
+
     "parse-bmfont-xml/xml2js/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
     "parse-bmfont-xml/xml2js/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
 
 
+    "pkg-dir/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
+
     "pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
     "pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
 
 
     "readable-stream/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
     "readable-stream/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
@@ -6740,6 +6844,8 @@
 
 
     "type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
     "type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
 
 
+    "unplugin/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
+
     "vitest/@vitest/expect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
     "vitest/@vitest/expect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
 
 
     "vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
     "vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
@@ -6964,6 +7070,12 @@
 
 
     "@opencode-ai/desktop/@actions/artifact/@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
     "@opencode-ai/desktop/@actions/artifact/@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
 
 
+    "@sentry/bundler-plugin-core/glob/minimatch/brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
+
+    "@sentry/bundler-plugin-core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+
+    "@sentry/bundler-plugin-core/glob/path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
+
     "@slack/web-api/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
     "@slack/web-api/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
 
 
     "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
     "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
@@ -7046,8 +7158,22 @@
 
 
     "opencontrol/@modelcontextprotocol/sdk/express/type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
     "opencontrol/@modelcontextprotocol/sdk/express/type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
 
 
+    "opentui-spinner/@opentui/core/bun-webgpu/@webgpu/types": ["@webgpu/types@0.1.69", "", {}, "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ=="],
+
+    "opentui-spinner/@opentui/core/bun-webgpu/bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lIsDkPzJzPl6yrB5CUOINJFPnTRv6fF/Q8J1mAr43ogSp86WZEg9XZKaT6f3EUJ+9ETogGoMnoj1q0AwHUTbAQ=="],
+
+    "opentui-spinner/@opentui/core/bun-webgpu/bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-uEddf5U7GvKIkM/BV18rUKtYHL6d0KeqBjNHwfqDH9QgEo9KVSKvJXS5I/sMefk5V5pIYE+8tQhtrREevhocng=="],
+
+    "opentui-spinner/@opentui/core/bun-webgpu/bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-Y/f15j9r8ba0xUz+3lATtS74OE+PPzQXO7Do/1eCluJcuOlfa77kMjvBK/ShWnem3Y9xqi59pebTPOGRB+CaJA=="],
+
+    "opentui-spinner/@opentui/core/bun-webgpu/bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-MHSFAKqizISb+C5NfDrFe3g0Al5Njnu0j/A+oO2Q+bIWX+fUYjBSowiYE1ZXJx65KuryuB+tiM7Qh6cQbVvkEg=="],
+
+    "opentui-spinner/@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+
     "ora/bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
     "ora/bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
 
 
+    "pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
+
     "pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
     "pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
 
 
     "pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
     "pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
@@ -7060,6 +7186,8 @@
 
 
     "tw-to-css/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
     "tw-to-css/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
 
 
+    "unplugin/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
+
     "@astrojs/check/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
     "@astrojs/check/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
 
 
     "@astrojs/check/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
     "@astrojs/check/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
@@ -7114,6 +7242,8 @@
 
 
     "@jsx-email/cli/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
     "@jsx-email/cli/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
 
 
+    "@sentry/bundler-plugin-core/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
     "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="],
     "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="],
 
 
     "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex-recursion": ["regex-recursion@5.1.1", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="],
     "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex-recursion": ["regex-recursion@5.1.1", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="],
@@ -7140,6 +7270,8 @@
 
 
     "opencontrol/@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
     "opencontrol/@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
 
 
+    "pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
+
     "pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
     "pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
 
 
     "rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
     "rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],

+ 24 - 0
infra/console.ts

@@ -115,6 +115,27 @@ const zenLiteCouponFirstMonth100 = new stripe.Coupon("ZenLiteCouponFirstMonth100
   appliesToProducts: [zenLiteProduct.id],
   appliesToProducts: [zenLiteProduct.id],
   duration: "once",
   duration: "once",
 })
 })
+const zenLiteCouponThreeMonths100 = new stripe.Coupon("ZenLiteCoupon3Months100", {
+  name: "3 months 100% off",
+  percentOff: 100,
+  appliesToProducts: [zenLiteProduct.id],
+  duration: "repeating",
+  durationInMonths: 3,
+})
+const zenLiteCouponSixMonths100 = new stripe.Coupon("ZenLiteCoupon6Months100", {
+  name: "6 months 100% off",
+  percentOff: 100,
+  appliesToProducts: [zenLiteProduct.id],
+  duration: "repeating",
+  durationInMonths: 6,
+})
+const zenLiteCouponTwelveMonths100 = new stripe.Coupon("ZenLiteCoupon12Months100", {
+  name: "12 months 100% off",
+  percentOff: 100,
+  appliesToProducts: [zenLiteProduct.id],
+  duration: "repeating",
+  durationInMonths: 12,
+})
 const zenLitePrice = new stripe.Price("ZenLitePrice", {
 const zenLitePrice = new stripe.Price("ZenLitePrice", {
   product: zenLiteProduct.id,
   product: zenLiteProduct.id,
   currency: "usd",
   currency: "usd",
@@ -131,6 +152,9 @@ const ZEN_LITE_PRICE = new sst.Linkable("ZEN_LITE_PRICE", {
     priceInr: 92900,
     priceInr: 92900,
     firstMonth50Coupon: zenLiteCouponFirstMonth50.id,
     firstMonth50Coupon: zenLiteCouponFirstMonth50.id,
     firstMonth100Coupon: zenLiteCouponFirstMonth100.id,
     firstMonth100Coupon: zenLiteCouponFirstMonth100.id,
+    threeMonths100Coupon: zenLiteCouponThreeMonths100.id,
+    sixMonths100Coupon: zenLiteCouponSixMonths100.id,
+    twelveMonths100Coupon: zenLiteCouponTwelveMonths100.id,
   },
   },
 })
 })
 
 

+ 4 - 4
nix/hashes.json

@@ -1,8 +1,8 @@
 {
 {
   "nodeModules": {
   "nodeModules": {
-    "x86_64-linux": "sha256-AgHhYsiygxbsBo3JN4HqHXKAwh8n1qeuSCe2qqxlxW4=",
-    "aarch64-linux": "sha256-h2lpWRQ5EDYnjpqZXtUAp1mxKLQxJ4m8MspgSY8Ev78=",
-    "aarch64-darwin": "sha256-xnd91+WyeAqn06run2ajsekxJvTMiLsnqNPe/rR8VTM=",
-    "x86_64-darwin": "sha256-rXpz45IOjGEk73xhP9VY86eOj2CZBg2l1vzwzTIOOOQ="
+    "x86_64-linux": "sha256-OtyfKTBEHsJpjzAjN9vCR0PzGzdK6CDHdyU7eZ6Gl1s=",
+    "aarch64-linux": "sha256-3eHJs3S/+uDUPAouWPsdBOlEvAOhOYx5bJzahL0tAJk=",
+    "aarch64-darwin": "sha256-rFXzrkhPVb3yM20J8R8m7GqroNNk1vAEz+o/Ks+iAI4=",
+    "x86_64-darwin": "sha256-lb1IGgbpxg723Qxj2WVPkxKUUmyOIsFOAhA5LoZ8GwY="
   }
   }
 }
 }

+ 1 - 1
nix/opencode.nix

@@ -64,7 +64,7 @@ stdenvNoCC.mkDerivation (finalAttrs: {
           [
           [
             ripgrep
             ripgrep
           ]
           ]
-          # bun runs sysctl to detect if dunning on rosetta2
+          # bun runs sysctl to detect if running on rosetta2
           ++ lib.optional stdenvNoCC.hostPlatform.isDarwin sysctl
           ++ lib.optional stdenvNoCC.hostPlatform.isDarwin sysctl
         )
         )
       }
       }

+ 8 - 5
package.json

@@ -27,15 +27,15 @@
       "packages/slack"
       "packages/slack"
     ],
     ],
     "catalog": {
     "catalog": {
-      "@effect/opentelemetry": "4.0.0-beta.48",
-      "@effect/platform-node": "4.0.0-beta.48",
+      "@effect/opentelemetry": "4.0.0-beta.57",
+      "@effect/platform-node": "4.0.0-beta.57",
       "@npmcli/arborist": "9.4.0",
       "@npmcli/arborist": "9.4.0",
       "@types/bun": "1.3.12",
       "@types/bun": "1.3.12",
       "@types/cross-spawn": "6.0.6",
       "@types/cross-spawn": "6.0.6",
       "@octokit/rest": "22.0.0",
       "@octokit/rest": "22.0.0",
       "@hono/zod-validator": "0.4.2",
       "@hono/zod-validator": "0.4.2",
-      "@opentui/core": "0.1.99",
-      "@opentui/solid": "0.1.99",
+      "@opentui/core": "0.2.0",
+      "@opentui/solid": "0.2.0",
       "ulid": "3.0.1",
       "ulid": "3.0.1",
       "@kobalte/core": "0.13.11",
       "@kobalte/core": "0.13.11",
       "@types/luxon": "3.7.1",
       "@types/luxon": "3.7.1",
@@ -46,13 +46,14 @@
       "@cloudflare/workers-types": "4.20251008.0",
       "@cloudflare/workers-types": "4.20251008.0",
       "@openauthjs/openauth": "0.0.0-20250322224806",
       "@openauthjs/openauth": "0.0.0-20250322224806",
       "@pierre/diffs": "1.1.0-beta.18",
       "@pierre/diffs": "1.1.0-beta.18",
+      "opentui-spinner": "0.0.6",
       "@solid-primitives/storage": "4.3.3",
       "@solid-primitives/storage": "4.3.3",
       "@tailwindcss/vite": "4.1.11",
       "@tailwindcss/vite": "4.1.11",
       "diff": "8.0.2",
       "diff": "8.0.2",
       "dompurify": "3.3.1",
       "dompurify": "3.3.1",
       "drizzle-kit": "1.0.0-beta.19-d95b7a4",
       "drizzle-kit": "1.0.0-beta.19-d95b7a4",
       "drizzle-orm": "1.0.0-beta.19-d95b7a4",
       "drizzle-orm": "1.0.0-beta.19-d95b7a4",
-      "effect": "4.0.0-beta.48",
+      "effect": "4.0.0-beta.57",
       "ai": "6.0.168",
       "ai": "6.0.168",
       "cross-spawn": "7.0.6",
       "cross-spawn": "7.0.6",
       "hono": "4.10.7",
       "hono": "4.10.7",
@@ -76,6 +77,8 @@
       "@solidjs/meta": "0.29.4",
       "@solidjs/meta": "0.29.4",
       "@solidjs/router": "0.15.4",
       "@solidjs/router": "0.15.4",
       "@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020",
       "@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020",
+      "@sentry/solid": "10.36.0",
+      "@sentry/vite-plugin": "4.6.0",
       "solid-js": "1.9.10",
       "solid-js": "1.9.10",
       "vite-plugin-solid": "2.11.10",
       "vite-plugin-solid": "2.11.10",
       "@lydell/node-pty": "1.2.0-beta.10"
       "@lydell/node-pty": "1.2.0-beta.10"

+ 4 - 2
packages/app/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@opencode-ai/app",
   "name": "@opencode-ai/app",
-  "version": "1.14.23",
+  "version": "1.14.31",
   "description": "",
   "description": "",
   "type": "module",
   "type": "module",
   "exports": {
   "exports": {
@@ -27,6 +27,7 @@
   "devDependencies": {
   "devDependencies": {
     "@happy-dom/global-registrator": "20.0.11",
     "@happy-dom/global-registrator": "20.0.11",
     "@playwright/test": "catalog:",
     "@playwright/test": "catalog:",
+    "@sentry/vite-plugin": "catalog:",
     "@tailwindcss/vite": "catalog:",
     "@tailwindcss/vite": "catalog:",
     "@tsconfig/bun": "1.0.9",
     "@tsconfig/bun": "1.0.9",
     "@types/bun": "catalog:",
     "@types/bun": "catalog:",
@@ -40,9 +41,10 @@
   },
   },
   "dependencies": {
   "dependencies": {
     "@kobalte/core": "catalog:",
     "@kobalte/core": "catalog:",
+    "@sentry/solid": "catalog:",
     "@opencode-ai/sdk": "workspace:*",
     "@opencode-ai/sdk": "workspace:*",
     "@opencode-ai/ui": "workspace:*",
     "@opencode-ai/ui": "workspace:*",
-    "@opencode-ai/shared": "workspace:*",
+    "@opencode-ai/core": "workspace:*",
     "@shikijs/transformers": "3.9.2",
     "@shikijs/transformers": "3.9.2",
     "@solid-primitives/active-element": "2.1.3",
     "@solid-primitives/active-element": "2.1.3",
     "@solid-primitives/audio": "1.4.2",
     "@solid-primitives/audio": "1.4.2",

+ 23 - 7
packages/app/src/app.tsx

@@ -1,4 +1,5 @@
 import "@/index.css"
 import "@/index.css"
+import * as Sentry from "@sentry/solid"
 import { I18nProvider } from "@opencode-ai/ui/context"
 import { I18nProvider } from "@opencode-ai/ui/context"
 import { DialogProvider } from "@opencode-ai/ui/context/dialog"
 import { DialogProvider } from "@opencode-ai/ui/context/dialog"
 import { FileComponentProvider } from "@opencode-ai/ui/context/file"
 import { FileComponentProvider } from "@opencode-ai/ui/context/file"
@@ -82,7 +83,15 @@ declare global {
 }
 }
 
 
 function QueryProvider(props: ParentProps) {
 function QueryProvider(props: ParentProps) {
-  const client = new QueryClient()
+  const client = new QueryClient({
+    defaultOptions: {
+      queries: {
+        refetchOnReconnect: false,
+        refetchOnMount: false,
+        refetchOnWindowFocus: false,
+      },
+    },
+  })
   return <QueryClientProvider client={client}>{props.children}</QueryClientProvider>
   return <QueryClientProvider client={client}>{props.children}</QueryClientProvider>
 }
 }
 
 
@@ -140,12 +149,19 @@ export function AppBaseProviders(props: ParentProps<{ locale?: Locale }>) {
       >
       >
         <LanguageProvider locale={props.locale}>
         <LanguageProvider locale={props.locale}>
           <UiI18nBridge>
           <UiI18nBridge>
-            <ErrorBoundary fallback={(error) => <ErrorPage error={error} />}>
-              <DialogProvider>
-                <MarkedProvider>
-                  <FileComponentProvider component={File}>{props.children}</FileComponentProvider>
-                </MarkedProvider>
-              </DialogProvider>
+            <ErrorBoundary
+              fallback={(error) => {
+                Sentry.captureException(error)
+                return <ErrorPage error={error} />
+              }}
+            >
+              <QueryProvider>
+                <DialogProvider>
+                  <MarkedProvider>
+                    <FileComponentProvider component={File}>{props.children}</FileComponentProvider>
+                  </MarkedProvider>
+                </DialogProvider>
+              </QueryProvider>
             </ErrorBoundary>
             </ErrorBoundary>
           </UiI18nBridge>
           </UiI18nBridge>
         </LanguageProvider>
         </LanguageProvider>

+ 1 - 1
packages/app/src/components/dialog-edit-project.tsx

@@ -9,7 +9,7 @@ import { createStore } from "solid-js/store"
 import { useGlobalSDK } from "@/context/global-sdk"
 import { useGlobalSDK } from "@/context/global-sdk"
 import { useGlobalSync } from "@/context/global-sync"
 import { useGlobalSync } from "@/context/global-sync"
 import { type LocalProject, getAvatarColors } from "@/context/layout"
 import { type LocalProject, getAvatarColors } from "@/context/layout"
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { Avatar } from "@opencode-ai/ui/avatar"
 import { Avatar } from "@opencode-ai/ui/avatar"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
 import { getProjectAvatarSource } from "@/pages/layout/sidebar-items"
 import { getProjectAvatarSource } from "@/pages/layout/sidebar-items"

+ 1 - 1
packages/app/src/components/dialog-fork.tsx

@@ -9,7 +9,7 @@ import { List } from "@opencode-ai/ui/list"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
 import { extractPromptFromParts } from "@/utils/prompt"
 import { extractPromptFromParts } from "@/utils/prompt"
 import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client"
 import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
+import { base64Encode } from "@opencode-ai/core/util/encode"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
 
 
 interface ForkableMessage {
 interface ForkableMessage {

+ 1 - 1
packages/app/src/components/dialog-select-directory.tsx

@@ -3,7 +3,7 @@ import { Dialog } from "@opencode-ai/ui/dialog"
 import { FileIcon } from "@opencode-ai/ui/file-icon"
 import { FileIcon } from "@opencode-ai/ui/file-icon"
 import { List } from "@opencode-ai/ui/list"
 import { List } from "@opencode-ai/ui/list"
 import type { ListRef } from "@opencode-ai/ui/list"
 import type { ListRef } from "@opencode-ai/ui/list"
-import { getDirectory, getFilename } from "@opencode-ai/shared/util/path"
+import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
 import fuzzysort from "fuzzysort"
 import fuzzysort from "fuzzysort"
 import { createMemo, createResource, createSignal } from "solid-js"
 import { createMemo, createResource, createSignal } from "solid-js"
 import { useGlobalSDK } from "@/context/global-sdk"
 import { useGlobalSDK } from "@/context/global-sdk"

+ 2 - 2
packages/app/src/components/dialog-select-file.tsx

@@ -4,8 +4,8 @@ import { FileIcon } from "@opencode-ai/ui/file-icon"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Keybind } from "@opencode-ai/ui/keybind"
 import { Keybind } from "@opencode-ai/ui/keybind"
 import { List } from "@opencode-ai/ui/list"
 import { List } from "@opencode-ai/ui/list"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
-import { getDirectory, getFilename } from "@opencode-ai/shared/util/path"
+import { base64Encode } from "@opencode-ai/core/util/encode"
+import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
 import { useNavigate } from "@solidjs/router"
 import { useNavigate } from "@solidjs/router"
 import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid-js"
 import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid-js"
 import { formatKeybind, useCommand, type CommandOption } from "@/context/command"
 import { formatKeybind, useCommand, type CommandOption } from "@/context/command"

+ 7 - 55
packages/app/src/components/dialog-select-mcp.tsx

@@ -1,13 +1,12 @@
-import { useMutation } from "@tanstack/solid-query"
-import { Component, createEffect, createMemo, on, Show } from "solid-js"
-import { createStore } from "solid-js/store"
+import { useMutation, useQueryClient } from "@tanstack/solid-query"
+import { Component, createMemo, Show } from "solid-js"
 import { useSync } from "@/context/sync"
 import { useSync } from "@/context/sync"
 import { useSDK } from "@/context/sdk"
 import { useSDK } from "@/context/sdk"
 import { Dialog } from "@opencode-ai/ui/dialog"
 import { Dialog } from "@opencode-ai/ui/dialog"
 import { List } from "@opencode-ai/ui/list"
 import { List } from "@opencode-ai/ui/list"
 import { Switch } from "@opencode-ai/ui/switch"
 import { Switch } from "@opencode-ai/ui/switch"
-import { showToast } from "@opencode-ai/ui/toast"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
+import { loadMcpQuery } from "@/context/global-sync"
 
 
 const statusLabels = {
 const statusLabels = {
   connected: "mcp.status.connected",
   connected: "mcp.status.connected",
@@ -20,48 +19,7 @@ export const DialogSelectMcp: Component = () => {
   const sync = useSync()
   const sync = useSync()
   const sdk = useSDK()
   const sdk = useSDK()
   const language = useLanguage()
   const language = useLanguage()
-  const [state, setState] = createStore({
-    done: false,
-    loading: false,
-  })
-
-  createEffect(
-    on(
-      () => sync.data.mcp_ready,
-      (ready, prev) => {
-        if (!ready && prev) setState("done", false)
-      },
-      { defer: true },
-    ),
-  )
-
-  createEffect(() => {
-    if (state.done || state.loading) return
-    if (sync.data.mcp_ready) {
-      setState("done", true)
-      return
-    }
-
-    setState("loading", true)
-    void sdk.client.mcp
-      .status()
-      .then((result) => {
-        sync.set("mcp", result.data ?? {})
-        sync.set("mcp_ready", true)
-        setState("done", true)
-      })
-      .catch((err) => {
-        setState("done", true)
-        showToast({
-          variant: "error",
-          title: language.t("common.requestFailed"),
-          description: err instanceof Error ? err.message : String(err),
-        })
-      })
-      .finally(() => {
-        setState("loading", false)
-      })
-  })
+  const queryClient = useQueryClient()
 
 
   const items = createMemo(() =>
   const items = createMemo(() =>
     Object.entries(sync.data.mcp ?? {})
     Object.entries(sync.data.mcp ?? {})
@@ -71,16 +29,10 @@ export const DialogSelectMcp: Component = () => {
 
 
   const toggle = useMutation(() => ({
   const toggle = useMutation(() => ({
     mutationFn: async (name: string) => {
     mutationFn: async (name: string) => {
-      const status = sync.data.mcp[name]
-      if (status?.status === "connected") {
-        await sdk.client.mcp.disconnect({ name })
-      } else {
-        await sdk.client.mcp.connect({ name })
-      }
-
-      const result = await sdk.client.mcp.status()
-      if (result.data) sync.set("mcp", result.data)
+      if (sync.data.mcp[name]?.status === "connected") await sdk.client.mcp.disconnect({ name })
+      else await sdk.client.mcp.connect({ name })
     },
     },
+    onSuccess: () => queryClient.refetchQueries({ queryKey: loadMcpQuery(sync.directory).queryKey }),
   }))
   }))
 
 
   const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length)
   const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length)

+ 1 - 1
packages/app/src/components/prompt-input/build-request-parts.ts

@@ -1,4 +1,4 @@
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { type AgentPartInput, type FilePartInput, type Part, type TextPartInput } from "@opencode-ai/sdk/v2/client"
 import { type AgentPartInput, type FilePartInput, type Part, type TextPartInput } from "@opencode-ai/sdk/v2/client"
 import type { FileSelection } from "@/context/file"
 import type { FileSelection } from "@/context/file"
 import { encodeFilePath } from "@/context/file/path"
 import { encodeFilePath } from "@/context/file/path"

+ 1 - 1
packages/app/src/components/prompt-input/context-items.tsx

@@ -2,7 +2,7 @@ import { Component, For, Show } from "solid-js"
 import { FileIcon } from "@opencode-ai/ui/file-icon"
 import { FileIcon } from "@opencode-ai/ui/file-icon"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { Tooltip } from "@opencode-ai/ui/tooltip"
 import { Tooltip } from "@opencode-ai/ui/tooltip"
-import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/shared/util/path"
+import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/core/util/path"
 import type { ContextItem } from "@/context/prompt"
 import type { ContextItem } from "@/context/prompt"
 
 
 type PromptContextItem = ContextItem & { key: string }
 type PromptContextItem = ContextItem & { key: string }

+ 1 - 1
packages/app/src/components/prompt-input/slash-popover.tsx

@@ -1,7 +1,7 @@
 import { Component, For, Match, Show, Switch } from "solid-js"
 import { Component, For, Match, Show, Switch } from "solid-js"
 import { FileIcon } from "@opencode-ai/ui/file-icon"
 import { FileIcon } from "@opencode-ai/ui/file-icon"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Icon } from "@opencode-ai/ui/icon"
-import { getDirectory, getFilename } from "@opencode-ai/shared/util/path"
+import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
 
 
 export type AtOption =
 export type AtOption =
   | { type: "agent"; name: string; display: string }
   | { type: "agent"; name: string; display: string }

+ 1 - 1
packages/app/src/components/prompt-input/submit.test.ts

@@ -74,7 +74,7 @@ beforeAll(async () => {
     showToast: () => 0,
     showToast: () => 0,
   }))
   }))
 
 
-  mock.module("@opencode-ai/shared/util/encode", () => ({
+  mock.module("@opencode-ai/core/util/encode", () => ({
     base64Encode: (value: string) => value,
     base64Encode: (value: string) => value,
   }))
   }))
 
 

+ 2 - 2
packages/app/src/components/prompt-input/submit.ts

@@ -1,7 +1,7 @@
 import type { Message, Session } from "@opencode-ai/sdk/v2/client"
 import type { Message, Session } from "@opencode-ai/sdk/v2/client"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
-import { Binary } from "@opencode-ai/shared/util/binary"
+import { base64Encode } from "@opencode-ai/core/util/encode"
+import { Binary } from "@opencode-ai/core/util/binary"
 import { useNavigate, useParams } from "@solidjs/router"
 import { useNavigate, useParams } from "@solidjs/router"
 import { batch, type Accessor } from "solid-js"
 import { batch, type Accessor } from "solid-js"
 import type { FileSelection } from "@/context/file"
 import type { FileSelection } from "@/context/file"

+ 2 - 2
packages/app/src/components/session/session-context-tab.tsx

@@ -1,8 +1,8 @@
 import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js"
 import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js"
 import type { JSX } from "solid-js"
 import type { JSX } from "solid-js"
 import { useSync } from "@/context/sync"
 import { useSync } from "@/context/sync"
-import { checksum } from "@opencode-ai/shared/util/encode"
-import { findLast } from "@opencode-ai/shared/util/array"
+import { checksum } from "@opencode-ai/core/util/encode"
+import { findLast } from "@opencode-ai/core/util/array"
 import { same } from "@/utils/same"
 import { same } from "@/utils/same"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Accordion } from "@opencode-ai/ui/accordion"
 import { Accordion } from "@opencode-ai/ui/accordion"

+ 1 - 1
packages/app/src/components/session/session-header.tsx

@@ -7,7 +7,7 @@ import { Keybind } from "@opencode-ai/ui/keybind"
 import { Spinner } from "@opencode-ai/ui/spinner"
 import { Spinner } from "@opencode-ai/ui/spinner"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
 import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
 import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js"
 import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js"
 import { createStore } from "solid-js/store"
 import { createStore } from "solid-js/store"
 import { Portal } from "solid-js/web"
 import { Portal } from "solid-js/web"

+ 1 - 1
packages/app/src/components/session/session-new-view.tsx

@@ -5,7 +5,7 @@ import { useSDK } from "@/context/sdk"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Mark } from "@opencode-ai/ui/logo"
 import { Mark } from "@opencode-ai/ui/logo"
-import { getDirectory, getFilename } from "@opencode-ai/shared/util/path"
+import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
 
 
 const MAIN_WORKTREE = "main"
 const MAIN_WORKTREE = "main"
 const CREATE_WORKTREE = "create"
 const CREATE_WORKTREE = "create"

+ 1 - 1
packages/app/src/components/session/session-sortable-tab.tsx

@@ -5,7 +5,7 @@ import { FileIcon } from "@opencode-ai/ui/file-icon"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { TooltipKeybind } from "@opencode-ai/ui/tooltip"
 import { TooltipKeybind } from "@opencode-ai/ui/tooltip"
 import { Tabs } from "@opencode-ai/ui/tabs"
 import { Tabs } from "@opencode-ai/ui/tabs"
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { useFile } from "@/context/file"
 import { useFile } from "@/context/file"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
 import { useCommand } from "@/context/command"
 import { useCommand } from "@/context/command"

+ 124 - 66
packages/app/src/components/settings-general.tsx

@@ -11,7 +11,9 @@ import { showToast } from "@opencode-ai/ui/toast"
 import { useParams } from "@solidjs/router"
 import { useParams } from "@solidjs/router"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
 import { usePermission } from "@/context/permission"
 import { usePermission } from "@/context/permission"
-import { usePlatform } from "@/context/platform"
+import { usePlatform, type DisplayBackend } from "@/context/platform"
+import { useGlobalSync } from "@/context/global-sync"
+import { useGlobalSDK } from "@/context/global-sdk"
 import {
 import {
   monoDefault,
   monoDefault,
   monoFontFamily,
   monoFontFamily,
@@ -40,6 +42,18 @@ type ThemeOption = {
   name: string
   name: string
 }
 }
 
 
+type ShellOption = {
+  path: string
+  name: string
+  acceptable: boolean
+}
+
+type ShellSelectOption = {
+  id: string
+  value: string
+  label: string
+}
+
 // To prevent audio from overlapping/playing very quickly when navigating the settings menus,
 // To prevent audio from overlapping/playing very quickly when navigating the settings menus,
 // delay the playback by 100ms during quick selection changes and pause existing sounds.
 // delay the playback by 100ms during quick selection changes and pause existing sounds.
 const stopDemoSound = () => {
 const stopDemoSound = () => {
@@ -75,10 +89,6 @@ export const SettingsGeneral: Component = () => {
   const params = useParams()
   const params = useParams()
   const settings = useSettings()
   const settings = useSettings()
 
 
-  onMount(() => {
-    void theme.loadThemes()
-  })
-
   const [store, setStore] = createStore({
   const [store, setStore] = createStore({
     checking: false,
     checking: false,
   })
   })
@@ -165,6 +175,70 @@ export const SettingsGeneral: Component = () => {
 
 
   const themeOptions = createMemo<ThemeOption[]>(() => theme.ids().map((id) => ({ id, name: theme.name(id) })))
   const themeOptions = createMemo<ThemeOption[]>(() => theme.ids().map((id) => ({ id, name: theme.name(id) })))
 
 
+  const globalSync = useGlobalSync()
+  const globalSdk = useGlobalSDK()
+
+  const [shells] = createResource(
+    () =>
+      globalSdk.client.pty
+        .shells()
+        .then((res) => res.data ?? [])
+        .catch(() => [] as ShellOption[]),
+    { initialValue: [] as ShellOption[] },
+  )
+
+  const [displayBackend, { refetch: refetchDisplayBackend }] = createResource(
+    () => (linux() && platform.getDisplayBackend ? true : false),
+    () => Promise.resolve(platform.getDisplayBackend?.() ?? null).catch(() => null as DisplayBackend | null),
+    { initialValue: null as DisplayBackend | null },
+  )
+
+  onMount(() => {
+    void theme.loadThemes()
+  })
+
+  const autoOption = { id: "auto", value: "", label: language.t("settings.general.row.shell.autoDefault") }
+  const currentShell = createMemo(() => globalSync.data.config.shell ?? "")
+
+  const shellOptions = createMemo<ShellSelectOption[]>(() => {
+    const list = shells.latest
+    const current = globalSync.data.config.shell
+
+    const nameCounts = new Map<string, number>()
+    for (const s of list) {
+      nameCounts.set(s.name, (nameCounts.get(s.name) || 0) + 1)
+    }
+
+    const options = [
+      autoOption,
+      ...list.map((s) => {
+        const ambiguousName = (nameCounts.get(s.name) || 0) > 1
+        const text = ambiguousName ? s.path : s.name
+        const label = s.acceptable ? text : `${text} (${language.t("settings.general.row.shell.terminalOnly")})`
+        return {
+          id: s.path,
+          // Prefer name over path - "bash" is much cleaner than the explicit full route even when it may change due to PATH.
+          value: ambiguousName ? s.path : s.name,
+          label,
+        }
+      }),
+    ]
+
+    if (current && !options.some((o) => o.value === current)) {
+      options.push({ id: current, value: current, label: current })
+    }
+
+    return options
+  })
+
+  const onDisplayBackendChange = (checked: boolean) => {
+    const update = platform.setDisplayBackend?.(checked ? "wayland" : "auto")
+    if (!update) return
+    void update.finally(() => {
+      void refetchDisplayBackend()
+    })
+  }
+
   const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [
   const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [
     { value: "system", label: language.t("theme.scheme.system") },
     { value: "system", label: language.t("theme.scheme.system") },
     { value: "light", label: language.t("theme.scheme.light") },
     { value: "light", label: language.t("theme.scheme.light") },
@@ -243,6 +317,28 @@ export const SettingsGeneral: Component = () => {
           </div>
           </div>
         </SettingsRow>
         </SettingsRow>
 
 
+        <SettingsRow
+          title={language.t("settings.general.row.shell.title")}
+          description={language.t("settings.general.row.shell.description")}
+        >
+          <Select
+            data-action="settings-shell"
+            options={shellOptions()}
+            current={shellOptions().find((o) => o.value === currentShell()) ?? autoOption}
+            value={(o) => o.id}
+            label={(o) => o.label}
+            onSelect={(option) => {
+              if (!option) return
+              if (option.value === currentShell()) return
+              globalSync.updateConfig({ shell: option.value })
+            }}
+            variant="secondary"
+            size="small"
+            triggerVariant="settings"
+            triggerStyle={{ "min-width": "180px" }}
+          />
+        </SettingsRow>
+
         <SettingsRow
         <SettingsRow
           title={language.t("settings.general.row.reasoningSummaries.title")}
           title={language.t("settings.general.row.reasoningSummaries.title")}
           description={language.t("settings.general.row.reasoningSummaries.description")}
           description={language.t("settings.general.row.reasoningSummaries.description")}
@@ -651,70 +747,32 @@ export const SettingsGeneral: Component = () => {
 
 
         <SoundsSection />
         <SoundsSection />
 
 
-        {/*<Show when={platform.platform === "desktop" && platform.os === "windows" && platform.getWslEnabled}>
-          {(_) => {
-            const [enabledResource, actions] = createResource(() => platform.getWslEnabled?.())
-            const enabled = () => (enabledResource.state === "pending" ? undefined : enabledResource.latest)
-
-            return (
-              <div class="flex flex-col gap-1">
-                <h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.desktop.section.wsl")}</h3>
-
-                <SettingsList>
-                  <SettingsRow
-                    title={language.t("settings.desktop.wsl.title")}
-                    description={language.t("settings.desktop.wsl.description")}
-                  >
-                    <div data-action="settings-wsl">
-                      <Switch
-                        checked={enabled() ?? false}
-                        disabled={enabledResource.state === "pending"}
-                        onChange={(checked) => platform.setWslEnabled?.(checked)?.finally(() => actions.refetch())}
-                      />
-                    </div>
-                  </SettingsRow>
-                </SettingsList>
-              </div>
-            )
-          }}
-        </Show>*/}
-
         <UpdatesSection />
         <UpdatesSection />
 
 
         <Show when={linux()}>
         <Show when={linux()}>
-          {(_) => {
-            const [valueResource, actions] = createResource(() => platform.getDisplayBackend?.())
-            const value = () => (valueResource.state === "pending" ? undefined : valueResource.latest)
-
-            const onChange = (checked: boolean) =>
-              platform.setDisplayBackend?.(checked ? "wayland" : "auto").finally(() => actions.refetch())
-
-            return (
-              <div class="flex flex-col gap-1">
-                <h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3>
-
-                <SettingsList>
-                  <SettingsRow
-                    title={
-                      <div class="flex items-center gap-2">
-                        <span>{language.t("settings.general.row.wayland.title")}</span>
-                        <Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
-                          <span class="text-text-weak">
-                            <Icon name="help" size="small" />
-                          </span>
-                        </Tooltip>
-                      </div>
-                    }
-                    description={language.t("settings.general.row.wayland.description")}
-                  >
-                    <div data-action="settings-wayland">
-                      <Switch checked={value() === "wayland"} onChange={onChange} />
-                    </div>
-                  </SettingsRow>
-                </SettingsList>
-              </div>
-            )
-          }}
+          <div class="flex flex-col gap-1">
+            <h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3>
+
+            <SettingsList>
+              <SettingsRow
+                title={
+                  <div class="flex items-center gap-2">
+                    <span>{language.t("settings.general.row.wayland.title")}</span>
+                    <Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
+                      <span class="text-text-weak">
+                        <Icon name="help" size="small" />
+                      </span>
+                    </Tooltip>
+                  </div>
+                }
+                description={language.t("settings.general.row.wayland.description")}
+              >
+                <div data-action="settings-wayland">
+                  <Switch checked={displayBackend.latest === "wayland"} onChange={onDisplayBackendChange} />
+                </div>
+              </SettingsRow>
+            </SettingsList>
+          </div>
         </Show>
         </Show>
 
 
         <Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}>
         <Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}>

+ 4 - 45
packages/app/src/components/status-popover-body.tsx

@@ -3,7 +3,7 @@ import { useDialog } from "@opencode-ai/ui/context/dialog"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Switch } from "@opencode-ai/ui/switch"
 import { Switch } from "@opencode-ai/ui/switch"
 import { Tabs } from "@opencode-ai/ui/tabs"
 import { Tabs } from "@opencode-ai/ui/tabs"
-import { useMutation } from "@tanstack/solid-query"
+import { useMutation, useQueryClient } from "@tanstack/solid-query"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
 import { useNavigate } from "@solidjs/router"
 import { useNavigate } from "@solidjs/router"
 import { type Accessor, createEffect, createMemo, For, type JSXElement, onCleanup, Show } from "solid-js"
 import { type Accessor, createEffect, createMemo, For, type JSXElement, onCleanup, Show } from "solid-js"
@@ -15,6 +15,7 @@ import { useSDK } from "@/context/sdk"
 import { normalizeServerUrl, ServerConnection, useServer } from "@/context/server"
 import { normalizeServerUrl, ServerConnection, useServer } from "@/context/server"
 import { useSync } from "@/context/sync"
 import { useSync } from "@/context/sync"
 import { useCheckServerHealth, type ServerHealth } from "@/utils/server-health"
 import { useCheckServerHealth, type ServerHealth } from "@/utils/server-health"
+import { loadMcpQuery } from "@/context/global-sync"
 
 
 const pollMs = 10_000
 const pollMs = 10_000
 
 
@@ -137,14 +138,14 @@ const useMcpToggleMutation = () => {
   const sync = useSync()
   const sync = useSync()
   const sdk = useSDK()
   const sdk = useSDK()
   const language = useLanguage()
   const language = useLanguage()
+  const queryClient = useQueryClient()
 
 
   return useMutation(() => ({
   return useMutation(() => ({
     mutationFn: async (name: string) => {
     mutationFn: async (name: string) => {
       const status = sync.data.mcp[name]
       const status = sync.data.mcp[name]
       await (status?.status === "connected" ? sdk.client.mcp.disconnect({ name }) : sdk.client.mcp.connect({ name }))
       await (status?.status === "connected" ? sdk.client.mcp.disconnect({ name }) : sdk.client.mcp.connect({ name }))
-      const result = await sdk.client.mcp.status()
-      if (result.data) sync.set("mcp", result.data)
     },
     },
+    onSuccess: () => queryClient.refetchQueries({ queryKey: loadMcpQuery(sync.directory).queryKey }),
     onError: (err) => {
     onError: (err) => {
       showToast({
       showToast({
         variant: "error",
         variant: "error",
@@ -162,14 +163,6 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
   const dialog = useDialog()
   const dialog = useDialog()
   const language = useLanguage()
   const language = useLanguage()
   const navigate = useNavigate()
   const navigate = useNavigate()
-  const sdk = useSDK()
-
-  const [load, setLoad] = createStore({
-    lspDone: false,
-    lspLoading: false,
-    mcpDone: false,
-    mcpLoading: false,
-  })
 
 
   const fail = (err: unknown) => {
   const fail = (err: unknown) => {
     showToast({
     showToast({
@@ -181,40 +174,6 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
 
 
   createEffect(() => {
   createEffect(() => {
     if (!props.shown()) return
     if (!props.shown()) return
-
-    if (!sync.data.mcp_ready && !load.mcpDone && !load.mcpLoading) {
-      setLoad("mcpLoading", true)
-      void sdk.client.mcp
-        .status()
-        .then((result) => {
-          sync.set("mcp", result.data ?? {})
-          sync.set("mcp_ready", true)
-        })
-        .catch((err) => {
-          setLoad("mcpDone", true)
-          fail(err)
-        })
-        .finally(() => {
-          setLoad("mcpLoading", false)
-        })
-    }
-
-    if (!sync.data.lsp_ready && !load.lspDone && !load.lspLoading) {
-      setLoad("lspLoading", true)
-      void sdk.client.lsp
-        .status()
-        .then((result) => {
-          sync.set("lsp", result.data ?? [])
-          sync.set("lsp_ready", true)
-        })
-        .catch((err) => {
-          setLoad("lspDone", true)
-          fail(err)
-        })
-        .finally(() => {
-          setLoad("lspLoading", false)
-        })
-    }
   })
   })
 
 
   let dialogRun = 0
   let dialogRun = 0

+ 1 - 1
packages/app/src/context/file.tsx

@@ -3,7 +3,7 @@ import { createStore, produce, reconcile } from "solid-js/store"
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
 import { useParams } from "@solidjs/router"
 import { useParams } from "@solidjs/router"
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { useSDK } from "./sdk"
 import { useSDK } from "./sdk"
 import { useSync } from "./sync"
 import { useSync } from "./sync"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"

+ 120 - 101
packages/app/src/context/global-sync.tsx

@@ -8,23 +8,32 @@ import type {
   Todo,
   Todo,
 } from "@opencode-ai/sdk/v2/client"
 } from "@opencode-ai/sdk/v2/client"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js"
 import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js"
 import { createStore, produce, reconcile } from "solid-js/store"
 import { createStore, produce, reconcile } from "solid-js/store"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
 import type { InitError } from "../pages/error"
 import type { InitError } from "../pages/error"
 import { useGlobalSDK } from "./global-sdk"
 import { useGlobalSDK } from "./global-sdk"
-import { bootstrapDirectory, bootstrapGlobal, clearProviderRev } from "./global-sync/bootstrap"
+import {
+  bootstrapDirectory,
+  bootstrapGlobal,
+  clearProviderRev,
+  loadGlobalConfigQuery,
+  loadPathQuery,
+  loadProjectsQuery,
+  loadProvidersQuery,
+} from "./global-sync/bootstrap"
 import { createChildStoreManager } from "./global-sync/child-store"
 import { createChildStoreManager } from "./global-sync/child-store"
 import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } from "./global-sync/event-reducer"
 import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } from "./global-sync/event-reducer"
-import { createRefreshQueue } from "./global-sync/queue"
 import { clearSessionPrefetchDirectory } from "./global-sync/session-prefetch"
 import { clearSessionPrefetchDirectory } from "./global-sync/session-prefetch"
 import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global-sync/session-load"
 import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global-sync/session-load"
 import { trimSessions } from "./global-sync/session-trim"
 import { trimSessions } from "./global-sync/session-trim"
 import type { ProjectMeta } from "./global-sync/types"
 import type { ProjectMeta } from "./global-sync/types"
 import { SESSION_RECENT_LIMIT } from "./global-sync/types"
 import { SESSION_RECENT_LIMIT } from "./global-sync/types"
 import { formatServerError } from "@/utils/server-errors"
 import { formatServerError } from "@/utils/server-errors"
-import { queryOptions, skipToken, useQueryClient } from "@tanstack/solid-query"
+import { queryOptions, skipToken, useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/solid-query"
+import { createRefreshQueue } from "./global-sync/queue"
+import { directoryKey } from "./global-sync/utils"
 
 
 type GlobalStore = {
 type GlobalStore = {
   ready: boolean
   ready: boolean
@@ -43,6 +52,18 @@ type GlobalStore = {
 export const loadSessionsQuery = (directory: string) =>
 export const loadSessionsQuery = (directory: string) =>
   queryOptions<null>({ queryKey: [directory, "loadSessions"], queryFn: skipToken })
   queryOptions<null>({ queryKey: [directory, "loadSessions"], queryFn: skipToken })
 
 
+export const loadMcpQuery = (directory: string, sdk?: OpencodeClient) =>
+  queryOptions({
+    queryKey: [directory, "mcp"],
+    queryFn: sdk ? () => sdk.mcp.status().then((r) => r.data ?? {}) : skipToken,
+  })
+
+export const loadLspQuery = (directory: string, sdk?: OpencodeClient) =>
+  queryOptions({
+    queryKey: [directory, "lsp"],
+    queryFn: sdk ? () => sdk.lsp.status().then((r) => r.data ?? []) : skipToken,
+  })
+
 function createGlobalSync() {
 function createGlobalSync() {
   const globalSDK = useGlobalSDK()
   const globalSDK = useGlobalSDK()
   const language = useLanguage()
   const language = useLanguage()
@@ -54,15 +75,34 @@ function createGlobalSync() {
   const sessionLoads = new Map<string, Promise<void>>()
   const sessionLoads = new Map<string, Promise<void>>()
   const sessionMeta = new Map<string, { limit: number }>()
   const sessionMeta = new Map<string, { limit: number }>()
 
 
+  const [configQuery, providerQuery, pathQuery] = useQueries(() => ({
+    queries: [loadGlobalConfigQuery(), loadProvidersQuery(null), loadPathQuery(null), loadProjectsQuery()],
+  }))
+
   const [globalStore, setGlobalStore] = createStore<GlobalStore>({
   const [globalStore, setGlobalStore] = createStore<GlobalStore>({
-    ready: false,
-    path: { state: "", config: "", worktree: "", directory: "", home: "" },
+    get ready() {
+      return bootstrap.isPending
+    },
     project: [],
     project: [],
     session_todo: {},
     session_todo: {},
-    provider: { all: [], connected: [], default: {} },
     provider_auth: {},
     provider_auth: {},
-    config: {},
-    reload: undefined,
+    get path() {
+      const EMPTY = { state: "", config: "", worktree: "", directory: "", home: "" }
+      if (pathQuery.isLoading) return EMPTY
+      return pathQuery.data ?? EMPTY
+    },
+    get provider() {
+      const EMPTY = { all: [], connected: [], default: {} }
+      if (providerQuery.isLoading) return EMPTY
+      return providerQuery.data ?? EMPTY
+    },
+    get config() {
+      if (configQuery.isLoading) return {}
+      return configQuery.data ?? {}
+    },
+    get reload() {
+      return updateConfigMutation.isPending ? "pending" : undefined
+    },
   })
   })
   const queryClient = useQueryClient()
   const queryClient = useQueryClient()
 
 
@@ -88,6 +128,22 @@ function createGlobalSync() {
     return (setGlobalStore as (...args: unknown[]) => unknown)(...input)
     return (setGlobalStore as (...args: unknown[]) => unknown)(...input)
   }) as typeof setGlobalStore
   }) as typeof setGlobalStore
 
 
+  const bootstrap = useQuery(() => ({
+    queryKey: ["bootstrap"],
+    queryFn: async () => {
+      await bootstrapGlobal({
+        globalSDK: globalSDK.client,
+        requestFailedTitle: language.t("common.requestFailed"),
+        translate: language.t,
+        formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
+        setGlobalStore: setBootStore,
+        queryClient,
+      })
+      bootedAt = Date.now()
+      return bootedAt
+    },
+  }))
+
   const set = ((...input: unknown[]) => {
   const set = ((...input: unknown[]) => {
     if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) {
     if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) {
       setProjects(input[1] as Project[] | ((draft: Project[]) => Project[]))
       setProjects(input[1] as Project[] | ((draft: Project[]) => Project[]))
@@ -114,10 +170,23 @@ function createGlobalSync() {
 
 
   const queue = createRefreshQueue({
   const queue = createRefreshQueue({
     paused,
     paused,
-    bootstrap,
+    key: directoryKey,
+    bootstrap: () => queryClient.fetchQuery({ queryKey: ["bootstrap"] }),
     bootstrapInstance,
     bootstrapInstance,
   })
   })
 
 
+  const sdkFor = (directory: string) => {
+    const key = directoryKey(directory)
+    const cached = sdkCache.get(key)
+    if (cached) return cached
+    const sdk = globalSDK.createClient({
+      directory,
+      throwOnError: true,
+    })
+    sdkCache.set(key, sdk)
+    return sdk
+  }
+
   const children = createChildStoreManager({
   const children = createChildStoreManager({
     owner,
     owner,
     isBooting: (directory) => booting.has(directory),
     isBooting: (directory) => booting.has(directory),
@@ -126,33 +195,28 @@ function createGlobalSync() {
       void bootstrapInstance(directory)
       void bootstrapInstance(directory)
     },
     },
     onDispose: (directory) => {
     onDispose: (directory) => {
-      queue.clear(directory)
-      sessionMeta.delete(directory)
-      sdkCache.delete(directory)
-      clearProviderRev(directory)
-      clearSessionPrefetchDirectory(directory)
+      const key = directoryKey(directory)
+      queue.clear(key)
+      sessionMeta.delete(key)
+      sdkCache.delete(key)
+      clearProviderRev(key)
+      clearSessionPrefetchDirectory(key)
     },
     },
     translate: language.t,
     translate: language.t,
+    getSdk: sdkFor,
+    global: {
+      provider: globalStore.provider,
+    },
   })
   })
 
 
-  const sdkFor = (directory: string) => {
-    const cached = sdkCache.get(directory)
-    if (cached) return cached
-    const sdk = globalSDK.createClient({
-      directory,
-      throwOnError: true,
-    })
-    sdkCache.set(directory, sdk)
-    return sdk
-  }
-
   async function loadSessions(directory: string) {
   async function loadSessions(directory: string) {
-    const pending = sessionLoads.get(directory)
+    const key = directoryKey(directory)
+    const pending = sessionLoads.get(key)
     if (pending) return pending
     if (pending) return pending
 
 
-    children.pin(directory)
+    children.pin(key)
     const [store, setStore] = children.child(directory, { bootstrap: false })
     const [store, setStore] = children.child(directory, { bootstrap: false })
-    const meta = sessionMeta.get(directory)
+    const meta = sessionMeta.get(key)
     if (meta && meta.limit >= store.limit) {
     if (meta && meta.limit >= store.limit) {
       const next = trimSessions(store.session, {
       const next = trimSessions(store.session, {
         limit: store.limit,
         limit: store.limit,
@@ -162,14 +226,14 @@ function createGlobalSync() {
         setStore("session", reconcile(next, { key: "id" }))
         setStore("session", reconcile(next, { key: "id" }))
         cleanupDroppedSessionCaches(store, setStore, next, setSessionTodo)
         cleanupDroppedSessionCaches(store, setStore, next, setSessionTodo)
       }
       }
-      children.unpin(directory)
+      children.unpin(key)
       return
       return
     }
     }
 
 
     const limit = Math.max(store.limit + SESSION_RECENT_LIMIT, SESSION_RECENT_LIMIT)
     const limit = Math.max(store.limit + SESSION_RECENT_LIMIT, SESSION_RECENT_LIMIT)
     const promise = queryClient
     const promise = queryClient
       .fetchQuery({
       .fetchQuery({
-        ...loadSessionsQuery(directory),
+        ...loadSessionsQuery(key),
         queryFn: () =>
         queryFn: () =>
           loadRootSessionsWithFallback({
           loadRootSessionsWithFallback({
             directory,
             directory,
@@ -199,7 +263,7 @@ function createGlobalSync() {
                 setStore("session", reconcile(sessions, { key: "id" }))
                 setStore("session", reconcile(sessions, { key: "id" }))
                 cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo)
                 cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo)
               })
               })
-              sessionMeta.set(directory, { limit })
+              sessionMeta.set(key, { limit })
             })
             })
             .catch((err) => {
             .catch((err) => {
               console.error("Failed to load sessions", err)
               console.error("Failed to load sessions", err)
@@ -214,23 +278,24 @@ function createGlobalSync() {
       })
       })
       .then(() => {})
       .then(() => {})
 
 
-    sessionLoads.set(directory, promise)
+    sessionLoads.set(key, promise)
     void promise.finally(() => {
     void promise.finally(() => {
-      sessionLoads.delete(directory)
-      children.unpin(directory)
+      sessionLoads.delete(key)
+      children.unpin(key)
     })
     })
     return promise
     return promise
   }
   }
 
 
   async function bootstrapInstance(directory: string) {
   async function bootstrapInstance(directory: string) {
-    if (!directory) return
-    const pending = booting.get(directory)
+    const key = directoryKey(directory)
+    if (!key) return
+    const pending = booting.get(key)
     if (pending) return pending
     if (pending) return pending
 
 
-    children.pin(directory)
+    children.pin(key)
     const promise = Promise.resolve().then(async () => {
     const promise = Promise.resolve().then(async () => {
       const child = children.ensureChild(directory)
       const child = children.ensureChild(directory)
-      const cache = children.vcsCache.get(directory)
+      const cache = children.vcsCache.get(key)
       if (!cache) return
       if (!cache) return
       const sdk = sdkFor(directory)
       const sdk = sdkFor(directory)
       await bootstrapDirectory({
       await bootstrapDirectory({
@@ -251,39 +316,27 @@ function createGlobalSync() {
       })
       })
     })
     })
 
 
-    booting.set(directory, promise)
+    booting.set(key, promise)
     void promise.finally(() => {
     void promise.finally(() => {
-      booting.delete(directory)
-      children.unpin(directory)
+      booting.delete(key)
+      children.unpin(key)
     })
     })
     return promise
     return promise
   }
   }
 
 
   const unsub = globalSDK.event.listen((e) => {
   const unsub = globalSDK.event.listen((e) => {
     const directory = e.name
     const directory = e.name
+    const key = directoryKey(directory)
     const event = e.details
     const event = e.details
     const recent = bootingRoot || Date.now() - bootedAt < 1500
     const recent = bootingRoot || Date.now() - bootedAt < 1500
 
 
-    if (event.type === "session.error") {
-      const error = event.properties.error
-      if (error?.name !== "MessageAbortedError") {
-        console.error("[global-sync] session error", {
-          scope: directory === "global" ? "global" : "workspace",
-          directory: directory === "global" ? undefined : directory,
-          project: directory === "global" ? undefined : getFilename(directory),
-          sessionID: event.properties.sessionID,
-          error,
-        })
-      }
-    }
-
     if (directory === "global") {
     if (directory === "global") {
       applyGlobalEvent({
       applyGlobalEvent({
         event,
         event,
         project: globalStore.project,
         project: globalStore.project,
         refresh: () => {
         refresh: () => {
           if (recent) return
           if (recent) return
-          queue.refresh()
+          bootstrap.refetch()
         },
         },
         setGlobalProject: setProjects,
         setGlobalProject: setProjects,
       })
       })
@@ -296,9 +349,9 @@ function createGlobalSync() {
       return
       return
     }
     }
 
 
-    const existing = children.children[directory]
+    const existing = children.children[key]
     if (!existing) return
     if (!existing) return
-    children.mark(directory)
+    children.mark(key)
     const [store, setStore] = existing
     const [store, setStore] = existing
     applyDirectoryEvent({
     applyDirectoryEvent({
       event,
       event,
@@ -307,14 +360,9 @@ function createGlobalSync() {
       setStore,
       setStore,
       push: queue.push,
       push: queue.push,
       setSessionTodo,
       setSessionTodo,
-      vcsCache: children.vcsCache.get(directory),
+      vcsCache: children.vcsCache.get(key),
       loadLsp: () => {
       loadLsp: () => {
-        void sdkFor(directory)
-          .lsp.status()
-          .then((x) => {
-            setStore("lsp", x.data ?? [])
-            setStore("lsp_ready", true)
-          })
+        void queryClient.fetchQuery(loadLspQuery(key, sdkFor(directory)))
       },
       },
     })
     })
   })
   })
@@ -325,27 +373,10 @@ function createGlobalSync() {
   })
   })
   onCleanup(() => {
   onCleanup(() => {
     for (const directory of Object.keys(children.children)) {
     for (const directory of Object.keys(children.children)) {
-      children.disposeDirectory(directory)
+      children.disposeDirectory(directoryKey(directory))
     }
     }
   })
   })
 
 
-  async function bootstrap() {
-    bootingRoot = true
-    try {
-      await bootstrapGlobal({
-        globalSDK: globalSDK.client,
-        requestFailedTitle: language.t("common.requestFailed"),
-        translate: language.t,
-        formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
-        setGlobalStore: setBootStore,
-        queryClient,
-      })
-      bootedAt = Date.now()
-    } finally {
-      bootingRoot = false
-    }
-  }
-
   onMount(() => {
   onMount(() => {
     if (typeof requestAnimationFrame === "function") {
     if (typeof requestAnimationFrame === "function") {
       eventFrame = requestAnimationFrame(() => {
       eventFrame = requestAnimationFrame(() => {
@@ -361,7 +392,6 @@ function createGlobalSync() {
         void globalSDK.event.start()
         void globalSDK.event.start()
       }, 0)
       }, 0)
     }
     }
-    void bootstrap()
   })
   })
 
 
   const projectApi = {
   const projectApi = {
@@ -374,21 +404,10 @@ function createGlobalSync() {
     },
     },
   }
   }
 
 
-  const updateConfig = async (config: Config) => {
-    setGlobalStore("reload", "pending")
-    return globalSDK.client.global.config
-      .update({ config })
-      .then(bootstrap)
-      .then(() => {
-        queue.refresh()
-        setGlobalStore("reload", undefined)
-        queue.refresh()
-      })
-      .catch((error) => {
-        setGlobalStore("reload", undefined)
-        throw error
-      })
-  }
+  const updateConfigMutation = useMutation(() => ({
+    mutationFn: (config: Config) => globalSDK.client.global.config.update({ config }),
+    onSuccess: () => bootstrap.refetch(),
+  }))
 
 
   return {
   return {
     data: globalStore,
     data: globalStore,
@@ -401,8 +420,8 @@ function createGlobalSync() {
     },
     },
     child: children.child,
     child: children.child,
     peek: children.peek,
     peek: children.peek,
-    bootstrap,
-    updateConfig,
+    // bootstrap,
+    updateConfig: updateConfigMutation.mutateAsync,
     project: projectApi,
     project: projectApi,
     todo: {
     todo: {
       set: setSessionTodo,
       set: setSessionTodo,

+ 99 - 107
packages/app/src/context/global-sync/bootstrap.ts

@@ -11,14 +11,15 @@ import type {
   Todo,
   Todo,
 } from "@opencode-ai/sdk/v2/client"
 } from "@opencode-ai/sdk/v2/client"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
-import { getFilename } from "@opencode-ai/shared/util/path"
-import { retry } from "@opencode-ai/shared/util/retry"
+import { getFilename } from "@opencode-ai/core/util/path"
+import { retry } from "@opencode-ai/core/util/retry"
 import { batch } from "solid-js"
 import { batch } from "solid-js"
 import { reconcile, type SetStoreFunction, type Store } from "solid-js/store"
 import { reconcile, type SetStoreFunction, type Store } from "solid-js/store"
 import type { State, VcsCache } from "./types"
 import type { State, VcsCache } from "./types"
 import { cmp, normalizeAgentList, normalizeProviderList } from "./utils"
 import { cmp, normalizeAgentList, normalizeProviderList } from "./utils"
 import { formatServerError } from "@/utils/server-errors"
 import { formatServerError } from "@/utils/server-errors"
 import { QueryClient, queryOptions, skipToken } from "@tanstack/solid-query"
 import { QueryClient, queryOptions, skipToken } from "@tanstack/solid-query"
+import { loadMcpQuery } from "../global-sync"
 
 
 type GlobalStore = {
 type GlobalStore = {
   ready: boolean
   ready: boolean
@@ -66,6 +67,62 @@ function runAll(list: Array<() => Promise<unknown>>) {
   return Promise.allSettled(list.map((item) => item()))
   return Promise.allSettled(list.map((item) => item()))
 }
 }
 
 
+function showErrors(input: {
+  errors: unknown[]
+  title: string
+  translate: (key: string, vars?: Record<string, string | number>) => string
+  formatMoreCount: (count: number) => string
+}) {
+  if (input.errors.length === 0) return
+  const message = formatServerError(input.errors[0], input.translate)
+  const more = input.errors.length > 1 ? input.formatMoreCount(input.errors.length - 1) : ""
+  showToast({
+    variant: "error",
+    title: input.title,
+    description: message + more,
+  })
+}
+
+export const loadGlobalConfigQuery = (
+  sdk?: OpencodeClient,
+  transform?: (x: Awaited<ReturnType<OpencodeClient["global"]["config"]["get"]>>) => void,
+) =>
+  queryOptions({
+    queryKey: ["config"],
+    queryFn: sdk
+      ? () =>
+          retry(() =>
+            sdk.global.config.get().then((x) => {
+              transform?.(x)
+              return x.data!
+            }),
+          )
+      : skipToken,
+  })
+
+export const loadProjectsQuery = (
+  sdk?: OpencodeClient,
+  transform?: (x: Awaited<ReturnType<OpencodeClient["project"]["list"]>>["data"]) => void,
+) =>
+  queryOptions({
+    queryKey: ["project"],
+    queryFn: sdk
+      ? () =>
+          retry(() =>
+            sdk.project
+              .list()
+              .then((x) => {
+                return (x.data ?? [])
+                  .filter((p) => !!p?.id)
+                  .filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
+                  .slice()
+                  .sort((a, b) => cmp(a.id, b.id))
+              })
+              .then(transform),
+          )
+      : skipToken,
+  })
+
 export async function bootstrapGlobal(input: {
 export async function bootstrapGlobal(input: {
   globalSDK: OpencodeClient
   globalSDK: OpencodeClient
   requestFailedTitle: string
   requestFailedTitle: string
@@ -74,53 +131,15 @@ export async function bootstrapGlobal(input: {
   setGlobalStore: SetStoreFunction<GlobalStore>
   setGlobalStore: SetStoreFunction<GlobalStore>
   queryClient: QueryClient
   queryClient: QueryClient
 }) {
 }) {
-  const fast = [
-    () =>
-      retry(() =>
-        input.globalSDK.global.config.get().then((x) => {
-          input.setGlobalStore("config", x.data!)
-        }),
-      ),
-  ]
-
   const slow = [
   const slow = [
+    () => input.queryClient.fetchQuery(loadGlobalConfigQuery(input.globalSDK)),
+    () => input.queryClient.fetchQuery(loadProvidersQuery(null, input.globalSDK)),
+    () => input.queryClient.fetchQuery(loadPathQuery(null, input.globalSDK)),
     () =>
     () =>
-      input.queryClient.fetchQuery({
-        ...loadProvidersQuery(null),
-        queryFn: () =>
-          retry(() =>
-            input.globalSDK.provider.list().then((x) => {
-              input.setGlobalStore("provider", normalizeProviderList(x.data!))
-              return null
-            }),
-          ),
-      }),
-    () =>
-      retry(() =>
-        input.globalSDK.path.get().then((x) => {
-          input.setGlobalStore("path", x.data!)
-        }),
-      ),
-    () =>
-      retry(() =>
-        input.globalSDK.project.list().then((x) => {
-          const projects = (x.data ?? [])
-            .filter((p) => !!p?.id)
-            .filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
-            .slice()
-            .sort((a, b) => cmp(a.id, b.id))
-          input.setGlobalStore("project", projects)
-        }),
+      input.queryClient.fetchQuery(
+        loadProjectsQuery(input.globalSDK, (data) => input.setGlobalStore("project", data ?? [])),
       ),
       ),
   ]
   ]
-  await runAll(fast)
-  // showErrors({
-  //   errors: errors(await runAll(fast)),
-  //   title: input.requestFailedTitle,
-  //   translate: input.translate,
-  //   formatMoreCount: input.formatMoreCount,
-  // })
-  await waitForPaint()
   await runAll(slow)
   await runAll(slow)
   // showErrors({
   // showErrors({
   //   errors: errors(),
   //   errors: errors(),
@@ -128,7 +147,6 @@ export async function bootstrapGlobal(input: {
   //   translate: input.translate,
   //   translate: input.translate,
   //   formatMoreCount: input.formatMoreCount,
   //   formatMoreCount: input.formatMoreCount,
   // })
   // })
-  input.setGlobalStore("ready", true)
 }
 }
 
 
 function groupBySession<T extends { id: string; sessionID: string }>(input: T[]) {
 function groupBySession<T extends { id: string; sessionID: string }>(input: T[]) {
@@ -179,26 +197,28 @@ function warmSessions(input: {
   ).then(() => undefined)
   ).then(() => undefined)
 }
 }
 
 
-export const loadProvidersQuery = (directory: string | null) =>
-  queryOptions<null>({ queryKey: [directory, "providers"], queryFn: skipToken })
+export const loadProvidersQuery = (directory: string | null, sdk?: OpencodeClient) =>
+  queryOptions({
+    queryKey: [directory, "providers"],
+    queryFn: sdk ? () => retry(() => sdk.provider.list().then((x) => normalizeProviderList(x.data!))) : skipToken,
+  })
 
 
 export const loadAgentsQuery = (
 export const loadAgentsQuery = (
   directory: string | null,
   directory: string | null,
   sdk?: OpencodeClient,
   sdk?: OpencodeClient,
   transform?: (x: Awaited<ReturnType<OpencodeClient["app"]["agents"]>>) => void,
   transform?: (x: Awaited<ReturnType<OpencodeClient["app"]["agents"]>>) => void,
 ) =>
 ) =>
-  queryOptions<null>({
+  queryOptions({
     queryKey: [directory, "agents"],
     queryKey: [directory, "agents"],
-    queryFn:
-      sdk && transform
-        ? () =>
-            retry(() =>
-              sdk.app
-                .agents()
-                .then(transform)
-                .then(() => null),
-            )
-        : skipToken,
+    queryFn: sdk
+      ? () =>
+          retry(() =>
+            sdk.app.agents().then((x) => {
+              transform?.(x)
+              return x.data!
+            }),
+          )
+      : skipToken,
   })
   })
 
 
 export const loadPathQuery = (
 export const loadPathQuery = (
@@ -208,16 +228,15 @@ export const loadPathQuery = (
 ) =>
 ) =>
   queryOptions<Path>({
   queryOptions<Path>({
     queryKey: [directory, "path"],
     queryKey: [directory, "path"],
-    queryFn:
-      sdk && transform
-        ? () =>
-            retry(() =>
-              sdk.path.get().then(async (x) => {
-                transform(x)
-                return x.data!
-              }),
-            )
-        : skipToken,
+    queryFn: sdk
+      ? () =>
+          retry(() =>
+            sdk.path.get().then(async (x) => {
+              transform?.(x)
+              return x.data!
+            }),
+          )
+      : skipToken,
   })
   })
 
 
 export async function bootstrapDirectory(input: {
 export async function bootstrapDirectory(input: {
@@ -241,19 +260,9 @@ export async function bootstrapDirectory(input: {
   const seededPath = input.global.path.directory === input.directory ? input.global.path : undefined
   const seededPath = input.global.path.directory === input.directory ? input.global.path : undefined
   if (seededProject) input.setStore("project", seededProject)
   if (seededProject) input.setStore("project", seededProject)
   if (seededPath) input.setStore("path", seededPath)
   if (seededPath) input.setStore("path", seededPath)
-  if (input.store.provider.all.length === 0 && input.global.provider.all.length > 0) {
-    input.setStore("provider", input.global.provider)
-  }
   if (Object.keys(input.store.config).length === 0 && Object.keys(input.global.config).length > 0) {
   if (Object.keys(input.store.config).length === 0 && Object.keys(input.global.config).length > 0) {
-    input.setStore("config", input.global.config)
+    input.setStore("config", reconcile(input.global.config, { merge: false }))
   }
   }
-  if (loading || input.store.provider.all.length === 0) {
-    input.setStore("provider_ready", false)
-  }
-  input.setStore("mcp_ready", false)
-  input.setStore("mcp", {})
-  input.setStore("lsp_ready", false)
-  input.setStore("lsp", [])
   if (loading) input.setStore("status", "partial")
   if (loading) input.setStore("status", "partial")
 
 
   const rev = (providerRev.get(input.directory) ?? 0) + 1
   const rev = (providerRev.get(input.directory) ?? 0) + 1
@@ -265,7 +274,8 @@ export async function bootstrapDirectory(input: {
         input.queryClient.ensureQueryData(
         input.queryClient.ensureQueryData(
           loadAgentsQuery(input.directory, input.sdk, (x) => input.setStore("agent", normalizeAgentList(x.data))),
           loadAgentsQuery(input.directory, input.sdk, (x) => input.setStore("agent", normalizeAgentList(x.data))),
         ),
         ),
-      () => retry(() => input.sdk.config.get().then((x) => input.setStore("config", x.data!))),
+      () =>
+        retry(() => input.sdk.config.get().then((x) => input.setStore("config", reconcile(x.data!, { merge: false })))),
       () => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))),
       () => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))),
       !seededProject &&
       !seededProject &&
         (() => retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id))),
         (() => retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id))),
@@ -339,33 +349,15 @@ export async function bootstrapDirectory(input: {
           }),
           }),
         ),
         ),
       () => Promise.resolve(input.loadSessions(input.directory)),
       () => Promise.resolve(input.loadSessions(input.directory)),
+      () => input.queryClient.fetchQuery(loadMcpQuery(input.directory, input.sdk)),
       () =>
       () =>
-        retry(() =>
-          input.sdk.mcp.status().then((x) => {
-            input.setStore("mcp", x.data!)
-            input.setStore("mcp_ready", true)
-          }),
-        ),
-      () =>
-        input.queryClient.ensureQueryData({
-          ...loadProvidersQuery(input.directory),
-          queryFn: () =>
-            retry(() => input.sdk.provider.list())
-              .then((x) => {
-                if (providerRev.get(input.directory) !== rev) return
-                input.setStore("provider", normalizeProviderList(x.data!))
-                input.setStore("provider_ready", true)
-              })
-              .catch((err) => {
-                if (providerRev.get(input.directory) !== rev) console.error("Failed to refresh provider list", err)
-                const project = getFilename(input.directory)
-                showToast({
-                  variant: "error",
-                  title: input.translate("toast.project.reloadFailed.title", { project }),
-                  description: formatServerError(err, input.translate),
-                })
-              })
-              .then(() => null),
+        input.queryClient.fetchQuery(loadProvidersQuery(input.directory, input.sdk)).catch((err) => {
+          const project = getFilename(input.directory)
+          showToast({
+            variant: "error",
+            title: input.translate("toast.project.reloadFailed.title", { project }),
+            description: formatServerError(err, input.translate),
+          })
         }),
         }),
     ].filter(Boolean) as (() => Promise<any>)[]
     ].filter(Boolean) as (() => Promise<any>)[]
 
 

+ 2 - 0
packages/app/src/context/global-sync/child-store.test.ts

@@ -22,6 +22,8 @@ describe("createChildStoreManager", () => {
       onBootstrap() {},
       onBootstrap() {},
       onDispose() {},
       onDispose() {},
       translate: (key) => key,
       translate: (key) => key,
+      getSdk: () => null!,
+      global: { provider: null! },
     })
     })
 
 
     Array.from({ length: 30 }, (_, index) => `/pinned-${index}`).forEach((directory) => {
     Array.from({ length: 30 }, (_, index) => `/pinned-${index}`).forEach((directory) => {

+ 101 - 50
packages/app/src/context/global-sync/child-store.ts

@@ -1,7 +1,7 @@
 import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js"
 import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js"
 import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
 import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
 import { Persist, persisted } from "@/utils/persist"
 import { Persist, persisted } from "@/utils/persist"
-import type { VcsInfo } from "@opencode-ai/sdk/v2/client"
+import type { OpencodeClient, ProviderListResponse, VcsInfo } from "@opencode-ai/sdk/v2/client"
 import {
 import {
   DIR_IDLE_TTL_MS,
   DIR_IDLE_TTL_MS,
   MAX_DIR_STORES,
   MAX_DIR_STORES,
@@ -14,8 +14,10 @@ import {
   type VcsCache,
   type VcsCache,
 } from "./types"
 } from "./types"
 import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction"
 import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction"
-import { useQuery } from "@tanstack/solid-query"
-import { loadPathQuery } from "./bootstrap"
+import { useQueries } from "@tanstack/solid-query"
+import { loadPathQuery, loadProvidersQuery } from "./bootstrap"
+import { loadLspQuery, loadMcpQuery } from "../global-sync"
+import { directoryKey, type DirectoryKey } from "./utils"
 
 
 export function createChildStoreManager(input: {
 export function createChildStoreManager(input: {
   owner: Owner
   owner: Owner
@@ -24,6 +26,10 @@ export function createChildStoreManager(input: {
   onBootstrap: (directory: string) => void
   onBootstrap: (directory: string) => void
   onDispose: (directory: string) => void
   onDispose: (directory: string) => void
   translate: (key: string, vars?: Record<string, string | number>) => string
   translate: (key: string, vars?: Record<string, string | number>) => string
+  getSdk: (directory: string) => OpencodeClient
+  global: {
+    provider: ProviderListResponse
+  }
 }) {
 }) {
   const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {}
   const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {}
   const vcsCache = new Map<string, VcsCache>()
   const vcsCache = new Map<string, VcsCache>()
@@ -34,30 +40,37 @@ export function createChildStoreManager(input: {
   const ownerPins = new WeakMap<object, Set<string>>()
   const ownerPins = new WeakMap<object, Set<string>>()
   const disposers = new Map<string, () => void>()
   const disposers = new Map<string, () => void>()
 
 
+  const markKey = (key: DirectoryKey) => {
+    if (!key) return
+    lifecycle.set(key, { lastAccessAt: Date.now() })
+    runEviction(key)
+  }
+
   const mark = (directory: string) => {
   const mark = (directory: string) => {
-    if (!directory) return
-    lifecycle.set(directory, { lastAccessAt: Date.now() })
-    runEviction(directory)
+    const key = directoryKey(directory)
+    markKey(key)
   }
   }
 
 
   const pin = (directory: string) => {
   const pin = (directory: string) => {
-    if (!directory) return
-    pins.set(directory, (pins.get(directory) ?? 0) + 1)
-    mark(directory)
+    const key = directoryKey(directory)
+    if (!key) return
+    pins.set(key, (pins.get(key) ?? 0) + 1)
+    markKey(key)
   }
   }
 
 
   const unpin = (directory: string) => {
   const unpin = (directory: string) => {
-    if (!directory) return
-    const next = (pins.get(directory) ?? 0) - 1
+    const key = directoryKey(directory)
+    if (!key) return
+    const next = (pins.get(key) ?? 0) - 1
     if (next > 0) {
     if (next > 0) {
-      pins.set(directory, next)
+      pins.set(key, next)
       return
       return
     }
     }
-    pins.delete(directory)
+    pins.delete(key)
     runEviction()
     runEviction()
   }
   }
 
 
-  const pinned = (directory: string) => (pins.get(directory) ?? 0) > 0
+  const pinned = (directory: string) => (pins.get(directoryKey(directory)) ?? 0) > 0
 
 
   const pinForOwner = (directory: string) => {
   const pinForOwner = (directory: string) => {
     const current = getOwner()
     const current = getOwner()
@@ -79,30 +92,31 @@ export function createChildStoreManager(input: {
     })
     })
   }
   }
 
 
-  function disposeDirectory(directory: string) {
+  function disposeDirectory(directory: DirectoryKey) {
+    const key = directory
     if (
     if (
       !canDisposeDirectory({
       !canDisposeDirectory({
-        directory,
-        hasStore: !!children[directory],
-        pinned: pinned(directory),
-        booting: input.isBooting(directory),
-        loadingSessions: input.isLoadingSessions(directory),
+        directory: key,
+        hasStore: !!children[key],
+        pinned: pinned(key),
+        booting: input.isBooting(key),
+        loadingSessions: input.isLoadingSessions(key),
       })
       })
     ) {
     ) {
       return false
       return false
     }
     }
 
 
-    vcsCache.delete(directory)
-    metaCache.delete(directory)
-    iconCache.delete(directory)
-    lifecycle.delete(directory)
-    const dispose = disposers.get(directory)
+    vcsCache.delete(key)
+    metaCache.delete(key)
+    iconCache.delete(key)
+    lifecycle.delete(key)
+    const dispose = disposers.get(key)
     if (dispose) {
     if (dispose) {
       dispose()
       dispose()
-      disposers.delete(directory)
+      disposers.delete(key)
     }
     }
-    delete children[directory]
-    input.onDispose(directory)
+    delete children[key]
+    input.onDispose(key)
     return true
     return true
   }
   }
 
 
@@ -119,13 +133,14 @@ export function createChildStoreManager(input: {
     }).filter((directory) => directory !== skip)
     }).filter((directory) => directory !== skip)
     if (list.length === 0) return
     if (list.length === 0) return
     for (const directory of list) {
     for (const directory of list) {
-      if (!disposeDirectory(directory)) continue
+      if (!disposeDirectory(directoryKey(directory))) continue
     }
     }
   }
   }
 
 
   function ensureChild(directory: string) {
   function ensureChild(directory: string) {
-    if (!directory) console.error("No directory provided")
-    if (!children[directory]) {
+    const key = directoryKey(directory)
+    if (!key) console.error("No directory provided")
+    if (!children[key]) {
       const vcs = runWithOwner(input.owner, () =>
       const vcs = runWithOwner(input.owner, () =>
         persisted(
         persisted(
           Persist.workspace(directory, "vcs", ["vcs.v1"]),
           Persist.workspace(directory, "vcs", ["vcs.v1"]),
@@ -134,7 +149,7 @@ export function createChildStoreManager(input: {
       )
       )
       if (!vcs) throw new Error(input.translate("error.childStore.persistedCacheCreateFailed"))
       if (!vcs) throw new Error(input.translate("error.childStore.persistedCacheCreateFailed"))
       const vcsStore = vcs[0]
       const vcsStore = vcs[0]
-      vcsCache.set(directory, { store: vcsStore, setStore: vcs[1], ready: vcs[3] })
+      vcsCache.set(key, { store: vcsStore, setStore: vcs[1], ready: vcs[3] })
 
 
       const meta = runWithOwner(input.owner, () =>
       const meta = runWithOwner(input.owner, () =>
         persisted(
         persisted(
@@ -143,7 +158,7 @@ export function createChildStoreManager(input: {
         ),
         ),
       )
       )
       if (!meta) throw new Error(input.translate("error.childStore.persistedProjectMetadataCreateFailed"))
       if (!meta) throw new Error(input.translate("error.childStore.persistedProjectMetadataCreateFailed"))
-      metaCache.set(directory, { store: meta[0], setStore: meta[1], ready: meta[3] })
+      metaCache.set(key, { store: meta[0], setStore: meta[1], ready: meta[3] })
 
 
       const icon = runWithOwner(input.owner, () =>
       const icon = runWithOwner(input.owner, () =>
         persisted(
         persisted(
@@ -152,19 +167,38 @@ export function createChildStoreManager(input: {
         ),
         ),
       )
       )
       if (!icon) throw new Error(input.translate("error.childStore.persistedProjectIconCreateFailed"))
       if (!icon) throw new Error(input.translate("error.childStore.persistedProjectIconCreateFailed"))
-      iconCache.set(directory, { store: icon[0], setStore: icon[1], ready: icon[3] })
+      iconCache.set(key, { store: icon[0], setStore: icon[1], ready: icon[3] })
 
 
       const init = () =>
       const init = () =>
         createRoot((dispose) => {
         createRoot((dispose) => {
+          const sdk = input.getSdk(directory)
+
+          const initialMeta = meta[0].value
           const initialIcon = icon[0].value
           const initialIcon = icon[0].value
 
 
-          const pathQuery = useQuery(() => loadPathQuery(directory))
+          const [pathQuery, mcpQuery, lspQuery, providerQuery] = useQueries(() => ({
+            queries: [
+              loadPathQuery(key, sdk),
+              loadMcpQuery(key, sdk),
+              loadLspQuery(key, sdk),
+              loadProvidersQuery(key, sdk),
+            ],
+          }))
+
           const child = createStore<State>({
           const child = createStore<State>({
             project: "",
             project: "",
-            projectMeta: undefined,
+            projectMeta: initialMeta,
             icon: initialIcon,
             icon: initialIcon,
-            provider_ready: false,
-            provider: { all: [], connected: [], default: {} },
+            get provider_ready() {
+              return !providerQuery.isLoading
+            },
+            get provider() {
+              const EMPTY = { all: [], connected: [], default: {} }
+              if (providerQuery.isLoading) return EMPTY
+              if (providerQuery.data?.all.length === 0 && input.global.provider.all.length > 0)
+                return input.global.provider
+              return providerQuery.data ?? EMPTY
+            },
             config: {},
             config: {},
             get path() {
             get path() {
               if (pathQuery.isLoading || !pathQuery.data)
               if (pathQuery.isLoading || !pathQuery.data)
@@ -181,22 +215,30 @@ export function createChildStoreManager(input: {
             todo: {},
             todo: {},
             permission: {},
             permission: {},
             question: {},
             question: {},
-            mcp_ready: false,
-            mcp: {},
-            lsp_ready: false,
-            lsp: [],
+            get mcp_ready() {
+              return !mcpQuery.isLoading
+            },
+            get mcp() {
+              return mcpQuery.isLoading ? {} : (mcpQuery.data ?? {})
+            },
+            get lsp_ready() {
+              return !lspQuery.isLoading
+            },
+            get lsp() {
+              return lspQuery.isLoading ? [] : (lspQuery.data ?? [])
+            },
             vcs: vcsStore.value,
             vcs: vcsStore.value,
             limit: 5,
             limit: 5,
             message: {},
             message: {},
             part: {},
             part: {},
           })
           })
-          children[directory] = child
-          disposers.set(directory, dispose)
+          children[key] = child
+          disposers.set(key, dispose)
 
 
           const onPersistedInit = (init: Promise<string> | string | null, run: () => void) => {
           const onPersistedInit = (init: Promise<string> | string | null, run: () => void) => {
             if (!(init instanceof Promise)) return
             if (!(init instanceof Promise)) return
             void init.then(() => {
             void init.then(() => {
-              if (children[directory] !== child) return
+              if (children[key] !== child) return
               run()
               run()
             })
             })
           }
           }
@@ -207,6 +249,11 @@ export function createChildStoreManager(input: {
             child[1]("vcs", (value) => value ?? cached)
             child[1]("vcs", (value) => value ?? cached)
           })
           })
 
 
+          onPersistedInit(meta[2], () => {
+            if (child[0].projectMeta !== initialMeta) return
+            child[1]("projectMeta", meta[0].value)
+          })
+
           onPersistedInit(icon[2], () => {
           onPersistedInit(icon[2], () => {
             if (child[0].icon !== initialIcon) return
             if (child[0].icon !== initialIcon) return
             child[1]("icon", icon[0].value)
             child[1]("icon", icon[0].value)
@@ -215,15 +262,16 @@ export function createChildStoreManager(input: {
 
 
       runWithOwner(input.owner, init)
       runWithOwner(input.owner, init)
     }
     }
-    mark(directory)
-    const childStore = children[directory]
+    markKey(key)
+    const childStore = children[key]
     if (!childStore) throw new Error(input.translate("error.childStore.storeCreateFailed"))
     if (!childStore) throw new Error(input.translate("error.childStore.storeCreateFailed"))
     return childStore
     return childStore
   }
   }
 
 
   function child(directory: string, options: ChildOptions = {}) {
   function child(directory: string, options: ChildOptions = {}) {
+    const key = directoryKey(directory)
     const childStore = ensureChild(directory)
     const childStore = ensureChild(directory)
-    pinForOwner(directory)
+    pinForOwner(key)
     const shouldBootstrap = options.bootstrap ?? true
     const shouldBootstrap = options.bootstrap ?? true
     if (shouldBootstrap && childStore[0].status === "loading") {
     if (shouldBootstrap && childStore[0].status === "loading") {
       input.onBootstrap(directory)
       input.onBootstrap(directory)
@@ -232,6 +280,7 @@ export function createChildStoreManager(input: {
   }
   }
 
 
   function peek(directory: string, options: ChildOptions = {}) {
   function peek(directory: string, options: ChildOptions = {}) {
+    const key = directoryKey(directory)
     const childStore = ensureChild(directory)
     const childStore = ensureChild(directory)
     const shouldBootstrap = options.bootstrap ?? true
     const shouldBootstrap = options.bootstrap ?? true
     if (shouldBootstrap && childStore[0].status === "loading") {
     if (shouldBootstrap && childStore[0].status === "loading") {
@@ -241,8 +290,9 @@ export function createChildStoreManager(input: {
   }
   }
 
 
   function projectMeta(directory: string, patch: ProjectMeta) {
   function projectMeta(directory: string, patch: ProjectMeta) {
+    const key = directoryKey(directory)
     const [store, setStore] = ensureChild(directory)
     const [store, setStore] = ensureChild(directory)
-    const cached = metaCache.get(directory)
+    const cached = metaCache.get(key)
     if (!cached) return
     if (!cached) return
     const previous = store.projectMeta ?? {}
     const previous = store.projectMeta ?? {}
     const icon = patch.icon ? { ...previous.icon, ...patch.icon } : previous.icon
     const icon = patch.icon ? { ...previous.icon, ...patch.icon } : previous.icon
@@ -258,8 +308,9 @@ export function createChildStoreManager(input: {
   }
   }
 
 
   function projectIcon(directory: string, value: string | undefined) {
   function projectIcon(directory: string, value: string | undefined) {
+    const key = directoryKey(directory)
     const [store, setStore] = ensureChild(directory)
     const [store, setStore] = ensureChild(directory)
-    const cached = iconCache.get(directory)
+    const cached = iconCache.get(key)
     if (!cached) return
     if (!cached) return
     if (store.icon === value) return
     if (store.icon === value) return
     cached.setStore("value", value)
     cached.setStore("value", value)

+ 1 - 1
packages/app/src/context/global-sync/event-reducer.ts

@@ -1,4 +1,4 @@
-import { Binary } from "@opencode-ai/shared/util/binary"
+import { Binary } from "@opencode-ai/core/util/binary"
 import { produce, reconcile, type SetStoreFunction, type Store } from "solid-js/store"
 import { produce, reconcile, type SetStoreFunction, type Store } from "solid-js/store"
 import type {
 import type {
   Message,
   Message,

+ 46 - 0
packages/app/src/context/global-sync/queue.test.ts

@@ -0,0 +1,46 @@
+import { describe, expect, test } from "bun:test"
+import { createRefreshQueue } from "./queue"
+import { directoryKey } from "./utils"
+
+const tick = () => new Promise((resolve) => setTimeout(resolve, 10))
+
+describe("createRefreshQueue", () => {
+  test("clears queued directories by normalized key", async () => {
+    const calls: string[] = []
+    const queue = createRefreshQueue({
+      paused: () => false,
+      key: directoryKey,
+      bootstrap: async () => {},
+      bootstrapInstance: (directory) => {
+        calls.push(directory)
+      },
+    })
+
+    queue.push("C:\\tmp\\demo")
+    queue.clear("C:/tmp/demo")
+
+    await tick()
+
+    expect(calls).toEqual([])
+    queue.dispose()
+  })
+
+  test("passes the original directory to bootstrapInstance", async () => {
+    const calls: string[] = []
+    const queue = createRefreshQueue({
+      paused: () => false,
+      key: directoryKey,
+      bootstrap: async () => {},
+      bootstrapInstance: (directory) => {
+        calls.push(directory)
+      },
+    })
+
+    queue.push("C:\\tmp\\demo")
+
+    await tick()
+
+    expect(calls).toEqual(["C:\\tmp\\demo"])
+    queue.dispose()
+  })
+})

+ 9 - 6
packages/app/src/context/global-sync/queue.ts

@@ -2,22 +2,25 @@ type QueueInput = {
   paused: () => boolean
   paused: () => boolean
   bootstrap: () => Promise<void>
   bootstrap: () => Promise<void>
   bootstrapInstance: (directory: string) => Promise<void> | void
   bootstrapInstance: (directory: string) => Promise<void> | void
+  key?: (directory: string) => string
 }
 }
 
 
 export function createRefreshQueue(input: QueueInput) {
 export function createRefreshQueue(input: QueueInput) {
-  const queued = new Set<string>()
+  const queued = new Map<string, string>()
   let root = false
   let root = false
   let running = false
   let running = false
   let timer: ReturnType<typeof setTimeout> | undefined
   let timer: ReturnType<typeof setTimeout> | undefined
 
 
+  const key = input.key ?? ((directory: string) => directory)
+
   const tick = () => new Promise<void>((resolve) => setTimeout(resolve, 0))
   const tick = () => new Promise<void>((resolve) => setTimeout(resolve, 0))
 
 
   const take = (count: number) => {
   const take = (count: number) => {
     if (queued.size === 0) return [] as string[]
     if (queued.size === 0) return [] as string[]
     const items: string[] = []
     const items: string[] = []
-    for (const item of queued) {
-      queued.delete(item)
-      items.push(item)
+    for (const [id, directory] of queued) {
+      queued.delete(id)
+      items.push(directory)
       if (items.length >= count) break
       if (items.length >= count) break
     }
     }
     return items
     return items
@@ -33,7 +36,7 @@ export function createRefreshQueue(input: QueueInput) {
 
 
   const push = (directory: string) => {
   const push = (directory: string) => {
     if (!directory) return
     if (!directory) return
-    queued.add(directory)
+    queued.set(key(directory), directory)
     if (input.paused()) return
     if (input.paused()) return
     schedule()
     schedule()
   }
   }
@@ -73,7 +76,7 @@ export function createRefreshQueue(input: QueueInput) {
     push,
     push,
     refresh,
     refresh,
     clear(directory: string) {
     clear(directory: string) {
-      queued.delete(directory)
+      queued.delete(key(directory))
     },
     },
     dispose() {
     dispose() {
       if (!timer) return
       if (!timer) return

+ 18 - 1
packages/app/src/context/global-sync/utils.test.ts

@@ -1,6 +1,6 @@
 import { describe, expect, test } from "bun:test"
 import { describe, expect, test } from "bun:test"
 import type { Agent } from "@opencode-ai/sdk/v2/client"
 import type { Agent } from "@opencode-ai/sdk/v2/client"
-import { normalizeAgentList } from "./utils"
+import { directoryKey, normalizeAgentList } from "./utils"
 
 
 const agent = (name = "build") =>
 const agent = (name = "build") =>
   ({
   ({
@@ -33,3 +33,20 @@ describe("normalizeAgentList", () => {
     expect(normalizeAgentList([{ name: "build" }, agent("docs")])).toEqual([agent("docs")])
     expect(normalizeAgentList([{ name: "build" }, agent("docs")])).toEqual([agent("docs")])
   })
   })
 })
 })
+
+describe("directoryKey", () => {
+  test("normalizes slashes", () => {
+    expect(String(directoryKey("C:\\Repos\\sst\\opencode"))).toBe("C:/Repos/sst/opencode")
+    expect(String(directoryKey("C:/Repos/sst/opencode"))).toBe("C:/Repos/sst/opencode")
+  })
+
+  test("preserves backslashes in posix paths", () => {
+    expect(String(directoryKey("/tmp/foo\\bar"))).toBe("/tmp/foo\\bar")
+  })
+
+  test("trims trailing slashes without breaking roots", () => {
+    expect(String(directoryKey("C:/Repos/sst/opencode/"))).toBe("C:/Repos/sst/opencode")
+    expect(String(directoryKey("C:/"))).toBe("C:/")
+    expect(String(directoryKey("/"))).toBe("/")
+  })
+})

+ 1 - 0
packages/app/src/context/global-sync/utils.ts

@@ -1,4 +1,5 @@
 import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client"
 import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client"
+export { pathKey as directoryKey, type PathKey as DirectoryKey } from "@/utils/path-key"
 
 
 export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
 export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
 
 

+ 8 - 1
packages/app/src/context/layout.tsx

@@ -391,7 +391,14 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
         ? globalSync.data.project.find((x) => x.id === projectID)
         ? globalSync.data.project.find((x) => x.id === projectID)
         : globalSync.data.project.find((x) => x.worktree === project.worktree)
         : globalSync.data.project.find((x) => x.worktree === project.worktree)
 
 
-      return { ...metadata, ...project }
+      // Preserve local icon override from per-workspace localStorage cache (childStore.icon).
+      // Without this, different subdirectories of the same git repo would share the same
+      // icon from the database instead of using their individual overrides.
+      const base = { ...metadata, ...project }
+      if (childStore.icon) {
+        return { ...base, icon: { ...base.icon, override: childStore.icon } }
+      }
+      return base
     }
     }
 
 
     const roots = createMemo(() => {
     const roots = createMemo(() => {

+ 2 - 2
packages/app/src/context/local.tsx

@@ -1,5 +1,5 @@
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import { createSimpleContext } from "@opencode-ai/ui/context"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
+import { base64Encode } from "@opencode-ai/core/util/encode"
 import { useParams } from "@solidjs/router"
 import { useParams } from "@solidjs/router"
 import { batch, createEffect, createMemo } from "solid-js"
 import { batch, createEffect, createMemo } from "solid-js"
 import { createStore } from "solid-js/store"
 import { createStore } from "solid-js/store"
@@ -382,7 +382,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
           setSaved("session", session, {
           setSaved("session", session, {
             agent: msg.agent,
             agent: msg.agent,
             model: msg.model,
             model: msg.model,
-            variant: msg.model.variant ?? null,
+            variant: msg.model?.variant ?? null,
           })
           })
         },
         },
       },
       },

+ 2 - 2
packages/app/src/context/notification.tsx

@@ -7,8 +7,8 @@ import { useGlobalSync } from "./global-sync"
 import { usePlatform } from "@/context/platform"
 import { usePlatform } from "@/context/platform"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
 import { useSettings } from "@/context/settings"
 import { useSettings } from "@/context/settings"
-import { Binary } from "@opencode-ai/shared/util/binary"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
+import { Binary } from "@opencode-ai/core/util/binary"
+import { base64Encode } from "@opencode-ai/core/util/encode"
 import { decode64 } from "@/utils/base64"
 import { decode64 } from "@/utils/base64"
 import { EventSessionError } from "@opencode-ai/sdk/v2"
 import { EventSessionError } from "@opencode-ai/sdk/v2"
 import { Persist, persisted } from "@/utils/persist"
 import { Persist, persisted } from "@/utils/persist"

+ 1 - 1
packages/app/src/context/permission-auto-respond.test.ts

@@ -1,6 +1,6 @@
 import { describe, expect, test } from "bun:test"
 import { describe, expect, test } from "bun:test"
 import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client"
 import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
+import { base64Encode } from "@opencode-ai/core/util/encode"
 import { autoRespondsPermission, isDirectoryAutoAccepting } from "./permission-auto-respond"
 import { autoRespondsPermission, isDirectoryAutoAccepting } from "./permission-auto-respond"
 
 
 const session = (input: { id: string; parentID?: string }) =>
 const session = (input: { id: string; parentID?: string }) =>

+ 1 - 1
packages/app/src/context/permission-auto-respond.ts

@@ -1,4 +1,4 @@
-import { base64Encode } from "@opencode-ai/shared/util/encode"
+import { base64Encode } from "@opencode-ai/core/util/encode"
 
 
 export function acceptKey(sessionID: string, directory?: string) {
 export function acceptKey(sessionID: string, directory?: string) {
   if (!directory) return sessionID
   if (!directory) return sessionID

+ 1 - 1
packages/app/src/context/prompt.tsx

@@ -1,5 +1,5 @@
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import { createSimpleContext } from "@opencode-ai/ui/context"
-import { checksum } from "@opencode-ai/shared/util/encode"
+import { checksum } from "@opencode-ai/core/util/encode"
 import { useParams } from "@solidjs/router"
 import { useParams } from "@solidjs/router"
 import { batch, createMemo, createRoot, getOwner, onCleanup } from "solid-js"
 import { batch, createMemo, createRoot, getOwner, onCleanup } from "solid-js"
 import { createStore, type SetStoreFunction } from "solid-js/store"
 import { createStore, type SetStoreFunction } from "solid-js/store"

+ 2 - 2
packages/app/src/context/sync.tsx

@@ -1,7 +1,7 @@
 import { batch, createMemo } from "solid-js"
 import { batch, createMemo } from "solid-js"
 import { createStore, produce, reconcile } from "solid-js/store"
 import { createStore, produce, reconcile } from "solid-js/store"
-import { Binary } from "@opencode-ai/shared/util/binary"
-import { retry } from "@opencode-ai/shared/util/retry"
+import { Binary } from "@opencode-ai/core/util/binary"
+import { retry } from "@opencode-ai/core/util/retry"
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import { createSimpleContext } from "@opencode-ai/ui/context"
 import {
 import {
   clearSessionPrefetch,
   clearSessionPrefetch,

+ 20 - 0
packages/app/src/entry.tsx

@@ -1,5 +1,6 @@
 // @refresh reload
 // @refresh reload
 
 
+import * as Sentry from "@sentry/solid"
 import { render } from "solid-js/web"
 import { render } from "solid-js/web"
 import { AppBaseProviders, AppInterface } from "@/app"
 import { AppBaseProviders, AppInterface } from "@/app"
 import { type Platform, PlatformProvider } from "@/context/platform"
 import { type Platform, PlatformProvider } from "@/context/platform"
@@ -125,6 +126,25 @@ const platform: Platform = {
   setDefaultServer: writeDefaultServerUrl,
   setDefaultServer: writeDefaultServerUrl,
 }
 }
 
 
+if (import.meta.env.VITE_SENTRY_DSN) {
+  Sentry.init({
+    dsn: import.meta.env.VITE_SENTRY_DSN,
+    environment: import.meta.env.VITE_SENTRY_ENVIRONMENT ?? import.meta.env.MODE,
+    release: import.meta.env.VITE_SENTRY_RELEASE ?? `web@${pkg.version}`,
+    initialScope: {
+      tags: {
+        platform: "web",
+      },
+    },
+    integrations: (integrations) => {
+      return integrations.filter(
+        (i) =>
+          i.name !== "Breadcrumbs" && !(import.meta.env.OPENCODE_CHANNEL === "prod" && i.name === "GlobalHandlers"),
+      )
+    },
+  })
+}
+
 if (root instanceof HTMLElement) {
 if (root instanceof HTMLElement) {
   const server: ServerConnection.Http = { type: "http", http: { url: getCurrentUrl() } }
   const server: ServerConnection.Http = { type: "http", http: { url: getCurrentUrl() } }
   render(
   render(

+ 4 - 0
packages/app/src/env.d.ts

@@ -2,6 +2,10 @@ interface ImportMetaEnv {
   readonly VITE_OPENCODE_SERVER_HOST: string
   readonly VITE_OPENCODE_SERVER_HOST: string
   readonly VITE_OPENCODE_SERVER_PORT: string
   readonly VITE_OPENCODE_SERVER_PORT: string
   readonly VITE_OPENCODE_CHANNEL?: "dev" | "beta" | "prod"
   readonly VITE_OPENCODE_CHANNEL?: "dev" | "beta" | "prod"
+
+  readonly VITE_SENTRY_DSN?: string
+  readonly VITE_SENTRY_ENVIRONMENT?: string
+  readonly VITE_SENTRY_RELEASE?: string
 }
 }
 
 
 interface ImportMeta {
 interface ImportMeta {

+ 2 - 2
packages/app/src/i18n/ar.ts

@@ -402,6 +402,8 @@ export const dict = {
   "error.page.description": "حدث خطأ أثناء تحميل التطبيق.",
   "error.page.description": "حدث خطأ أثناء تحميل التطبيق.",
   "error.page.details.label": "تفاصيل الخطأ",
   "error.page.details.label": "تفاصيل الخطأ",
   "error.page.action.restart": "إعادة تشغيل",
   "error.page.action.restart": "إعادة تشغيل",
+  "error.page.action.report": "الإبلاغ عن الخطأ",
+  "error.page.action.reported": "تم الإبلاغ عن الخطأ",
   "error.page.action.checking": "جارٍ التحقق...",
   "error.page.action.checking": "جارٍ التحقق...",
   "error.page.action.checkUpdates": "التحقق من وجود تحديثات",
   "error.page.action.checkUpdates": "التحقق من وجود تحديثات",
   "error.page.action.updateTo": "تحديث إلى {{version}}",
   "error.page.action.updateTo": "تحديث إلى {{version}}",
@@ -721,8 +723,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "جلب محتوى من عنوان URL",
   "settings.permissions.tool.webfetch.description": "جلب محتوى من عنوان URL",
   "settings.permissions.tool.websearch.title": "بحث الويب",
   "settings.permissions.tool.websearch.title": "بحث الويب",
   "settings.permissions.tool.websearch.description": "البحث في الويب",
   "settings.permissions.tool.websearch.description": "البحث في الويب",
-  "settings.permissions.tool.codesearch.title": "بحث الكود",
-  "settings.permissions.tool.codesearch.description": "البحث عن كود على الويب",
   "settings.permissions.tool.external_directory.title": "دليل خارجي",
   "settings.permissions.tool.external_directory.title": "دليل خارجي",
   "settings.permissions.tool.external_directory.description": "الوصول إلى الملفات خارج دليل المشروع",
   "settings.permissions.tool.external_directory.description": "الوصول إلى الملفات خارج دليل المشروع",
   "settings.permissions.tool.doom_loop.title": "حلقة الموت",
   "settings.permissions.tool.doom_loop.title": "حلقة الموت",

+ 2 - 2
packages/app/src/i18n/br.ts

@@ -403,6 +403,8 @@ export const dict = {
   "error.page.description": "Ocorreu um erro ao carregar a aplicação.",
   "error.page.description": "Ocorreu um erro ao carregar a aplicação.",
   "error.page.details.label": "Detalhes do Erro",
   "error.page.details.label": "Detalhes do Erro",
   "error.page.action.restart": "Reiniciar",
   "error.page.action.restart": "Reiniciar",
+  "error.page.action.report": "Reportar erro",
+  "error.page.action.reported": "Erro reportado",
   "error.page.action.checking": "Verificando...",
   "error.page.action.checking": "Verificando...",
   "error.page.action.checkUpdates": "Verificar atualizações",
   "error.page.action.checkUpdates": "Verificar atualizações",
   "error.page.action.updateTo": "Atualizar para {{version}}",
   "error.page.action.updateTo": "Atualizar para {{version}}",
@@ -732,8 +734,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Buscar conteúdo de uma URL",
   "settings.permissions.tool.webfetch.description": "Buscar conteúdo de uma URL",
   "settings.permissions.tool.websearch.title": "Pesquisa Web",
   "settings.permissions.tool.websearch.title": "Pesquisa Web",
   "settings.permissions.tool.websearch.description": "Pesquisar na web",
   "settings.permissions.tool.websearch.description": "Pesquisar na web",
-  "settings.permissions.tool.codesearch.title": "Pesquisa de Código",
-  "settings.permissions.tool.codesearch.description": "Pesquisar código na web",
   "settings.permissions.tool.external_directory.title": "Diretório Externo",
   "settings.permissions.tool.external_directory.title": "Diretório Externo",
   "settings.permissions.tool.external_directory.description": "Acessar arquivos fora do diretório do projeto",
   "settings.permissions.tool.external_directory.description": "Acessar arquivos fora do diretório do projeto",
   "settings.permissions.tool.doom_loop.title": "Loop Infinito",
   "settings.permissions.tool.doom_loop.title": "Loop Infinito",

+ 2 - 2
packages/app/src/i18n/bs.ts

@@ -449,6 +449,8 @@ export const dict = {
   "error.page.description": "Došlo je do greške prilikom učitavanja aplikacije.",
   "error.page.description": "Došlo je do greške prilikom učitavanja aplikacije.",
   "error.page.details.label": "Detalji greške",
   "error.page.details.label": "Detalji greške",
   "error.page.action.restart": "Restartuj",
   "error.page.action.restart": "Restartuj",
+  "error.page.action.report": "Prijavi grešku",
+  "error.page.action.reported": "Greška prijavljena",
   "error.page.action.checking": "Provjera...",
   "error.page.action.checking": "Provjera...",
   "error.page.action.checkUpdates": "Provjeri ažuriranja",
   "error.page.action.checkUpdates": "Provjeri ažuriranja",
   "error.page.action.updateTo": "Ažuriraj na {{version}}",
   "error.page.action.updateTo": "Ažuriraj na {{version}}",
@@ -806,8 +808,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Preuzmi sadržaj sa URL-a",
   "settings.permissions.tool.webfetch.description": "Preuzmi sadržaj sa URL-a",
   "settings.permissions.tool.websearch.title": "Web pretraga",
   "settings.permissions.tool.websearch.title": "Web pretraga",
   "settings.permissions.tool.websearch.description": "Pretražuj web",
   "settings.permissions.tool.websearch.description": "Pretražuj web",
-  "settings.permissions.tool.codesearch.title": "Pretraga koda",
-  "settings.permissions.tool.codesearch.description": "Pretraži kod na webu",
   "settings.permissions.tool.external_directory.title": "Vanjski direktorij",
   "settings.permissions.tool.external_directory.title": "Vanjski direktorij",
   "settings.permissions.tool.external_directory.description": "Pristup datotekama izvan direktorija projekta",
   "settings.permissions.tool.external_directory.description": "Pristup datotekama izvan direktorija projekta",
   "settings.permissions.tool.doom_loop.title": "Beskonačna petlja",
   "settings.permissions.tool.doom_loop.title": "Beskonačna petlja",

+ 2 - 2
packages/app/src/i18n/da.ts

@@ -446,6 +446,8 @@ export const dict = {
   "error.page.description": "Der opstod en fejl under indlæsning af applikationen.",
   "error.page.description": "Der opstod en fejl under indlæsning af applikationen.",
   "error.page.details.label": "Fejldetaljer",
   "error.page.details.label": "Fejldetaljer",
   "error.page.action.restart": "Genstart",
   "error.page.action.restart": "Genstart",
+  "error.page.action.report": "Rapportér fejl",
+  "error.page.action.reported": "Fejl rapporteret",
   "error.page.action.checking": "Tjekker...",
   "error.page.action.checking": "Tjekker...",
   "error.page.action.checkUpdates": "Tjek for opdateringer",
   "error.page.action.checkUpdates": "Tjek for opdateringer",
   "error.page.action.updateTo": "Opdater til {{version}}",
   "error.page.action.updateTo": "Opdater til {{version}}",
@@ -800,8 +802,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Hent indhold fra en URL",
   "settings.permissions.tool.webfetch.description": "Hent indhold fra en URL",
   "settings.permissions.tool.websearch.title": "Websøgning",
   "settings.permissions.tool.websearch.title": "Websøgning",
   "settings.permissions.tool.websearch.description": "Søg på nettet",
   "settings.permissions.tool.websearch.description": "Søg på nettet",
-  "settings.permissions.tool.codesearch.title": "Kodesøgning",
-  "settings.permissions.tool.codesearch.description": "Søg kode på nettet",
   "settings.permissions.tool.external_directory.title": "Ekstern mappe",
   "settings.permissions.tool.external_directory.title": "Ekstern mappe",
   "settings.permissions.tool.external_directory.description": "Få adgang til filer uden for projektmappen",
   "settings.permissions.tool.external_directory.description": "Få adgang til filer uden for projektmappen",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",

+ 2 - 2
packages/app/src/i18n/de.ts

@@ -410,6 +410,8 @@ export const dict = {
   "error.page.description": "Beim Laden der Anwendung ist ein Fehler aufgetreten.",
   "error.page.description": "Beim Laden der Anwendung ist ein Fehler aufgetreten.",
   "error.page.details.label": "Fehlerdetails",
   "error.page.details.label": "Fehlerdetails",
   "error.page.action.restart": "Neustart",
   "error.page.action.restart": "Neustart",
+  "error.page.action.report": "Fehler melden",
+  "error.page.action.reported": "Fehler gemeldet",
   "error.page.action.checking": "Prüfen...",
   "error.page.action.checking": "Prüfen...",
   "error.page.action.checkUpdates": "Nach Updates suchen",
   "error.page.action.checkUpdates": "Nach Updates suchen",
   "error.page.action.updateTo": "Auf {{version}} aktualisieren",
   "error.page.action.updateTo": "Auf {{version}} aktualisieren",
@@ -743,8 +745,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Inhalt von einer URL abrufen",
   "settings.permissions.tool.webfetch.description": "Inhalt von einer URL abrufen",
   "settings.permissions.tool.websearch.title": "Web-Suche",
   "settings.permissions.tool.websearch.title": "Web-Suche",
   "settings.permissions.tool.websearch.description": "Das Web durchsuchen",
   "settings.permissions.tool.websearch.description": "Das Web durchsuchen",
-  "settings.permissions.tool.codesearch.title": "Code-Suche",
-  "settings.permissions.tool.codesearch.description": "Code im Web durchsuchen",
   "settings.permissions.tool.external_directory.title": "Externes Verzeichnis",
   "settings.permissions.tool.external_directory.title": "Externes Verzeichnis",
   "settings.permissions.tool.external_directory.description": "Zugriff auf Dateien außerhalb des Projektverzeichnisses",
   "settings.permissions.tool.external_directory.description": "Zugriff auf Dateien außerhalb des Projektverzeichnisses",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",

+ 7 - 2
packages/app/src/i18n/en.ts

@@ -465,6 +465,8 @@ export const dict = {
   "error.page.description": "An error occurred while loading the application.",
   "error.page.description": "An error occurred while loading the application.",
   "error.page.details.label": "Error Details",
   "error.page.details.label": "Error Details",
   "error.page.action.restart": "Restart",
   "error.page.action.restart": "Restart",
+  "error.page.action.report": "Report Error",
+  "error.page.action.reported": "Error Reported",
   "error.page.action.checking": "Checking...",
   "error.page.action.checking": "Checking...",
   "error.page.action.checkUpdates": "Check for updates",
   "error.page.action.checkUpdates": "Check for updates",
   "error.page.action.updateTo": "Update to {{version}}",
   "error.page.action.updateTo": "Update to {{version}}",
@@ -728,6 +730,11 @@ export const dict = {
 
 
   "settings.general.row.language.title": "Language",
   "settings.general.row.language.title": "Language",
   "settings.general.row.language.description": "Change the display language for OpenCode",
   "settings.general.row.language.description": "Change the display language for OpenCode",
+  "settings.general.row.shell.title": "Terminal Shell",
+  "settings.general.row.shell.description":
+    "Choose the shell used for your terminal. Compatible shells are also used for agent tool calls.",
+  "settings.general.row.shell.autoDefault": "Auto (Default)",
+  "settings.general.row.shell.terminalOnly": "terminal only",
   "settings.general.row.appearance.title": "Appearance",
   "settings.general.row.appearance.title": "Appearance",
   "settings.general.row.appearance.description": "Customise how OpenCode looks on your device",
   "settings.general.row.appearance.description": "Customise how OpenCode looks on your device",
   "settings.general.row.colorScheme.title": "Color scheme",
   "settings.general.row.colorScheme.title": "Color scheme",
@@ -915,8 +922,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Fetch content from a URL",
   "settings.permissions.tool.webfetch.description": "Fetch content from a URL",
   "settings.permissions.tool.websearch.title": "Web Search",
   "settings.permissions.tool.websearch.title": "Web Search",
   "settings.permissions.tool.websearch.description": "Search the web",
   "settings.permissions.tool.websearch.description": "Search the web",
-  "settings.permissions.tool.codesearch.title": "Code Search",
-  "settings.permissions.tool.codesearch.description": "Search code on the web",
   "settings.permissions.tool.external_directory.title": "External Directory",
   "settings.permissions.tool.external_directory.title": "External Directory",
   "settings.permissions.tool.external_directory.description": "Access files outside the project directory",
   "settings.permissions.tool.external_directory.description": "Access files outside the project directory",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",

+ 2 - 2
packages/app/src/i18n/es.ts

@@ -449,6 +449,8 @@ export const dict = {
   "error.page.description": "Ocurrió un error al cargar la aplicación.",
   "error.page.description": "Ocurrió un error al cargar la aplicación.",
   "error.page.details.label": "Detalles del error",
   "error.page.details.label": "Detalles del error",
   "error.page.action.restart": "Reiniciar",
   "error.page.action.restart": "Reiniciar",
+  "error.page.action.report": "Informar error",
+  "error.page.action.reported": "Error informado",
   "error.page.action.checking": "Comprobando...",
   "error.page.action.checking": "Comprobando...",
   "error.page.action.checkUpdates": "Buscar actualizaciones",
   "error.page.action.checkUpdates": "Buscar actualizaciones",
   "error.page.action.updateTo": "Actualizar a {{version}}",
   "error.page.action.updateTo": "Actualizar a {{version}}",
@@ -813,8 +815,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Obtener contenido de una URL",
   "settings.permissions.tool.webfetch.description": "Obtener contenido de una URL",
   "settings.permissions.tool.websearch.title": "Búsqueda Web",
   "settings.permissions.tool.websearch.title": "Búsqueda Web",
   "settings.permissions.tool.websearch.description": "Buscar en la web",
   "settings.permissions.tool.websearch.description": "Buscar en la web",
-  "settings.permissions.tool.codesearch.title": "Búsqueda de Código",
-  "settings.permissions.tool.codesearch.description": "Buscar código en la web",
   "settings.permissions.tool.external_directory.title": "Directorio Externo",
   "settings.permissions.tool.external_directory.title": "Directorio Externo",
   "settings.permissions.tool.external_directory.description": "Acceder a archivos fuera del directorio del proyecto",
   "settings.permissions.tool.external_directory.description": "Acceder a archivos fuera del directorio del proyecto",
   "settings.permissions.tool.doom_loop.title": "Bucle Infinito",
   "settings.permissions.tool.doom_loop.title": "Bucle Infinito",

+ 2 - 2
packages/app/src/i18n/fr.ts

@@ -406,6 +406,8 @@ export const dict = {
   "error.page.description": "Une erreur s'est produite lors du chargement de l'application.",
   "error.page.description": "Une erreur s'est produite lors du chargement de l'application.",
   "error.page.details.label": "Détails de l'erreur",
   "error.page.details.label": "Détails de l'erreur",
   "error.page.action.restart": "Redémarrer",
   "error.page.action.restart": "Redémarrer",
+  "error.page.action.report": "Signaler l'erreur",
+  "error.page.action.reported": "Erreur signalée",
   "error.page.action.checking": "Vérification...",
   "error.page.action.checking": "Vérification...",
   "error.page.action.checkUpdates": "Vérifier les mises à jour",
   "error.page.action.checkUpdates": "Vérifier les mises à jour",
   "error.page.action.updateTo": "Mettre à jour vers {{version}}",
   "error.page.action.updateTo": "Mettre à jour vers {{version}}",
@@ -741,8 +743,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Récupérer le contenu d'une URL",
   "settings.permissions.tool.webfetch.description": "Récupérer le contenu d'une URL",
   "settings.permissions.tool.websearch.title": "Recherche Web",
   "settings.permissions.tool.websearch.title": "Recherche Web",
   "settings.permissions.tool.websearch.description": "Rechercher sur le web",
   "settings.permissions.tool.websearch.description": "Rechercher sur le web",
-  "settings.permissions.tool.codesearch.title": "Recherche de code",
-  "settings.permissions.tool.codesearch.description": "Rechercher du code sur le web",
   "settings.permissions.tool.external_directory.title": "Répertoire externe",
   "settings.permissions.tool.external_directory.title": "Répertoire externe",
   "settings.permissions.tool.external_directory.description": "Accéder aux fichiers en dehors du répertoire du projet",
   "settings.permissions.tool.external_directory.description": "Accéder aux fichiers en dehors du répertoire du projet",
   "settings.permissions.tool.doom_loop.title": "Boucle infernale",
   "settings.permissions.tool.doom_loop.title": "Boucle infernale",

+ 2 - 2
packages/app/src/i18n/ja.ts

@@ -402,6 +402,8 @@ export const dict = {
   "error.page.description": "アプリケーションの読み込み中にエラーが発生しました。",
   "error.page.description": "アプリケーションの読み込み中にエラーが発生しました。",
   "error.page.details.label": "エラー詳細",
   "error.page.details.label": "エラー詳細",
   "error.page.action.restart": "再起動",
   "error.page.action.restart": "再起動",
+  "error.page.action.report": "エラーを報告",
+  "error.page.action.reported": "エラーを報告しました",
   "error.page.action.checking": "確認中...",
   "error.page.action.checking": "確認中...",
   "error.page.action.checkUpdates": "アップデートを確認",
   "error.page.action.checkUpdates": "アップデートを確認",
   "error.page.action.updateTo": "{{version}}にアップデート",
   "error.page.action.updateTo": "{{version}}にアップデート",
@@ -727,8 +729,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "URLからコンテンツを取得",
   "settings.permissions.tool.webfetch.description": "URLからコンテンツを取得",
   "settings.permissions.tool.websearch.title": "Web検索",
   "settings.permissions.tool.websearch.title": "Web検索",
   "settings.permissions.tool.websearch.description": "ウェブを検索",
   "settings.permissions.tool.websearch.description": "ウェブを検索",
-  "settings.permissions.tool.codesearch.title": "コード検索",
-  "settings.permissions.tool.codesearch.description": "ウェブ上のコードを検索",
   "settings.permissions.tool.external_directory.title": "外部ディレクトリ",
   "settings.permissions.tool.external_directory.title": "外部ディレクトリ",
   "settings.permissions.tool.external_directory.description": "プロジェクトディレクトリ外のファイルへのアクセス",
   "settings.permissions.tool.external_directory.description": "プロジェクトディレクトリ外のファイルへのアクセス",
   "settings.permissions.tool.doom_loop.title": "無限ループ",
   "settings.permissions.tool.doom_loop.title": "無限ループ",

+ 2 - 2
packages/app/src/i18n/ko.ts

@@ -401,6 +401,8 @@ export const dict = {
   "error.page.description": "애플리케이션을 로드하는 동안 오류가 발생했습니다.",
   "error.page.description": "애플리케이션을 로드하는 동안 오류가 발생했습니다.",
   "error.page.details.label": "오류 세부 정보",
   "error.page.details.label": "오류 세부 정보",
   "error.page.action.restart": "다시 시작",
   "error.page.action.restart": "다시 시작",
+  "error.page.action.report": "오류 신고",
+  "error.page.action.reported": "오류가 신고됨",
   "error.page.action.checking": "확인 중...",
   "error.page.action.checking": "확인 중...",
   "error.page.action.checkUpdates": "업데이트 확인",
   "error.page.action.checkUpdates": "업데이트 확인",
   "error.page.action.updateTo": "{{version}} 버전으로 업데이트",
   "error.page.action.updateTo": "{{version}} 버전으로 업데이트",
@@ -722,8 +724,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "URL에서 콘텐츠 가져오기",
   "settings.permissions.tool.webfetch.description": "URL에서 콘텐츠 가져오기",
   "settings.permissions.tool.websearch.title": "웹 검색",
   "settings.permissions.tool.websearch.title": "웹 검색",
   "settings.permissions.tool.websearch.description": "웹 검색",
   "settings.permissions.tool.websearch.description": "웹 검색",
-  "settings.permissions.tool.codesearch.title": "코드 검색",
-  "settings.permissions.tool.codesearch.description": "웹에서 코드 검색",
   "settings.permissions.tool.external_directory.title": "외부 디렉터리",
   "settings.permissions.tool.external_directory.title": "외부 디렉터리",
   "settings.permissions.tool.external_directory.description": "프로젝트 디렉터리 외부의 파일에 액세스",
   "settings.permissions.tool.external_directory.description": "프로젝트 디렉터리 외부의 파일에 액세스",
   "settings.permissions.tool.doom_loop.title": "무한 반복",
   "settings.permissions.tool.doom_loop.title": "무한 반복",

+ 2 - 2
packages/app/src/i18n/no.ts

@@ -450,6 +450,8 @@ export const dict = {
   "error.page.description": "Det oppstod en feil under lasting av applikasjonen.",
   "error.page.description": "Det oppstod en feil under lasting av applikasjonen.",
   "error.page.details.label": "Feildetaljer",
   "error.page.details.label": "Feildetaljer",
   "error.page.action.restart": "Start på nytt",
   "error.page.action.restart": "Start på nytt",
+  "error.page.action.report": "Rapporter feil",
+  "error.page.action.reported": "Feil rapportert",
   "error.page.action.checking": "Sjekker...",
   "error.page.action.checking": "Sjekker...",
   "error.page.action.checkUpdates": "Se etter oppdateringer",
   "error.page.action.checkUpdates": "Se etter oppdateringer",
   "error.page.action.updateTo": "Oppdater til {{version}}",
   "error.page.action.updateTo": "Oppdater til {{version}}",
@@ -807,8 +809,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Hent innhold fra en URL",
   "settings.permissions.tool.webfetch.description": "Hent innhold fra en URL",
   "settings.permissions.tool.websearch.title": "Websøk",
   "settings.permissions.tool.websearch.title": "Websøk",
   "settings.permissions.tool.websearch.description": "Søk på nettet",
   "settings.permissions.tool.websearch.description": "Søk på nettet",
-  "settings.permissions.tool.codesearch.title": "Kodesøk",
-  "settings.permissions.tool.codesearch.description": "Søk etter kode på nettet",
   "settings.permissions.tool.external_directory.title": "Ekstern mappe",
   "settings.permissions.tool.external_directory.title": "Ekstern mappe",
   "settings.permissions.tool.external_directory.description": "Få tilgang til filer utenfor prosjektmappen",
   "settings.permissions.tool.external_directory.description": "Få tilgang til filer utenfor prosjektmappen",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",

+ 2 - 2
packages/app/src/i18n/pl.ts

@@ -403,6 +403,8 @@ export const dict = {
   "error.page.description": "Wystąpił błąd podczas ładowania aplikacji.",
   "error.page.description": "Wystąpił błąd podczas ładowania aplikacji.",
   "error.page.details.label": "Szczegóły błędu",
   "error.page.details.label": "Szczegóły błędu",
   "error.page.action.restart": "Restartuj",
   "error.page.action.restart": "Restartuj",
+  "error.page.action.report": "Zgłoś błąd",
+  "error.page.action.reported": "Błąd zgłoszony",
   "error.page.action.checking": "Sprawdzanie...",
   "error.page.action.checking": "Sprawdzanie...",
   "error.page.action.checkUpdates": "Sprawdź aktualizacje",
   "error.page.action.checkUpdates": "Sprawdź aktualizacje",
   "error.page.action.updateTo": "Zaktualizuj do {{version}}",
   "error.page.action.updateTo": "Zaktualizuj do {{version}}",
@@ -729,8 +731,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Pobieranie zawartości z adresu URL",
   "settings.permissions.tool.webfetch.description": "Pobieranie zawartości z adresu URL",
   "settings.permissions.tool.websearch.title": "Wyszukiwanie w sieci",
   "settings.permissions.tool.websearch.title": "Wyszukiwanie w sieci",
   "settings.permissions.tool.websearch.description": "Przeszukiwanie sieci",
   "settings.permissions.tool.websearch.description": "Przeszukiwanie sieci",
-  "settings.permissions.tool.codesearch.title": "Wyszukiwanie kodu",
-  "settings.permissions.tool.codesearch.description": "Przeszukiwanie kodu w sieci",
   "settings.permissions.tool.external_directory.title": "Katalog zewnętrzny",
   "settings.permissions.tool.external_directory.title": "Katalog zewnętrzny",
   "settings.permissions.tool.external_directory.description": "Dostęp do plików poza katalogiem projektu",
   "settings.permissions.tool.external_directory.description": "Dostęp do plików poza katalogiem projektu",
   "settings.permissions.tool.doom_loop.title": "Zapętlenie",
   "settings.permissions.tool.doom_loop.title": "Zapętlenie",

+ 2 - 2
packages/app/src/i18n/ru.ts

@@ -448,6 +448,8 @@ export const dict = {
   "error.page.description": "Произошла ошибка при загрузке приложения.",
   "error.page.description": "Произошла ошибка при загрузке приложения.",
   "error.page.details.label": "Детали ошибки",
   "error.page.details.label": "Детали ошибки",
   "error.page.action.restart": "Перезапустить",
   "error.page.action.restart": "Перезапустить",
+  "error.page.action.report": "Сообщить об ошибке",
+  "error.page.action.reported": "Об ошибке сообщено",
   "error.page.action.checking": "Проверка...",
   "error.page.action.checking": "Проверка...",
   "error.page.action.checkUpdates": "Проверить обновления",
   "error.page.action.checkUpdates": "Проверить обновления",
   "error.page.action.updateTo": "Обновить до {{version}}",
   "error.page.action.updateTo": "Обновить до {{version}}",
@@ -808,8 +810,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Получение контента по URL",
   "settings.permissions.tool.webfetch.description": "Получение контента по URL",
   "settings.permissions.tool.websearch.title": "Web Search",
   "settings.permissions.tool.websearch.title": "Web Search",
   "settings.permissions.tool.websearch.description": "Поиск в интернете",
   "settings.permissions.tool.websearch.description": "Поиск в интернете",
-  "settings.permissions.tool.codesearch.title": "Code Search",
-  "settings.permissions.tool.codesearch.description": "Поиск кода в интернете",
   "settings.permissions.tool.external_directory.title": "Внешняя директория",
   "settings.permissions.tool.external_directory.title": "Внешняя директория",
   "settings.permissions.tool.external_directory.description": "Доступ к файлам вне директории проекта",
   "settings.permissions.tool.external_directory.description": "Доступ к файлам вне директории проекта",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",

+ 2 - 2
packages/app/src/i18n/th.ts

@@ -447,6 +447,8 @@ export const dict = {
   "error.page.description": "เกิดข้อผิดพลาดระหว่างการโหลดแอปพลิเคชัน",
   "error.page.description": "เกิดข้อผิดพลาดระหว่างการโหลดแอปพลิเคชัน",
   "error.page.details.label": "รายละเอียดข้อผิดพลาด",
   "error.page.details.label": "รายละเอียดข้อผิดพลาด",
   "error.page.action.restart": "รีสตาร์ท",
   "error.page.action.restart": "รีสตาร์ท",
+  "error.page.action.report": "รายงานข้อผิดพลาด",
+  "error.page.action.reported": "รายงานข้อผิดพลาดแล้ว",
   "error.page.action.checking": "กำลังตรวจสอบ...",
   "error.page.action.checking": "กำลังตรวจสอบ...",
   "error.page.action.checkUpdates": "ตรวจสอบการอัปเดต",
   "error.page.action.checkUpdates": "ตรวจสอบการอัปเดต",
   "error.page.action.updateTo": "อัปเดตเป็น {{version}}",
   "error.page.action.updateTo": "อัปเดตเป็น {{version}}",
@@ -796,8 +798,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "ดึงเนื้อหาจาก URL",
   "settings.permissions.tool.webfetch.description": "ดึงเนื้อหาจาก URL",
   "settings.permissions.tool.websearch.title": "ค้นหาเว็บ",
   "settings.permissions.tool.websearch.title": "ค้นหาเว็บ",
   "settings.permissions.tool.websearch.description": "ค้นหาบนเว็บ",
   "settings.permissions.tool.websearch.description": "ค้นหาบนเว็บ",
-  "settings.permissions.tool.codesearch.title": "ค้นหาโค้ด",
-  "settings.permissions.tool.codesearch.description": "ค้นหาโค้ดบนเว็บ",
   "settings.permissions.tool.external_directory.title": "ไดเรกทอรีภายนอก",
   "settings.permissions.tool.external_directory.title": "ไดเรกทอรีภายนอก",
   "settings.permissions.tool.external_directory.description": "เข้าถึงไฟล์นอกไดเรกทอรีโปรเจกต์",
   "settings.permissions.tool.external_directory.description": "เข้าถึงไฟล์นอกไดเรกทอรีโปรเจกต์",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",

+ 2 - 2
packages/app/src/i18n/tr.ts

@@ -452,6 +452,8 @@ export const dict = {
   "error.page.description": "Uygulama yüklenirken bir hata oluştu.",
   "error.page.description": "Uygulama yüklenirken bir hata oluştu.",
   "error.page.details.label": "Hata Detayları",
   "error.page.details.label": "Hata Detayları",
   "error.page.action.restart": "Yeniden Başlat",
   "error.page.action.restart": "Yeniden Başlat",
+  "error.page.action.report": "Hatayı Bildir",
+  "error.page.action.reported": "Hata Bildirildi",
   "error.page.action.checking": "Kontrol ediliyor...",
   "error.page.action.checking": "Kontrol ediliyor...",
   "error.page.action.checkUpdates": "Güncellemeleri kontrol et",
   "error.page.action.checkUpdates": "Güncellemeleri kontrol et",
   "error.page.action.updateTo": "{{version}} sürümüne güncelle",
   "error.page.action.updateTo": "{{version}} sürümüne güncelle",
@@ -816,8 +818,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "Bir URL'den içerik getir",
   "settings.permissions.tool.webfetch.description": "Bir URL'den içerik getir",
   "settings.permissions.tool.websearch.title": "Web Ara",
   "settings.permissions.tool.websearch.title": "Web Ara",
   "settings.permissions.tool.websearch.description": "Web'de ara",
   "settings.permissions.tool.websearch.description": "Web'de ara",
-  "settings.permissions.tool.codesearch.title": "Kod Ara",
-  "settings.permissions.tool.codesearch.description": "Web'de kod ara",
   "settings.permissions.tool.external_directory.title": "Harici Dizin",
   "settings.permissions.tool.external_directory.title": "Harici Dizin",
   "settings.permissions.tool.external_directory.description": "Proje dizini dışındaki dosyalara eriş",
   "settings.permissions.tool.external_directory.description": "Proje dizini dışındaki dosyalara eriş",
   "settings.permissions.tool.doom_loop.title": "Sonsuz Döngü",
   "settings.permissions.tool.doom_loop.title": "Sonsuz Döngü",

+ 2 - 2
packages/app/src/i18n/zh.ts

@@ -452,6 +452,8 @@ export const dict = {
   "error.page.description": "加载应用程序时发生错误。",
   "error.page.description": "加载应用程序时发生错误。",
   "error.page.details.label": "错误详情",
   "error.page.details.label": "错误详情",
   "error.page.action.restart": "重启",
   "error.page.action.restart": "重启",
+  "error.page.action.report": "上报错误",
+  "error.page.action.reported": "错误已上报",
   "error.page.action.checking": "检查中...",
   "error.page.action.checking": "检查中...",
   "error.page.action.checkUpdates": "检查更新",
   "error.page.action.checkUpdates": "检查更新",
   "error.page.action.updateTo": "更新到 {{version}}",
   "error.page.action.updateTo": "更新到 {{version}}",
@@ -793,8 +795,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "从 URL 获取内容",
   "settings.permissions.tool.webfetch.description": "从 URL 获取内容",
   "settings.permissions.tool.websearch.title": "网页搜索",
   "settings.permissions.tool.websearch.title": "网页搜索",
   "settings.permissions.tool.websearch.description": "搜索网页",
   "settings.permissions.tool.websearch.description": "搜索网页",
-  "settings.permissions.tool.codesearch.title": "代码搜索",
-  "settings.permissions.tool.codesearch.description": "在网上搜索代码",
   "settings.permissions.tool.external_directory.title": "外部目录",
   "settings.permissions.tool.external_directory.title": "外部目录",
   "settings.permissions.tool.external_directory.description": "访问项目目录之外的文件",
   "settings.permissions.tool.external_directory.description": "访问项目目录之外的文件",
   "settings.permissions.tool.doom_loop.title": "死循环",
   "settings.permissions.tool.doom_loop.title": "死循环",

+ 2 - 2
packages/app/src/i18n/zht.ts

@@ -445,6 +445,8 @@ export const dict = {
   "error.page.description": "載入應用程式時發生錯誤。",
   "error.page.description": "載入應用程式時發生錯誤。",
   "error.page.details.label": "錯誤詳情",
   "error.page.details.label": "錯誤詳情",
   "error.page.action.restart": "重新啟動",
   "error.page.action.restart": "重新啟動",
+  "error.page.action.report": "回報錯誤",
+  "error.page.action.reported": "已回報錯誤",
   "error.page.action.checking": "檢查中...",
   "error.page.action.checking": "檢查中...",
   "error.page.action.checkUpdates": "檢查更新",
   "error.page.action.checkUpdates": "檢查更新",
   "error.page.action.updateTo": "更新到 {{version}}",
   "error.page.action.updateTo": "更新到 {{version}}",
@@ -789,8 +791,6 @@ export const dict = {
   "settings.permissions.tool.webfetch.description": "從 URL 取得內容",
   "settings.permissions.tool.webfetch.description": "從 URL 取得內容",
   "settings.permissions.tool.websearch.title": "Web Search",
   "settings.permissions.tool.websearch.title": "Web Search",
   "settings.permissions.tool.websearch.description": "搜尋網頁",
   "settings.permissions.tool.websearch.description": "搜尋網頁",
-  "settings.permissions.tool.codesearch.title": "Code Search",
-  "settings.permissions.tool.codesearch.description": "在網路上搜尋程式碼",
   "settings.permissions.tool.external_directory.title": "外部目錄",
   "settings.permissions.tool.external_directory.title": "外部目錄",
   "settings.permissions.tool.external_directory.description": "存取專案目錄之外的檔案",
   "settings.permissions.tool.external_directory.description": "存取專案目錄之外的檔案",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",
   "settings.permissions.tool.doom_loop.title": "Doom Loop",

+ 1 - 1
packages/app/src/pages/directory-layout.tsx

@@ -1,6 +1,6 @@
 import { DataProvider } from "@opencode-ai/ui/context"
 import { DataProvider } from "@opencode-ai/ui/context"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
+import { base64Encode } from "@opencode-ai/core/util/encode"
 import { useLocation, useNavigate, useParams } from "@solidjs/router"
 import { useLocation, useNavigate, useParams } from "@solidjs/router"
 import { createEffect, createMemo, createResource, type ParentProps, Show } from "solid-js"
 import { createEffect, createMemo, createResource, type ParentProps, Show } from "solid-js"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"

+ 20 - 2
packages/app/src/pages/error.tsx

@@ -1,7 +1,8 @@
 import { TextField } from "@opencode-ai/ui/text-field"
 import { TextField } from "@opencode-ai/ui/text-field"
+import * as Sentry from "@sentry/solid"
 import { Logo } from "@opencode-ai/ui/logo"
 import { Logo } from "@opencode-ai/ui/logo"
 import { Button } from "@opencode-ai/ui/button"
 import { Button } from "@opencode-ai/ui/button"
-import { Component, Show } from "solid-js"
+import { Component, createSignal, Show } from "solid-js"
 import { createStore } from "solid-js/store"
 import { createStore } from "solid-js/store"
 import { usePlatform } from "@/context/platform"
 import { usePlatform } from "@/context/platform"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
@@ -270,10 +271,27 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
           label={language.t("error.page.details.label")}
           label={language.t("error.page.details.label")}
           hideLabel
           hideLabel
         />
         />
-        <div class="flex items-center gap-3">
+        <div class="flex flex-row items-center justify-center gap-3 flex-wrap max-w-64">
           <Button size="large" onClick={platform.restart}>
           <Button size="large" onClick={platform.restart}>
             {language.t("error.page.action.restart")}
             {language.t("error.page.action.restart")}
           </Button>
           </Button>
+          <Show when={Sentry.isEnabled}>
+            {(_) => {
+              const [reported, setReported] = createSignal(false)
+              return (
+                <Button
+                  size="large"
+                  disabled={reported()}
+                  onClick={() => {
+                    Sentry.captureException(props.error)
+                    setReported(true)
+                  }}
+                >
+                  {language.t(reported() ? "error.page.action.reported" : "error.page.action.report")}
+                </Button>
+              )
+            }}
+          </Show>
           <Show when={platform.checkUpdate}>
           <Show when={platform.checkUpdate}>
             <Show
             <Show
               when={store.version}
               when={store.version}

+ 1 - 1
packages/app/src/pages/home.tsx

@@ -3,7 +3,7 @@ import { Button } from "@opencode-ai/ui/button"
 import { Logo } from "@opencode-ai/ui/logo"
 import { Logo } from "@opencode-ai/ui/logo"
 import { useLayout } from "@/context/layout"
 import { useLayout } from "@/context/layout"
 import { useNavigate } from "@solidjs/router"
 import { useNavigate } from "@solidjs/router"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
+import { base64Encode } from "@opencode-ai/core/util/encode"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Icon } from "@opencode-ai/ui/icon"
 import { usePlatform } from "@/context/platform"
 import { usePlatform } from "@/context/platform"
 import { DateTime } from "luxon"
 import { DateTime } from "luxon"

+ 37 - 43
packages/app/src/pages/layout.tsx

@@ -17,7 +17,7 @@ import { useLocation, useNavigate, useParams } from "@solidjs/router"
 import { useLayout, LocalProject } from "@/context/layout"
 import { useLayout, LocalProject } from "@/context/layout"
 import { useGlobalSync } from "@/context/global-sync"
 import { useGlobalSync } from "@/context/global-sync"
 import { Persist, persisted } from "@/utils/persist"
 import { Persist, persisted } from "@/utils/persist"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
+import { base64Encode } from "@opencode-ai/core/util/encode"
 import { decode64 } from "@/utils/base64"
 import { decode64 } from "@/utils/base64"
 import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
 import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
 import { Button } from "@opencode-ai/ui/button"
 import { Button } from "@opencode-ai/ui/button"
@@ -25,7 +25,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
 import { Tooltip } from "@opencode-ai/ui/tooltip"
 import { Tooltip } from "@opencode-ai/ui/tooltip"
 import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
 import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
 import { Dialog } from "@opencode-ai/ui/dialog"
 import { Dialog } from "@opencode-ai/ui/dialog"
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { Session, type Message } from "@opencode-ai/sdk/v2/client"
 import { Session, type Message } from "@opencode-ai/sdk/v2/client"
 import { usePlatform } from "@/context/platform"
 import { usePlatform } from "@/context/platform"
 import { useSettings } from "@/context/settings"
 import { useSettings } from "@/context/settings"
@@ -48,8 +48,8 @@ import {
 } from "@/context/global-sync/session-prefetch"
 } from "@/context/global-sync/session-prefetch"
 import { useNotification } from "@/context/notification"
 import { useNotification } from "@/context/notification"
 import { usePermission } from "@/context/permission"
 import { usePermission } from "@/context/permission"
-import { Binary } from "@opencode-ai/shared/util/binary"
-import { retry } from "@opencode-ai/shared/util/retry"
+import { Binary } from "@opencode-ai/core/util/binary"
+import { retry } from "@opencode-ai/core/util/retry"
 import { playSoundById } from "@/utils/sound"
 import { playSoundById } from "@/utils/sound"
 import { createAim } from "@/utils/aim"
 import { createAim } from "@/utils/aim"
 import { setNavigate } from "@/utils/notification-click"
 import { setNavigate } from "@/utils/notification-click"
@@ -64,13 +64,13 @@ import { DebugBar } from "@/components/debug-bar"
 import { Titlebar } from "@/components/titlebar"
 import { Titlebar } from "@/components/titlebar"
 import { useServer } from "@/context/server"
 import { useServer } from "@/context/server"
 import { useLanguage, type Locale } from "@/context/language"
 import { useLanguage, type Locale } from "@/context/language"
+import { pathKey } from "@/utils/path-key"
 import {
 import {
   displayName,
   displayName,
   effectiveWorkspaceOrder,
   effectiveWorkspaceOrder,
   errorMessage,
   errorMessage,
   latestRootSession,
   latestRootSession,
   sortedRootSessions,
   sortedRootSessions,
-  workspaceKey,
 } from "./layout/helpers"
 } from "./layout/helpers"
 import {
 import {
   collectNewSessionDeepLinks,
   collectNewSessionDeepLinks,
@@ -164,7 +164,7 @@ export default function Layout(props: ParentProps) {
 
 
   const editor = createInlineEditorController()
   const editor = createInlineEditorController()
   const setBusy = (directory: string, value: boolean) => {
   const setBusy = (directory: string, value: boolean) => {
-    const key = workspaceKey(directory)
+    const key = pathKey(directory)
     if (value) {
     if (value) {
       setState("busyWorkspaces", key, true)
       setState("busyWorkspaces", key, true)
       return
       return
@@ -176,7 +176,7 @@ export default function Layout(props: ParentProps) {
       }),
       }),
     )
     )
   }
   }
-  const isBusy = (directory: string) => !!state.busyWorkspaces[workspaceKey(directory)]
+  const isBusy = (directory: string) => !!state.busyWorkspaces[pathKey(directory)]
   const navLeave = { current: undefined as number | undefined }
   const navLeave = { current: undefined as number | undefined }
   const sortNow = () => state.sortNow
   const sortNow = () => state.sortNow
   let sizet: number | undefined
   let sizet: number | undefined
@@ -497,8 +497,8 @@ export default function Layout(props: ParentProps) {
         }
         }
 
 
         const currentSession = params.id
         const currentSession = params.id
-        if (workspaceKey(directory) === workspaceKey(currentDir()) && props.sessionID === currentSession) return
-        if (workspaceKey(directory) === workspaceKey(currentDir()) && session?.parentID === currentSession) return
+        if (pathKey(directory) === pathKey(currentDir()) && props.sessionID === currentSession) return
+        if (pathKey(directory) === pathKey(currentDir()) && session?.parentID === currentSession) return
 
 
         dismissSessionAlert(sessionKey)
         dismissSessionAlert(sessionKey)
 
 
@@ -556,14 +556,14 @@ export default function Layout(props: ParentProps) {
   const currentProject = createMemo(() => {
   const currentProject = createMemo(() => {
     const directory = currentDir()
     const directory = currentDir()
     if (!directory) return
     if (!directory) return
-    const key = workspaceKey(directory)
+    const key = pathKey(directory)
 
 
     const projects = layout.projects.list()
     const projects = layout.projects.list()
 
 
-    const sandbox = projects.find((p) => p.sandboxes?.some((item) => workspaceKey(item) === key))
+    const sandbox = projects.find((p) => p.sandboxes?.some((item) => pathKey(item) === key))
     if (sandbox) return sandbox
     if (sandbox) return sandbox
 
 
-    const direct = projects.find((p) => workspaceKey(p.worktree) === key)
+    const direct = projects.find((p) => pathKey(p.worktree) === key)
     if (direct) return direct
     if (direct) return direct
 
 
     const [child] = globalSync.child(directory, { bootstrap: false })
     const [child] = globalSync.child(directory, { bootstrap: false })
@@ -596,7 +596,7 @@ export default function Layout(props: ParentProps) {
   })
   })
 
 
   const workspaceName = (directory: string, projectId?: string, branch?: string) => {
   const workspaceName = (directory: string, projectId?: string, branch?: string) => {
-    const key = workspaceKey(directory)
+    const key = pathKey(directory)
     const direct = store.workspaceName[key] ?? store.workspaceName[directory]
     const direct = store.workspaceName[key] ?? store.workspaceName[directory]
     if (direct) return direct
     if (direct) return direct
     if (!projectId) return
     if (!projectId) return
@@ -605,7 +605,7 @@ export default function Layout(props: ParentProps) {
   }
   }
 
 
   const setWorkspaceName = (directory: string, next: string, projectId?: string, branch?: string) => {
   const setWorkspaceName = (directory: string, next: string, projectId?: string, branch?: string) => {
-    const key = workspaceKey(directory)
+    const key = pathKey(directory)
     setStore("workspaceName", key, next)
     setStore("workspaceName", key, next)
     if (!projectId) return
     if (!projectId) return
     if (!branch) return
     if (!branch) return
@@ -633,7 +633,7 @@ export default function Layout(props: ParentProps) {
     const activeDir = currentDir()
     const activeDir = currentDir()
     return workspaceIds(project).filter((directory) => {
     return workspaceIds(project).filter((directory) => {
       const expanded = store.workspaceExpanded[directory] ?? directory === project.worktree
       const expanded = store.workspaceExpanded[directory] ?? directory === project.worktree
-      const active = workspaceKey(directory) === workspaceKey(activeDir)
+      const active = pathKey(directory) === pathKey(activeDir)
       return expanded || active
       return expanded || active
     })
     })
   })
   })
@@ -644,10 +644,9 @@ export default function Layout(props: ParentProps) {
     const projects = layout.projects.list()
     const projects = layout.projects.list()
     for (const [directory, expanded] of Object.entries(store.workspaceExpanded)) {
     for (const [directory, expanded] of Object.entries(store.workspaceExpanded)) {
       if (!expanded) continue
       if (!expanded) continue
-      const key = workspaceKey(directory)
+      const key = pathKey(directory)
       const project = projects.find(
       const project = projects.find(
-        (item) =>
-          workspaceKey(item.worktree) === key || item.sandboxes?.some((sandbox) => workspaceKey(sandbox) === key),
+        (item) => pathKey(item.worktree) === key || item.sandboxes?.some((sandbox) => pathKey(sandbox) === key),
       )
       )
       if (!project) continue
       if (!project) continue
       if (project.vcs === "git" && layout.sidebar.workspaces(project.worktree)()) continue
       if (project.vcs === "git" && layout.sidebar.workspaces(project.worktree)()) continue
@@ -700,7 +699,7 @@ export default function Layout(props: ParentProps) {
       seen: lru,
       seen: lru,
       keep: sessionID,
       keep: sessionID,
       limit: PREFETCH_MAX_SESSIONS_PER_DIR,
       limit: PREFETCH_MAX_SESSIONS_PER_DIR,
-      preserve: params.id && workspaceKey(directory) === workspaceKey(currentDir()) ? [params.id] : undefined,
+      preserve: params.id && pathKey(directory) === pathKey(currentDir()) ? [params.id] : undefined,
     })
     })
   }
   }
 
 
@@ -1221,17 +1220,14 @@ export default function Layout(props: ParentProps) {
   }
   }
 
 
   function projectRoot(directory: string) {
   function projectRoot(directory: string) {
-    const key = workspaceKey(directory)
+    const key = pathKey(directory)
     const project = layout.projects
     const project = layout.projects
       .list()
       .list()
-      .find(
-        (item) =>
-          workspaceKey(item.worktree) === key || item.sandboxes?.some((sandbox) => workspaceKey(sandbox) === key),
-      )
+      .find((item) => pathKey(item.worktree) === key || item.sandboxes?.some((sandbox) => pathKey(sandbox) === key))
     if (project) return project.worktree
     if (project) return project.worktree
 
 
     const known = Object.entries(store.workspaceOrder).find(
     const known = Object.entries(store.workspaceOrder).find(
-      ([root, dirs]) => workspaceKey(root) === key || dirs.some((item) => workspaceKey(item) === key),
+      ([root, dirs]) => pathKey(root) === key || dirs.some((item) => pathKey(item) === key),
     )
     )
     if (known) return known[0]
     if (known) return known[0]
 
 
@@ -1283,7 +1279,7 @@ export default function Layout(props: ParentProps) {
       : [root]
       : [root]
     const canOpen = (value: string | undefined) => {
     const canOpen = (value: string | undefined) => {
       if (!value) return false
       if (!value) return false
-      return dirs.some((item) => workspaceKey(item) === workspaceKey(value))
+      return dirs.some((item) => pathKey(item) === pathKey(value))
     }
     }
     const refreshDirs = async (target?: string) => {
     const refreshDirs = async (target?: string) => {
       if (!target || target === root || canOpen(target)) return canOpen(target)
       if (!target || target === root || canOpen(target)) return canOpen(target)
@@ -1409,9 +1405,9 @@ export default function Layout(props: ParentProps) {
 
 
   function closeProject(directory: string) {
   function closeProject(directory: string) {
     const list = layout.projects.list()
     const list = layout.projects.list()
-    const key = workspaceKey(directory)
-    const index = list.findIndex((x) => workspaceKey(x.worktree) === key)
-    const active = workspaceKey(currentProject()?.worktree ?? "") === key
+    const key = pathKey(directory)
+    const index = list.findIndex((x) => pathKey(x.worktree) === key)
+    const active = pathKey(currentProject()?.worktree ?? "") === key
     if (index === -1) return
     if (index === -1) return
     const next = list[index + 1]
     const next = list[index + 1]
 
 
@@ -1485,8 +1481,8 @@ export default function Layout(props: ParentProps) {
     if (directory === root) return
     if (directory === root) return
 
 
     const current = currentDir()
     const current = currentDir()
-    const currentKey = workspaceKey(current)
-    const deletedKey = workspaceKey(directory)
+    const currentKey = pathKey(current)
+    const deletedKey = pathKey(directory)
     const shouldLeave = leaveDeletedWorkspace || (!!params.dir && currentKey === deletedKey)
     const shouldLeave = leaveDeletedWorkspace || (!!params.dir && currentKey === deletedKey)
     if (!leaveDeletedWorkspace && shouldLeave) {
     if (!leaveDeletedWorkspace && shouldLeave) {
       navigateWithSidebarReset(`/${base64Encode(root)}/session`)
       navigateWithSidebarReset(`/${base64Encode(root)}/session`)
@@ -1509,7 +1505,7 @@ export default function Layout(props: ParentProps) {
 
 
     if (!result) return
     if (!result) return
 
 
-    if (workspaceKey(store.lastProjectSession[root]?.directory ?? "") === workspaceKey(directory)) {
+    if (pathKey(store.lastProjectSession[root]?.directory ?? "") === pathKey(directory)) {
       clearLastProjectSession(root)
       clearLastProjectSession(root)
     }
     }
 
 
@@ -1529,12 +1525,12 @@ export default function Layout(props: ParentProps) {
     if (shouldLeave) return
     if (shouldLeave) return
 
 
     const nextCurrent = currentDir()
     const nextCurrent = currentDir()
-    const nextKey = workspaceKey(nextCurrent)
+    const nextKey = pathKey(nextCurrent)
     const project = layout.projects.list().find((item) => item.worktree === root)
     const project = layout.projects.list().find((item) => item.worktree === root)
     const dirs = project
     const dirs = project
       ? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root])
       ? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root])
       : [root]
       : [root]
-    const valid = dirs.some((item) => workspaceKey(item) === nextKey)
+    const valid = dirs.some((item) => pathKey(item) === nextKey)
 
 
     if (params.dir && projectRoot(nextCurrent) === root && !valid) {
     if (params.dir && projectRoot(nextCurrent) === root && !valid) {
       navigateWithSidebarReset(`/${base64Encode(root)}/session`)
       navigateWithSidebarReset(`/${base64Encode(root)}/session`)
@@ -1640,7 +1636,7 @@ export default function Layout(props: ParentProps) {
     })
     })
 
 
     const handleDelete = () => {
     const handleDelete = () => {
-      const leaveDeletedWorkspace = !!params.dir && workspaceKey(currentDir()) === workspaceKey(props.directory)
+      const leaveDeletedWorkspace = !!params.dir && pathKey(currentDir()) === pathKey(props.directory)
       if (leaveDeletedWorkspace) {
       if (leaveDeletedWorkspace) {
         navigateWithSidebarReset(`/${base64Encode(props.root)}/session`)
         navigateWithSidebarReset(`/${base64Encode(props.root)}/session`)
       }
       }
@@ -1867,11 +1863,9 @@ export default function Layout(props: ParentProps) {
     const local = project.worktree
     const local = project.worktree
     const dirs = [local, ...(project.sandboxes ?? [])]
     const dirs = [local, ...(project.sandboxes ?? [])]
     const active = currentProject()
     const active = currentProject()
-    const directory = workspaceKey(active?.worktree ?? "") === workspaceKey(project.worktree) ? currentDir() : undefined
+    const directory = pathKey(active?.worktree ?? "") === pathKey(project.worktree) ? currentDir() : undefined
     const extra =
     const extra =
-      directory &&
-      workspaceKey(directory) !== workspaceKey(local) &&
-      !dirs.some((item) => workspaceKey(item) === workspaceKey(directory))
+      directory && pathKey(directory) !== pathKey(local) && !dirs.some((item) => pathKey(item) === pathKey(directory))
         ? directory
         ? directory
         : undefined
         : undefined
     const pending = extra ? WorktreeState.get(extra)?.status === "pending" : false
     const pending = extra ? WorktreeState.get(extra)?.status === "pending" : false
@@ -1916,7 +1910,7 @@ export default function Layout(props: ParentProps) {
     setStore(
     setStore(
       "workspaceOrder",
       "workspaceOrder",
       project.worktree,
       project.worktree,
-      result.filter((directory) => workspaceKey(directory) !== workspaceKey(project.worktree)),
+      result.filter((directory) => pathKey(directory) !== pathKey(project.worktree)),
     )
     )
   }
   }
 
 
@@ -1942,8 +1936,8 @@ export default function Layout(props: ParentProps) {
     setWorkspaceName(created.directory, created.branch, project.id, created.branch)
     setWorkspaceName(created.directory, created.branch, project.id, created.branch)
 
 
     const local = project.worktree
     const local = project.worktree
-    const key = workspaceKey(created.directory)
-    const root = workspaceKey(local)
+    const key = pathKey(created.directory)
+    const root = pathKey(local)
 
 
     setBusy(created.directory, true)
     setBusy(created.directory, true)
     WorktreeState.pending(created.directory)
     WorktreeState.pending(created.directory)
@@ -1954,7 +1948,7 @@ export default function Layout(props: ParentProps) {
     setStore("workspaceOrder", project.worktree, (prev) => {
     setStore("workspaceOrder", project.worktree, (prev) => {
       const existing = prev ?? []
       const existing = prev ?? []
       const next = existing.filter((item) => {
       const next = existing.filter((item) => {
-        const id = workspaceKey(item)
+        const id = pathKey(item)
         return id !== root && id !== key
         return id !== root && id !== key
       })
       })
       return [created.directory, ...next]
       return [created.directory, ...next]

+ 8 - 8
packages/app/src/pages/layout/helpers.test.ts

@@ -14,8 +14,8 @@ import {
   errorMessage,
   errorMessage,
   hasProjectPermissions,
   hasProjectPermissions,
   latestRootSession,
   latestRootSession,
-  workspaceKey,
 } from "./helpers"
 } from "./helpers"
+import { pathKey } from "@/utils/path-key"
 
 
 const session = (input: Partial<Session> & Pick<Session, "id" | "directory">) =>
 const session = (input: Partial<Session> & Pick<Session, "id" | "directory">) =>
   ({
   ({
@@ -104,16 +104,16 @@ describe("layout deep links", () => {
 
 
 describe("layout workspace helpers", () => {
 describe("layout workspace helpers", () => {
   test("normalizes trailing slash in workspace key", () => {
   test("normalizes trailing slash in workspace key", () => {
-    expect(workspaceKey("/tmp/demo///")).toBe("/tmp/demo")
-    expect(workspaceKey("C:\\tmp\\demo\\\\")).toBe("C:/tmp/demo")
+    expect(String(pathKey("/tmp/demo///"))).toBe("/tmp/demo")
+    expect(String(pathKey("C:\\tmp\\demo\\\\"))).toBe("C:/tmp/demo")
   })
   })
 
 
   test("preserves posix and drive roots in workspace key", () => {
   test("preserves posix and drive roots in workspace key", () => {
-    expect(workspaceKey("/")).toBe("/")
-    expect(workspaceKey("///")).toBe("/")
-    expect(workspaceKey("C:\\")).toBe("C:/")
-    expect(workspaceKey("C://")).toBe("C:/")
-    expect(workspaceKey("C:///")).toBe("C:/")
+    expect(String(pathKey("/"))).toBe("/")
+    expect(String(pathKey("///"))).toBe("/")
+    expect(String(pathKey("C:\\"))).toBe("C:/")
+    expect(String(pathKey("C://"))).toBe("C:/")
+    expect(String(pathKey("C:///"))).toBe("C:/")
   })
   })
 
 
   test("keeps local first while preserving known order", () => {
   test("keeps local first while preserving known order", () => {

+ 6 - 13
packages/app/src/pages/layout/helpers.ts

@@ -1,19 +1,12 @@
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { type Session } from "@opencode-ai/sdk/v2/client"
 import { type Session } from "@opencode-ai/sdk/v2/client"
+import { pathKey } from "@/utils/path-key"
 
 
 type SessionStore = {
 type SessionStore = {
   session?: Session[]
   session?: Session[]
   path: { directory: string }
   path: { directory: string }
 }
 }
 
 
-export const workspaceKey = (directory: string) => {
-  const value = directory.replaceAll("\\", "/")
-  const drive = value.match(/^([A-Za-z]:)\/+$/)
-  if (drive) return `${drive[1]}/`
-  if (/^\/+$/i.test(value)) return "/"
-  return value.replace(/\/+$/, "")
-}
-
 function sortSessions(now: number) {
 function sortSessions(now: number) {
   const oneMinuteAgo = now - 60 * 1000
   const oneMinuteAgo = now - 60 * 1000
   return (a: Session, b: Session) => {
   return (a: Session, b: Session) => {
@@ -29,7 +22,7 @@ function sortSessions(now: number) {
 }
 }
 
 
 const isRootVisibleSession = (session: Session, directory: string) =>
 const isRootVisibleSession = (session: Session, directory: string) =>
-  workspaceKey(session.directory) === workspaceKey(directory) && !session.parentID && !session.time?.archived
+  pathKey(session.directory) === pathKey(directory) && !session.parentID && !session.time?.archived
 
 
 export const roots = (store: SessionStore) =>
 export const roots = (store: SessionStore) =>
   (store.session ?? []).filter((session) => isRootVisibleSession(session, store.path.directory))
   (store.session ?? []).filter((session) => isRootVisibleSession(session, store.path.directory))
@@ -72,11 +65,11 @@ export const errorMessage = (err: unknown, fallback: string) => {
 }
 }
 
 
 export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted?: string[]) => {
 export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted?: string[]) => {
-  const root = workspaceKey(local)
+  const root = pathKey(local)
   const live = new Map<string, string>()
   const live = new Map<string, string>()
 
 
   for (const dir of dirs) {
   for (const dir of dirs) {
-    const key = workspaceKey(dir)
+    const key = pathKey(dir)
     if (key === root) continue
     if (key === root) continue
     if (!live.has(key)) live.set(key, dir)
     if (!live.has(key)) live.set(key, dir)
   }
   }
@@ -85,7 +78,7 @@ export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted
 
 
   const result = [local]
   const result = [local]
   for (const dir of persisted) {
   for (const dir of persisted) {
-    const key = workspaceKey(dir)
+    const key = pathKey(dir)
     if (key === root) continue
     if (key === root) continue
     const match = live.get(key)
     const match = live.get(key)
     if (!match) continue
     if (!match) continue

+ 5 - 4
packages/app/src/pages/layout/sidebar-items.tsx

@@ -4,7 +4,7 @@ import { Icon } from "@opencode-ai/ui/icon"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { Spinner } from "@opencode-ai/ui/spinner"
 import { Spinner } from "@opencode-ai/ui/spinner"
 import { Tooltip } from "@opencode-ai/ui/tooltip"
 import { Tooltip } from "@opencode-ai/ui/tooltip"
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { A, useParams } from "@solidjs/router"
 import { A, useParams } from "@solidjs/router"
 import { type Accessor, createMemo, For, type JSX, Match, Show, Switch } from "solid-js"
 import { type Accessor, createMemo, For, type JSX, Match, Show, Switch } from "solid-js"
 import { useGlobalSync } from "@/context/global-sync"
 import { useGlobalSync } from "@/context/global-sync"
@@ -20,9 +20,10 @@ import { childSessionOnPath, hasProjectPermissions } from "./helpers"
 const OPENCODE_PROJECT_ID = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750"
 const OPENCODE_PROJECT_ID = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750"
 
 
 export function getProjectAvatarSource(id?: string, icon?: { color?: string; url?: string; override?: string }) {
 export function getProjectAvatarSource(id?: string, icon?: { color?: string; url?: string; override?: string }) {
-  return id === OPENCODE_PROJECT_ID
-    ? "https://opencode.ai/favicon.svg"
-    : (icon?.override ?? (icon?.color ? undefined : icon?.url))
+  if (id === OPENCODE_PROJECT_ID) return "https://opencode.ai/favicon.svg"
+  if (icon?.override) return icon?.override
+  if (icon?.color) return undefined
+  return icon?.url
 }
 }
 
 
 export const ProjectIcon = (props: { project: LocalProject; class?: string; notify?: boolean }): JSX.Element => {
 export const ProjectIcon = (props: { project: LocalProject; class?: string; notify?: boolean }): JSX.Element => {

+ 1 - 1
packages/app/src/pages/layout/sidebar-project.tsx

@@ -1,6 +1,6 @@
 import { createMemo, For, Show, type Accessor, type JSX } from "solid-js"
 import { createMemo, For, Show, type Accessor, type JSX } from "solid-js"
 import { createStore } from "solid-js/store"
 import { createStore } from "solid-js/store"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
+import { base64Encode } from "@opencode-ai/core/util/encode"
 import { Button } from "@opencode-ai/ui/button"
 import { Button } from "@opencode-ai/ui/button"
 import { ContextMenu } from "@opencode-ai/ui/context-menu"
 import { ContextMenu } from "@opencode-ai/ui/context-menu"
 import { HoverCard } from "@opencode-ai/ui/hover-card"
 import { HoverCard } from "@opencode-ai/ui/hover-card"

+ 5 - 4
packages/app/src/pages/layout/sidebar-workspace.tsx

@@ -3,8 +3,8 @@ import { createEffect, createMemo, For, Show, type Accessor, type JSX } from "so
 import { createStore } from "solid-js/store"
 import { createStore } from "solid-js/store"
 import { createSortable } from "@thisbeyond/solid-dnd"
 import { createSortable } from "@thisbeyond/solid-dnd"
 import { createMediaQuery } from "@solid-primitives/media"
 import { createMediaQuery } from "@solid-primitives/media"
-import { base64Encode } from "@opencode-ai/shared/util/encode"
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { base64Encode } from "@opencode-ai/core/util/encode"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { Button } from "@opencode-ai/ui/button"
 import { Button } from "@opencode-ai/ui/button"
 import { Collapsible } from "@opencode-ai/ui/collapsible"
 import { Collapsible } from "@opencode-ai/ui/collapsible"
 import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
 import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
@@ -16,8 +16,9 @@ import { type Session } from "@opencode-ai/sdk/v2/client"
 import { type LocalProject } from "@/context/layout"
 import { type LocalProject } from "@/context/layout"
 import { loadSessionsQuery, useGlobalSync } from "@/context/global-sync"
 import { loadSessionsQuery, useGlobalSync } from "@/context/global-sync"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
+import { pathKey } from "@/utils/path-key"
 import { NewSessionItem, SessionItem, SessionSkeleton } from "./sidebar-items"
 import { NewSessionItem, SessionItem, SessionSkeleton } from "./sidebar-items"
-import { sortedRootSessions, workspaceKey } from "./helpers"
+import { sortedRootSessions } from "./helpers"
 import { useQuery } from "@tanstack/solid-query"
 import { useQuery } from "@tanstack/solid-query"
 
 
 type InlineEditorComponent = (props: {
 type InlineEditorComponent = (props: {
@@ -309,7 +310,7 @@ export const SortableWorkspace = (props: {
   const slug = createMemo(() => base64Encode(props.directory))
   const slug = createMemo(() => base64Encode(props.directory))
   const sessions = createMemo(() => sortedRootSessions(workspaceStore, props.sortNow()))
   const sessions = createMemo(() => sortedRootSessions(workspaceStore, props.sortNow()))
   const local = createMemo(() => props.directory === props.project.worktree)
   const local = createMemo(() => props.directory === props.project.worktree)
-  const active = createMemo(() => workspaceKey(props.ctx.currentDir()) === workspaceKey(props.directory))
+  const active = createMemo(() => pathKey(props.ctx.currentDir()) === pathKey(props.directory))
   const workspaceValue = createMemo(() => {
   const workspaceValue = createMemo(() => {
     const branch = workspaceStore.vcs?.branch
     const branch = workspaceStore.vcs?.branch
     const name = branch ?? getFilename(props.directory)
     const name = branch ?? getFilename(props.directory)

+ 1 - 1
packages/app/src/pages/session.tsx

@@ -28,7 +28,7 @@ import { createAutoScroll } from "@opencode-ai/ui/hooks"
 import { previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
 import { previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
 import { Button } from "@opencode-ai/ui/button"
 import { Button } from "@opencode-ai/ui/button"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
-import { checksum } from "@opencode-ai/shared/util/encode"
+import { checksum } from "@opencode-ai/core/util/encode"
 import { useSearchParams } from "@solidjs/router"
 import { useSearchParams } from "@solidjs/router"
 import { NewSessionView, SessionHeader } from "@/components/session"
 import { NewSessionView, SessionHeader } from "@/components/session"
 import { useComments } from "@/context/comments"
 import { useComments } from "@/context/comments"

+ 1 - 1
packages/app/src/pages/session/file-tabs.tsx

@@ -6,7 +6,7 @@ import type { FileSearchHandle } from "@opencode-ai/ui/file"
 import { useFileComponent } from "@opencode-ai/ui/context/file"
 import { useFileComponent } from "@opencode-ai/ui/context/file"
 import { cloneSelectedLineRange, previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
 import { cloneSelectedLineRange, previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
 import { createLineCommentController } from "@opencode-ai/ui/line-comment-annotations"
 import { createLineCommentController } from "@opencode-ai/ui/line-comment-annotations"
-import { sampledChecksum } from "@opencode-ai/shared/util/encode"
+import { sampledChecksum } from "@opencode-ai/core/util/encode"
 import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
 import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { Tabs } from "@opencode-ai/ui/tabs"
 import { Tabs } from "@opencode-ai/ui/tabs"

+ 2 - 2
packages/app/src/pages/session/message-timeline.tsx

@@ -15,8 +15,8 @@ import { ScrollView } from "@opencode-ai/ui/scroll-view"
 import { TextField } from "@opencode-ai/ui/text-field"
 import { TextField } from "@opencode-ai/ui/text-field"
 import type { AssistantMessage, Message as MessageType, Part, TextPart, UserMessage } from "@opencode-ai/sdk/v2"
 import type { AssistantMessage, Message as MessageType, Part, TextPart, UserMessage } from "@opencode-ai/sdk/v2"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
-import { Binary } from "@opencode-ai/shared/util/binary"
-import { getFilename } from "@opencode-ai/shared/util/path"
+import { Binary } from "@opencode-ai/core/util/binary"
+import { getFilename } from "@opencode-ai/core/util/path"
 import { Popover as KobaltePopover } from "@kobalte/core/popover"
 import { Popover as KobaltePopover } from "@kobalte/core/popover"
 import { shouldMarkBoundaryGesture, normalizeWheelDelta } from "@/pages/session/message-gesture"
 import { shouldMarkBoundaryGesture, normalizeWheelDelta } from "@/pages/session/message-gesture"
 import { SessionContextUsage } from "@/components/session-context-usage"
 import { SessionContextUsage } from "@/components/session-context-usage"

+ 1 - 1
packages/app/src/pages/session/use-session-commands.tsx

@@ -14,7 +14,7 @@ import { useSettings } from "@/context/settings"
 import { useSync } from "@/context/sync"
 import { useSync } from "@/context/sync"
 import { useTerminal } from "@/context/terminal"
 import { useTerminal } from "@/context/terminal"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
-import { findLast } from "@opencode-ai/shared/util/array"
+import { findLast } from "@opencode-ai/core/util/array"
 import { createSessionTabs } from "@/pages/session/helpers"
 import { createSessionTabs } from "@/pages/session/helpers"
 import { extractPromptFromParts } from "@/utils/prompt"
 import { extractPromptFromParts } from "@/utils/prompt"
 import { UserMessage } from "@opencode-ai/sdk/v2"
 import { UserMessage } from "@opencode-ai/sdk/v2"

+ 1 - 1
packages/app/src/utils/base64.ts

@@ -1,4 +1,4 @@
-import { base64Decode } from "@opencode-ai/shared/util/encode"
+import { base64Decode } from "@opencode-ai/core/util/encode"
 
 
 export function decode64(value: string | undefined) {
 export function decode64(value: string | undefined) {
   if (value === undefined) return
   if (value === undefined) return

+ 24 - 0
packages/app/src/utils/path-key.ts

@@ -0,0 +1,24 @@
+export type PathKey = string & { _brand: "PathKey" }
+
+const isDrive = (value: string) => {
+  if (value.length !== 2) return false
+  const code = value.charCodeAt(0)
+  return value[1] === ":" && ((code >= 65 && code <= 90) || (code >= 97 && code <= 122))
+}
+
+const trimTrailingSlashes = (value: string) => {
+  for (let i = value.length - 1; i >= 0; i--) {
+    if (value[i] !== "/") return value.slice(0, i + 1)
+  }
+  return ""
+}
+
+const isWindowsPath = (value: string) => value[1] === ":" || value.startsWith("\\\\")
+
+export const pathKey = (path: string) => {
+  const value = isWindowsPath(path) ? path.replaceAll("\\", "/") : path
+  const trimmed = trimTrailingSlashes(value)
+  if (!trimmed && value.startsWith("/")) return "/" as PathKey
+  if (isDrive(trimmed)) return `${trimmed}/` as PathKey
+  return trimmed as PathKey
+}

+ 52 - 0
packages/app/src/utils/persist.test.ts

@@ -1,6 +1,8 @@
 import { beforeAll, beforeEach, describe, expect, mock, test } from "bun:test"
 import { beforeAll, beforeEach, describe, expect, mock, test } from "bun:test"
 
 
 type PersistTestingType = typeof import("./persist").PersistTesting
 type PersistTestingType = typeof import("./persist").PersistTesting
+type PersistType = typeof import("./persist").Persist
+type RemovePersistedType = typeof import("./persist").removePersisted
 
 
 class MemoryStorage implements Storage {
 class MemoryStorage implements Storage {
   private values = new Map<string, string>()
   private values = new Map<string, string>()
@@ -45,6 +47,8 @@ class MemoryStorage implements Storage {
 const storage = new MemoryStorage()
 const storage = new MemoryStorage()
 
 
 let persistTesting: PersistTestingType
 let persistTesting: PersistTestingType
+let Persist: PersistType
+let removePersisted: RemovePersistedType
 
 
 beforeAll(async () => {
 beforeAll(async () => {
   mock.module("@/context/platform", () => ({
   mock.module("@/context/platform", () => ({
@@ -53,6 +57,8 @@ beforeAll(async () => {
 
 
   const mod = await import("./persist")
   const mod = await import("./persist")
   persistTesting = mod.PersistTesting
   persistTesting = mod.PersistTesting
+  Persist = mod.Persist
+  removePersisted = mod.removePersisted
 })
 })
 
 
 beforeEach(() => {
 beforeEach(() => {
@@ -112,4 +118,50 @@ describe("persist localStorage resilience", () => {
     expect(result.endsWith(".dat")).toBeTrue()
     expect(result.endsWith(".dat")).toBeTrue()
     expect(/[:\\/]/.test(result)).toBeFalse()
     expect(/[:\\/]/.test(result)).toBeFalse()
   })
   })
+
+  test("workspace target keeps raw path storage as legacy fallback", () => {
+    const target = Persist.workspace("C:\\Users\\foo", "vcs")
+
+    expect(target.storage).toBe(persistTesting.workspaceStorage("C:/Users/foo"))
+    expect(target.legacyStorageNames).toEqual([persistTesting.workspaceStorage("C:\\Users\\foo")])
+  })
+
+  test("workspace target keeps backslash storage as fallback for normalized Windows paths", () => {
+    const target = Persist.workspace("C:/Users/foo", "vcs")
+
+    expect(target.storage).toBe(persistTesting.workspaceStorage("C:/Users/foo"))
+    expect(target.legacyStorageNames).toEqual([persistTesting.workspaceStorage("C:\\Users\\foo")])
+  })
+
+  test("migrates direct legacy keys into scoped storage", () => {
+    storage.setItem("legacy.workspace", '{"value":2}')
+    const target = Persist.workspace("C:/Users/foo", "demo", ["legacy.workspace"])
+    const current = persistTesting.localStorageWithPrefix(target.storage!)
+    const legacyStore = persistTesting.localStorageDirect()
+
+    const result = persistTesting.migrateLegacy({
+      current,
+      legacyStore,
+      stores: [],
+      keys: target.legacy!,
+      key: target.key,
+      defaults: { value: 1 },
+    })
+
+    expect(result).toBe('{"value":2}')
+    expect(storage.getItem(`${target.storage}:${target.key}`)).toBe('{"value":2}')
+    expect(legacyStore.getItem("legacy.workspace")).toBeNull()
+    expect(storage.getItem("legacy.workspace")).toBeNull()
+  })
+
+  test("removes legacy workspace storage when removing persisted target", () => {
+    const target = Persist.workspace("C:\\Users\\foo", "terminal")
+    storage.setItem(`${target.storage}:${target.key}`, '{"value":1}')
+    storage.setItem(`${target.legacyStorageNames![0]}:${target.key}`, '{"value":2}')
+
+    removePersisted(target)
+
+    expect(storage.getItem(`${target.storage}:${target.key}`)).toBeNull()
+    expect(storage.getItem(`${target.legacyStorageNames![0]}:${target.key}`)).toBeNull()
+  })
 })
 })

+ 194 - 59
packages/app/src/utils/persist.ts

@@ -1,8 +1,9 @@
 import { Platform, usePlatform } from "@/context/platform"
 import { Platform, usePlatform } from "@/context/platform"
 import { makePersisted, type AsyncStorage, type SyncStorage } from "@solid-primitives/storage"
 import { makePersisted, type AsyncStorage, type SyncStorage } from "@solid-primitives/storage"
-import { checksum } from "@opencode-ai/shared/util/encode"
+import { checksum } from "@opencode-ai/core/util/encode"
 import { createResource, type Accessor } from "solid-js"
 import { createResource, type Accessor } from "solid-js"
 import type { SetStoreFunction, Store } from "solid-js/store"
 import type { SetStoreFunction, Store } from "solid-js/store"
+import { pathKey } from "@/utils/path-key"
 
 
 type InitType = Promise<string> | string | null
 type InitType = Promise<string> | string | null
 type PersistedWithReady<T> = [
 type PersistedWithReady<T> = [
@@ -14,6 +15,7 @@ type PersistedWithReady<T> = [
 
 
 type PersistTarget = {
 type PersistTarget = {
   storage?: string
   storage?: string
+  legacyStorageNames?: string[]
   key: string
   key: string
   legacy?: string[]
   legacy?: string[]
   migrate?: (value: unknown) => unknown
   migrate?: (value: unknown) => unknown
@@ -208,12 +210,153 @@ function normalize(defaults: unknown, raw: string, migrate?: (value: unknown) =>
   return JSON.stringify(merged)
   return JSON.stringify(merged)
 }
 }
 
 
+function readCurrent(input: {
+  storage: SyncStorage
+  key: string
+  defaults: unknown
+  migrate?: (value: unknown) => unknown
+}) {
+  const raw = input.storage.getItem(input.key)
+  if (raw === null) return
+  const next = normalize(input.defaults, raw, input.migrate)
+  if (next === undefined) {
+    input.storage.removeItem(input.key)
+    return null
+  }
+  if (raw !== next) input.storage.setItem(input.key, next)
+  return next
+}
+
+function migrateLegacy(input: {
+  current: SyncStorage
+  legacyStore?: SyncStorage
+  stores: SyncStorage[]
+  keys: string[]
+  key: string
+  defaults: unknown
+  migrate?: (value: unknown) => unknown
+}) {
+  for (const store of input.stores) {
+    const raw = store.getItem(input.key)
+    if (raw === null) continue
+
+    const next = normalize(input.defaults, raw, input.migrate)
+    if (next === undefined) {
+      store.removeItem(input.key)
+      continue
+    }
+    input.current.setItem(input.key, next)
+    store.removeItem(input.key)
+    return next
+  }
+
+  if (!input.legacyStore) return null
+
+  for (const key of input.keys) {
+    const raw = input.legacyStore.getItem(key)
+    if (raw === null) continue
+
+    const next = normalize(input.defaults, raw, input.migrate)
+    if (next === undefined) {
+      input.legacyStore.removeItem(key)
+      continue
+    }
+    input.current.setItem(input.key, next)
+    input.legacyStore.removeItem(key)
+    return next
+  }
+
+  return null
+}
+
+async function readCurrentAsync(input: {
+  storage: AsyncStorage
+  key: string
+  defaults: unknown
+  migrate?: (value: unknown) => unknown
+}) {
+  const raw = await input.storage.getItem(input.key)
+  if (raw === null) return
+  const next = normalize(input.defaults, raw, input.migrate)
+  if (next === undefined) {
+    await input.storage.removeItem(input.key).catch(() => undefined)
+    return null
+  }
+  if (raw !== next) await input.storage.setItem(input.key, next)
+  return next
+}
+
+async function removeAsync(storage: AsyncStorage, key: string) {
+  try {
+    await storage.removeItem(key)
+  } catch {}
+}
+
+async function migrateLegacyAsync(input: {
+  current: AsyncStorage
+  legacyStore?: AsyncStorage
+  stores: AsyncStorage[]
+  keys: string[]
+  key: string
+  defaults: unknown
+  migrate?: (value: unknown) => unknown
+}) {
+  for (const store of input.stores) {
+    const raw = await store.getItem(input.key)
+    if (raw === null) continue
+
+    const next = normalize(input.defaults, raw, input.migrate)
+    if (next === undefined) {
+      await removeAsync(store, input.key)
+      continue
+    }
+    await input.current.setItem(input.key, next)
+    await store.removeItem(input.key)
+    return next
+  }
+
+  if (!input.legacyStore) return null
+
+  for (const key of input.keys) {
+    const raw = await input.legacyStore.getItem(key)
+    if (raw === null) continue
+
+    const next = normalize(input.defaults, raw, input.migrate)
+    if (next === undefined) {
+      await removeAsync(input.legacyStore, key)
+      continue
+    }
+    await input.current.setItem(input.key, next)
+    await input.legacyStore.removeItem(key)
+    return next
+  }
+
+  return null
+}
+
 function workspaceStorage(dir: string) {
 function workspaceStorage(dir: string) {
   const head = (dir.slice(0, 12) || "workspace").replace(/[^a-zA-Z0-9._-]/g, "-")
   const head = (dir.slice(0, 12) || "workspace").replace(/[^a-zA-Z0-9._-]/g, "-")
   const sum = checksum(dir) ?? "0"
   const sum = checksum(dir) ?? "0"
   return `opencode.workspace.${head}.${sum}.dat`
   return `opencode.workspace.${head}.${sum}.dat`
 }
 }
 
 
+function legacyWorkspaceStorage(dir: string) {
+  const storage = workspaceStorage(pathKey(dir))
+  const result = new Set<string>()
+  const raw = workspaceStorage(dir)
+  if (raw !== storage) result.add(raw)
+
+  const key = pathKey(dir)
+  const drive = key.length >= 3 && key[1] === ":" && key[2] === "/"
+  if (drive) {
+    const backslash = workspaceStorage(key.replaceAll("/", "\\"))
+    if (backslash !== storage) result.add(backslash)
+  }
+
+  if (result.size === 0) return
+  return [...result]
+}
+
 function localStorageWithPrefix(prefix: string): SyncStorage {
 function localStorageWithPrefix(prefix: string): SyncStorage {
   const base = `${prefix}:`
   const base = `${prefix}:`
   const scope = `prefix:${prefix}`
   const scope = `prefix:${prefix}`
@@ -304,6 +447,7 @@ function localStorageDirect(): SyncStorage {
 export const PersistTesting = {
 export const PersistTesting = {
   localStorageDirect,
   localStorageDirect,
   localStorageWithPrefix,
   localStorageWithPrefix,
+  migrateLegacy,
   normalize,
   normalize,
   workspaceStorage,
   workspaceStorage,
 }
 }
@@ -313,10 +457,17 @@ export const Persist = {
     return { storage: GLOBAL_STORAGE, key, legacy }
     return { storage: GLOBAL_STORAGE, key, legacy }
   },
   },
   workspace(dir: string, key: string, legacy?: string[]): PersistTarget {
   workspace(dir: string, key: string, legacy?: string[]): PersistTarget {
-    return { storage: workspaceStorage(dir), key: `workspace:${key}`, legacy }
+    const storage = workspaceStorage(pathKey(dir))
+    return { storage, legacyStorageNames: legacyWorkspaceStorage(dir), key: `workspace:${key}`, legacy }
   },
   },
   session(dir: string, session: string, key: string, legacy?: string[]): PersistTarget {
   session(dir: string, session: string, key: string, legacy?: string[]): PersistTarget {
-    return { storage: workspaceStorage(dir), key: `session:${session}:${key}`, legacy }
+    const storage = workspaceStorage(pathKey(dir))
+    return {
+      storage,
+      legacyStorageNames: legacyWorkspaceStorage(dir),
+      key: `session:${session}:${key}`,
+      legacy,
+    }
   },
   },
   scoped(dir: string, session: string | undefined, key: string, legacy?: string[]): PersistTarget {
   scoped(dir: string, session: string | undefined, key: string, legacy?: string[]): PersistTarget {
     if (session) return Persist.session(dir, session, key, legacy)
     if (session) return Persist.session(dir, session, key, legacy)
@@ -324,11 +475,18 @@ export const Persist = {
   },
   },
 }
 }
 
 
-export function removePersisted(target: { storage?: string; key: string }, platform?: Platform) {
+export function removePersisted(
+  target: { storage?: string; legacyStorageNames?: string[]; key: string },
+  platform?: Platform,
+) {
   const isDesktop = platform?.platform === "desktop" && !!platform.storage
   const isDesktop = platform?.platform === "desktop" && !!platform.storage
 
 
   if (isDesktop) {
   if (isDesktop) {
-    return platform.storage?.(target.storage)?.removeItem(target.key)
+    void platform.storage?.(target.storage)?.removeItem(target.key)
+    for (const storage of target.legacyStorageNames ?? []) {
+      void platform.storage?.(storage)?.removeItem(target.key)
+    }
+    return
   }
   }
 
 
   if (!target.storage) {
   if (!target.storage) {
@@ -337,6 +495,9 @@ export function removePersisted(target: { storage?: string; key: string }, platf
   }
   }
 
 
   localStorageWithPrefix(target.storage).removeItem(target.key)
   localStorageWithPrefix(target.storage).removeItem(target.key)
+  for (const storage of target.legacyStorageNames ?? []) {
+    localStorageWithPrefix(storage).removeItem(target.key)
+  }
 }
 }
 
 
 export function persisted<T>(
 export function persisted<T>(
@@ -363,39 +524,27 @@ export function persisted<T>(
     return platform.storage?.(LEGACY_STORAGE)
     return platform.storage?.(LEGACY_STORAGE)
   })()
   })()
 
 
+  const legacyStorageNames = config.legacyStorageNames ?? []
+
   const storage = (() => {
   const storage = (() => {
     if (!isDesktop) {
     if (!isDesktop) {
       const current = currentStorage as SyncStorage
       const current = currentStorage as SyncStorage
       const legacyStore = legacyStorage as SyncStorage
       const legacyStore = legacyStorage as SyncStorage
+      const legacyStores = legacyStorageNames.map(localStorageWithPrefix)
 
 
       const api: SyncStorage = {
       const api: SyncStorage = {
         getItem: (key) => {
         getItem: (key) => {
-          const raw = current.getItem(key)
-          if (raw !== null) {
-            const next = normalize(defaults, raw, config.migrate)
-            if (next === undefined) {
-              current.removeItem(key)
-              return null
-            }
-            if (raw !== next) current.setItem(key, next)
-            return next
-          }
-
-          for (const legacyKey of legacy) {
-            const legacyRaw = legacyStore.getItem(legacyKey)
-            if (legacyRaw === null) continue
-
-            const next = normalize(defaults, legacyRaw, config.migrate)
-            if (next === undefined) {
-              legacyStore.removeItem(legacyKey)
-              continue
-            }
-            current.setItem(key, next)
-            legacyStore.removeItem(legacyKey)
-            return next
-          }
-
-          return null
+          const value = readCurrent({ storage: current, key, defaults, migrate: config.migrate })
+          if (value !== undefined) return value
+          return migrateLegacy({
+            current,
+            legacyStore,
+            stores: legacyStores,
+            keys: legacy,
+            key,
+            defaults,
+            migrate: config.migrate,
+          })
         },
         },
         setItem: (key, value) => {
         setItem: (key, value) => {
           current.setItem(key, value)
           current.setItem(key, value)
@@ -410,37 +559,23 @@ export function persisted<T>(
 
 
     const current = currentStorage as AsyncStorage
     const current = currentStorage as AsyncStorage
     const legacyStore = legacyStorage as AsyncStorage | undefined
     const legacyStore = legacyStorage as AsyncStorage | undefined
+    const legacyStores = legacyStorageNames
+      .map((name) => platform.storage?.(name) as AsyncStorage | undefined)
+      .filter((x) => !!x)
 
 
     const api: AsyncStorage = {
     const api: AsyncStorage = {
       getItem: async (key) => {
       getItem: async (key) => {
-        const raw = await current.getItem(key)
-        if (raw !== null) {
-          const next = normalize(defaults, raw, config.migrate)
-          if (next === undefined) {
-            await current.removeItem(key).catch(() => undefined)
-            return null
-          }
-          if (raw !== next) await current.setItem(key, next)
-          return next
-        }
-
-        if (!legacyStore) return null
-
-        for (const legacyKey of legacy) {
-          const legacyRaw = await legacyStore.getItem(legacyKey)
-          if (legacyRaw === null) continue
-
-          const next = normalize(defaults, legacyRaw, config.migrate)
-          if (next === undefined) {
-            await legacyStore.removeItem(legacyKey).catch(() => undefined)
-            continue
-          }
-          await current.setItem(key, next)
-          await legacyStore.removeItem(legacyKey)
-          return next
-        }
-
-        return null
+        const value = await readCurrentAsync({ storage: current, key, defaults, migrate: config.migrate })
+        if (value !== undefined) return value
+        return migrateLegacyAsync({
+          current,
+          legacyStore,
+          stores: legacyStores,
+          keys: legacy,
+          key,
+          defaults,
+          migrate: config.migrate,
+        })
       },
       },
       setItem: async (key, value) => {
       setItem: async (key, value) => {
         await current.setItem(key, value)
         await current.setItem(key, value)

+ 20 - 2
packages/app/vite.config.ts

@@ -1,8 +1,26 @@
+import { sentryVitePlugin } from "@sentry/vite-plugin"
 import { defineConfig } from "vite"
 import { defineConfig } from "vite"
 import desktopPlugin from "./vite"
 import desktopPlugin from "./vite"
 
 
+const sentry =
+  process.env.SENTRY_AUTH_TOKEN && process.env.SENTRY_ORG && process.env.SENTRY_PROJECT
+    ? sentryVitePlugin({
+        authToken: process.env.SENTRY_AUTH_TOKEN,
+        org: process.env.SENTRY_ORG,
+        project: process.env.SENTRY_PROJECT,
+        telemetry: false,
+        release: {
+          name: process.env.SENTRY_RELEASE ?? process.env.VITE_SENTRY_RELEASE,
+        },
+        sourcemaps: {
+          assets: "./dist/**",
+          filesToDeleteAfterUpload: "./dist/**/*.map",
+        },
+      })
+    : false
+
 export default defineConfig({
 export default defineConfig({
-  plugins: [desktopPlugin] as any,
+  plugins: [desktopPlugin, sentry] as any,
   server: {
   server: {
     host: "0.0.0.0",
     host: "0.0.0.0",
     allowedHosts: true,
     allowedHosts: true,
@@ -10,6 +28,6 @@ export default defineConfig({
   },
   },
   build: {
   build: {
     target: "esnext",
     target: "esnext",
-    // sourcemap: true,
+    sourcemap: true,
   },
   },
 })
 })

+ 1 - 1
packages/console/app/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@opencode-ai/console-app",
   "name": "@opencode-ai/console-app",
-  "version": "1.14.23",
+  "version": "1.14.31",
   "type": "module",
   "type": "module",
   "license": "MIT",
   "license": "MIT",
   "scripts": {
   "scripts": {

Файловите разлики са ограничени, защото са твърде много
+ 3 - 0
packages/console/app/src/component/icon.tsx


+ 2 - 2
packages/console/app/src/config.ts

@@ -9,8 +9,8 @@ export const config = {
   github: {
   github: {
     repoUrl: "https://github.com/anomalyco/opencode",
     repoUrl: "https://github.com/anomalyco/opencode",
     starsFormatted: {
     starsFormatted: {
-      compact: "140K",
-      full: "140,000",
+      compact: "150K",
+      full: "150,000",
     },
     },
   },
   },
 
 

+ 4 - 4
packages/console/app/src/i18n/ar.ts

@@ -249,7 +249,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | نماذج برمجة منخفضة التكلفة للجميع",
   "go.title": "OpenCode Go | نماذج برمجة منخفضة التكلفة للجميع",
   "go.meta.description":
   "go.meta.description":
-    "يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود طلب سخية لمدة 5 ساعات لـ GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7.",
+    "يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود طلب سخية لمدة 5 ساعات لـ GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash.",
   "go.hero.title": "نماذج برمجة منخفضة التكلفة للجميع",
   "go.hero.title": "نماذج برمجة منخفضة التكلفة للجميع",
   "go.hero.body":
   "go.hero.body":
     "يجلب Go البرمجة الوكيلة للمبرمجين حول العالم. يوفر حدودًا سخية ووصولًا موثوقًا إلى أقوى النماذج مفتوحة المصدر، حتى تتمكن من البناء باستخدام وكلاء أقوياء دون القلق بشأن التكلفة أو التوفر.",
     "يجلب Go البرمجة الوكيلة للمبرمجين حول العالم. يوفر حدودًا سخية ووصولًا موثوقًا إلى أقوى النماذج مفتوحة المصدر، حتى تتمكن من البناء باستخدام وكلاء أقوياء دون القلق بشأن التكلفة أو التوفر.",
@@ -300,7 +300,7 @@ export const dict = {
   "go.problem.item2": "حدود سخية ووصول موثوق",
   "go.problem.item2": "حدود سخية ووصول موثوق",
   "go.problem.item3": "مصمم لأكبر عدد ممكن من المبرمجين",
   "go.problem.item3": "مصمم لأكبر عدد ممكن من المبرمجين",
   "go.problem.item4":
   "go.problem.item4":
-    "يتضمن GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7",
+    "يتضمن GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash",
   "go.how.title": "كيف يعمل Go",
   "go.how.title": "كيف يعمل Go",
   "go.how.body": "يبدأ Go من $5 للشهر الأول، ثم $10/شهر. يمكنك استخدامه مع OpenCode أو أي وكيل.",
   "go.how.body": "يبدأ Go من $5 للشهر الأول، ثم $10/شهر. يمكنك استخدامه مع OpenCode أو أي وكيل.",
   "go.how.step1.title": "أنشئ حسابًا",
   "go.how.step1.title": "أنشئ حسابًا",
@@ -324,7 +324,7 @@ export const dict = {
   "go.faq.a2": "يتضمن Go النماذج المدرجة أدناه، مع حدود سخية وإتاحة موثوقة.",
   "go.faq.a2": "يتضمن Go النماذج المدرجة أدناه، مع حدود سخية وإتاحة موثوقة.",
   "go.faq.q3": "هل Go هو نفسه Zen؟",
   "go.faq.q3": "هل Go هو نفسه Zen؟",
   "go.faq.a3":
   "go.faq.a3":
-    "لا. Zen هو الدفع حسب الاستخدام، بينما يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود سخية ووصول موثوق إلى نماذج المصدر المفتوح GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7.",
+    "لا. Zen هو الدفع حسب الاستخدام، بينما يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود سخية ووصول موثوق إلى نماذج المصدر المفتوح GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash.",
   "go.faq.q4": "كم تكلفة Go؟",
   "go.faq.q4": "كم تكلفة Go؟",
   "go.faq.a4.p1.beforePricing": "تكلفة Go",
   "go.faq.a4.p1.beforePricing": "تكلفة Go",
   "go.faq.a4.p1.pricingLink": "$5 للشهر الأول",
   "go.faq.a4.p1.pricingLink": "$5 للشهر الأول",
@@ -347,7 +347,7 @@ export const dict = {
 
 
   "go.faq.q9": "ما الفرق بين النماذج المجانية وGo؟",
   "go.faq.q9": "ما الفرق بين النماذج المجانية وGo؟",
   "go.faq.a9":
   "go.faq.a9":
-    "تشمل النماذج المجانية Big Pickle بالإضافة إلى النماذج الترويجية المتاحة في ذلك الوقت، مع حصة 200 طلب/يوم. يتضمن Go نماذج GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 مع حصص طلبات أعلى مطبقة عبر نوافذ متجددة (5 ساعات، أسبوعيًا، وشهريًا)، تعادل تقريبًا 12 دولارًا كل 5 ساعات، و30 دولارًا في الأسبوع، و60 دولارًا في الشهر (تختلف أعداد الطلبات الفعلية حسب النموذج والاستخدام).",
+    "تشمل النماذج المجانية Big Pickle بالإضافة إلى النماذج الترويجية المتاحة في ذلك الوقت، مع حصة 200 طلب/يوم. يتضمن Go نماذج GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash مع حصص طلبات أعلى مطبقة عبر نوافذ متجددة (5 ساعات، أسبوعيًا، وشهريًا)، تعادل تقريبًا 12 دولارًا كل 5 ساعات، و30 دولارًا في الأسبوع، و60 دولارًا في الشهر (تختلف أعداد الطلبات الفعلية حسب النموذج والاستخدام).",
 
 
   "zen.api.error.rateLimitExceeded": "تم تجاوز حد الطلبات. يرجى المحاولة مرة أخرى لاحقًا.",
   "zen.api.error.rateLimitExceeded": "تم تجاوز حد الطلبات. يرجى المحاولة مرة أخرى لاحقًا.",
   "zen.api.error.modelNotSupported": "النموذج {{model}} غير مدعوم",
   "zen.api.error.modelNotSupported": "النموذج {{model}} غير مدعوم",

+ 4 - 4
packages/console/app/src/i18n/br.ts

@@ -253,7 +253,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | Modelos de codificação de baixo custo para todos",
   "go.title": "OpenCode Go | Modelos de codificação de baixo custo para todos",
   "go.meta.description":
   "go.meta.description":
-    "O Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos de solicitação de 5 horas para GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7.",
+    "O Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos de solicitação de 5 horas para GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash.",
   "go.hero.title": "Modelos de codificação de baixo custo para todos",
   "go.hero.title": "Modelos de codificação de baixo custo para todos",
   "go.hero.body":
   "go.hero.body":
     "O Go traz a codificação com agentes para programadores em todo o mundo. Oferecendo limites generosos e acesso confiável aos modelos de código aberto mais capazes, para que você possa construir com agentes poderosos sem se preocupar com custos ou disponibilidade.",
     "O Go traz a codificação com agentes para programadores em todo o mundo. Oferecendo limites generosos e acesso confiável aos modelos de código aberto mais capazes, para que você possa construir com agentes poderosos sem se preocupar com custos ou disponibilidade.",
@@ -305,7 +305,7 @@ export const dict = {
   "go.problem.item2": "Limites generosos e acesso confiável",
   "go.problem.item2": "Limites generosos e acesso confiável",
   "go.problem.item3": "Feito para o maior número possível de programadores",
   "go.problem.item3": "Feito para o maior número possível de programadores",
   "go.problem.item4":
   "go.problem.item4":
-    "Inclui GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7",
+    "Inclui GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash",
   "go.how.title": "Como o Go funciona",
   "go.how.title": "Como o Go funciona",
   "go.how.body":
   "go.how.body":
     "O Go começa em $5 no primeiro mês, depois $10/mês. Você pode usá-lo com o OpenCode ou qualquer agente.",
     "O Go começa em $5 no primeiro mês, depois $10/mês. Você pode usá-lo com o OpenCode ou qualquer agente.",
@@ -331,7 +331,7 @@ export const dict = {
   "go.faq.a2": "O Go inclui os modelos listados abaixo, com limites generosos e acesso confiável.",
   "go.faq.a2": "O Go inclui os modelos listados abaixo, com limites generosos e acesso confiável.",
   "go.faq.q3": "O Go é o mesmo que o Zen?",
   "go.faq.q3": "O Go é o mesmo que o Zen?",
   "go.faq.a3":
   "go.faq.a3":
-    "Não. Zen é pay-as-you-go, enquanto o Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos e acesso confiável aos modelos open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7.",
+    "Não. Zen é pay-as-you-go, enquanto o Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos e acesso confiável aos modelos open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash.",
   "go.faq.q4": "Quanto custa o Go?",
   "go.faq.q4": "Quanto custa o Go?",
   "go.faq.a4.p1.beforePricing": "O Go custa",
   "go.faq.a4.p1.beforePricing": "O Go custa",
   "go.faq.a4.p1.pricingLink": "$5 no primeiro mês",
   "go.faq.a4.p1.pricingLink": "$5 no primeiro mês",
@@ -355,7 +355,7 @@ export const dict = {
 
 
   "go.faq.q9": "Qual a diferença entre os modelos gratuitos e o Go?",
   "go.faq.q9": "Qual a diferença entre os modelos gratuitos e o Go?",
   "go.faq.a9":
   "go.faq.a9":
-    "Os modelos gratuitos incluem Big Pickle e modelos promocionais disponíveis no momento, com uma cota de 200 requisições/dia. O Go inclui GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7 com cotas de requisição mais altas aplicadas em janelas móveis (5 horas, semanal e mensal), aproximadamente equivalentes a $12 por 5 horas, $30 por semana e $60 por mês (as contagens reais de requisições variam de acordo com o modelo e o uso).",
+    "Os modelos gratuitos incluem Big Pickle e modelos promocionais disponíveis no momento, com uma cota de 200 requisições/dia. O Go inclui GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash com cotas de requisição mais altas aplicadas em janelas móveis (5 horas, semanal e mensal), aproximadamente equivalentes a $12 por 5 horas, $30 por semana e $60 por mês (as contagens reais de requisições variam de acordo com o modelo e o uso).",
 
 
   "zen.api.error.rateLimitExceeded": "Limite de taxa excedido. Por favor, tente novamente mais tarde.",
   "zen.api.error.rateLimitExceeded": "Limite de taxa excedido. Por favor, tente novamente mais tarde.",
   "zen.api.error.modelNotSupported": "Modelo {{model}} não suportado",
   "zen.api.error.modelNotSupported": "Modelo {{model}} não suportado",

+ 4 - 4
packages/console/app/src/i18n/da.ts

@@ -251,7 +251,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | Kodningsmodeller til lav pris for alle",
   "go.title": "OpenCode Go | Kodningsmodeller til lav pris for alle",
   "go.meta.description":
   "go.meta.description":
-    "Go starter ved $5 for den første måned, derefter $10/måned, med generøse 5-timers anmodningsgrænser for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 og MiniMax M2.7.",
+    "Go starter ved $5 for den første måned, derefter $10/måned, med generøse 5-timers anmodningsgrænser for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash.",
   "go.hero.title": "Kodningsmodeller til lav pris for alle",
   "go.hero.title": "Kodningsmodeller til lav pris for alle",
   "go.hero.body":
   "go.hero.body":
     "Go bringer agentisk kodning til programmører over hele verden. Med generøse grænser og pålidelig adgang til de mest kapable open source-modeller, så du kan bygge med kraftfulde agenter uden at bekymre dig om omkostninger eller tilgængelighed.",
     "Go bringer agentisk kodning til programmører over hele verden. Med generøse grænser og pålidelig adgang til de mest kapable open source-modeller, så du kan bygge med kraftfulde agenter uden at bekymre dig om omkostninger eller tilgængelighed.",
@@ -302,7 +302,7 @@ export const dict = {
   "go.problem.item2": "Generøse grænser og pålidelig adgang",
   "go.problem.item2": "Generøse grænser og pålidelig adgang",
   "go.problem.item3": "Bygget til så mange programmører som muligt",
   "go.problem.item3": "Bygget til så mange programmører som muligt",
   "go.problem.item4":
   "go.problem.item4":
-    "Inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 og MiniMax M2.7",
+    "Inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash",
   "go.how.title": "Hvordan Go virker",
   "go.how.title": "Hvordan Go virker",
   "go.how.body":
   "go.how.body":
     "Go starter ved $5 for den første måned, derefter $10/måned. Du kan bruge det med OpenCode eller enhver agent.",
     "Go starter ved $5 for den første måned, derefter $10/måned. Du kan bruge det med OpenCode eller enhver agent.",
@@ -328,7 +328,7 @@ export const dict = {
   "go.faq.a2": "Go inkluderer modellerne nedenfor med generøse grænser og pålidelig adgang.",
   "go.faq.a2": "Go inkluderer modellerne nedenfor med generøse grænser og pålidelig adgang.",
   "go.faq.q3": "Er Go det samme som Zen?",
   "go.faq.q3": "Er Go det samme som Zen?",
   "go.faq.a3":
   "go.faq.a3":
-    "Nej. Zen er pay-as-you-go, mens Go starter ved $5 for den første måned, derefter $10/måned, med generøse grænser og pålidelig adgang til open source-modellerne GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 og MiniMax M2.7.",
+    "Nej. Zen er pay-as-you-go, mens Go starter ved $5 for den første måned, derefter $10/måned, med generøse grænser og pålidelig adgang til open source-modellerne GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash.",
   "go.faq.q4": "Hvad koster Go?",
   "go.faq.q4": "Hvad koster Go?",
   "go.faq.a4.p1.beforePricing": "Go koster",
   "go.faq.a4.p1.beforePricing": "Go koster",
   "go.faq.a4.p1.pricingLink": "$5 første måned",
   "go.faq.a4.p1.pricingLink": "$5 første måned",
@@ -351,7 +351,7 @@ export const dict = {
 
 
   "go.faq.q9": "Hvad er forskellen på gratis modeller og Go?",
   "go.faq.q9": "Hvad er forskellen på gratis modeller og Go?",
   "go.faq.a9":
   "go.faq.a9":
-    "Gratis modeller inkluderer Big Pickle plus salgsfremmende modeller tilgængelige på det tidspunkt, med en kvote på 200 forespørgsler/dag. Go inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 og MiniMax M2.7 med højere anmodningskvoter håndhævet over rullende vinduer (5-timers, ugentlig og månedlig), nogenlunde svarende til $12 pr. 5 timer, $30 pr. uge og $60 pr. måned (faktiske anmodningstal varierer efter model og brug).",
+    "Gratis modeller inkluderer Big Pickle plus salgsfremmende modeller tilgængelige på det tidspunkt, med en kvote på 200 forespørgsler/dag. Go inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash med højere anmodningskvoter håndhævet over rullende vinduer (5-timers, ugentlig og månedlig), nogenlunde svarende til $12 pr. 5 timer, $30 pr. uge og $60 pr. måned (faktiske anmodningstal varierer efter model og brug).",
 
 
   "zen.api.error.rateLimitExceeded": "Hastighedsgrænse overskredet. Prøv venligst igen senere.",
   "zen.api.error.rateLimitExceeded": "Hastighedsgrænse overskredet. Prøv venligst igen senere.",
   "zen.api.error.modelNotSupported": "Model {{model}} understøttes ikke",
   "zen.api.error.modelNotSupported": "Model {{model}} understøttes ikke",

+ 4 - 4
packages/console/app/src/i18n/de.ts

@@ -253,7 +253,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | Kostengünstige Coding-Modelle für alle",
   "go.title": "OpenCode Go | Kostengünstige Coding-Modelle für alle",
   "go.meta.description":
   "go.meta.description":
-    "Go beginnt bei $5 für den ersten Monat, danach $10/Monat, mit großzügigen 5-Stunden-Anfragelimits für GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 und MiniMax M2.7.",
+    "Go beginnt bei $5 für den ersten Monat, danach $10/Monat, mit großzügigen 5-Stunden-Anfragelimits für GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro und DeepSeek V4 Flash.",
   "go.hero.title": "Kostengünstige Coding-Modelle für alle",
   "go.hero.title": "Kostengünstige Coding-Modelle für alle",
   "go.hero.body":
   "go.hero.body":
     "Go bringt Agentic Coding zu Programmierern auf der ganzen Welt. Mit großzügigen Limits und zuverlässigem Zugang zu den leistungsfähigsten Open-Source-Modellen, damit du mit leistungsstarken Agenten entwickeln kannst, ohne dir Gedanken über Kosten oder Verfügbarkeit zu machen.",
     "Go bringt Agentic Coding zu Programmierern auf der ganzen Welt. Mit großzügigen Limits und zuverlässigem Zugang zu den leistungsfähigsten Open-Source-Modellen, damit du mit leistungsstarken Agenten entwickeln kannst, ohne dir Gedanken über Kosten oder Verfügbarkeit zu machen.",
@@ -304,7 +304,7 @@ export const dict = {
   "go.problem.item2": "Großzügige Limits und zuverlässiger Zugang",
   "go.problem.item2": "Großzügige Limits und zuverlässiger Zugang",
   "go.problem.item3": "Für so viele Programmierer wie möglich gebaut",
   "go.problem.item3": "Für so viele Programmierer wie möglich gebaut",
   "go.problem.item4":
   "go.problem.item4":
-    "Beinhaltet GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 und MiniMax M2.7",
+    "Beinhaltet GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro und DeepSeek V4 Flash",
   "go.how.title": "Wie Go funktioniert",
   "go.how.title": "Wie Go funktioniert",
   "go.how.body":
   "go.how.body":
     "Go beginnt bei $5 für den ersten Monat, danach $10/Monat. Du kannst es mit OpenCode oder jedem Agenten nutzen.",
     "Go beginnt bei $5 für den ersten Monat, danach $10/Monat. Du kannst es mit OpenCode oder jedem Agenten nutzen.",
@@ -330,7 +330,7 @@ export const dict = {
   "go.faq.a2": "Go umfasst die unten aufgeführten Modelle mit großzügigen Limits und zuverlässigem Zugriff.",
   "go.faq.a2": "Go umfasst die unten aufgeführten Modelle mit großzügigen Limits und zuverlässigem Zugriff.",
   "go.faq.q3": "Ist Go dasselbe wie Zen?",
   "go.faq.q3": "Ist Go dasselbe wie Zen?",
   "go.faq.a3":
   "go.faq.a3":
-    "Nein. Zen ist Pay-as-you-go, während Go bei $5 für den ersten Monat beginnt, danach $10/Monat, mit großzügigen Limits und zuverlässigem Zugang zu den Open-Source-Modellen GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 und MiniMax M2.7.",
+    "Nein. Zen ist Pay-as-you-go, während Go bei $5 für den ersten Monat beginnt, danach $10/Monat, mit großzügigen Limits und zuverlässigem Zugang zu den Open-Source-Modellen GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro und DeepSeek V4 Flash.",
   "go.faq.q4": "Wie viel kostet Go?",
   "go.faq.q4": "Wie viel kostet Go?",
   "go.faq.a4.p1.beforePricing": "Go kostet",
   "go.faq.a4.p1.beforePricing": "Go kostet",
   "go.faq.a4.p1.pricingLink": "$5 im ersten Monat",
   "go.faq.a4.p1.pricingLink": "$5 im ersten Monat",
@@ -354,7 +354,7 @@ export const dict = {
 
 
   "go.faq.q9": "Was ist der Unterschied zwischen kostenlosen Modellen und Go?",
   "go.faq.q9": "Was ist der Unterschied zwischen kostenlosen Modellen und Go?",
   "go.faq.a9":
   "go.faq.a9":
-    "Kostenlose Modelle beinhalten Big Pickle sowie Werbemodelle, die zum jeweiligen Zeitpunkt verfügbar sind, mit einem Kontingent von 200 Anfragen/Tag. Go beinhaltet GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 und MiniMax M2.7 mit höheren Anfragekontingenten, die über rollierende Zeitfenster (5 Stunden, wöchentlich und monatlich) durchgesetzt werden, grob äquivalent zu $12 pro 5 Stunden, $30 pro Woche und $60 pro Monat (tatsächliche Anfragezahlen variieren je nach Modell und Nutzung).",
+    "Kostenlose Modelle beinhalten Big Pickle sowie Werbemodelle, die zum jeweiligen Zeitpunkt verfügbar sind, mit einem Kontingent von 200 Anfragen/Tag. Go beinhaltet GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro und DeepSeek V4 Flash mit höheren Anfragekontingenten, die über rollierende Zeitfenster (5 Stunden, wöchentlich und monatlich) durchgesetzt werden, grob äquivalent zu $12 pro 5 Stunden, $30 pro Woche und $60 pro Monat (tatsächliche Anfragezahlen variieren je nach Modell und Nutzung).",
 
 
   "zen.api.error.rateLimitExceeded": "Ratenlimit überschritten. Bitte versuche es später erneut.",
   "zen.api.error.rateLimitExceeded": "Ratenlimit überschritten. Bitte versuche es später erneut.",
   "zen.api.error.modelNotSupported": "Modell {{model}} wird nicht unterstützt",
   "zen.api.error.modelNotSupported": "Modell {{model}} wird nicht unterstützt",

+ 4 - 4
packages/console/app/src/i18n/en.ts

@@ -248,7 +248,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | Low cost coding models for everyone",
   "go.title": "OpenCode Go | Low cost coding models for everyone",
   "go.meta.description":
   "go.meta.description":
-    "Go starts at $5 for your first month, then $10/month, with generous 5-hour request limits for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, and MiniMax M2.7.",
+    "Go starts at $5 for your first month, then $10/month, with generous 5-hour request limits for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash.",
   "go.banner.badge": "3x",
   "go.banner.badge": "3x",
   "go.banner.text": "Kimi K2.6 gets 3× usage limits through April 27",
   "go.banner.text": "Kimi K2.6 gets 3× usage limits through April 27",
   "go.hero.title": "Low cost coding models for everyone",
   "go.hero.title": "Low cost coding models for everyone",
@@ -298,7 +298,7 @@ export const dict = {
   "go.problem.item2": "Generous limits and reliable access",
   "go.problem.item2": "Generous limits and reliable access",
   "go.problem.item3": "Built for as many programmers as possible",
   "go.problem.item3": "Built for as many programmers as possible",
   "go.problem.item4":
   "go.problem.item4":
-    "Includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, and MiniMax M2.7",
+    "Includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash",
   "go.how.title": "How Go works",
   "go.how.title": "How Go works",
   "go.how.body": "Go starts at $5 for your first month, then $10/month. You can use it with OpenCode or any agent.",
   "go.how.body": "Go starts at $5 for your first month, then $10/month. You can use it with OpenCode or any agent.",
   "go.how.step1.title": "Create an account",
   "go.how.step1.title": "Create an account",
@@ -323,7 +323,7 @@ export const dict = {
   "go.faq.a2": "Go includes the models listed below, with generous limits and reliable access.",
   "go.faq.a2": "Go includes the models listed below, with generous limits and reliable access.",
   "go.faq.q3": "Is Go the same as Zen?",
   "go.faq.q3": "Is Go the same as Zen?",
   "go.faq.a3":
   "go.faq.a3":
-    "No. Zen is pay-as-you-go, while Go starts at $5 for your first month, then $10/month, with generous limits and reliable access to open-source models GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, and MiniMax M2.7.",
+    "No. Zen is pay-as-you-go, while Go starts at $5 for your first month, then $10/month, with generous limits and reliable access to open-source models GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash.",
   "go.faq.q4": "How much does Go cost?",
   "go.faq.q4": "How much does Go cost?",
   "go.faq.a4.p1.beforePricing": "Go costs",
   "go.faq.a4.p1.beforePricing": "Go costs",
   "go.faq.a4.p1.pricingLink": "$5 first month",
   "go.faq.a4.p1.pricingLink": "$5 first month",
@@ -347,7 +347,7 @@ export const dict = {
 
 
   "go.faq.q9": "What is the difference between free models and Go?",
   "go.faq.q9": "What is the difference between free models and Go?",
   "go.faq.a9":
   "go.faq.a9":
-    "Free models include Big Pickle plus promotional models available at the time, with a quota of 200 requests/day. Go includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, and MiniMax M2.7 with higher request quotas enforced across rolling windows (5-hour, weekly, and monthly), roughly equivalent to $12 per 5 hours, $30 per week, and $60 per month (actual request counts vary by model and usage).",
+    "Free models include Big Pickle plus promotional models available at the time, with a quota of 200 requests/day. Go includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash with higher request quotas enforced across rolling windows (5-hour, weekly, and monthly), roughly equivalent to $12 per 5 hours, $30 per week, and $60 per month (actual request counts vary by model and usage).",
 
 
   "zen.api.error.rateLimitExceeded": "Rate limit exceeded. Please try again later.",
   "zen.api.error.rateLimitExceeded": "Rate limit exceeded. Please try again later.",
   "zen.api.error.modelNotSupported": "Model {{model}} not supported",
   "zen.api.error.modelNotSupported": "Model {{model}} not supported",

+ 4 - 4
packages/console/app/src/i18n/es.ts

@@ -254,7 +254,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | Modelos de programación de bajo coste para todos",
   "go.title": "OpenCode Go | Modelos de programación de bajo coste para todos",
   "go.meta.description":
   "go.meta.description":
-    "Go comienza en $5 el primer mes, luego 10 $/mes, con generosos límites de solicitudes de 5 horas para GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 y MiniMax M2.7.",
+    "Go comienza en $5 el primer mes, luego 10 $/mes, con generosos límites de solicitudes de 5 horas para GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro y DeepSeek V4 Flash.",
   "go.hero.title": "Modelos de programación de bajo coste para todos",
   "go.hero.title": "Modelos de programación de bajo coste para todos",
   "go.hero.body":
   "go.hero.body":
     "Go lleva la programación agéntica a programadores de todo el mundo. Ofrece límites generosos y acceso fiable a los modelos de código abierto más capaces, para que puedas crear con agentes potentes sin preocuparte por el coste o la disponibilidad.",
     "Go lleva la programación agéntica a programadores de todo el mundo. Ofrece límites generosos y acceso fiable a los modelos de código abierto más capaces, para que puedas crear con agentes potentes sin preocuparte por el coste o la disponibilidad.",
@@ -306,7 +306,7 @@ export const dict = {
   "go.problem.item2": "Límites generosos y acceso fiable",
   "go.problem.item2": "Límites generosos y acceso fiable",
   "go.problem.item3": "Creado para tantos programadores como sea posible",
   "go.problem.item3": "Creado para tantos programadores como sea posible",
   "go.problem.item4":
   "go.problem.item4":
-    "Incluye GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 y MiniMax M2.7",
+    "Incluye GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro y DeepSeek V4 Flash",
   "go.how.title": "Cómo funciona Go",
   "go.how.title": "Cómo funciona Go",
   "go.how.body": "Go comienza en $5 el primer mes, luego 10 $/mes. Puedes usarlo con OpenCode o cualquier agente.",
   "go.how.body": "Go comienza en $5 el primer mes, luego 10 $/mes. Puedes usarlo con OpenCode o cualquier agente.",
   "go.how.step1.title": "Crear una cuenta",
   "go.how.step1.title": "Crear una cuenta",
@@ -331,7 +331,7 @@ export const dict = {
   "go.faq.a2": "Go incluye los modelos que se indican abajo, con límites generosos y acceso confiable.",
   "go.faq.a2": "Go incluye los modelos que se indican abajo, con límites generosos y acceso confiable.",
   "go.faq.q3": "¿Es Go lo mismo que Zen?",
   "go.faq.q3": "¿Es Go lo mismo que Zen?",
   "go.faq.a3":
   "go.faq.a3":
-    "No. Zen es pago por uso, mientras que Go comienza en $5 el primer mes, luego 10 $/mes, con límites generosos y acceso fiable a los modelos de código abierto GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 y MiniMax M2.7.",
+    "No. Zen es pago por uso, mientras que Go comienza en $5 el primer mes, luego 10 $/mes, con límites generosos y acceso fiable a los modelos de código abierto GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro y DeepSeek V4 Flash.",
   "go.faq.q4": "¿Cuánto cuesta Go?",
   "go.faq.q4": "¿Cuánto cuesta Go?",
   "go.faq.a4.p1.beforePricing": "Go cuesta",
   "go.faq.a4.p1.beforePricing": "Go cuesta",
   "go.faq.a4.p1.pricingLink": "$5 el primer mes",
   "go.faq.a4.p1.pricingLink": "$5 el primer mes",
@@ -355,7 +355,7 @@ export const dict = {
 
 
   "go.faq.q9": "¿Cuál es la diferencia entre los modelos gratuitos y Go?",
   "go.faq.q9": "¿Cuál es la diferencia entre los modelos gratuitos y Go?",
   "go.faq.a9":
   "go.faq.a9":
-    "Los modelos gratuitos incluyen Big Pickle más modelos promocionales disponibles en el momento, con una cuota de 200 solicitudes/día. Go incluye GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 y MiniMax M2.7 con cuotas de solicitud más altas aplicadas a través de ventanas móviles (5 horas, semanal y mensual), aproximadamente equivalente a 12 $ por 5 horas, 30 $ por semana y 60 $ por mes (los recuentos reales de solicitudes varían según el modelo y el uso).",
+    "Los modelos gratuitos incluyen Big Pickle más modelos promocionales disponibles en el momento, con una cuota de 200 solicitudes/día. Go incluye GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro y DeepSeek V4 Flash con cuotas de solicitud más altas aplicadas a través de ventanas móviles (5 horas, semanal y mensual), aproximadamente equivalente a 12 $ por 5 horas, 30 $ por semana y 60 $ por mes (los recuentos reales de solicitudes varían según el modelo y el uso).",
 
 
   "zen.api.error.rateLimitExceeded": "Límite de tasa excedido. Por favor, inténtalo de nuevo más tarde.",
   "zen.api.error.rateLimitExceeded": "Límite de tasa excedido. Por favor, inténtalo de nuevo más tarde.",
   "zen.api.error.modelNotSupported": "Modelo {{model}} no soportado",
   "zen.api.error.modelNotSupported": "Modelo {{model}} no soportado",

+ 4 - 4
packages/console/app/src/i18n/fr.ts

@@ -255,7 +255,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | Modèles de code à faible coût pour tous",
   "go.title": "OpenCode Go | Modèles de code à faible coût pour tous",
   "go.meta.description":
   "go.meta.description":
-    "Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites de requêtes généreuses sur 5 heures pour GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 et MiniMax M2.7.",
+    "Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites de requêtes généreuses sur 5 heures pour GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro et DeepSeek V4 Flash.",
   "go.hero.title": "Modèles de code à faible coût pour tous",
   "go.hero.title": "Modèles de code à faible coût pour tous",
   "go.hero.body":
   "go.hero.body":
     "Go apporte le codage agentique aux programmeurs du monde entier. Offrant des limites généreuses et un accès fiable aux modèles open source les plus capables, pour que vous puissiez construire avec des agents puissants sans vous soucier du coût ou de la disponibilité.",
     "Go apporte le codage agentique aux programmeurs du monde entier. Offrant des limites généreuses et un accès fiable aux modèles open source les plus capables, pour que vous puissiez construire avec des agents puissants sans vous soucier du coût ou de la disponibilité.",
@@ -306,7 +306,7 @@ export const dict = {
   "go.problem.item2": "Limites généreuses et accès fiable",
   "go.problem.item2": "Limites généreuses et accès fiable",
   "go.problem.item3": "Conçu pour autant de programmeurs que possible",
   "go.problem.item3": "Conçu pour autant de programmeurs que possible",
   "go.problem.item4":
   "go.problem.item4":
-    "Inclut GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 et MiniMax M2.7",
+    "Inclut GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro et DeepSeek V4 Flash",
   "go.how.title": "Comment fonctionne Go",
   "go.how.title": "Comment fonctionne Go",
   "go.how.body":
   "go.how.body":
     "Go commence à $5 pour le premier mois, puis 10 $/mois. Vous pouvez l'utiliser avec OpenCode ou n'importe quel agent.",
     "Go commence à $5 pour le premier mois, puis 10 $/mois. Vous pouvez l'utiliser avec OpenCode ou n'importe quel agent.",
@@ -332,7 +332,7 @@ export const dict = {
   "go.faq.a2": "Go inclut les modèles ci-dessous, avec des limites généreuses et un accès fiable.",
   "go.faq.a2": "Go inclut les modèles ci-dessous, avec des limites généreuses et un accès fiable.",
   "go.faq.q3": "Est-ce que Go est la même chose que Zen ?",
   "go.faq.q3": "Est-ce que Go est la même chose que Zen ?",
   "go.faq.a3":
   "go.faq.a3":
-    "Non. Zen est un paiement à l'utilisation, tandis que Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites généreuses et un accès fiable aux modèles open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 et MiniMax M2.7.",
+    "Non. Zen est un paiement à l'utilisation, tandis que Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites généreuses et un accès fiable aux modèles open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro et DeepSeek V4 Flash.",
   "go.faq.q4": "Combien coûte Go ?",
   "go.faq.q4": "Combien coûte Go ?",
   "go.faq.a4.p1.beforePricing": "Go coûte",
   "go.faq.a4.p1.beforePricing": "Go coûte",
   "go.faq.a4.p1.pricingLink": "$5 le premier mois",
   "go.faq.a4.p1.pricingLink": "$5 le premier mois",
@@ -355,7 +355,7 @@ export const dict = {
     "Oui, vous pouvez utiliser Go avec n'importe quel agent. Suivez les instructions de configuration dans votre agent de code préféré.",
     "Oui, vous pouvez utiliser Go avec n'importe quel agent. Suivez les instructions de configuration dans votre agent de code préféré.",
   "go.faq.q9": "Quelle est la différence entre les modèles gratuits et Go ?",
   "go.faq.q9": "Quelle est la différence entre les modèles gratuits et Go ?",
   "go.faq.a9":
   "go.faq.a9":
-    "Les modèles gratuits incluent Big Pickle ainsi que des modèles promotionnels disponibles à ce moment-là, avec un quota de 200 requêtes/jour. Go inclut GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 et MiniMax M2.7 avec des quotas de requêtes plus élevés appliqués sur des fenêtres glissantes (5 heures, hebdomadaire et mensuelle), à peu près équivalent à 12 $ par 5 heures, 30 $ par semaine et 60 $ par mois (le nombre réel de requêtes varie selon le modèle et l'utilisation).",
+    "Les modèles gratuits incluent Big Pickle ainsi que des modèles promotionnels disponibles à ce moment-là, avec un quota de 200 requêtes/jour. Go inclut GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro et DeepSeek V4 Flash avec des quotas de requêtes plus élevés appliqués sur des fenêtres glissantes (5 heures, hebdomadaire et mensuelle), à peu près équivalent à 12 $ par 5 heures, 30 $ par semaine et 60 $ par mois (le nombre réel de requêtes varie selon le modèle et l'utilisation).",
 
 
   "zen.api.error.rateLimitExceeded": "Limite de débit dépassée. Veuillez réessayer plus tard.",
   "zen.api.error.rateLimitExceeded": "Limite de débit dépassée. Veuillez réessayer plus tard.",
   "zen.api.error.modelNotSupported": "Modèle {{model}} non pris en charge",
   "zen.api.error.modelNotSupported": "Modèle {{model}} non pris en charge",

+ 4 - 4
packages/console/app/src/i18n/it.ts

@@ -251,7 +251,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | Modelli di coding a basso costo per tutti",
   "go.title": "OpenCode Go | Modelli di coding a basso costo per tutti",
   "go.meta.description":
   "go.meta.description":
-    "Go inizia a $5 per il primo mese, poi $10/mese, con generosi limiti di richiesta di 5 ore per GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7.",
+    "Go inizia a $5 per il primo mese, poi $10/mese, con generosi limiti di richiesta di 5 ore per GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash.",
   "go.hero.title": "Modelli di coding a basso costo per tutti",
   "go.hero.title": "Modelli di coding a basso costo per tutti",
   "go.hero.body":
   "go.hero.body":
     "Go porta il coding agentico ai programmatori di tutto il mondo. Offrendo limiti generosi e un accesso affidabile ai modelli open source più capaci, in modo da poter costruire con agenti potenti senza preoccuparsi dei costi o della disponibilità.",
     "Go porta il coding agentico ai programmatori di tutto il mondo. Offrendo limiti generosi e un accesso affidabile ai modelli open source più capaci, in modo da poter costruire con agenti potenti senza preoccuparsi dei costi o della disponibilità.",
@@ -302,7 +302,7 @@ export const dict = {
   "go.problem.item2": "Limiti generosi e accesso affidabile",
   "go.problem.item2": "Limiti generosi e accesso affidabile",
   "go.problem.item3": "Costruito per il maggior numero possibile di programmatori",
   "go.problem.item3": "Costruito per il maggior numero possibile di programmatori",
   "go.problem.item4":
   "go.problem.item4":
-    "Include GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7",
+    "Include GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash",
   "go.how.title": "Come funziona Go",
   "go.how.title": "Come funziona Go",
   "go.how.body": "Go inizia a $5 per il primo mese, poi $10/mese. Puoi usarlo con OpenCode o qualsiasi agente.",
   "go.how.body": "Go inizia a $5 per il primo mese, poi $10/mese. Puoi usarlo con OpenCode o qualsiasi agente.",
   "go.how.step1.title": "Crea un account",
   "go.how.step1.title": "Crea un account",
@@ -327,7 +327,7 @@ export const dict = {
   "go.faq.a2": "Go include i modelli elencati di seguito, con limiti generosi e accesso affidabile.",
   "go.faq.a2": "Go include i modelli elencati di seguito, con limiti generosi e accesso affidabile.",
   "go.faq.q3": "Go è lo stesso di Zen?",
   "go.faq.q3": "Go è lo stesso di Zen?",
   "go.faq.a3":
   "go.faq.a3":
-    "No. Zen è a consumo, mentre Go inizia a $5 per il primo mese, poi $10/mese, con limiti generosi e accesso affidabile ai modelli open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7.",
+    "No. Zen è a consumo, mentre Go inizia a $5 per il primo mese, poi $10/mese, con limiti generosi e accesso affidabile ai modelli open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash.",
   "go.faq.q4": "Quanto costa Go?",
   "go.faq.q4": "Quanto costa Go?",
   "go.faq.a4.p1.beforePricing": "Go costa",
   "go.faq.a4.p1.beforePricing": "Go costa",
   "go.faq.a4.p1.pricingLink": "$5 il primo mese",
   "go.faq.a4.p1.pricingLink": "$5 il primo mese",
@@ -351,7 +351,7 @@ export const dict = {
 
 
   "go.faq.q9": "Qual è la differenza tra i modelli gratuiti e Go?",
   "go.faq.q9": "Qual è la differenza tra i modelli gratuiti e Go?",
   "go.faq.a9":
   "go.faq.a9":
-    "I modelli gratuiti includono Big Pickle più modelli promozionali disponibili al momento, con una quota di 200 richieste/giorno. Go include GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7 con quote di richiesta più elevate applicate su finestre mobili (5 ore, settimanale e mensile), approssimativamente equivalenti a $12 ogni 5 ore, $30 a settimana e $60 al mese (il conteggio effettivo delle richieste varia in base al modello e all'utilizzo).",
+    "I modelli gratuiti includono Big Pickle più modelli promozionali disponibili al momento, con una quota di 200 richieste/giorno. Go include GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash con quote di richiesta più elevate applicate su finestre mobili (5 ore, settimanale e mensile), approssimativamente equivalenti a $12 ogni 5 ore, $30 a settimana e $60 al mese (il conteggio effettivo delle richieste varia in base al modello e all'utilizzo).",
 
 
   "zen.api.error.rateLimitExceeded": "Limite di richieste superato. Riprova più tardi.",
   "zen.api.error.rateLimitExceeded": "Limite di richieste superato. Riprova più tardi.",
   "zen.api.error.modelNotSupported": "Modello {{model}} non supportato",
   "zen.api.error.modelNotSupported": "Modello {{model}} non supportato",

+ 4 - 4
packages/console/app/src/i18n/ja.ts

@@ -250,7 +250,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | すべての人のための低価格なコーディングモデル",
   "go.title": "OpenCode Go | すべての人のための低価格なコーディングモデル",
   "go.meta.description":
   "go.meta.description":
-    "Goは最初の月$5、その後$10/月で、GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2-Pro、MiMo-V2-Omni、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.5 Plus、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7に対して5時間のゆとりあるリクエスト上限があります。",
+    "Goは最初の月$5、その後$10/月で、GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2-Pro、MiMo-V2-Omni、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.5 Plus、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro、DeepSeek V4 Flashに対して5時間のゆとりあるリクエスト上限があります。",
   "go.hero.title": "すべての人のための低価格なコーディングモデル",
   "go.hero.title": "すべての人のための低価格なコーディングモデル",
   "go.hero.body":
   "go.hero.body":
     "Goは、世界中のプログラマーにエージェント型コーディングをもたらします。最も高性能なオープンソースモデルへの十分な制限と安定したアクセスを提供し、コストや可用性を気にすることなく強力なエージェントで構築できます。",
     "Goは、世界中のプログラマーにエージェント型コーディングをもたらします。最も高性能なオープンソースモデルへの十分な制限と安定したアクセスを提供し、コストや可用性を気にすることなく強力なエージェントで構築できます。",
@@ -302,7 +302,7 @@ export const dict = {
   "go.problem.item2": "十分な制限と安定したアクセス",
   "go.problem.item2": "十分な制限と安定したアクセス",
   "go.problem.item3": "できるだけ多くのプログラマーのために構築",
   "go.problem.item3": "できるだけ多くのプログラマーのために構築",
   "go.problem.item4":
   "go.problem.item4":
-    "GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2-Pro、MiMo-V2-Omni、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.5 Plus、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7を含む",
+    "GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2-Pro、MiMo-V2-Omni、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.5 Plus、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro、DeepSeek V4 Flashを含む",
   "go.how.title": "Goの仕組み",
   "go.how.title": "Goの仕組み",
   "go.how.body": "Goは最初の月$5、その後$10/月で始まります。OpenCodeまたは任意のエージェントで使えます。",
   "go.how.body": "Goは最初の月$5、その後$10/月で始まります。OpenCodeまたは任意のエージェントで使えます。",
   "go.how.step1.title": "アカウントを作成",
   "go.how.step1.title": "アカウントを作成",
@@ -327,7 +327,7 @@ export const dict = {
   "go.faq.a2": "Go には、十分な利用上限と安定したアクセスを備えた、以下のモデルが含まれます。",
   "go.faq.a2": "Go には、十分な利用上限と安定したアクセスを備えた、以下のモデルが含まれます。",
   "go.faq.q3": "GoはZenと同じですか?",
   "go.faq.q3": "GoはZenと同じですか?",
   "go.faq.a3":
   "go.faq.a3":
-    "いいえ。Zenは従量課金制ですが、Goは最初の月$5、その後$10/月で始まり、GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2-Pro、MiMo-V2-Omni、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.5 Plus、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7のオープンソースモデルに対して、ゆとりある上限と信頼できるアクセスを提供します。",
+    "いいえ。Zenは従量課金制ですが、Goは最初の月$5、その後$10/月で始まり、GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2-Pro、MiMo-V2-Omni、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.5 Plus、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro、DeepSeek V4 Flashのオープンソースモデルに対して、ゆとりある上限と信頼できるアクセスを提供します。",
   "go.faq.q4": "Goの料金は?",
   "go.faq.q4": "Goの料金は?",
   "go.faq.a4.p1.beforePricing": "Goは",
   "go.faq.a4.p1.beforePricing": "Goは",
   "go.faq.a4.p1.pricingLink": "最初の月$5",
   "go.faq.a4.p1.pricingLink": "最初の月$5",
@@ -351,7 +351,7 @@ export const dict = {
 
 
   "go.faq.q9": "無料モデルとGoの違いは何ですか?",
   "go.faq.q9": "無料モデルとGoの違いは何ですか?",
   "go.faq.a9":
   "go.faq.a9":
-    "無料モデルにはBig Pickleと、その時点で利用可能なプロモーションモデルが含まれ、1日200リクエストの制限があります。GoにはGLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2-Pro、MiMo-V2-Omni、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.5 Plus、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7が含まれ、ローリングウィンドウ(5時間、週間、月間)全体でより高いリクエスト制限が適用されます。これは概算で5時間あたり$12、週間$30、月間$60相当です(実際のリクエスト数はモデルと使用状況により異なります)。",
+    "無料モデルにはBig Pickleと、その時点で利用可能なプロモーションモデルが含まれ、1日200リクエストの制限があります。GoにはGLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2-Pro、MiMo-V2-Omni、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.5 Plus、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro、DeepSeek V4 Flashが含まれ、ローリングウィンドウ(5時間、週間、月間)全体でより高いリクエスト制限が適用されます。これは概算で5時間あたり$12、週間$30、月間$60相当です(実際のリクエスト数はモデルと使用状況により異なります)。",
 
 
   "zen.api.error.rateLimitExceeded": "レート制限を超えました。後でもう一度お試しください。",
   "zen.api.error.rateLimitExceeded": "レート制限を超えました。後でもう一度お試しください。",
   "zen.api.error.modelNotSupported": "モデル {{model}} はサポートされていません",
   "zen.api.error.modelNotSupported": "モデル {{model}} はサポートされていません",

+ 4 - 4
packages/console/app/src/i18n/ko.ts

@@ -247,7 +247,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | 모두를 위한 저비용 코딩 모델",
   "go.title": "OpenCode Go | 모두를 위한 저비용 코딩 모델",
   "go.meta.description":
   "go.meta.description":
-    "Go는 첫 달 $5, 이후 $10/월로 시작하며, GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7에 대해 넉넉한 5시간 요청 한도를 제공합니다.",
+    "Go는 첫 달 $5, 이후 $10/월로 시작하며, GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, DeepSeek V4 Flash에 대해 넉넉한 5시간 요청 한도를 제공합니다.",
   "go.hero.title": "모두를 위한 저비용 코딩 모델",
   "go.hero.title": "모두를 위한 저비용 코딩 모델",
   "go.hero.body":
   "go.hero.body":
     "Go는 전 세계 프로그래머들에게 에이전트 코딩을 제공합니다. 가장 유능한 오픈 소스 모델에 대한 넉넉한 한도와 안정적인 액세스를 제공하므로, 비용이나 가용성 걱정 없이 강력한 에이전트로 빌드할 수 있습니다.",
     "Go는 전 세계 프로그래머들에게 에이전트 코딩을 제공합니다. 가장 유능한 오픈 소스 모델에 대한 넉넉한 한도와 안정적인 액세스를 제공하므로, 비용이나 가용성 걱정 없이 강력한 에이전트로 빌드할 수 있습니다.",
@@ -299,7 +299,7 @@ export const dict = {
   "go.problem.item2": "넉넉한 한도와 안정적인 액세스",
   "go.problem.item2": "넉넉한 한도와 안정적인 액세스",
   "go.problem.item3": "가능한 한 많은 프로그래머를 위해 제작됨",
   "go.problem.item3": "가능한 한 많은 프로그래머를 위해 제작됨",
   "go.problem.item4":
   "go.problem.item4":
-    "GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7 포함",
+    "GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, DeepSeek V4 Flash 포함",
   "go.how.title": "Go 작동 방식",
   "go.how.title": "Go 작동 방식",
   "go.how.body": "Go는 첫 달 $5, 이후 $10/월로 시작합니다. OpenCode 또는 어떤 에이전트와도 함께 사용할 수 있습니다.",
   "go.how.body": "Go는 첫 달 $5, 이후 $10/월로 시작합니다. OpenCode 또는 어떤 에이전트와도 함께 사용할 수 있습니다.",
   "go.how.step1.title": "계정 생성",
   "go.how.step1.title": "계정 생성",
@@ -323,7 +323,7 @@ export const dict = {
   "go.faq.a2": "Go에는 넉넉한 한도와 안정적인 액세스를 제공하는 아래 모델이 포함됩니다.",
   "go.faq.a2": "Go에는 넉넉한 한도와 안정적인 액세스를 제공하는 아래 모델이 포함됩니다.",
   "go.faq.q3": "Go는 Zen과 같은가요?",
   "go.faq.q3": "Go는 Zen과 같은가요?",
   "go.faq.a3":
   "go.faq.a3":
-    "아니요. Zen은 종량제인 반면, Go는 첫 달 $5, 이후 $10/월로 시작하며, GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7 오픈 소스 모델에 대한 넉넉한 한도와 안정적인 액세스를 제공합니다.",
+    "아니요. Zen은 종량제인 반면, Go는 첫 달 $5, 이후 $10/월로 시작하며, GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, DeepSeek V4 Flash 오픈 소스 모델에 대한 넉넉한 한도와 안정적인 액세스를 제공합니다.",
   "go.faq.q4": "Go 비용은 얼마인가요?",
   "go.faq.q4": "Go 비용은 얼마인가요?",
   "go.faq.a4.p1.beforePricing": "Go 비용은",
   "go.faq.a4.p1.beforePricing": "Go 비용은",
   "go.faq.a4.p1.pricingLink": "첫 달 $5",
   "go.faq.a4.p1.pricingLink": "첫 달 $5",
@@ -346,7 +346,7 @@ export const dict = {
 
 
   "go.faq.q9": "무료 모델과 Go의 차이점은 무엇인가요?",
   "go.faq.q9": "무료 모델과 Go의 차이점은 무엇인가요?",
   "go.faq.a9":
   "go.faq.a9":
-    "무료 모델에는 Big Pickle과 당시 사용 가능한 프로모션 모델이 포함되며, 하루 200회 요청 할당량이 적용됩니다. Go는 GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7를 포함하며, 롤링 윈도우(5시간, 주간, 월간)에 걸쳐 더 높은 요청 할당량을 적용합니다. 이는 대략 5시간당 $12, 주당 $30, 월 $60에 해당합니다(실제 요청 수는 모델 및 사용량에 따라 다름).",
+    "무료 모델에는 Big Pickle과 당시 사용 가능한 프로모션 모델이 포함되며, 하루 200회 요청 할당량이 적용됩니다. Go는 GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, DeepSeek V4 Flash를 포함하며, 롤링 윈도우(5시간, 주간, 월간)에 걸쳐 더 높은 요청 할당량을 적용합니다. 이는 대략 5시간당 $12, 주당 $30, 월 $60에 해당합니다(실제 요청 수는 모델 및 사용량에 따라 다름).",
 
 
   "zen.api.error.rateLimitExceeded": "속도 제한을 초과했습니다. 나중에 다시 시도해 주세요.",
   "zen.api.error.rateLimitExceeded": "속도 제한을 초과했습니다. 나중에 다시 시도해 주세요.",
   "zen.api.error.modelNotSupported": "{{model}} 모델은 지원되지 않습니다",
   "zen.api.error.modelNotSupported": "{{model}} 모델은 지원되지 않습니다",

+ 4 - 4
packages/console/app/src/i18n/no.ts

@@ -251,7 +251,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | Rimelige kodemodeller for alle",
   "go.title": "OpenCode Go | Rimelige kodemodeller for alle",
   "go.meta.description":
   "go.meta.description":
-    "Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse 5-timers forespørselsgrenser for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 og MiniMax M2.7.",
+    "Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse 5-timers forespørselsgrenser for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash.",
   "go.hero.title": "Rimelige kodemodeller for alle",
   "go.hero.title": "Rimelige kodemodeller for alle",
   "go.hero.body":
   "go.hero.body":
     "Go bringer agent-koding til programmerere over hele verden. Med rause grenser og pålitelig tilgang til de mest kapable åpen kildekode-modellene, kan du bygge med kraftige agenter uten å bekymre deg for kostnader eller tilgjengelighet.",
     "Go bringer agent-koding til programmerere over hele verden. Med rause grenser og pålitelig tilgang til de mest kapable åpen kildekode-modellene, kan du bygge med kraftige agenter uten å bekymre deg for kostnader eller tilgjengelighet.",
@@ -302,7 +302,7 @@ export const dict = {
   "go.problem.item2": "Rause grenser og pålitelig tilgang",
   "go.problem.item2": "Rause grenser og pålitelig tilgang",
   "go.problem.item3": "Bygget for så mange programmerere som mulig",
   "go.problem.item3": "Bygget for så mange programmerere som mulig",
   "go.problem.item4":
   "go.problem.item4":
-    "Inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 og MiniMax M2.7",
+    "Inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash",
   "go.how.title": "Hvordan Go fungerer",
   "go.how.title": "Hvordan Go fungerer",
   "go.how.body":
   "go.how.body":
     "Go starter på $5 for den første måneden, deretter $10/måned. Du kan bruke det med OpenCode eller hvilken som helst agent.",
     "Go starter på $5 for den første måneden, deretter $10/måned. Du kan bruke det med OpenCode eller hvilken som helst agent.",
@@ -328,7 +328,7 @@ export const dict = {
   "go.faq.a2": "Go inkluderer modellene nedenfor, med høye grenser og pålitelig tilgang.",
   "go.faq.a2": "Go inkluderer modellene nedenfor, med høye grenser og pålitelig tilgang.",
   "go.faq.q3": "Er Go det samme som Zen?",
   "go.faq.q3": "Er Go det samme som Zen?",
   "go.faq.a3":
   "go.faq.a3":
-    "Nei. Zen er betaling etter bruk, mens Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse grenser og pålitelig tilgang til åpen kildekode-modellene GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 og MiniMax M2.7.",
+    "Nei. Zen er betaling etter bruk, mens Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse grenser og pålitelig tilgang til åpen kildekode-modellene GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash.",
   "go.faq.q4": "Hva koster Go?",
   "go.faq.q4": "Hva koster Go?",
   "go.faq.a4.p1.beforePricing": "Go koster",
   "go.faq.a4.p1.beforePricing": "Go koster",
   "go.faq.a4.p1.pricingLink": "$5 første måned",
   "go.faq.a4.p1.pricingLink": "$5 første måned",
@@ -352,7 +352,7 @@ export const dict = {
 
 
   "go.faq.q9": "Hva er forskjellen mellom gratis modeller og Go?",
   "go.faq.q9": "Hva er forskjellen mellom gratis modeller og Go?",
   "go.faq.a9":
   "go.faq.a9":
-    "Gratis modeller inkluderer Big Pickle pluss kampanjemodeller tilgjengelig på det tidspunktet, med en kvote på 200 forespørsler/dag. Go inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 og MiniMax M2.7 med høyere kvoter håndhevet over rullerende vinduer (5 timer, ukentlig og månedlig), omtrent tilsvarende $12 per 5 timer, $30 per uke og $60 per måned (faktiske forespørselsantall varierer etter modell og bruk).",
+    "Gratis modeller inkluderer Big Pickle pluss kampanjemodeller tilgjengelig på det tidspunktet, med en kvote på 200 forespørsler/dag. Go inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash med høyere kvoter håndhevet over rullerende vinduer (5 timer, ukentlig og månedlig), omtrent tilsvarende $12 per 5 timer, $30 per uke og $60 per måned (faktiske forespørselsantall varierer etter modell og bruk).",
 
 
   "zen.api.error.rateLimitExceeded": "Rate limit overskredet. Vennligst prøv igjen senere.",
   "zen.api.error.rateLimitExceeded": "Rate limit overskredet. Vennligst prøv igjen senere.",
   "zen.api.error.modelNotSupported": "Modell {{model}} støttes ikke",
   "zen.api.error.modelNotSupported": "Modell {{model}} støttes ikke",

+ 4 - 4
packages/console/app/src/i18n/pl.ts

@@ -252,7 +252,7 @@ export const dict = {
 
 
   "go.title": "OpenCode Go | Niskokosztowe modele do kodowania dla każdego",
   "go.title": "OpenCode Go | Niskokosztowe modele do kodowania dla każdego",
   "go.meta.description":
   "go.meta.description":
-    "Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc, z hojnymi 5-godzinnymi limitami zapytań dla GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 i MiniMax M2.7.",
+    "Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc, z hojnymi 5-godzinnymi limitami zapytań dla GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro i DeepSeek V4 Flash.",
   "go.hero.title": "Niskokosztowe modele do kodowania dla każdego",
   "go.hero.title": "Niskokosztowe modele do kodowania dla każdego",
   "go.hero.body":
   "go.hero.body":
     "Go udostępnia programowanie z agentami programistom na całym świecie. Oferuje hojne limity i niezawodny dostęp do najzdolniejszych modeli open source, dzięki czemu możesz budować za pomocą potężnych agentów, nie martwiąc się o koszty czy dostępność.",
     "Go udostępnia programowanie z agentami programistom na całym świecie. Oferuje hojne limity i niezawodny dostęp do najzdolniejszych modeli open source, dzięki czemu możesz budować za pomocą potężnych agentów, nie martwiąc się o koszty czy dostępność.",
@@ -303,7 +303,7 @@ export const dict = {
   "go.problem.item2": "Hojne limity i niezawodny dostęp",
   "go.problem.item2": "Hojne limity i niezawodny dostęp",
   "go.problem.item3": "Stworzony dla jak największej liczby programistów",
   "go.problem.item3": "Stworzony dla jak największej liczby programistów",
   "go.problem.item4":
   "go.problem.item4":
-    "Zawiera GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 i MiniMax M2.7",
+    "Zawiera GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro i DeepSeek V4 Flash",
   "go.how.title": "Jak działa Go",
   "go.how.title": "Jak działa Go",
   "go.how.body":
   "go.how.body":
     "Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc. Możesz go używać z OpenCode lub dowolnym agentem.",
     "Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc. Możesz go używać z OpenCode lub dowolnym agentem.",
@@ -329,7 +329,7 @@ export const dict = {
   "go.faq.a2": "Go obejmuje poniższe modele z wysokimi limitami i niezawodnym dostępem.",
   "go.faq.a2": "Go obejmuje poniższe modele z wysokimi limitami i niezawodnym dostępem.",
   "go.faq.q3": "Czy Go to to samo co Zen?",
   "go.faq.q3": "Czy Go to to samo co Zen?",
   "go.faq.a3":
   "go.faq.a3":
-    "Nie. Zen to model płatności za użycie, podczas gdy Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc, z hojnymi limitami i niezawodnym dostępem do modeli open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 i MiniMax M2.7.",
+    "Nie. Zen to model płatności za użycie, podczas gdy Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc, z hojnymi limitami i niezawodnym dostępem do modeli open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro i DeepSeek V4 Flash.",
   "go.faq.q4": "Ile kosztuje Go?",
   "go.faq.q4": "Ile kosztuje Go?",
   "go.faq.a4.p1.beforePricing": "Go kosztuje",
   "go.faq.a4.p1.beforePricing": "Go kosztuje",
   "go.faq.a4.p1.pricingLink": "$5 za pierwszy miesiąc",
   "go.faq.a4.p1.pricingLink": "$5 za pierwszy miesiąc",
@@ -353,7 +353,7 @@ export const dict = {
 
 
   "go.faq.q9": "Jaka jest różnica między darmowymi modelami a Go?",
   "go.faq.q9": "Jaka jest różnica między darmowymi modelami a Go?",
   "go.faq.a9":
   "go.faq.a9":
-    "Darmowe modele obejmują Big Pickle oraz modele promocyjne dostępne w danym momencie, z limitem 200 zapytań/dzień. Go zawiera GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 i MiniMax M2.7 z wyższymi limitami zapytań egzekwowanymi w oknach kroczących (5-godzinnych, tygodniowych i miesięcznych), w przybliżeniu równoważnymi $12 na 5 godzin, $30 tygodniowo i $60 miesięcznie (rzeczywista liczba zapytań zależy od modelu i użycia).",
+    "Darmowe modele obejmują Big Pickle oraz modele promocyjne dostępne w danym momencie, z limitem 200 zapytań/dzień. Go zawiera GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro i DeepSeek V4 Flash z wyższymi limitami zapytań egzekwowanymi w oknach kroczących (5-godzinnych, tygodniowych i miesięcznych), w przybliżeniu równoważnymi $12 na 5 godzin, $30 tygodniowo i $60 miesięcznie (rzeczywista liczba zapytań zależy od modelu i użycia).",
 
 
   "zen.api.error.rateLimitExceeded": "Przekroczono limit zapytań. Spróbuj ponownie później.",
   "zen.api.error.rateLimitExceeded": "Przekroczono limit zapytań. Spróbuj ponownie później.",
   "zen.api.error.modelNotSupported": "Model {{model}} nie jest obsługiwany",
   "zen.api.error.modelNotSupported": "Model {{model}} nie jest obsługiwany",

Някои файлове не бяха показани, защото твърде много файлове са промени