diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td
index 412716a18dd8..4be318d51892 100644
--- a/.github/VOUCHED.td
+++ b/.github/VOUCHED.td
@@ -8,6 +8,7 @@
# - Denounce with minus prefix: -username or -platform:username.
# - Optional details after a space following the handle.
adamdotdevin
+-agusbasari29 AI PR slop
ariane-emory
-florianleibert
fwang
diff --git a/.github/workflows/docs-locale-sync.yml b/.github/workflows/docs-locale-sync.yml
index 8cd0cc52e2a2..1aafc5d1e3b1 100644
--- a/.github/workflows/docs-locale-sync.yml
+++ b/.github/workflows/docs-locale-sync.yml
@@ -12,13 +12,14 @@ jobs:
if: github.actor != 'opencode-agent[bot]'
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
- id-token: write
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
+ persist-credentials: false
fetch-depth: 0
+ ref: ${{ github.ref_name }}
- name: Setup Bun
uses: ./.github/actions/setup-bun
@@ -51,9 +52,54 @@ jobs:
uses: sst/opencode/github@latest
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
+ GITHUB_TOKEN: ${{ steps.committer.outputs.token }}
+ OPENCODE_CONFIG_CONTENT: |
+ {
+ "permission": {
+ "*": "deny",
+ "read": {
+ "*": "deny",
+ "packages/web/src/content/docs": "allow",
+ "packages/web/src/content/docs/*": "allow",
+ "packages/web/src/content/docs/*.mdx": "allow",
+ "packages/web/src/content/docs/*/*.mdx": "allow",
+ ".opencode": "allow",
+ ".opencode/agent": "allow",
+ ".opencode/agent/glossary": "allow",
+ ".opencode/agent/translator.md": "allow",
+ ".opencode/agent/glossary/*.md": "allow"
+ },
+ "edit": {
+ "*": "deny",
+ "packages/web/src/content/docs/*/*.mdx": "allow"
+ },
+ "glob": {
+ "*": "deny",
+ "packages/web/src/content/docs*": "allow",
+ ".opencode/agent/glossary*": "allow"
+ },
+ "task": {
+ "*": "deny",
+ "translator": "allow"
+ }
+ },
+ "agent": {
+ "translator": {
+ "permission": {
+ "*": "deny",
+ "read": {
+ "*": "deny",
+ ".opencode/agent/translator.md": "allow",
+ ".opencode/agent/glossary/*.md": "allow"
+ }
+ }
+ }
+ }
+ }
with:
- model: opencode/gpt-5.2
+ model: opencode/gpt-5.3-codex
agent: docs
+ use_github_token: true
prompt: |
Update localized docs to match the latest English docs changes.
@@ -67,10 +113,11 @@ jobs:
2. You MUST use the Task tool for translation work and launch subagents with subagent_type `translator` (defined in .opencode/agent/translator.md).
3. Do not translate directly in the primary agent. Use translator subagent output as the source for locale text updates.
4. Run translator subagent Task calls in parallel whenever file/locale translation work is independent.
- 5. Preserve frontmatter keys, internal links, code blocks, and existing locale-specific metadata unless the English change requires an update.
- 6. Keep locale docs structure aligned with their corresponding English pages.
- 7. Do not modify English source docs in packages/web/src/content/docs/*.mdx.
- 8. If no locale updates are needed, make no changes.
+ 5. Use only the minimum tools needed for this task (read/glob, file edits, and translator Task). Do not use shell, web, search, or GitHub tools for translation work.
+ 6. Preserve frontmatter keys, internal links, code blocks, and existing locale-specific metadata unless the English change requires an update.
+ 7. Keep locale docs structure aligned with their corresponding English pages.
+ 8. Do not modify English source docs in packages/web/src/content/docs/*.mdx.
+ 9. If no locale updates are needed, make no changes.
- name: Commit and push locale docs updates
if: steps.changes.outputs.has_changes == 'true'
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 431581f5966e..8d4c9038a7e4 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -41,6 +41,13 @@ jobs:
- uses: ./.github/actions/setup-bun
+ - name: Setup git committer
+ id: committer
+ uses: ./.github/actions/setup-git-committer
+ with:
+ opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
+ opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
+
- name: Install OpenCode
if: inputs.bump || inputs.version
run: bun i -g opencode-ai
@@ -49,14 +56,16 @@ jobs:
run: |
./script/version.ts
env:
- GH_TOKEN: ${{ github.token }}
+ GH_TOKEN: ${{ steps.committer.outputs.token }}
OPENCODE_BUMP: ${{ inputs.bump }}
OPENCODE_VERSION: ${{ inputs.version }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
+ GH_REPO: ${{ (github.ref_name == 'beta' && 'anomalyco/opencode-beta') || github.repository }}
outputs:
version: ${{ steps.version.outputs.version }}
release: ${{ steps.version.outputs.release }}
tag: ${{ steps.version.outputs.tag }}
+ repo: ${{ steps.version.outputs.repo }}
build-cli:
needs: version
@@ -69,6 +78,13 @@ jobs:
- uses: ./.github/actions/setup-bun
+ - name: Setup git committer
+ id: committer
+ uses: ./.github/actions/setup-git-committer
+ with:
+ opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
+ opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
+
- name: Build
id: build
run: |
@@ -76,7 +92,8 @@ jobs:
env:
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
OPENCODE_RELEASE: ${{ needs.version.outputs.release }}
- GH_TOKEN: ${{ github.token }}
+ GH_REPO: ${{ needs.version.outputs.repo }}
+ GH_TOKEN: ${{ steps.committer.outputs.token }}
- uses: actions/upload-artifact@v4
with:
@@ -189,6 +206,13 @@ jobs:
if: contains(matrix.settings.host, 'ubuntu')
run: cargo tauri --version
+ - name: Setup git committer
+ id: committer
+ uses: ./.github/actions/setup-git-committer
+ with:
+ opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
+ opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
+
- name: Build and upload artifacts
uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a
timeout-minutes: 60
@@ -196,14 +220,16 @@ jobs:
projectPath: packages/desktop
uploadWorkflowArtifacts: true
tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }}
- args: --target ${{ matrix.settings.target }} --config ./src-tauri/tauri.prod.conf.json --verbose
+ args: --target ${{ matrix.settings.target }} --config ${{ (github.ref_name == 'beta' && './src-tauri/tauri.beta.conf.json') || './src-tauri/tauri.prod.conf.json' }} --verbose
updaterJsonPreferNsis: true
releaseId: ${{ needs.version.outputs.release }}
tagName: ${{ needs.version.outputs.tag }}
releaseDraft: true
releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext]
+ repo: ${{ (github.ref_name == 'beta' && 'opencode-beta') || '' }}
+ releaseCommitish: ${{ github.sha }}
env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_TOKEN: ${{ steps.committer.outputs.token }}
TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: true
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
@@ -280,4 +306,5 @@ jobs:
OPENCODE_RELEASE: ${{ needs.version.outputs.release }}
AUR_KEY: ${{ secrets.AUR_KEY }}
GITHUB_TOKEN: ${{ steps.committer.outputs.token }}
+ GH_REPO: ${{ needs.version.outputs.repo }}
NPM_CONFIG_PROVENANCE: false
diff --git a/.opencode/agent/glossary/README.md b/.opencode/agent/glossary/README.md
new file mode 100644
index 000000000000..983900381ca9
--- /dev/null
+++ b/.opencode/agent/glossary/README.md
@@ -0,0 +1,63 @@
+# Locale Glossaries
+
+Use this folder for locale-specific translation guidance that supplements `.opencode/agent/translator.md`.
+
+The global glossary in `translator.md` remains the source of truth for shared do-not-translate terms (commands, code, paths, product names, etc.). These locale files capture community learnings about phrasing and terminology preferences.
+
+## File Naming
+
+- One file per locale
+- Use lowercase locale slugs that match docs locales when possible (for example, `zh-cn.md`, `zh-tw.md`)
+- If only language-level guidance exists, use the language code (for example, `fr.md`)
+- Some repo locale slugs may be aliases/non-BCP47 for consistency (for example, `br` for Brazilian Portuguese / `pt-BR`)
+
+## What To Put In A Locale File
+
+- **Sources**: PRs/issues/discussions that motivated the guidance
+- **Do Not Translate (Locale Additions)**: locale-specific terms or casing decisions
+- **Preferred Terms**: recurring UI/docs words with preferred translations
+- **Guidance**: tone, style, and consistency notes
+- **Avoid** (optional): common literal translations or wording we should avoid
+- If the repo uses a locale alias slug, document the alias in **Guidance** (for example, prose may mention `pt-BR` while config/examples use `br`)
+
+Prefer guidance that is:
+
+- Repeated across multiple docs/screens
+- Easy to apply consistently
+- Backed by a community contribution or review discussion
+
+## Template
+
+```md
+# Glossary
+
+## Sources
+
+- PR #12345: https://github.com/anomalyco/opencode/pull/12345
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing)
+
+## Preferred Terms
+
+| English | Preferred | Notes |
+| ------- | --------- | --------- |
+| prompt | ... | preferred |
+| session | ... | preferred |
+
+## Guidance
+
+- Prefer natural phrasing over literal translation
+
+## Avoid
+
+- Avoid ... when ...
+```
+
+## Contribution Notes
+
+- Mark entries as preferred when they may evolve
+- Keep examples short
+- Add or update the `Sources` section whenever you add a new rule
+- Prefer PR-backed guidance over invented term mappings; start with general guidance if no term-level corrections exist yet
diff --git a/.opencode/agent/glossary/ar.md b/.opencode/agent/glossary/ar.md
new file mode 100644
index 000000000000..37355522a0a5
--- /dev/null
+++ b/.opencode/agent/glossary/ar.md
@@ -0,0 +1,28 @@
+# ar Glossary
+
+## Sources
+
+- PR #9947: https://github.com/anomalyco/opencode/pull/9947
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections.
+
+## Guidance
+
+- Prefer natural Arabic phrasing over literal translation
+- Keep tone clear and direct in UI labels and docs prose
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+- For RTL text, treat code, commands, and paths as LTR artifacts and keep their character order unchanged
+
+## Avoid
+
+- Avoid translating product and protocol names that are fixed identifiers
+- Avoid mixing multiple Arabic terms for the same recurring UI action once a preferred term is established
diff --git a/.opencode/agent/glossary/br.md b/.opencode/agent/glossary/br.md
new file mode 100644
index 000000000000..fd3e7251cd90
--- /dev/null
+++ b/.opencode/agent/glossary/br.md
@@ -0,0 +1,34 @@
+# br Glossary
+
+## Sources
+
+- PR #10086: https://github.com/anomalyco/opencode/pull/10086
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Locale code `br` in repo config, code, and paths (repo alias for Brazilian Portuguese)
+
+## Preferred Terms
+
+These are PR-backed locale naming preferences and may evolve.
+
+| English / Context | Preferred | Notes |
+| ---------------------------------------- | ------------------------------ | ------------------------------------------------------------- |
+| Brazilian Portuguese (prose locale name) | `pt-BR` | Use standard locale naming in prose when helpful |
+| Repo locale slug (code/config) | `br` | PR #10086 uses `br` for consistency/simplicity |
+| Browser locale detection | `pt`, `pt-br`, `pt-BR` -> `br` | Preserve this mapping in docs/examples about locale detection |
+
+## Guidance
+
+- This file covers Brazilian Portuguese (`pt-BR`), but the repo locale code is `br`
+- Use natural Brazilian Portuguese phrasing over literal translation
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+- Keep repo locale identifiers as implemented in code/config (`br`) even when prose mentions `pt-BR`
+
+## Avoid
+
+- Avoid changing repo locale code references from `br` to `pt-br` in code snippets, paths, or config examples
+- Avoid mixing Portuguese variants when a Brazilian Portuguese form is established
diff --git a/.opencode/agent/glossary/bs.md b/.opencode/agent/glossary/bs.md
new file mode 100644
index 000000000000..aa3bd96f6f94
--- /dev/null
+++ b/.opencode/agent/glossary/bs.md
@@ -0,0 +1,33 @@
+# bs Glossary
+
+## Sources
+
+- PR #12283: https://github.com/anomalyco/opencode/pull/12283
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+These are PR-backed locale naming preferences and may evolve.
+
+| English / Context | Preferred | Notes |
+| ---------------------------------- | ---------- | ------------------------------------------------- |
+| Bosnian language label (UI) | `Bosanski` | PR #12283 tested switching language to `Bosanski` |
+| Repo locale slug (code/config) | `bs` | Preserve in code, config, paths, and examples |
+| Browser locale detection (Bosnian) | `bs` | PR #12283 added `bs` locale auto-detection |
+
+## Guidance
+
+- Use natural Bosnian phrasing over literal translation
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+- Keep repo locale references as `bs` in code/config, and use `Bosanski` for the user-facing language name when applicable
+
+## Avoid
+
+- Avoid changing repo locale references from `bs` to another slug in code snippets or config examples
+- Avoid translating product and protocol names that are fixed identifiers
diff --git a/.opencode/agent/glossary/da.md b/.opencode/agent/glossary/da.md
new file mode 100644
index 000000000000..e63222170109
--- /dev/null
+++ b/.opencode/agent/glossary/da.md
@@ -0,0 +1,27 @@
+# da Glossary
+
+## Sources
+
+- PR #9821: https://github.com/anomalyco/opencode/pull/9821
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections.
+
+## Guidance
+
+- Prefer natural Danish phrasing over literal translation
+- Keep tone clear and direct in UI labels and docs prose
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+
+## Avoid
+
+- Avoid translating product and protocol names that are fixed identifiers
+- Avoid mixing multiple Danish terms for the same recurring UI action once a preferred term is established
diff --git a/.opencode/agent/glossary/de.md b/.opencode/agent/glossary/de.md
new file mode 100644
index 000000000000..0d2c49faceae
--- /dev/null
+++ b/.opencode/agent/glossary/de.md
@@ -0,0 +1,27 @@
+# de Glossary
+
+## Sources
+
+- PR #9817: https://github.com/anomalyco/opencode/pull/9817
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections.
+
+## Guidance
+
+- Prefer natural German phrasing over literal translation
+- Keep tone clear and direct in UI labels and docs prose
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+
+## Avoid
+
+- Avoid translating product and protocol names that are fixed identifiers
+- Avoid mixing multiple German terms for the same recurring UI action once a preferred term is established
diff --git a/.opencode/agent/glossary/es.md b/.opencode/agent/glossary/es.md
new file mode 100644
index 000000000000..dc9b977ecffa
--- /dev/null
+++ b/.opencode/agent/glossary/es.md
@@ -0,0 +1,27 @@
+# es Glossary
+
+## Sources
+
+- PR #9817: https://github.com/anomalyco/opencode/pull/9817
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections.
+
+## Guidance
+
+- Prefer natural Spanish phrasing over literal translation
+- Keep tone clear and direct in UI labels and docs prose
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+
+## Avoid
+
+- Avoid translating product and protocol names that are fixed identifiers
+- Avoid mixing multiple Spanish terms for the same recurring UI action once a preferred term is established
diff --git a/.opencode/agent/glossary/fr.md b/.opencode/agent/glossary/fr.md
new file mode 100644
index 000000000000..074c4de110a0
--- /dev/null
+++ b/.opencode/agent/glossary/fr.md
@@ -0,0 +1,27 @@
+# fr Glossary
+
+## Sources
+
+- PR #9821: https://github.com/anomalyco/opencode/pull/9821
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections.
+
+## Guidance
+
+- Prefer natural French phrasing over literal translation
+- Keep tone clear and direct in UI labels and docs prose
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+
+## Avoid
+
+- Avoid translating product and protocol names that are fixed identifiers
+- Avoid mixing multiple French terms for the same recurring UI action once a preferred term is established
diff --git a/.opencode/agent/glossary/ja.md b/.opencode/agent/glossary/ja.md
new file mode 100644
index 000000000000..f0159ca96690
--- /dev/null
+++ b/.opencode/agent/glossary/ja.md
@@ -0,0 +1,33 @@
+# ja Glossary
+
+## Sources
+
+- PR #9821: https://github.com/anomalyco/opencode/pull/9821
+- PR #13160: https://github.com/anomalyco/opencode/pull/13160
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+These are PR-backed wording preferences and may evolve.
+
+| English / Context | Preferred | Notes |
+| --------------------------- | ----------------------- | ------------------------------------- |
+| WSL integration (UI label) | `WSL連携` | PR #13160 prefers this over `WSL統合` |
+| WSL integration description | `WindowsのWSL環境で...` | PR #13160 improved phrasing naturally |
+
+## Guidance
+
+- Prefer natural Japanese phrasing over literal translation
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+- In WSL integration text, follow PR #13160 wording direction for more natural Japanese phrasing
+
+## Avoid
+
+- Avoid `WSL統合` in the WSL integration UI context where `WSL連携` is the reviewed wording
+- Avoid translating product and protocol names that are fixed identifiers
diff --git a/.opencode/agent/glossary/ko.md b/.opencode/agent/glossary/ko.md
new file mode 100644
index 000000000000..71385c8a10ac
--- /dev/null
+++ b/.opencode/agent/glossary/ko.md
@@ -0,0 +1,27 @@
+# ko Glossary
+
+## Sources
+
+- PR #9817: https://github.com/anomalyco/opencode/pull/9817
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections.
+
+## Guidance
+
+- Prefer natural Korean phrasing over literal translation
+- Keep tone clear and direct in UI labels and docs prose
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+
+## Avoid
+
+- Avoid translating product and protocol names that are fixed identifiers
+- Avoid mixing multiple Korean terms for the same recurring UI action once a preferred term is established
diff --git a/.opencode/agent/glossary/no.md b/.opencode/agent/glossary/no.md
new file mode 100644
index 000000000000..d7159dca4107
--- /dev/null
+++ b/.opencode/agent/glossary/no.md
@@ -0,0 +1,38 @@
+# no Glossary
+
+## Sources
+
+- PR #10018: https://github.com/anomalyco/opencode/pull/10018
+- PR #12935: https://github.com/anomalyco/opencode/pull/12935
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Sound names (PR #10018 notes these were intentionally left untranslated)
+
+## Preferred Terms
+
+These are PR-backed corrections and may evolve.
+
+| English / Context | Preferred | Notes |
+| ----------------------------------- | ------------ | ----------------------------- |
+| Save (data persistence action) | `Lagre` | Prefer over `Spare` |
+| Disabled (feature/state) | `deaktivert` | Prefer over `funksjonshemmet` |
+| API keys | `API Nøkler` | Prefer over `API Taster` |
+| Cost (noun) | `Kostnad` | Prefer over verb form `Koste` |
+| Show/View (imperative button label) | `Vis` | Prefer over `Utsikt` |
+
+## Guidance
+
+- Prefer natural Norwegian Bokmal (Bokmål) wording over literal translation
+- Keep tone clear and practical in UI labels
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+- Keep recurring UI terms consistent once a preferred term is chosen
+
+## Avoid
+
+- Avoid `Spare` for save actions in persistence contexts
+- Avoid `funksjonshemmet` for disabled feature states
+- Avoid `API Taster`, `Koste`, and `Utsikt` in the corrected contexts above
diff --git a/.opencode/agent/glossary/pl.md b/.opencode/agent/glossary/pl.md
new file mode 100644
index 000000000000..e9bad7a51567
--- /dev/null
+++ b/.opencode/agent/glossary/pl.md
@@ -0,0 +1,27 @@
+# pl Glossary
+
+## Sources
+
+- PR #9884: https://github.com/anomalyco/opencode/pull/9884
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections.
+
+## Guidance
+
+- Prefer natural Polish phrasing over literal translation
+- Keep tone clear and direct in UI labels and docs prose
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+
+## Avoid
+
+- Avoid translating product and protocol names that are fixed identifiers
+- Avoid mixing multiple Polish terms for the same recurring UI action once a preferred term is established
diff --git a/.opencode/agent/glossary/ru.md b/.opencode/agent/glossary/ru.md
new file mode 100644
index 000000000000..6fee0f94c06f
--- /dev/null
+++ b/.opencode/agent/glossary/ru.md
@@ -0,0 +1,27 @@
+# ru Glossary
+
+## Sources
+
+- PR #9882: https://github.com/anomalyco/opencode/pull/9882
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections.
+
+## Guidance
+
+- Prefer natural Russian phrasing over literal translation
+- Keep tone clear and direct in UI labels and docs prose
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+
+## Avoid
+
+- Avoid translating product and protocol names that are fixed identifiers
+- Avoid mixing multiple Russian terms for the same recurring UI action once a preferred term is established
diff --git a/.opencode/agent/glossary/th.md b/.opencode/agent/glossary/th.md
new file mode 100644
index 000000000000..7b5a31d16bfc
--- /dev/null
+++ b/.opencode/agent/glossary/th.md
@@ -0,0 +1,34 @@
+# th Glossary
+
+## Sources
+
+- PR #10809: https://github.com/anomalyco/opencode/pull/10809
+- PR #11496: https://github.com/anomalyco/opencode/pull/11496
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code)
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- Commands, flags, file paths, and code literals (keep exactly as written)
+
+## Preferred Terms
+
+These are PR-backed preferences and may evolve.
+
+| English / Context | Preferred | Notes |
+| ------------------------------------- | --------------------- | -------------------------------------------------------------------------------- |
+| Thai language label in language lists | `ไทย` | PR #10809 standardized this across locales |
+| Language names in language pickers | Native names (static) | PR #11496: keep names like `English`, `Deutsch`, `ไทย` consistent across locales |
+
+## Guidance
+
+- Prefer natural Thai phrasing over literal translation
+- Keep tone short and clear for buttons and labels
+- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths
+- Keep language names static/native in language pickers instead of translating them per current locale (PR #11496)
+
+## Avoid
+
+- Avoid translating language names differently per current locale in language lists
+- Avoid changing `ไทย` to another display form for the Thai language option unless the product standard changes
diff --git a/.opencode/agent/glossary/zh-cn.md b/.opencode/agent/glossary/zh-cn.md
new file mode 100644
index 000000000000..054e94b7e83a
--- /dev/null
+++ b/.opencode/agent/glossary/zh-cn.md
@@ -0,0 +1,42 @@
+# zh-cn Glossary
+
+## Sources
+
+- PR #13942: https://github.com/anomalyco/opencode/pull/13942
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only when it is part of commands, package names, paths, or code)
+- `OpenCode Zen`
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- `Model Context Protocol` (prefer the English expansion when introducing `MCP`)
+
+## Preferred Terms
+
+These are preferred terms for docs/UI prose and may evolve.
+
+| English | Preferred | Notes |
+| ----------------------- | --------- | ------------------------------------------- |
+| prompt | 提示词 | Keep `--prompt` unchanged in flags/code |
+| session | 会话 | |
+| provider | 提供商 | |
+| share link / shared URL | 分享链接 | Prefer `分享` for user-facing share actions |
+| headless (server) | 无界面 | Docs wording |
+| authentication | 认证 | Prefer in auth/OAuth contexts |
+| cache | 缓存 | |
+| keybind / shortcut | 快捷键 | User-facing docs wording |
+| workflow | 工作流 | e.g. GitHub Actions workflow |
+
+## Guidance
+
+- Prefer natural, concise phrasing over literal translation
+- Keep the tone direct and friendly (PR #13942 consistently moved wording in this direction)
+- Preserve technical artifacts exactly: commands, flags, code, inline code, URLs, file paths, model IDs
+- Keep enum-like values in English when they are literals (for example, `default`, `json`)
+- Prefer consistent terminology across pages once a term is chosen (`会话`, `提供商`, `提示词`, etc.)
+
+## Avoid
+
+- Avoid `opencode` in prose when referring to the product name; use `OpenCode`
+- Avoid mixing alternative terms for the same concept across docs when a preferred term is already established
diff --git a/.opencode/agent/glossary/zh-tw.md b/.opencode/agent/glossary/zh-tw.md
new file mode 100644
index 000000000000..283660e12198
--- /dev/null
+++ b/.opencode/agent/glossary/zh-tw.md
@@ -0,0 +1,42 @@
+# zh-tw Glossary
+
+## Sources
+
+- PR #13942: https://github.com/anomalyco/opencode/pull/13942
+
+## Do Not Translate (Locale Additions)
+
+- `OpenCode` (preserve casing in prose; keep `opencode` only when it is part of commands, package names, paths, or code)
+- `OpenCode Zen`
+- `OpenCode CLI`
+- `CLI`, `TUI`, `MCP`, `OAuth`
+- `Model Context Protocol` (prefer the English expansion when introducing `MCP`)
+
+## Preferred Terms
+
+These are preferred terms for docs/UI prose and may evolve.
+
+| English | Preferred | Notes |
+| ----------------------- | --------- | ------------------------------------------- |
+| prompt | 提示詞 | Keep `--prompt` unchanged in flags/code |
+| session | 工作階段 | |
+| provider | 供應商 | |
+| share link / shared URL | 分享連結 | Prefer `分享` for user-facing share actions |
+| headless (server) | 無介面 | Docs wording |
+| authentication | 認證 | Prefer in auth/OAuth contexts |
+| cache | 快取 | |
+| keybind / shortcut | 快捷鍵 | User-facing docs wording |
+| workflow | 工作流程 | e.g. GitHub Actions workflow |
+
+## Guidance
+
+- Prefer natural, concise phrasing over literal translation
+- Keep the tone direct and friendly (PR #13942 consistently moved wording in this direction)
+- Preserve technical artifacts exactly: commands, flags, code, inline code, URLs, file paths, model IDs
+- Keep enum-like values in English when they are literals (for example, `default`, `json`)
+- Prefer consistent terminology across pages once a term is chosen (`工作階段`, `供應商`, `提示詞`, etc.)
+
+## Avoid
+
+- Avoid `opencode` in prose when referring to the product name; use `OpenCode`
+- Avoid mixing alternative terms for the same concept across docs when a preferred term is already established
diff --git a/.opencode/agent/translator.md b/.opencode/agent/translator.md
index 7886cf5f395e..f0b3f8e9270b 100644
--- a/.opencode/agent/translator.md
+++ b/.opencode/agent/translator.md
@@ -1,7 +1,7 @@
---
description: Translate content for a specified locale while preserving technical terms
mode: subagent
-model: opencode/gemini-3-pro
+model: opencode/gemini-3.1-pro
---
You are a professional translator and localization specialist.
@@ -13,10 +13,25 @@ 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/agent/glossary/.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.
---
diff --git a/.opencode/tool/github-triage.ts b/.opencode/tool/github-triage.ts
index ed80f49d5413..8ad0212ad074 100644
--- a/.opencode/tool/github-triage.ts
+++ b/.opencode/tool/github-triage.ts
@@ -5,8 +5,16 @@ import DESCRIPTION from "./github-triage.txt"
const TEAM = {
desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"],
zen: ["fwang", "MrMushrooooom"],
- tui: ["thdxr", "kommander", "rekram1-node"],
- core: ["thdxr", "rekram1-node", "jlongster"],
+ tui: [
+ "thdxr",
+ "kommander",
+ // "rekram1-node" (on vacation)
+ ],
+ core: [
+ "thdxr",
+ // "rekram1-node", (on vacation)
+ "jlongster",
+ ],
docs: ["R44VC0RP"],
windows: ["Hona"],
} as const
@@ -42,10 +50,7 @@ async function githubFetch(endpoint: string, options: RequestInit = {}) {
export default tool({
description: DESCRIPTION,
args: {
- assignee: tool.schema
- .enum(ASSIGNEES as [string, ...string[]])
- .describe("The username of the assignee")
- .default("rekram1-node"),
+ assignee: tool.schema.enum(ASSIGNEES as [string, ...string[]]).describe("The username of the assignee"),
labels: tool.schema
.array(tool.schema.enum(["nix", "opentui", "perf", "web", "desktop", "zen", "docs", "windows", "core"]))
.describe("The labels(s) to add to the issue")
@@ -68,7 +73,8 @@ export default tool({
results.push("Dropped label: nix (issue does not mention nix)")
}
- const assignee = nix ? "rekram1-node" : web ? pick(TEAM.desktop) : args.assignee
+ // const assignee = nix ? "rekram1-node" : web ? pick(TEAM.desktop) : args.assignee
+ const assignee = web ? pick(TEAM.desktop) : args.assignee
if (labels.includes("zen") && !zen) {
throw new Error("Only add the zen label when issue title/body contains 'zen'")
diff --git a/.opencode/tool/github-triage.txt b/.opencode/tool/github-triage.txt
index 4369ed23512f..1a2d69bdb5b9 100644
--- a/.opencode/tool/github-triage.txt
+++ b/.opencode/tool/github-triage.txt
@@ -4,3 +4,5 @@ Choose labels and assignee using the current triage policy and ownership rules.
Pick the most fitting labels for the issue and assign one owner.
If unsure, choose the team/section with the most overlap with the issue and assign a member from that team at random.
+
+(Note: rekram1-node is on vacation, do not assign issues to him.)
diff --git a/README.ar.md b/README.ar.md
index f24e598d5eb9..aeb2f04b72c1 100644
--- a/README.ar.md
+++ b/README.ar.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.bn.md b/README.bn.md
new file mode 100644
index 000000000000..a3ffdc0d80e9
--- /dev/null
+++ b/README.bn.md
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+ওপেন সোর্স এআই কোডিং এজেন্ট।
+
+
+
+
+
+
+
+ English |
+ 简体中文 |
+ 繁體中文 |
+ 한국어 |
+ Deutsch |
+ Español |
+ Français |
+ Italiano |
+ Dansk |
+ 日本語 |
+ Polski |
+ Русский |
+ Bosanski |
+ العربية |
+ Norsk |
+ Português (Brasil) |
+ ไทย |
+ Türkçe |
+ Українська |
+ বাংলা
+
+
+[](https://opencode.ai)
+
+---
+
+### ইনস্টলেশন (Installation)
+
+```bash
+# YOLO
+curl -fsSL https://opencode.ai/install | bash
+
+# Package managers
+npm i -g opencode-ai@latest # or bun/pnpm/yarn
+scoop install opencode # Windows
+choco install opencode # Windows
+brew install anomalyco/tap/opencode # macOS and Linux (recommended, always up to date)
+brew install opencode # macOS and Linux (official brew formula, updated less)
+sudo pacman -S opencode # Arch Linux (Stable)
+paru -S opencode-bin # Arch Linux (Latest from AUR)
+mise use -g opencode # Any OS
+nix run nixpkgs#opencode # or github:anomalyco/opencode for latest dev branch
+```
+
+> [!TIP]
+> ইনস্টল করার আগে ০.১.x এর চেয়ে পুরোনো ভার্সনগুলো মুছে ফেলুন।
+
+### ডেস্কটপ অ্যাপ (BETA)
+
+OpenCode ডেস্কটপ অ্যাপ্লিকেশন হিসেবেও উপলব্ধ। সরাসরি [রিলিজ পেজ](https://github.com/anomalyco/opencode/releases) অথবা [opencode.ai/download](https://opencode.ai/download) থেকে ডাউনলোড করুন।
+
+| প্ল্যাটফর্ম | ডাউনলোড |
+| --------------------- | ------------------------------------- |
+| macOS (Apple Silicon) | `opencode-desktop-darwin-aarch64.dmg` |
+| macOS (Intel) | `opencode-desktop-darwin-x64.dmg` |
+| Windows | `opencode-desktop-windows-x64.exe` |
+| Linux | `.deb`, `.rpm`, or AppImage |
+
+```bash
+# macOS (Homebrew)
+brew install --cask opencode-desktop
+# Windows (Scoop)
+scoop bucket add extras; scoop install extras/opencode-desktop
+```
+
+#### ইনস্টলেশন ডিরেক্টরি (Installation Directory)
+
+ইনস্টল স্ক্রিপ্টটি ইনস্টলেশন পাতের জন্য নিম্নলিখিত অগ্রাধিকার ক্রম মেনে চলে:
+
+1. `$OPENCODE_INSTALL_DIR` - কাস্টম ইনস্টলেশন ডিরেক্টরি
+2. `$XDG_BIN_DIR` - XDG বেস ডিরেক্টরি স্পেসিফিকেশন সমর্থিত পাথ
+3. `$HOME/bin` - সাধারণ ব্যবহারকারী বাইনারি ডিরেক্টরি (যদি বিদ্যমান থাকে বা তৈরি করা যায়)
+4. `$HOME/.opencode/bin` - ডিফল্ট ফলব্যাক
+
+```bash
+# উদাহরণ
+OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash
+XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
+```
+
+### এজেন্টস (Agents)
+
+OpenCode এ দুটি বিল্ট-ইন এজেন্ট রয়েছে যা আপনি `Tab` কি(key) দিয়ে পরিবর্তন করতে পারবেন।
+
+- **build** - ডিফল্ট, ডেভেলপমেন্টের কাজের জন্য সম্পূর্ণ অ্যাক্সেসযুক্ত এজেন্ট
+- **plan** - বিশ্লেষণ এবং কোড এক্সপ্লোরেশনের জন্য রিড-ওনলি এজেন্ট
+ - ডিফল্টভাবে ফাইল এডিট করতে দেয় না
+ - ব্যাশ কমান্ড চালানোর আগে অনুমতি চায়
+ - অপরিচিত কোডবেস এক্সপ্লোর করা বা পরিবর্তনের পরিকল্পনা করার জন্য আদর্শ
+
+এছাড়াও জটিল অনুসন্ধান এবং মাল্টিস্টেপ টাস্কের জন্য একটি **general** সাবএজেন্ট অন্তর্ভুক্ত রয়েছে।
+এটি অভ্যন্তরীণভাবে ব্যবহৃত হয় এবং মেসেজে `@general` লিখে ব্যবহার করা যেতে পারে।
+
+এজেন্টদের সম্পর্কে আরও জানুন: [docs](https://opencode.ai/docs/agents)।
+
+### ডকুমেন্টেশন (Documentation)
+
+কিভাবে OpenCode কনফিগার করবেন সে সম্পর্কে আরও তথ্যের জন্য, [**আমাদের ডকস দেখুন**](https://opencode.ai/docs)।
+
+### অবদান (Contributing)
+
+আপনি যদি OpenCode এ অবদান রাখতে চান, অনুগ্রহ করে একটি পুল রিকোয়েস্ট সাবমিট করার আগে আমাদের [কন্ট্রিবিউটিং ডকস](./CONTRIBUTING.md) পড়ে নিন।
+
+### OpenCode এর উপর বিল্ডিং (Building on OpenCode)
+
+আপনি যদি এমন প্রজেক্টে কাজ করেন যা OpenCode এর সাথে সম্পর্কিত এবং প্রজেক্টের নামের অংশ হিসেবে "opencode" ব্যবহার করেন, উদাহরণস্বরূপ "opencode-dashboard" বা "opencode-mobile", তবে দয়া করে আপনার README তে একটি নোট যোগ করে স্পষ্ট করুন যে এই প্রজেক্টটি OpenCode দল দ্বারা তৈরি হয়নি এবং আমাদের সাথে এর কোনো সরাসরি সম্পর্ক নেই।
+
+### সচরাচর জিজ্ঞাসিত প্রশ্নাবলী (FAQ)
+
+#### এটি ক্লড কোড (Claude Code) থেকে কীভাবে আলাদা?
+
+ক্যাপাবিলিটির দিক থেকে এটি ক্লড কোডের (Claude Code) মতই। এখানে মূল পার্থক্যগুলো দেওয়া হলো:
+
+- ১০০% ওপেন সোর্স
+- কোনো প্রোভাইডারের সাথে আবদ্ধ নয়। যদিও আমরা [OpenCode Zen](https://opencode.ai/zen) এর মাধ্যমে মডেলসমূহ ব্যবহারের পরামর্শ দিই, OpenCode ক্লড (Claude), ওপেনএআই (OpenAI), গুগল (Google), অথবা লোকাল মডেলগুলোর সাথেও ব্যবহার করা যেতে পারে। যেমন যেমন মডেলগুলো উন্নত হবে, তাদের মধ্যকার পার্থক্য কমে আসবে এবং দামও কমবে, তাই প্রোভাইডার-অজ্ঞাস্টিক হওয়া খুবই গুরুত্বপূর্ণ।
+- আউট-অফ-দ্য-বক্স LSP সাপোর্ট
+- TUI এর উপর ফোকাস। OpenCode নিওভিম (neovim) ব্যবহারকারী এবং [terminal.shop](https://terminal.shop) এর নির্মাতাদের দ্বারা তৈরি; আমরা টার্মিনালে কী কী সম্ভব তার সীমাবদ্ধতা ছাড়িয়ে যাওয়ার চেষ্টা করছি।
+- ক্লায়েন্ট/সার্ভার আর্কিটেকচার। এটি যেমন OpenCode কে আপনার কম্পিউটারে চালানোর সুযোগ দেয়, তেমনি আপনি মোবাইল অ্যাপ থেকে রিমোটলি এটি নিয়ন্ত্রণ করতে পারবেন, অর্থাৎ TUI ফ্রন্টএন্ড কেবল সম্ভাব্য ক্লায়েন্টগুলোর মধ্যে একটি।
+
+---
+
+**আমাদের কমিউনিটিতে যুক্ত হোন** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode)
diff --git a/README.br.md b/README.br.md
index 4802c4996f63..6044dad6c0a0 100644
--- a/README.br.md
+++ b/README.br.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.bs.md b/README.bs.md
index 9ad6852018c0..ef54a8369573 100644
--- a/README.bs.md
+++ b/README.bs.md
@@ -33,7 +33,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.da.md b/README.da.md
index 4b1302dbc3c2..3ffbbe820291 100644
--- a/README.da.md
+++ b/README.da.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.de.md b/README.de.md
index 16116dc72f23..64c6628b45ee 100644
--- a/README.de.md
+++ b/README.de.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.es.md b/README.es.md
index 5c18ff4aca7c..875c8b0832a1 100644
--- a/README.es.md
+++ b/README.es.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.fr.md b/README.fr.md
index 0382164bedc5..254d38577b19 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.it.md b/README.it.md
index c966ccec4916..b1f75c2d2c7c 100644
--- a/README.it.md
+++ b/README.it.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.ja.md b/README.ja.md
index 11109e7eb408..31d11dcf1a1d 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.ko.md b/README.ko.md
index 23fea76b1ebd..5f9de2cf3f73 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.md b/README.md
index 99b4b2c50ff9..620415c96173 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.no.md b/README.no.md
index 9b9e90dc3850..125e18125746 100644
--- a/README.no.md
+++ b/README.no.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.pl.md b/README.pl.md
index fced98dfc3a1..61ef5870c135 100644
--- a/README.pl.md
+++ b/README.pl.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.ru.md b/README.ru.md
index a7c590c16b7c..fed1101c0ec3 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.th.md b/README.th.md
index 0999167f239c..01d68dd8dcc5 100644
--- a/README.th.md
+++ b/README.th.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.tr.md b/README.tr.md
index 67f84e4ddbce..bfb18e1b43b2 100644
--- a/README.tr.md
+++ b/README.tr.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.uk.md b/README.uk.md
index 77e859a45d73..ed20fbf23686 100644
--- a/README.uk.md
+++ b/README.uk.md
@@ -33,7 +33,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.zh.md b/README.zh.md
index 113d476b2ed3..c6d1c1d11f0f 100644
--- a/README.zh.md
+++ b/README.zh.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/README.zht.md b/README.zht.md
index b5181044438d..0dd44a9f0fc3 100644
--- a/README.zht.md
+++ b/README.zht.md
@@ -32,7 +32,8 @@
Português (Brasil) |
ไทย |
Türkçe |
- Українська
+ Українська |
+ বাংলা
[](https://opencode.ai)
diff --git a/bun.lock b/bun.lock
index 2240f3055889..04da112cf791 100644
--- a/bun.lock
+++ b/bun.lock
@@ -25,7 +25,7 @@
},
"packages/app": {
"name": "@opencode-ai/app",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -75,7 +75,7 @@
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -109,7 +109,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -136,7 +136,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
@@ -160,7 +160,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -184,7 +184,7 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@opencode-ai/app": "workspace:*",
"@opencode-ai/ui": "workspace:*",
@@ -217,7 +217,7 @@
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@opencode-ai/ui": "workspace:*",
"@opencode-ai/util": "workspace:*",
@@ -246,7 +246,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -262,7 +262,7 @@
},
"packages/opencode": {
"name": "opencode",
- "version": "1.2.9",
+ "version": "1.2.10",
"bin": {
"opencode": "./bin/opencode",
},
@@ -376,7 +376,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"zod": "catalog:",
@@ -396,7 +396,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
- "version": "1.2.9",
+ "version": "1.2.10",
"devDependencies": {
"@hey-api/openapi-ts": "0.90.10",
"@tsconfig/node22": "catalog:",
@@ -407,7 +407,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -420,7 +420,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -462,7 +462,7 @@
},
"packages/util": {
"name": "@opencode-ai/util",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"zod": "catalog:",
},
@@ -473,7 +473,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
- "version": "1.2.9",
+ "version": "1.2.10",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
diff --git a/github/action.yml b/github/action.yml
index 8652bb8c1517..3d983a160995 100644
--- a/github/action.yml
+++ b/github/action.yml
@@ -30,6 +30,10 @@ inputs:
description: "Comma-separated list of trigger phrases (case-insensitive). Defaults to '/opencode,/oc'"
required: false
+ variant:
+ description: "Model variant for provider-specific reasoning effort (e.g., high, max, minimal)"
+ required: false
+
oidc_base_url:
description: "Base URL for OIDC token exchange API. Only required when running a custom GitHub App install. Defaults to https://api.opencode.ai"
required: false
@@ -71,4 +75,5 @@ runs:
PROMPT: ${{ inputs.prompt }}
USE_GITHUB_TOKEN: ${{ inputs.use_github_token }}
MENTIONS: ${{ inputs.mentions }}
+ VARIANT: ${{ inputs.variant }}
OIDC_BASE_URL: ${{ inputs.oidc_base_url }}
diff --git a/packages/app/e2e/actions.ts b/packages/app/e2e/actions.ts
index d42c0fcebb9e..a7ccba61752b 100644
--- a/packages/app/e2e/actions.ts
+++ b/packages/app/e2e/actions.ts
@@ -225,7 +225,7 @@ export async function hoverSessionItem(page: Page, sessionID: string) {
export async function openSessionMoreMenu(page: Page, sessionID: string) {
await expect(page).toHaveURL(new RegExp(`/session/${sessionID}(?:[/?#]|$)`))
- const scroller = page.locator(".session-scroller").first()
+ const scroller = page.locator(".scroll-view__viewport").first()
await expect(scroller).toBeVisible()
await expect(scroller.getByRole("heading", { level: 1 }).first()).toBeVisible({ timeout: 30_000 })
diff --git a/packages/app/e2e/selectors.ts b/packages/app/e2e/selectors.ts
index be0bc057176a..5fad2c06b528 100644
--- a/packages/app/e2e/selectors.ts
+++ b/packages/app/e2e/selectors.ts
@@ -20,11 +20,8 @@ export const settingsNotificationsAgentSelector = '[data-action="settings-notifi
export const settingsNotificationsPermissionsSelector = '[data-action="settings-notifications-permissions"]'
export const settingsNotificationsErrorsSelector = '[data-action="settings-notifications-errors"]'
export const settingsSoundsAgentSelector = '[data-action="settings-sounds-agent"]'
-export const settingsSoundsAgentEnabledSelector = '[data-action="settings-sounds-agent-enabled"]'
export const settingsSoundsPermissionsSelector = '[data-action="settings-sounds-permissions"]'
-export const settingsSoundsPermissionsEnabledSelector = '[data-action="settings-sounds-permissions-enabled"]'
export const settingsSoundsErrorsSelector = '[data-action="settings-sounds-errors"]'
-export const settingsSoundsErrorsEnabledSelector = '[data-action="settings-sounds-errors-enabled"]'
export const settingsUpdatesStartupSelector = '[data-action="settings-updates-startup"]'
export const settingsReleaseNotesSelector = '[data-action="settings-release-notes"]'
diff --git a/packages/app/e2e/session/session.spec.ts b/packages/app/e2e/session/session.spec.ts
index 93eaee5cb0bf..68d992949964 100644
--- a/packages/app/e2e/session/session.spec.ts
+++ b/packages/app/e2e/session/session.spec.ts
@@ -44,7 +44,7 @@ test("session can be renamed via header menu", async ({ page, sdk, gotoSession }
const menu = await openSessionMoreMenu(page, session.id)
await clickMenuItem(menu, /rename/i)
- const input = page.locator(".session-scroller").locator(inlineInputSelector).first()
+ const input = page.locator(".scroll-view__viewport").locator(inlineInputSelector).first()
await expect(input).toBeVisible()
await expect(input).toBeFocused()
await input.fill(renamedTitle)
diff --git a/packages/app/e2e/settings/settings.spec.ts b/packages/app/e2e/settings/settings.spec.ts
index 9fbcf79f5ee7..c2a8522eb051 100644
--- a/packages/app/e2e/settings/settings.spec.ts
+++ b/packages/app/e2e/settings/settings.spec.ts
@@ -9,7 +9,6 @@ import {
settingsNotificationsPermissionsSelector,
settingsReleaseNotesSelector,
settingsSoundsAgentSelector,
- settingsSoundsAgentEnabledSelector,
settingsSoundsErrorsSelector,
settingsSoundsPermissionsSelector,
settingsThemeSelector,
@@ -336,21 +335,19 @@ test("changing sound agent selection persists in localStorage", async ({ page, g
expect(stored?.sounds?.agent).not.toBe("staplebops-01")
})
-test("disabling agent sound disables sound selection", async ({ page, gotoSession }) => {
+test("selecting none disables agent sound", async ({ page, gotoSession }) => {
await gotoSession()
const dialog = await openSettings(page)
const select = dialog.locator(settingsSoundsAgentSelector)
- const switchContainer = dialog.locator(settingsSoundsAgentEnabledSelector)
const trigger = select.locator('[data-slot="select-select-trigger"]')
await expect(select).toBeVisible()
- await expect(switchContainer).toBeVisible()
await expect(trigger).toBeEnabled()
- await switchContainer.locator('[data-slot="switch-control"]').click()
- await page.waitForTimeout(100)
-
- await expect(trigger).toBeDisabled()
+ await trigger.click()
+ const items = page.locator('[data-slot="select-select-item"]')
+ await expect(items.first()).toBeVisible()
+ await items.first().click()
const stored = await page.evaluate((key) => {
const raw = localStorage.getItem(key)
diff --git a/packages/app/e2e/terminal/terminal-init.spec.ts b/packages/app/e2e/terminal/terminal-init.spec.ts
index 87934b66e381..18991bf76364 100644
--- a/packages/app/e2e/terminal/terminal-init.spec.ts
+++ b/packages/app/e2e/terminal/terminal-init.spec.ts
@@ -6,6 +6,7 @@ test("smoke terminal mounts and can create a second tab", async ({ page, gotoSes
await gotoSession()
const terminals = page.locator(terminalSelector)
+ const tabs = page.locator('#terminal-panel [data-slot="tabs-trigger"]')
const opened = await terminals.first().isVisible()
if (!opened) {
@@ -21,6 +22,7 @@ test("smoke terminal mounts and can create a second tab", async ({ page, gotoSes
await page.locator(promptSelector).click()
await page.keyboard.press("Control+Alt+T")
- await expect(terminals).toHaveCount(2)
- await expect(terminals.nth(1).locator("textarea")).toHaveCount(1)
+ await expect(tabs).toHaveCount(2)
+ await expect(terminals).toHaveCount(1)
+ await expect(terminals.first().locator("textarea")).toHaveCount(1)
})
diff --git a/packages/app/package.json b/packages/app/package.json
index 385205a0c191..b9397b0f40de 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
- "version": "1.2.9",
+ "version": "1.2.10",
"description": "",
"type": "module",
"exports": {
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx
index b1c608ffcc99..adfd592f8d01 100644
--- a/packages/app/src/components/prompt-input.tsx
+++ b/packages/app/src/components/prompt-input.tsx
@@ -89,6 +89,8 @@ const EXAMPLES = [
"prompt.example.25",
] as const
+const NON_EMPTY_TEXT = /[^\s\u200B]/
+
export const PromptInput: Component = (props) => {
const sdk = useSDK()
const sync = useSync()
@@ -636,7 +638,9 @@ export const PromptInput: Component = (props) => {
let buffer = ""
const flushText = () => {
- const content = buffer.replace(/\r\n?/g, "\n").replace(/\u200B/g, "")
+ let content = buffer
+ if (content.includes("\r")) content = content.replace(/\r\n?/g, "\n")
+ if (content.includes("\u200B")) content = content.replace(/\u200B/g, "")
buffer = ""
if (!content) return
parts.push({ type: "text", content, start: position, end: position + content.length })
@@ -714,10 +718,12 @@ export const PromptInput: Component = (props) => {
const rawParts = parseFromDOM()
const images = imageAttachments()
const cursorPosition = getCursorPosition(editorRef)
- const rawText = rawParts.map((p) => ("content" in p ? p.content : "")).join("")
- const trimmed = rawText.replace(/\u200B/g, "").trim()
+ const rawText =
+ rawParts.length === 1 && rawParts[0]?.type === "text"
+ ? rawParts[0].content
+ : rawParts.map((p) => ("content" in p ? p.content : "")).join("")
const hasNonText = rawParts.some((part) => part.type !== "text")
- const shouldReset = trimmed.length === 0 && !hasNonText && images.length === 0
+ const shouldReset = !NON_EMPTY_TEXT.test(rawText) && !hasNonText && images.length === 0
if (shouldReset) {
closePopover()
@@ -757,19 +763,31 @@ export const PromptInput: Component = (props) => {
}
const addPart = (part: ContentPart) => {
+ if (part.type === "image") return false
+
const selection = window.getSelection()
- if (!selection || selection.rangeCount === 0) return
+ if (!selection) return false
- const cursorPosition = getCursorPosition(editorRef)
- const currentPrompt = prompt.current()
- const rawText = currentPrompt.map((p) => ("content" in p ? p.content : "")).join("")
- const textBeforeCursor = rawText.substring(0, cursorPosition)
- const atMatch = textBeforeCursor.match(/@(\S*)$/)
+ if (selection.rangeCount === 0 || !editorRef.contains(selection.anchorNode)) {
+ editorRef.focus()
+ const cursor = prompt.cursor() ?? promptLength(prompt.current())
+ setCursorPosition(editorRef, cursor)
+ }
+
+ if (selection.rangeCount === 0) return false
+ const range = selection.getRangeAt(0)
+ if (!editorRef.contains(range.startContainer)) return false
if (part.type === "file" || part.type === "agent") {
+ const cursorPosition = getCursorPosition(editorRef)
+ const rawText = prompt
+ .current()
+ .map((p) => ("content" in p ? p.content : ""))
+ .join("")
+ const textBeforeCursor = rawText.substring(0, cursorPosition)
+ const atMatch = textBeforeCursor.match(/@(\S*)$/)
const pill = createPill(part)
const gap = document.createTextNode(" ")
- const range = selection.getRangeAt(0)
if (atMatch) {
const start = atMatch.index ?? cursorPosition - atMatch[0].length
@@ -784,8 +802,9 @@ export const PromptInput: Component = (props) => {
range.collapse(true)
selection.removeAllRanges()
selection.addRange(range)
- } else if (part.type === "text") {
- const range = selection.getRangeAt(0)
+ }
+
+ if (part.type === "text") {
const fragment = createTextFragment(part.content)
const last = fragment.lastChild
range.deleteContents()
@@ -821,6 +840,7 @@ export const PromptInput: Component = (props) => {
handleInput()
closePopover()
+ return true
}
const addToHistory = (prompt: Prompt, mode: "normal" | "shell") => {
diff --git a/packages/app/src/components/prompt-input/attachments.ts b/packages/app/src/components/prompt-input/attachments.ts
index 9ea2e62a65f0..a9e4e496512c 100644
--- a/packages/app/src/components/prompt-input/attachments.ts
+++ b/packages/app/src/components/prompt-input/attachments.ts
@@ -7,6 +7,19 @@ import { getCursorPosition } from "./editor-dom"
export const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"]
export const ACCEPTED_FILE_TYPES = [...ACCEPTED_IMAGE_TYPES, "application/pdf"]
+const LARGE_PASTE_CHARS = 8000
+const LARGE_PASTE_BREAKS = 120
+
+function largePaste(text: string) {
+ if (text.length >= LARGE_PASTE_CHARS) return true
+ let breaks = 0
+ for (const char of text) {
+ if (char !== "\n") continue
+ breaks += 1
+ if (breaks >= LARGE_PASTE_BREAKS) return true
+ }
+ return false
+}
type PromptAttachmentsInput = {
editor: () => HTMLDivElement | undefined
@@ -14,7 +27,7 @@ type PromptAttachmentsInput = {
isDialogActive: () => boolean
setDraggingType: (type: "image" | "@mention" | null) => void
focusEditor: () => void
- addPart: (part: ContentPart) => void
+ addPart: (part: ContentPart) => boolean
readClipboardImage?: () => Promise
}
@@ -89,6 +102,13 @@ export function createPromptAttachments(input: PromptAttachmentsInput) {
}
if (!plainText) return
+
+ if (largePaste(plainText)) {
+ if (input.addPart({ type: "text", content: plainText, start: 0, end: 0 })) return
+ input.focusEditor()
+ if (input.addPart({ type: "text", content: plainText, start: 0, end: 0 })) return
+ }
+
const inserted = typeof document.execCommand === "function" && document.execCommand("insertText", false, plainText)
if (inserted) return
diff --git a/packages/app/src/components/prompt-input/editor-dom.test.ts b/packages/app/src/components/prompt-input/editor-dom.test.ts
index 15e759f44ac0..3088522a59f6 100644
--- a/packages/app/src/components/prompt-input/editor-dom.test.ts
+++ b/packages/app/src/components/prompt-input/editor-dom.test.ts
@@ -24,6 +24,28 @@ describe("prompt-input editor dom", () => {
expect((container.childNodes[1] as HTMLElement).tagName).toBe("BR")
})
+ test("createTextFragment avoids break-node explosion for large multiline content", () => {
+ const content = Array.from({ length: 220 }, () => "line").join("\n")
+ const fragment = createTextFragment(content)
+ const container = document.createElement("div")
+ container.appendChild(fragment)
+
+ expect(container.childNodes.length).toBe(1)
+ expect(container.childNodes[0]?.nodeType).toBe(Node.TEXT_NODE)
+ expect(container.textContent).toBe(content)
+ })
+
+ test("createTextFragment keeps terminal break in large multiline fallback", () => {
+ const content = `${Array.from({ length: 220 }, () => "line").join("\n")}\n`
+ const fragment = createTextFragment(content)
+ const container = document.createElement("div")
+ container.appendChild(fragment)
+
+ expect(container.childNodes.length).toBe(2)
+ expect(container.childNodes[0]?.textContent).toBe(content.slice(0, -1))
+ expect((container.childNodes[1] as HTMLElement).tagName).toBe("BR")
+ })
+
test("length helpers treat breaks as one char and ignore zero-width chars", () => {
const container = document.createElement("div")
container.appendChild(document.createTextNode("ab\u200B"))
diff --git a/packages/app/src/components/prompt-input/editor-dom.ts b/packages/app/src/components/prompt-input/editor-dom.ts
index 4850a26ecef9..8575140d7d54 100644
--- a/packages/app/src/components/prompt-input/editor-dom.ts
+++ b/packages/app/src/components/prompt-input/editor-dom.ts
@@ -1,5 +1,20 @@
+const MAX_BREAKS = 200
+
export function createTextFragment(content: string): DocumentFragment {
const fragment = document.createDocumentFragment()
+ let breaks = 0
+ for (const char of content) {
+ if (char !== "\n") continue
+ breaks += 1
+ if (breaks > MAX_BREAKS) {
+ const tail = content.endsWith("\n")
+ const text = tail ? content.slice(0, -1) : content
+ if (text) fragment.appendChild(document.createTextNode(text))
+ if (tail) fragment.appendChild(document.createElement("br"))
+ return fragment
+ }
+ }
+
const segments = content.split("\n")
segments.forEach((segment, index) => {
if (segment) {
diff --git a/packages/app/src/components/session/session-context-tab.tsx b/packages/app/src/components/session/session-context-tab.tsx
index 162e016c6f4a..1ea97c395c43 100644
--- a/packages/app/src/components/session/session-context-tab.tsx
+++ b/packages/app/src/components/session/session-context-tab.tsx
@@ -11,6 +11,7 @@ import { Accordion } from "@opencode-ai/ui/accordion"
import { StickyAccordionHeader } from "@opencode-ai/ui/sticky-accordion-header"
import { Code } from "@opencode-ai/ui/code"
import { Markdown } from "@opencode-ai/ui/markdown"
+import { ScrollView } from "@opencode-ai/ui/scroll-view"
import type { Message, Part, UserMessage } from "@opencode-ai/sdk/v2/client"
import { useLanguage } from "@/context/language"
import { getSessionContextMetrics } from "./session-context-metrics"
@@ -268,9 +269,9 @@ export function SessionContextTab() {
})
return (
- {
+ {
scroll = el
restoreScroll()
}}
@@ -336,6 +337,6 @@ export function SessionContextTab() {
-
+
)
}
diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx
index ae8fc200f2d0..825d1dab6cff 100644
--- a/packages/app/src/components/session/session-header.tsx
+++ b/packages/app/src/components/session/session-header.tsx
@@ -452,7 +452,10 @@ export function SessionHeader() {
variant: "ghost",
class:
"rounded-md h-[24px] px-3 border border-border-weak-base bg-surface-panel shadow-none data-[expanded]:bg-surface-base-active",
- classList: { "rounded-r-none": share.shareUrl() !== undefined },
+ classList: {
+ "rounded-r-none": share.shareUrl() !== undefined,
+ "border-r-0": share.shareUrl() !== undefined,
+ },
style: { scale: 1 },
}}
trigger={{language.t("session.share.action.share")}}
diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx
index df71fd77e868..cf993840dc8f 100644
--- a/packages/app/src/components/settings-general.tsx
+++ b/packages/app/src/components/settings-general.tsx
@@ -20,12 +20,17 @@ let demoSoundState = {
// 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.
-const playDemoSound = (src: string) => {
+const stopDemoSound = () => {
if (demoSoundState.cleanup) {
demoSoundState.cleanup()
}
-
clearTimeout(demoSoundState.timeout)
+ demoSoundState.cleanup = undefined
+}
+
+const playDemoSound = (src: string | undefined) => {
+ stopDemoSound()
+ if (!src) return
demoSoundState.timeout = setTimeout(() => {
demoSoundState.cleanup = playSound(src)
@@ -132,11 +137,17 @@ export const SettingsGeneral: Component = () => {
] as const
const fontOptionsList = [...fontOptions]
- const soundOptions = [...SOUND_OPTIONS]
+ const noneSound = { id: "none", label: "sound.option.none", src: undefined } as const
+ const soundOptions = [noneSound, ...SOUND_OPTIONS]
- const soundSelectProps = (current: () => string, set: (id: string) => void) => ({
+ const soundSelectProps = (
+ enabled: () => boolean,
+ current: () => string,
+ setEnabled: (value: boolean) => void,
+ set: (id: string) => void,
+ ) => ({
options: soundOptions,
- current: soundOptions.find((o) => o.id === current()),
+ current: enabled() ? (soundOptions.find((o) => o.id === current()) ?? noneSound) : noneSound,
value: (o: (typeof soundOptions)[number]) => o.id,
label: (o: (typeof soundOptions)[number]) => language.t(o.label),
onHighlight: (option: (typeof soundOptions)[number] | undefined) => {
@@ -145,6 +156,12 @@ export const SettingsGeneral: Component = () => {
},
onSelect: (option: (typeof soundOptions)[number] | undefined) => {
if (!option) return
+ if (option.id === "none") {
+ setEnabled(false)
+ stopDemoSound()
+ return
+ }
+ setEnabled(true)
set(option.id)
playDemoSound(option.src)
},
@@ -250,6 +267,18 @@ export const SettingsGeneral: Component = () => {
)}
+
+
+
+ settings.general.setShowReasoningSummaries(checked)}
+ />
+
+
)
@@ -307,66 +336,45 @@ export const SettingsGeneral: Component = () => {
title={language.t("settings.general.sounds.agent.title")}
description={language.t("settings.general.sounds.agent.description")}
>
-
-
- settings.sounds.setAgentEnabled(checked)}
- />
-
-
+