Compare commits

..
49 Commits
Author SHA1 Message Date
opencode 52716db649 release: v1.0.120
Update Nix Hashes / update (push) Has been cancelled
2025-11-29 02:34:23 +00:00
Dax Raad 9ca4b464ea tui: improve task display in session view to show tool names and completion status 2025-11-28 21:19:15 -05:00
Dax Raad 204a31b6bb Merge remote-tracking branch 'origin/dev' into dev 2025-11-28 21:13:13 -05:00
Dax Raad 813d287a09 core: add explore agent for fast codebase navigation and improve task UI display 2025-11-28 21:13:07 -05:00
Adam 4dd9f33eba fix: diffs double rendering when CSR'd 2025-11-28 20:08:50 -06:00
Adam 5953378a12 fix: theme-color value 2025-11-28 19:57:01 -06:00
Github Action b419eed295 Update Nix flake.lock and hashes 2025-11-29 01:55:02 +00:00
Sebastian Herrlinger 52deb7f352 opentui diffs 2025-11-29 02:52:17 +01:00
Dax Raad a4f3aecbaa ignore 2025-11-28 20:29:58 -05:00
GitHub Action 49ff6a852a chore: format code 2025-11-28 22:43:33 +00:00
Justin Vogt 7f537d2e98 docs: Add tip to tools about subagents re todos (#4875) 2025-11-28 15:49:24 -06:00
Aiden Cline 753443b16f ci: add community contributors to the changelog 2025-11-28 15:44:21 -06:00
Ariane EmoryGitHub Actionopencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>rekram1-nodeAiden Cline
33c63be980 feat: persist thinking blocks display to KV and indicate its current display state in the command_list (resolves #4582) (#4810)
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-11-28 15:01:05 -06:00
Aiden Cline b6efca42b4 ci: use haiku 2025-11-28 14:41:32 -06:00
Github Action fa6eadc39a Update Nix flake.lock and hashes 2025-11-28 20:32:46 +00:00
Aiden Cline 8789acefa6 bump openrouter & google ai sdk packages 2025-11-28 14:30:22 -06:00
Aiden Cline 0e280017e6 Revert "fix: title gen when first msg(s) are shell invocations (#4874)"
This reverts commit 17e8322c29.
2025-11-28 12:18:02 -06:00
Aiden ClineandGitHub Action 17e8322c29 fix: title gen when first msg(s) are shell invocations (#4874)
Co-authored-by: GitHub Action <action@github.com>
2025-11-28 11:55:22 -06:00
Sergio Garciaandopencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> 96eda740cd docs: formatter: false and lsp: false (#4833)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2025-11-28 11:22:48 -06:00
jaovandjesuso fa84612357 tweak: gemini retry message to not be explicitly about gemini 3 (#4864)
Co-authored-by: jesuso <j.ochoa@norteconecta.net>
2025-11-28 11:14:53 -06:00
Dax Raad cf1f63eda3 ci stuff 2025-11-28 10:59:52 -05:00
Dax Raad 9704f5ce89 add otui-diffs 2025-11-28 10:59:07 -05:00
GitHub Action 0eaec2af82 ignore: update download stats 2025-11-28 2025-11-28 12:04:42 +00:00
Adam 398d35dc97 fix: theme-color value 2025-11-28 05:48:07 -06:00
Adam 5efeaae093 fix: desktop and share layouts 2025-11-28 05:35:35 -06:00
Adam cb2dd34a5e fix: unified diff as default 2025-11-28 05:35:35 -06:00
Christoph 7112a706b8 core: add built-in Dart LSP server and formatter (#4841) 2025-11-28 00:33:45 -06:00
Github Action 025a47d01f Update Nix flake.lock and hashes 2025-11-28 06:27:49 +00:00
DS 13f89fdb8f fix: filter empty messages in toModelMessage (#4811) 2025-11-28 00:26:48 -06:00
Aiden Cline cc78d50ef6 bump anthropic package 2025-11-28 00:25:16 -06:00
Adam a8985b1849 fix(desktop): layout 2025-11-27 20:15:49 -06:00
Adam 6a1552f65c fix: unwrap solid store part 2025-11-27 20:15:49 -06:00
Dax Raad 776091cc23 ci: add bun version check to pre-push hook to ensure version consistency 2025-11-27 15:50:23 -05:00
Dax Raad f385524f48 fix lock 2025-11-27 15:48:16 -05:00
Dax Raad 350982e636 tui: simplify model dialog ordering logic to reduce complexity 2025-11-27 14:38:51 -05:00
Dax Raad 5854455815 tui: improve provider dialog text clarity for better user guidance 2025-11-27 14:09:53 -05:00
Dax Raad 9ecaf618db tui: fix provider sorting to prioritize recommended options 2025-11-27 13:54:42 -05:00
Dax Raad 95b667d21e tui: remove cancel keybind hint from prompt dialog to simplify UI 2025-11-27 13:48:34 -05:00
Dax Raad a0b689c140 tui: hide favorite keybind in model dialog when disconnected to prevent errors 2025-11-27 13:42:36 -05:00
GitHub Action ea52ed41be chore: format code 2025-11-27 17:15:41 +00:00
Jay V 5a50d54fda ignore: lock 2025-11-27 12:14:50 -05:00
Jay V 35d118b0c4 ignore: add reply-to support for enterprise form emails 2025-11-27 12:12:44 -05:00
Albert O'SheaandGithub Action ea7c213f5d nix: fix workflow failing on PRs (#4820)
Co-authored-by: Github Action <action@github.com>
2025-11-27 10:05:51 -06:00
Frank 70dd6dd394 doc: slashing kimi k2 thinking price 2025-11-27 09:58:57 -05:00
GitHub Action 049510afbd ignore: update download stats 2025-11-27 2025-11-27 12:04:30 +00:00
Adam c120447fd0 fix: desktop layout and scroll gutters 2025-11-27 05:41:50 -06:00
Adam feb1f36126 fix: session turn margins 2025-11-27 05:25:39 -06:00
GitHub Action d6ef47bb2d chore: format code 2025-11-27 11:06:46 +00:00
Adam 50fd416d49 fix: simpler sanitize 2025-11-27 05:05:55 -06:00
56 changed files with 454 additions and 367 deletions
+1 -1
View File
@@ -30,4 +30,4 @@ jobs:
env: env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
with: with:
model: opencode/glm-4.6 model: opencode/claude-haiku-4-5
+2
View File
@@ -1,11 +1,13 @@
name: snapshot name: snapshot
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
- test-bedrock - test-bedrock
- v0 - v0
- otui-diffs
concurrency: ${{ github.workflow }}-${{ github.ref }} concurrency: ${{ github.workflow }}-${{ github.ref }}
+2
View File
@@ -18,6 +18,7 @@ on:
jobs: jobs:
update: update:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
SYSTEM: x86_64-linux SYSTEM: x86_64-linux
@@ -29,6 +30,7 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0 fetch-depth: 0
ref: ${{ github.head_ref || github.ref_name }} ref: ${{ github.head_ref || github.ref_name }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
- name: Setup Nix - name: Setup Nix
uses: DeterminateSystems/nix-installer-action@v20 uses: DeterminateSystems/nix-installer-action@v20
+7
View File
@@ -1,2 +1,9 @@
#!/bin/sh #!/bin/sh
# Check if bun version matches package.json
EXPECTED_VERSION=$(grep '"packageManager"' package.json | sed 's/.*"bun@\([^"]*\)".*/\1/')
CURRENT_VERSION=$(bun --version)
if [ "$CURRENT_VERSION" != "$EXPECTED_VERSION" ]; then
echo "Error: Bun version $CURRENT_VERSION does not match expected version $EXPECTED_VERSION from package.json"
exit 1
fi
bun typecheck bun typecheck
+7
View File
@@ -16,5 +16,12 @@
"type": "remote", "type": "remote",
"url": "https://mcp.exa.ai/mcp", "url": "https://mcp.exa.ai/mcp",
}, },
"morph": {
"type": "local",
"command": ["bunx", "@morphllm/morphmcp"],
"environment": {
"ENABLED_TOOLS": "warp_grep",
},
},
}, },
} }
+2
View File
@@ -152,3 +152,5 @@
| 2025-11-24 | 856,733 (+10,124) | 804,033 (+8,964) | 1,660,766 (+19,088) | | 2025-11-24 | 856,733 (+10,124) | 804,033 (+8,964) | 1,660,766 (+19,088) |
| 2025-11-25 | 869,423 (+12,690) | 817,339 (+13,306) | 1,686,762 (+25,996) | | 2025-11-25 | 869,423 (+12,690) | 817,339 (+13,306) | 1,686,762 (+25,996) |
| 2025-11-26 | 881,414 (+11,991) | 832,518 (+15,179) | 1,713,932 (+27,170) | | 2025-11-26 | 881,414 (+11,991) | 832,518 (+15,179) | 1,713,932 (+27,170) |
| 2025-11-27 | 893,960 (+12,546) | 846,180 (+13,662) | 1,740,140 (+26,208) |
| 2025-11-28 | 901,741 (+7,781) | 856,482 (+10,302) | 1,758,223 (+18,083) |
+39 -39
View File
@@ -20,7 +20,7 @@
}, },
"packages/console/app": { "packages/console/app": {
"name": "@opencode-ai/console-app", "name": "@opencode-ai/console-app",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"@cloudflare/vite-plugin": "1.15.2", "@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1", "@ibm/plex": "6.4.1",
@@ -48,7 +48,7 @@
}, },
"packages/console/core": { "packages/console/core": {
"name": "@opencode-ai/console-core", "name": "@opencode-ai/console-core",
"version": "1.0.119", "version": "1.0.120",
"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",
@@ -75,7 +75,7 @@
}, },
"packages/console/function": { "packages/console/function": {
"name": "@opencode-ai/console-function", "name": "@opencode-ai/console-function",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"@ai-sdk/anthropic": "2.0.0", "@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2", "@ai-sdk/openai": "2.0.2",
@@ -99,7 +99,7 @@
}, },
"packages/console/mail": { "packages/console/mail": {
"name": "@opencode-ai/console-mail", "name": "@opencode-ai/console-mail",
"version": "1.0.119", "version": "1.0.120",
"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",
@@ -123,7 +123,7 @@
}, },
"packages/desktop": { "packages/desktop": {
"name": "@opencode-ai/desktop", "name": "@opencode-ai/desktop",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"@kobalte/core": "catalog:", "@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
@@ -164,7 +164,7 @@
}, },
"packages/enterprise": { "packages/enterprise": {
"name": "@opencode-ai/enterprise", "name": "@opencode-ai/enterprise",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"@opencode-ai/ui": "workspace:*", "@opencode-ai/ui": "workspace:*",
"@opencode-ai/util": "workspace:*", "@opencode-ai/util": "workspace:*",
@@ -192,7 +192,7 @@
}, },
"packages/function": { "packages/function": {
"name": "@opencode-ai/function", "name": "@opencode-ai/function",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"@octokit/auth-app": "8.0.1", "@octokit/auth-app": "8.0.1",
"@octokit/rest": "22.0.0", "@octokit/rest": "22.0.0",
@@ -208,7 +208,7 @@
}, },
"packages/opencode": { "packages/opencode": {
"name": "opencode", "name": "opencode",
"version": "1.0.119", "version": "1.0.120",
"bin": { "bin": {
"opencode": "./bin/opencode", "opencode": "./bin/opencode",
}, },
@@ -217,10 +217,10 @@
"@actions/github": "6.0.1", "@actions/github": "6.0.1",
"@agentclientprotocol/sdk": "0.5.1", "@agentclientprotocol/sdk": "0.5.1",
"@ai-sdk/amazon-bedrock": "3.0.57", "@ai-sdk/amazon-bedrock": "3.0.57",
"@ai-sdk/anthropic": "2.0.45", "@ai-sdk/anthropic": "2.0.50",
"@ai-sdk/azure": "2.0.73", "@ai-sdk/azure": "2.0.73",
"@ai-sdk/google": "2.0.42", "@ai-sdk/google": "2.0.44",
"@ai-sdk/google-vertex": "3.0.74", "@ai-sdk/google-vertex": "3.0.81",
"@ai-sdk/mcp": "0.0.8", "@ai-sdk/mcp": "0.0.8",
"@ai-sdk/openai": "2.0.71", "@ai-sdk/openai": "2.0.71",
"@ai-sdk/openai-compatible": "1.0.27", "@ai-sdk/openai-compatible": "1.0.27",
@@ -235,9 +235,9 @@
"@opencode-ai/script": "workspace:*", "@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@opencode-ai/util": "workspace:*", "@opencode-ai/util": "workspace:*",
"@openrouter/ai-sdk-provider": "1.2.5", "@openrouter/ai-sdk-provider": "1.2.8",
"@opentui/core": "0.1.50", "@opentui/core": "0.1.51",
"@opentui/solid": "0.1.50", "@opentui/solid": "0.1.51",
"@parcel/watcher": "2.5.1", "@parcel/watcher": "2.5.1",
"@pierre/precision-diffs": "catalog:", "@pierre/precision-diffs": "catalog:",
"@solid-primitives/event-bus": "1.1.2", "@solid-primitives/event-bus": "1.1.2",
@@ -256,7 +256,7 @@
"jsonc-parser": "3.3.1", "jsonc-parser": "3.3.1",
"minimatch": "10.0.3", "minimatch": "10.0.3",
"open": "10.1.2", "open": "10.1.2",
"opentui-spinner": "0.0.5", "opentui-spinner": "0.0.6",
"partial-json": "0.1.7", "partial-json": "0.1.7",
"remeda": "catalog:", "remeda": "catalog:",
"solid-js": "catalog:", "solid-js": "catalog:",
@@ -295,7 +295,7 @@
}, },
"packages/plugin": { "packages/plugin": {
"name": "@opencode-ai/plugin", "name": "@opencode-ai/plugin",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"zod": "catalog:", "zod": "catalog:",
@@ -315,7 +315,7 @@
}, },
"packages/sdk/js": { "packages/sdk/js": {
"name": "@opencode-ai/sdk", "name": "@opencode-ai/sdk",
"version": "1.0.119", "version": "1.0.120",
"devDependencies": { "devDependencies": {
"@hey-api/openapi-ts": "0.81.0", "@hey-api/openapi-ts": "0.81.0",
"@tsconfig/node22": "catalog:", "@tsconfig/node22": "catalog:",
@@ -326,7 +326,7 @@
}, },
"packages/slack": { "packages/slack": {
"name": "@opencode-ai/slack", "name": "@opencode-ai/slack",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1", "@slack/bolt": "^3.17.1",
@@ -339,7 +339,7 @@
}, },
"packages/tauri": { "packages/tauri": {
"name": "@opencode-ai/tauri", "name": "@opencode-ai/tauri",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2",
"@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-opener": "^2",
@@ -352,7 +352,7 @@
}, },
"packages/ui": { "packages/ui": {
"name": "@opencode-ai/ui", "name": "@opencode-ai/ui",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"@kobalte/core": "catalog:", "@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
@@ -384,7 +384,7 @@
}, },
"packages/util": { "packages/util": {
"name": "@opencode-ai/util", "name": "@opencode-ai/util",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"zod": "catalog:", "zod": "catalog:",
}, },
@@ -394,7 +394,7 @@
}, },
"packages/web": { "packages/web": {
"name": "@opencode-ai/web", "name": "@opencode-ai/web",
"version": "1.0.119", "version": "1.0.120",
"dependencies": { "dependencies": {
"@astrojs/cloudflare": "12.6.3", "@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1", "@astrojs/markdown-remark": "6.3.1",
@@ -491,9 +491,9 @@
"@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="],
"@ai-sdk/google": ["@ai-sdk/google@2.0.42", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Jdn+3TZm4iIt62CUjjUoIOshqFIXyzNmUDfkSVV4FcjlSo5+AuhzI1KC7QiNHlqPNejzR6NLIqGJx96VAES34g=="], "@ai-sdk/google": ["@ai-sdk/google@2.0.44", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-c5dck36FjqiVoeeMJQLTEmUheoURcGTU/nBT6iJu8/nZiKFT/y8pD85KMDRB7RerRYaaQOtslR2d6/5PditiRw=="],
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.74", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.45", "@ai-sdk/google": "2.0.42", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "google-auth-library": "^9.15.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W0375p41RQOheAmy7iJGtuJWQWX/aKkO4sJHf6eIYa3bkz93Cbo1aRG1X7ocyMusLZ3dIaW7x6X9WHD8IHkNfg=="], "@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.81", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.50", "@ai-sdk/google": "2.0.44", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", "google-auth-library": "^9.15.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yrl5Ug0Mqwo9ya45oxczgy2RWgpEA/XQQCSFYP+3NZMQ4yA3Iim1vkOjVCsGaZZ8rjVk395abi1ZMZV0/6rqVA=="],
"@ai-sdk/mcp": ["@ai-sdk/mcp@0.0.8", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "pkce-challenge": "^5.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9y9GuGcZ9/+pMIHfpOCJgZVp+AZMv6TkjX2NVT17SQZvTF2N8LXuCXyoUPyi1PxIxzxl0n463LxxaB2O6olC+Q=="], "@ai-sdk/mcp": ["@ai-sdk/mcp@0.0.8", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "pkce-challenge": "^5.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9y9GuGcZ9/+pMIHfpOCJgZVp+AZMv6TkjX2NVT17SQZvTF2N8LXuCXyoUPyi1PxIxzxl0n463LxxaB2O6olC+Q=="],
@@ -1079,27 +1079,27 @@
"@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@1.2.5", "", { "dependencies": { "@openrouter/sdk": "^0.1.8" }, "peerDependencies": { "ai": "^5.0.0", "zod": "^3.24.1 || ^v4" } }, "sha512-NrvJFPvdEUo6DYUQIVWPGfhafuZ2PAIX7+CUMKGknv8TcTNVo0TyP1y5SU7Bgjf/Wup9/74UFKUB07icOhVZjQ=="], "@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@1.2.8", "", { "dependencies": { "@openrouter/sdk": "^0.1.8" }, "peerDependencies": { "ai": "^5.0.0", "zod": "^3.24.1 || ^v4" } }, "sha512-pQT8AzZBKg9f4bkt4doF486ZlhK0XjKkevrLkiqYgfh1Jplovieu28nK4Y+xy3sF18/mxjqh9/2y6jh01qzLrA=="],
"@openrouter/sdk": ["@openrouter/sdk@0.1.27", "", { "dependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-RH//L10bSmc81q25zAZudiI4kNkLgxF2E+WU42vghp3N6TEvZ6F0jK7uT3tOxkEn91gzmMw9YVmDENy7SJsajQ=="], "@openrouter/sdk": ["@openrouter/sdk@0.1.27", "", { "dependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-RH//L10bSmc81q25zAZudiI4kNkLgxF2E+WU42vghp3N6TEvZ6F0jK7uT3tOxkEn91gzmMw9YVmDENy7SJsajQ=="],
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
"@opentui/core": ["@opentui/core@0.1.50", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.50", "@opentui/core-darwin-x64": "0.1.50", "@opentui/core-linux-arm64": "0.1.50", "@opentui/core-linux-x64": "0.1.50", "@opentui/core-win32-arm64": "0.1.50", "@opentui/core-win32-x64": "0.1.50", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-QhjwT2f8AIQj0gbL/WQ2M93sl2/qp9+Kqxyh4dOhp8z3qnTc5D7J105VrMyeWZW7/P27ubgbFAqqWXrZ4FsuLw=="], "@opentui/core": ["@opentui/core@0.1.51", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.51", "@opentui/core-darwin-x64": "0.1.51", "@opentui/core-linux-arm64": "0.1.51", "@opentui/core-linux-x64": "0.1.51", "@opentui/core-win32-arm64": "0.1.51", "@opentui/core-win32-x64": "0.1.51", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-9w9vg2nYC4eTKdh5en7WpBB44Nrib3uMtcPNXr2JxftjzDXU5Qmcv3vbKbxHqgzgk7FYm2Z9OFAOk7innbnplA=="],
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.50", "", { "os": "darwin", "cpu": "arm64" }, "sha512-FKqTDOsZl9TXF7KN2SdZKoRHQNvqKSY27AG3jhKCoiyLGdaNCAsaeBWqAmpnL4E4kMkV3aiQSCrKTrYsaevvOg=="], "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.51", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5EicEs5JQiMmr3rzKdGfHnsRXJ7cv/pxz0/C2Xcg+jKMSUmUvS7LE3Mi3HBY05GjZyB/EZ3bYyHqXB4nanYTNg=="],
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.50", "", { "os": "darwin", "cpu": "x64" }, "sha512-GczVNqqpM/HtsgeBB08K6zL1B7oc6Y5G2cMklo06LrYRdDkFdDtY5fNNnJR2/psZWzTrI3M+sLnKWgUGD5CxUQ=="], "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.51", "", { "os": "darwin", "cpu": "x64" }, "sha512-mG9WlKdv0yWCIldFEAzgnm1NCyOtfiVG8zVZLiGaLhsL7Jii+f4RpOmlvFlb2sSSTxjC3HxRzWPC7WBSzk4txA=="],
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.50", "", { "os": "linux", "cpu": "arm64" }, "sha512-+CKMhweEXH0tLGM6qqaqk6DyCEmwrTVubTtez/pSM3GgcROSXIBui9TEZpIlPgSCVmjbotGS6eSIg4oU+p9o7w=="], "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.51", "", { "os": "linux", "cpu": "arm64" }, "sha512-CNUx8nvkKRCsoLg/z7W8tD0hBFEUE9yEpqgyACc/ODdaaRLrUPQkBgUYWM/XGV9OnoqnpqLSKf95+DoUaTZ0lA=="],
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.50", "", { "os": "linux", "cpu": "x64" }, "sha512-yv5KWiMohAK9bsi1gth9DDZDpoJA1EDHexjhThsPT8EH82g13T088dnJZuJWUE9dr1OwTCQG8DyorNxX3ViEGQ=="], "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.51", "", { "os": "linux", "cpu": "x64" }, "sha512-GpZ8vqX2dPyvBKvCRkBJp5x7zsmbixNCsCfzSVG9VIAVyT6qhylT2wrTOTGJLzDfc12ugdeL/1WUIR80YvwARA=="],
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.50", "", { "os": "win32", "cpu": "arm64" }, "sha512-6/6pURTRNTLFKF8IhYVi7U+T/HGMeURav9LIYw7yfcOibd0kLMthmemhS0Lzyk5dmtp0T4V4NmRmtlq/fIzyjQ=="], "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.51", "", { "os": "win32", "cpu": "arm64" }, "sha512-eIc13B9dmoJ2x0EEsd4JWKCLzgVPBv+6Gn6vbjaSL9T/+14XRKMQuEdnQmU6+/KruAumfK+qSXlFdmjhFKrDOA=="],
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.50", "", { "os": "win32", "cpu": "x64" }, "sha512-EME8GBFq9uCLbH5js8fH7/xY4ZtLIZlt3bkYKT6lPiCNdaf/6ebg+F/ObPXFkJrc8VeV1ql2bXhQ6RLi7izvAA=="], "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.51", "", { "os": "win32", "cpu": "x64" }, "sha512-ytpWgA3oNLehI5s9pxLAtg5kVO7Npq6onGPTZUikM5LlH71bX3Bm4PP8qLkD0AG9zDiO7ndp93ZFuT9yGqKPiQ=="],
"@opentui/solid": ["@opentui/solid@0.1.50", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.50", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-q778kp/eksh8UOPSQO2h8h9CGGDqepTf9u2WYTS2HYHRAI2SRtUWpN9L7Euyt3BtG9L/wpsIOHK/ufPhQH1X6A=="], "@opentui/solid": ["@opentui/solid@0.1.51", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.51", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-CBohbgFjUVG2P6/iAN52OCGaK0s5Wc2VWKyNrs7Fd9mFDMrC/IfrGAeaDeJXdQ8T0YhZ0PiUVijzFILCi2yOUw=="],
"@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=="],
@@ -2969,7 +2969,7 @@
"openid-client": ["openid-client@5.6.4", "", { "dependencies": { "jose": "^4.15.4", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA=="], "openid-client": ["openid-client@5.6.4", "", { "dependencies": { "jose": "^4.15.4", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA=="],
"opentui-spinner": ["opentui-spinner@0.0.5", "", { "dependencies": { "cli-spinners": "^3.3.0" }, "peerDependencies": { "@opentui/core": "^0.1.49", "@opentui/react": "^0.1.49", "@opentui/solid": "^0.1.49", "typescript": "^5" }, "optionalPeers": ["@opentui/react", "@opentui/solid"] }, "sha512-abSWzWA7eyuD0PjerAWbBznLmOQn+8xRDaLGCVIs4ctETi2laNFr5KwicYnPXsHZpPc2neV7WtQm+diCEfOhLA=="], "opentui-spinner": ["opentui-spinner@0.0.6", "", { "dependencies": { "cli-spinners": "^3.3.0" }, "peerDependencies": { "@opentui/core": "^0.1.49", "@opentui/react": "^0.1.49", "@opentui/solid": "^0.1.49", "typescript": "^5" }, "optionalPeers": ["@opentui/react", "@opentui/solid"] }, "sha512-xupLOeVQEAXEvVJCvHkfX6fChDWmJIPHe5jyUrVb8+n4XVTX8mBNhitFfB9v2ZbkC1H2UwPab/ElePHoW37NcA=="],
"own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
@@ -3743,11 +3743,11 @@
"@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], "@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
"@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], "@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ=="],
"@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.45", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Ipv62vavDCmrV/oE/lXehL9FzwQuZOnnlhPEftWizx464Wb6lvnBTJx8uhmEYruFSzOWTI95Z33ncZ4tA8E6RQ=="], "@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.50", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw=="],
"@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], "@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ=="],
"@ai-sdk/mcp/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], "@ai-sdk/mcp/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
@@ -4073,7 +4073,7 @@
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.45", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Ipv62vavDCmrV/oE/lXehL9FzwQuZOnnlhPEftWizx464Wb6lvnBTJx8uhmEYruFSzOWTI95Z33ncZ4tA8E6RQ=="], "opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.50", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw=="],
"opencode/@ai-sdk/openai": ["@ai-sdk/openai@2.0.71", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tg+gj+R0z/On9P4V7hy7/7o04cQPjKGayMCL3gzWD/aNGjAKkhEnaocuNDidSnghizt8g2zJn16cAuAolnW+qQ=="], "opencode/@ai-sdk/openai": ["@ai-sdk/openai@2.0.71", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tg+gj+R0z/On9P4V7hy7/7o04cQPjKGayMCL3gzWD/aNGjAKkhEnaocuNDidSnghizt8g2zJn16cAuAolnW+qQ=="],
@@ -4619,7 +4619,7 @@
"jsonwebtoken/jws/jwa": ["jwa@1.4.2", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw=="], "jsonwebtoken/jws/jwa": ["jwa@1.4.2", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw=="],
"opencode/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], "opencode/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ=="],
"opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], "opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
Generated
+3 -3
View File
@@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1764138170, "lastModified": 1764290847,
"narHash": "sha256-2bCmfCUZyi2yj9FFXYKwsDiaZmizN75cLhI/eWmf3tk=", "narHash": "sha256-VwPgoDgnd628GdE3KyLqTyPF1WWh0VwT5UoKygoi8sg=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "bb813de6d2241bcb1b5af2d3059f560c66329967", "rev": "cd5fedfc384cb98d9fd3827b55f4522f49efda42",
"type": "github" "type": "github"
}, },
"original": { "original": {
+1 -1
View File
@@ -1,3 +1,3 @@
{ {
"nodeModules": "sha256-dTGBX5mde/hQP36MSFwq3G81OdwpcYRl8bcjLpesbPw=" "nodeModules": "sha256-4OrnnZy44edgShLMUrYFsO+07gAnL//pGKO/ehWu5P4="
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/console-app", "name": "@opencode-ai/console-app",
"version": "1.0.119", "version": "1.0.120",
"type": "module", "type": "module",
"scripts": { "scripts": {
"typecheck": "tsgo --noEmit", "typecheck": "tsgo --noEmit",
@@ -36,6 +36,7 @@ ${body.email}`.trim()
to: "contact@anoma.ly", to: "contact@anoma.ly",
subject: `Enterprise Inquiry from ${body.name}`, subject: `Enterprise Inquiry from ${body.name}`,
body: emailContent, body: emailContent,
replyTo: body.email,
}) })
return Response.json({ success: true, message: "Form submitted successfully" }, { status: 200 }) return Response.json({ success: true, message: "Form submitted successfully" }, { status: 200 })
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core", "name": "@opencode-ai/console-core",
"version": "1.0.119", "version": "1.0.120",
"private": true, "private": true,
"type": "module", "type": "module",
"dependencies": { "dependencies": {
+2
View File
@@ -22,6 +22,7 @@ export namespace AWS {
to: z.string(), to: z.string(),
subject: z.string(), subject: z.string(),
body: z.string(), body: z.string(),
replyTo: z.string().optional(),
}), }),
async (input) => { async (input) => {
const res = await createClient().fetch("https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", { const res = await createClient().fetch("https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", {
@@ -35,6 +36,7 @@ export namespace AWS {
Destination: { Destination: {
ToAddresses: [input.to], ToAddresses: [input.to],
}, },
...(input.replyTo && { ReplyToAddresses: [input.replyTo] }),
Content: { Content: {
Simple: { Simple: {
Subject: { Subject: {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/console-function", "name": "@opencode-ai/console-function",
"version": "1.0.119", "version": "1.0.120",
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"private": true, "private": true,
"type": "module", "type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/console-mail", "name": "@opencode-ai/console-mail",
"version": "1.0.119", "version": "1.0.120",
"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",
+2 -1
View File
@@ -9,7 +9,8 @@
<link rel="shortcut icon" href="/favicon.ico" /> <link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" /> <link rel="manifest" href="/site.webmanifest" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#F8F7F7" />
<meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
<meta property="og:image" content="/social-share.png" /> <meta property="og:image" content="/social-share.png" />
<meta property="twitter:image" content="/social-share.png" /> <meta property="twitter:image" content="/social-share.png" />
</head> </head>
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/desktop", "name": "@opencode-ai/desktop",
"version": "1.0.119", "version": "1.0.120",
"description": "", "description": "",
"type": "module", "type": "module",
"scripts": { "scripts": {
+25 -14
View File
@@ -333,14 +333,19 @@ export default function Page() {
flex: layout.review.state() === "pane", flex: layout.review.state() === "pane",
}} }}
> >
<div class="relative shrink-0 py-3 flex flex-col gap-6 flex-1 min-h-0 w-full max-w-2xl mx-auto"> <div
classList={{
"relative shrink-0 py-3 flex flex-col gap-6 flex-1 min-h-0 w-full": true,
"max-w-146 mx-auto": !wide(),
}}
>
<Switch> <Switch>
<Match when={session.id}> <Match when={session.id}>
<div class="flex items-start justify-start h-full min-h-0"> <div class="flex items-start justify-start h-full min-h-0">
<Show when={session.messages.user().length > 1}> <Show when={session.messages.user().length > 1}>
<> <>
<MessageNav <MessageNav
class="@6xl:hidden mt-3 mr-8" class="@6xl:hidden mt-2.5 absolute left-6"
messages={session.messages.user()} messages={session.messages.user()}
current={session.messages.active()} current={session.messages.active()}
onMessageSelect={session.messages.setActive} onMessageSelect={session.messages.setActive}
@@ -349,9 +354,9 @@ export default function Page() {
/> />
<MessageNav <MessageNav
classList={{ classList={{
"hidden @6xl:flex": true, "hidden @6xl:flex absolute": true,
"mt-0.5 mr-3 absolute right-full": wide(), "mt-0.5 left-[calc(((100%_-_min(100%,_36.5rem))_/_2)-1.5rem)] -translate-x-full": wide(),
"mt-3 mr-8": !wide(), "mt-2.5 left-6": !wide(),
}} }}
messages={session.messages.user()} messages={session.messages.user()}
current={session.messages.active()} current={session.messages.active()}
@@ -364,12 +369,16 @@ export default function Page() {
<SessionTurn <SessionTurn
sessionID={session.id!} sessionID={session.id!}
messageID={session.messages.active()?.id!} messageID={session.messages.active()?.id!}
classes={{ root: "pb-20 flex-1 min-w-0", content: "pb-20", container: "px-6" }} classes={{
root: "pb-20 flex-1 min-w-0",
content: "pb-20",
container: "w-full " + (wide() ? "max-w-146 mx-auto px-6" : "pr-6 pl-18"),
}}
/> />
</div> </div>
</Match> </Match>
<Match when={true}> <Match when={true}>
<div class="size-full flex flex-col pb-45 justify-end items-start gap-4 flex-[1_0_0] self-stretch"> <div class="size-full flex flex-col pb-45 justify-end items-start gap-4 flex-[1_0_0] self-stretch max-w-146 mx-auto px-6">
<div class="text-20-medium text-text-weaker">New session</div> <div class="text-20-medium text-text-weaker">New session</div>
<div class="flex justify-center items-center gap-3"> <div class="flex justify-center items-center gap-3">
<Icon name="folder" size="small" /> <Icon name="folder" size="small" />
@@ -390,12 +399,14 @@ export default function Page() {
</div> </div>
</Match> </Match>
</Switch> </Switch>
<div class="absolute inset-x-0 px-6 max-w-2xl flex flex-col justify-center items-center z-50 mx-auto bottom-8"> <div class="absolute inset-x-0 bottom-8 flex flex-col justify-center items-center z-50">
<PromptInput <div class="w-full max-w-146 px-6">
ref={(el) => { <PromptInput
inputRef = el ref={(el) => {
}} inputRef = el
/> }}
/>
</div>
</div> </div>
</div> </div>
<Show when={layout.review.state() === "pane" && session.diffs().length}> <Show when={layout.review.state() === "pane" && session.diffs().length}>
@@ -498,7 +509,7 @@ export default function Page() {
</DragOverlay> </DragOverlay>
</DragDropProvider> </DragDropProvider>
<Show when={session.layout.tabs.active}> <Show when={session.layout.tabs.active}>
<div class="absolute inset-x-0 px-6 max-w-2xl flex flex-col justify-center items-center z-50 mx-auto bottom-8"> <div class="absolute inset-x-0 px-6 max-w-146 flex flex-col justify-center items-center z-50 mx-auto bottom-8">
<PromptInput <PromptInput
ref={(el) => { ref={(el) => {
inputRef = el inputRef = el
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/enterprise", "name": "@opencode-ai/enterprise",
"version": "1.0.119", "version": "1.0.120",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
+2
View File
@@ -9,6 +9,8 @@ export default createHandler(() => (
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OpenCode</title> <title>OpenCode</title>
<meta name="theme-color" content="#F8F7F7" />
<meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
<meta property="og:image" content="/social-share.png" /> <meta property="og:image" content="/social-share.png" />
<meta property="twitter:image" content="/social-share.png" /> <meta property="twitter:image" content="/social-share.png" />
{assets} {assets}
@@ -171,7 +171,7 @@ export default function () {
}) })
const title = () => ( const title = () => (
<div class="flex flex-col gap-4 shrink-0"> <div class="flex flex-col gap-4">
<div class="h-8 flex gap-4 items-center justify-start self-stretch"> <div class="h-8 flex gap-4 items-center justify-start self-stretch">
<div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base"> <div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base">
<Mark class="shrink-0 w-3 my-0.5" /> <Mark class="shrink-0 w-3 my-0.5" />
@@ -215,7 +215,6 @@ export default function () {
) )
const wide = createMemo(() => diffs().length === 0) const wide = createMemo(() => diffs().length === 0)
const columnPadding = () => (wide() ? "px-6" : "px-21 @4xl:px-6")
return ( return (
<div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col"> <div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col">
@@ -243,44 +242,44 @@ export default function () {
</div> </div>
</header> </header>
<div class="select-text flex flex-col flex-1 min-h-0"> <div class="select-text flex flex-col flex-1 min-h-0">
<div class="hidden md:flex w-full flex-1 min-h-0"> <div classList={{ "hidden w-full flex-1 min-h-0": true, "md:flex": wide(), "lg:flex": !wide() }}>
<div <div
classList={{ classList={{
"@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full mx-auto": true, "@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full": true,
"max-w-2xl": true, "mx-auto max-w-146": !wide(),
}} }}
> >
<div class={columnPadding()}>{title()}</div> <div
classList={{
"w-full flex justify-start items-start min-w-0": true,
"max-w-146 mx-auto px-6": wide(),
"pr-6 pl-18": !wide(),
}}
>
{title()}
</div>
<div class="flex items-start justify-start h-full min-h-0"> <div class="flex items-start justify-start h-full min-h-0">
<Show when={messages().length > 1}> <Show when={messages().length > 1}>
<> <>
<div class="md:hidden absolute right-full"> <MessageNav
<MessageNav class="@6xl:hidden mt-2.5 absolute left-6"
class="mt-2 mr-3" messages={messages()}
messages={messages()} current={activeMessage()}
current={activeMessage()} onMessageSelect={setActiveMessage}
onMessageSelect={setActiveMessage} size="compact"
size="compact" />
/> <MessageNav
</div>
<div
classList={{ classList={{
"hidden md:block": true, "hidden @6xl:flex absolute": true,
"absolute right-[90%]": !wide(), "mt-0.5 left-[calc(((100%_-_min(100%,_36.5rem))_/_2)-1.5rem)] -translate-x-full":
"absolute right-full": wide(), wide(),
"mt-2.5 left-6": !wide(),
}} }}
> messages={messages()}
<MessageNav current={activeMessage()}
classList={{ onMessageSelect={setActiveMessage}
"mt-2.5 mr-3": !wide(), size={wide() ? "normal" : "compact"}
"mt-0.5 mr-8": wide(), />
}}
messages={messages()}
current={activeMessage()}
onMessageSelect={setActiveMessage}
size={wide() ? "normal" : "compact"}
/>
</div>
</> </>
</Show> </Show>
<SessionTurn <SessionTurn
@@ -288,11 +287,11 @@ export default function () {
messageID={store.messageId ?? firstUserMessage()!.id!} messageID={store.messageId ?? firstUserMessage()!.id!}
classes={{ classes={{
root: "grow", root: "grow",
content: "flex flex-col justify-between", content: "flex flex-col justify-between items-start",
container: `${columnPadding()} pb-20`, container: "w-full pb-20 " + (wide() ? "max-w-146 mx-auto px-6" : "pr-6 pl-18"),
}} }}
> >
<div class={`${columnPadding()} flex items-center justify-center pb-8 shrink-0`}> <div classList={{ "w-full flex items-center justify-center pb-8 shrink-0": true }}>
<Logo class="w-58.5 opacity-12" /> <Logo class="w-58.5 opacity-12" />
</div> </div>
</SessionTurn> </SessionTurn>
@@ -313,7 +312,7 @@ export default function () {
</div> </div>
<Switch> <Switch>
<Match when={diffs().length > 0}> <Match when={diffs().length > 0}>
<Tabs class="md:hidden"> <Tabs classList={{ "md:hidden": wide(), "lg:hidden": !wide() }}>
<Tabs.List> <Tabs.List>
<Tabs.Trigger value="session" class="w-1/2" classes={{ button: "w-full" }}> <Tabs.Trigger value="session" class="w-1/2" classes={{ button: "w-full" }}>
Session Session
@@ -344,7 +343,9 @@ export default function () {
</Tabs> </Tabs>
</Match> </Match>
<Match when={true}> <Match when={true}>
<div class="md:hidden !overflow-hidden">{turns()}</div> <div classList={{ "!overflow-hidden": true, "md:hidden": wide(), "lg:hidden": !wide() }}>
{turns()}
</div>
</Match> </Match>
</Switch> </Switch>
</div> </div>
+6 -6
View File
@@ -1,7 +1,7 @@
id = "opencode" id = "opencode"
name = "OpenCode" name = "OpenCode"
description = "The AI coding agent built for the terminal" description = "The AI coding agent built for the terminal"
version = "1.0.119" version = "1.0.120"
schema_version = 1 schema_version = 1
authors = ["Anomaly"] authors = ["Anomaly"]
repository = "https://github.com/sst/opencode" repository = "https://github.com/sst/opencode"
@@ -11,26 +11,26 @@ name = "OpenCode"
icon = "./icons/opencode.svg" icon = "./icons/opencode.svg"
[agent_servers.opencode.targets.darwin-aarch64] [agent_servers.opencode.targets.darwin-aarch64]
archive = "https://github.com/sst/opencode/releases/download/v1.0.119/opencode-darwin-arm64.zip" archive = "https://github.com/sst/opencode/releases/download/v1.0.120/opencode-darwin-arm64.zip"
cmd = "./opencode" cmd = "./opencode"
args = ["acp"] args = ["acp"]
[agent_servers.opencode.targets.darwin-x86_64] [agent_servers.opencode.targets.darwin-x86_64]
archive = "https://github.com/sst/opencode/releases/download/v1.0.119/opencode-darwin-x64.zip" archive = "https://github.com/sst/opencode/releases/download/v1.0.120/opencode-darwin-x64.zip"
cmd = "./opencode" cmd = "./opencode"
args = ["acp"] args = ["acp"]
[agent_servers.opencode.targets.linux-aarch64] [agent_servers.opencode.targets.linux-aarch64]
archive = "https://github.com/sst/opencode/releases/download/v1.0.119/opencode-linux-arm64.zip" archive = "https://github.com/sst/opencode/releases/download/v1.0.120/opencode-linux-arm64.zip"
cmd = "./opencode" cmd = "./opencode"
args = ["acp"] args = ["acp"]
[agent_servers.opencode.targets.linux-x86_64] [agent_servers.opencode.targets.linux-x86_64]
archive = "https://github.com/sst/opencode/releases/download/v1.0.119/opencode-linux-x64.zip" archive = "https://github.com/sst/opencode/releases/download/v1.0.120/opencode-linux-x64.zip"
cmd = "./opencode" cmd = "./opencode"
args = ["acp"] args = ["acp"]
[agent_servers.opencode.targets.windows-x86_64] [agent_servers.opencode.targets.windows-x86_64]
archive = "https://github.com/sst/opencode/releases/download/v1.0.119/opencode-windows-x64.zip" archive = "https://github.com/sst/opencode/releases/download/v1.0.120/opencode-windows-x64.zip"
cmd = "./opencode.exe" cmd = "./opencode.exe"
args = ["acp"] args = ["acp"]
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/function", "name": "@opencode-ai/function",
"version": "1.0.119", "version": "1.0.120",
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"private": true, "private": true,
"type": "module", "type": "module",
+8 -8
View File
@@ -1,6 +1,6 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"version": "1.0.119", "version": "1.0.120",
"name": "opencode", "name": "opencode",
"type": "module", "type": "module",
"private": true, "private": true,
@@ -43,10 +43,10 @@
"@actions/github": "6.0.1", "@actions/github": "6.0.1",
"@agentclientprotocol/sdk": "0.5.1", "@agentclientprotocol/sdk": "0.5.1",
"@ai-sdk/amazon-bedrock": "3.0.57", "@ai-sdk/amazon-bedrock": "3.0.57",
"@ai-sdk/anthropic": "2.0.45", "@ai-sdk/anthropic": "2.0.50",
"@ai-sdk/azure": "2.0.73", "@ai-sdk/azure": "2.0.73",
"@ai-sdk/google": "2.0.42", "@ai-sdk/google": "2.0.44",
"@ai-sdk/google-vertex": "3.0.74", "@ai-sdk/google-vertex": "3.0.81",
"@ai-sdk/mcp": "0.0.8", "@ai-sdk/mcp": "0.0.8",
"@ai-sdk/openai": "2.0.71", "@ai-sdk/openai": "2.0.71",
"@ai-sdk/openai-compatible": "1.0.27", "@ai-sdk/openai-compatible": "1.0.27",
@@ -61,9 +61,9 @@
"@opencode-ai/script": "workspace:*", "@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@opencode-ai/util": "workspace:*", "@opencode-ai/util": "workspace:*",
"@openrouter/ai-sdk-provider": "1.2.5", "@openrouter/ai-sdk-provider": "1.2.8",
"@opentui/core": "0.1.50", "@opentui/core": "0.1.51",
"@opentui/solid": "0.1.50", "@opentui/solid": "0.1.51",
"@parcel/watcher": "2.5.1", "@parcel/watcher": "2.5.1",
"@pierre/precision-diffs": "catalog:", "@pierre/precision-diffs": "catalog:",
"@solid-primitives/event-bus": "1.1.2", "@solid-primitives/event-bus": "1.1.2",
@@ -82,7 +82,7 @@
"jsonc-parser": "3.3.1", "jsonc-parser": "3.3.1",
"minimatch": "10.0.3", "minimatch": "10.0.3",
"open": "10.1.2", "open": "10.1.2",
"opentui-spinner": "0.0.5", "opentui-spinner": "0.0.6",
"partial-json": "0.1.7", "partial-json": "0.1.7",
"remeda": "catalog:", "remeda": "catalog:",
"solid-js": "catalog:", "solid-js": "catalog:",
+35
View File
@@ -114,6 +114,41 @@ export namespace Agent {
mode: "subagent", mode: "subagent",
builtIn: true, builtIn: true,
}, },
explore: {
name: "explore",
tools: {
todoread: false,
todowrite: false,
edit: false,
write: false,
...defaultTools,
},
description: `Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. "src/components/**/*.tsx"), search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "very thorough" for comprehensive analysis across multiple locations and naming conventions. (Tools: All tools)`,
prompt: [
`You are a file search specialist. You excel at thoroughly navigating and exploring codebases.`,
``,
`Your strengths:`,
`- Rapidly finding files using glob patterns`,
`- Searching code and text with powerful regex patterns`,
`- Reading and analyzing file contents`,
``,
`Guidelines:`,
`- Use Glob for broad file pattern matching`,
`- Use Grep for searching file contents with regex`,
`- Use Read when you know the specific file path you need to read`,
`- Use Bash for file operations like copying, moving, or listing directory contents`,
`- Adapt your search approach based on the thoroughness level specified by the caller`,
`- Return file paths as absolute paths in your final response`,
`- For clear communication, avoid using emojis`,
`- Do not create any files, or run bash commands that modify the user's system state in any way`,
``,
`Complete the user's search request efficiently and report your findings clearly.`,
].join("\n"),
options: {},
permission: agentPermission,
mode: "subagent",
builtIn: true,
},
build: { build: {
name: "build", name: "build",
tools: { ...defaultTools }, tools: { ...defaultTools },
@@ -6,6 +6,7 @@ import { DialogSelect, type DialogSelectRef } from "@tui/ui/dialog-select"
import { useDialog } from "@tui/ui/dialog" import { useDialog } from "@tui/ui/dialog"
import { createDialogProviderOptions, DialogProvider } from "./dialog-provider" import { createDialogProviderOptions, DialogProvider } from "./dialog-provider"
import { Keybind } from "@/util/keybind" import { Keybind } from "@/util/keybind"
import { iife } from "@/util/iife"
export function DialogModel() { export function DialogModel() {
const local = useLocal() const local = useLocal()
@@ -21,73 +22,48 @@ export function DialogModel() {
const options = createMemo(() => { const options = createMemo(() => {
const query = ref()?.filter const query = ref()?.filter
const favorites = local.model.favorite() const favorites = connected() ? local.model.favorite() : []
const recents = local.model.recent() const recents = local.model.recent()
const currentModel = local.model.current()
const orderedRecents = currentModel const recentList = recents
? [
currentModel,
...recents.filter(
(item) => item.providerID !== currentModel.providerID || item.modelID !== currentModel.modelID,
),
]
: recents
const isCurrent = (item: { providerID: string; modelID: string }) =>
currentModel && item.providerID === currentModel.providerID && item.modelID === currentModel.modelID
const currentIsFavorite = currentModel && favorites.some((fav) => isCurrent(fav))
const recentList = orderedRecents
.filter((item) => !favorites.some((fav) => fav.providerID === item.providerID && fav.modelID === item.modelID)) .filter((item) => !favorites.some((fav) => fav.providerID === item.providerID && fav.modelID === item.modelID))
.slice(0, 5) .slice(0, 5)
const orderedFavorites = currentModel const favoriteOptions = !query
? [...favorites.filter((item) => isCurrent(item)), ...favorites.filter((item) => !isCurrent(item))] ? favorites.flatMap((item) => {
: favorites const provider = sync.data.provider.find((x) => x.id === item.providerID)
if (!provider) return []
const orderedRecentList = const model = provider.models[item.modelID]
currentModel && !currentIsFavorite if (!model) return []
? [...recentList.filter((item) => isCurrent(item)), ...recentList.filter((item) => !isCurrent(item))] return [
: recentList {
key: item,
const favoriteOptions = value: {
!query && favorites.length > 0 providerID: provider.id,
? orderedFavorites.flatMap((item) => { modelID: model.id,
const provider = sync.data.provider.find((x) => x.id === item.providerID)
if (!provider) return []
const model = provider.models[item.modelID]
if (!model) return []
return [
{
key: item,
value: {
providerID: provider.id,
modelID: model.id,
},
title: model.name ?? item.modelID,
description: `${provider.name}`,
category: "Favorites",
disabled: provider.id === "opencode" && model.id.includes("-nano"),
footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
onSelect: () => {
dialog.clear()
local.model.set(
{
providerID: provider.id,
modelID: model.id,
},
{ recent: true },
)
},
}, },
] title: model.name ?? item.modelID,
}) description: provider.name,
: [] category: "Favorites",
disabled: provider.id === "opencode" && model.id.includes("-nano"),
footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
onSelect: () => {
dialog.clear()
local.model.set(
{
providerID: provider.id,
modelID: model.id,
},
{ recent: true },
)
},
},
]
})
: []
const recentOptions = !query const recentOptions = !query
? orderedRecentList.flatMap((item) => { ? recentList.flatMap((item) => {
const provider = sync.data.provider.find((x) => x.id === item.providerID) const provider = sync.data.provider.find((x) => x.id === item.providerID)
if (!provider) return [] if (!provider) return []
const model = provider.models[item.modelID] const model = provider.models[item.modelID]
@@ -137,13 +113,14 @@ export function DialogModel() {
providerID: provider.id, providerID: provider.id,
modelID: model, modelID: model,
} }
const favorite = favorites.some(
(item) => item.providerID === value.providerID && item.modelID === value.modelID,
)
return { return {
value, value,
title: info.name ?? model, title: info.name ?? model,
description: connected() ? `${provider.name}${favorite ? " ★" : ""}` : undefined, description: favorites.some(
(item) => item.providerID === value.providerID && item.modelID === value.modelID,
)
? "(Favorite)"
: undefined,
category: connected() ? provider.name : undefined, category: connected() ? provider.name : undefined,
disabled: provider.id === "opencode" && model.includes("-nano"), disabled: provider.id === "opencode" && model.includes("-nano"),
footer: info.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined, footer: info.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined,
@@ -165,10 +142,10 @@ export function DialogModel() {
const inFavorites = favorites.some( const inFavorites = favorites.some(
(item) => item.providerID === value.providerID && item.modelID === value.modelID, (item) => item.providerID === value.providerID && item.modelID === value.modelID,
) )
const inRecents = orderedRecents.some( if (inFavorites) return false
const inRecents = recents.some(
(item) => item.providerID === value.providerID && item.modelID === value.modelID, (item) => item.providerID === value.providerID && item.modelID === value.modelID,
) )
if (inFavorites) return false
if (inRecents) return false if (inRecents) return false
return true return true
}), }),
@@ -196,7 +173,7 @@ export function DialogModel() {
keybind={[ keybind={[
{ {
keybind: { ctrl: true, name: "a", meta: false, shift: false, leader: false }, keybind: { ctrl: true, name: "a", meta: false, shift: false, leader: false },
title: connected() ? "Connect provider" : "More providers", title: connected() ? "Connect provider" : "View all providers",
onTrigger() { onTrigger() {
dialog.replace(() => <DialogProvider />) dialog.replace(() => <DialogProvider />)
}, },
@@ -204,6 +181,7 @@ export function DialogModel() {
{ {
keybind: Keybind.parse("ctrl+f")[0], keybind: Keybind.parse("ctrl+f")[0],
title: "Favorite", title: "Favorite",
disabled: !connected(),
onTrigger: (option) => { onTrigger: (option) => {
local.model.toggleFavorite(option.value as { providerID: string; modelID: string }) local.model.toggleFavorite(option.value as { providerID: string; modelID: string })
}, },
@@ -26,13 +26,15 @@ export function createDialogProviderOptions() {
const options = createMemo(() => { const options = createMemo(() => {
return pipe( return pipe(
sync.data.provider_next.all, sync.data.provider_next.all,
sortBy((x) => PROVIDER_PRIORITY[x.id] ?? 99),
map((provider) => ({ map((provider) => ({
title: provider.name, title: provider.name,
value: provider.id, value: provider.id,
footer: { description: {
opencode: "Recommended", opencode: "(Recommended)",
anthropic: "Claude Max or API key", anthropic: "(Claude Max or API key)",
}[provider.id], }[provider.id],
category: provider.id in PROVIDER_PRIORITY ? "Popular" : "Other",
async onSelect() { async onSelect() {
const methods = sync.data.provider_auth[provider.id] ?? [ const methods = sync.data.provider_auth[provider.id] ?? [
{ {
@@ -85,7 +87,6 @@ export function createDialogProviderOptions() {
} }
}, },
})), })),
sortBy((x) => PROVIDER_PRIORITY[x.value] ?? 99),
) )
}) })
return options return options
@@ -239,7 +239,7 @@ export function Autocomplete(props: {
}, },
{ {
display: "/thinking", display: "/thinking",
description: "toggle thinking blocks", description: "toggle thinking visibility",
onSelect: () => command.trigger("session.toggle.thinking"), onSelect: () => command.trigger("session.toggle.thinking"),
}, },
) )
@@ -850,7 +850,7 @@ export function Prompt(props: PromptProps) {
const r = retry() const r = retry()
if (!r) return if (!r) return
if (r.message.includes("exceeded your current quota") && r.message.includes("gemini")) if (r.message.includes("exceeded your current quota") && r.message.includes("gemini"))
return "gemini 3 way too hot right now" return "gemini is way too hot right now"
if (r.message.length > 50) return r.message.slice(0, 50) + "..." if (r.message.length > 50) return r.message.slice(0, 50) + "..."
return r.message return r.message
}) })
@@ -81,6 +81,7 @@ const context = createContext<{
conceal: () => boolean conceal: () => boolean
showThinking: () => boolean showThinking: () => boolean
showTimestamps: () => boolean showTimestamps: () => boolean
diffWrapMode: () => "word" | "none"
sync: ReturnType<typeof useSync> sync: ReturnType<typeof useSync>
}>() }>()
@@ -111,8 +112,9 @@ export function Session() {
const dimensions = useTerminalDimensions() const dimensions = useTerminalDimensions()
const [sidebar, setSidebar] = createSignal<"show" | "hide" | "auto">(kv.get("sidebar", "auto")) const [sidebar, setSidebar] = createSignal<"show" | "hide" | "auto">(kv.get("sidebar", "auto"))
const [conceal, setConceal] = createSignal(true) const [conceal, setConceal] = createSignal(true)
const [showThinking, setShowThinking] = createSignal(true) const [showThinking, setShowThinking] = createSignal(kv.get("thinking_visibility", true))
const [showTimestamps, setShowTimestamps] = createSignal(kv.get("timestamps", "hide") === "show") const [showTimestamps, setShowTimestamps] = createSignal(kv.get("timestamps", "hide") === "show")
const [diffWrapMode, setDiffWrapMode] = createSignal<"word" | "none">("word")
const wide = createMemo(() => dimensions().width > 120) const wide = createMemo(() => dimensions().width > 120)
const sidebarVisible = createMemo(() => { const sidebarVisible = createMemo(() => {
@@ -430,11 +432,24 @@ export function Session() {
}, },
}, },
{ {
title: "Toggle thinking blocks", title: showThinking() ? "Hide thinking" : "Show thinking",
value: "session.toggle.thinking", value: "session.toggle.thinking",
category: "Session", category: "Session",
onSelect: (dialog) => { onSelect: (dialog) => {
setShowThinking((prev) => !prev) setShowThinking((prev) => {
const next = !prev
kv.set("thinking_visibility", next)
return next
})
dialog.clear()
},
},
{
title: "Toggle diff wrapping",
value: "session.toggle.diffwrap",
category: "Session",
onSelect: (dialog) => {
setDiffWrapMode((prev) => (prev === "word" ? "none" : "word"))
dialog.clear() dialog.clear()
}, },
}, },
@@ -739,6 +754,7 @@ export function Session() {
conceal, conceal,
showThinking, showThinking,
showTimestamps, showTimestamps,
diffWrapMode,
sync, sync,
}} }}
> >
@@ -1298,21 +1314,9 @@ ToolRegistry.register<typeof WriteTool>({
container: "block", container: "block",
render(props) { render(props) {
const { theme, syntax } = useTheme() const { theme, syntax } = useTheme()
const lines = createMemo(
() => (typeof props.input.content === "string" ? props.input.content.split("\n") : []),
[] as string[],
)
const code = createMemo(() => { const code = createMemo(() => {
if (!props.input.content) return "" if (!props.input.content) return ""
const text = props.input.content return props.input.content
return text
})
const numbers = createMemo(() => {
const pad = lines().length.toString().length
return lines()
.map((_, index) => index + 1)
.map((x) => x.toString().padStart(pad, " "))
}) })
const diagnostics = createMemo(() => props.metadata.diagnostics?.[props.input.filePath ?? ""] ?? []) const diagnostics = createMemo(() => props.metadata.diagnostics?.[props.input.filePath ?? ""] ?? [])
@@ -1322,14 +1326,9 @@ ToolRegistry.register<typeof WriteTool>({
<ToolTitle icon="←" fallback="Preparing write..." when={props.input.filePath}> <ToolTitle icon="←" fallback="Preparing write..." when={props.input.filePath}>
Wrote {props.input.filePath} Wrote {props.input.filePath}
</ToolTitle> </ToolTitle>
<box flexDirection="row"> <line_number fg={theme.textMuted} minWidth={3} paddingRight={1}>
<box flexShrink={0}> <code fg={theme.text} filetype={filetype(props.input.filePath!)} syntaxStyle={syntax()} content={code()} />
<For each={numbers()}>{(value) => <text style={{ fg: theme.textMuted }}>{value}</text>}</For> </line_number>
</box>
<box paddingLeft={1} flexGrow={1}>
<code fg={theme.text} filetype={filetype(props.input.filePath!)} syntaxStyle={syntax()} content={code()} />
</box>
</box>
<Show when={diagnostics().length}> <Show when={diagnostics().length}>
<For each={diagnostics()}> <For each={diagnostics()}>
{(diagnostic) => ( {(diagnostic) => (
@@ -1401,15 +1400,15 @@ ToolRegistry.register<typeof TaskTool>({
return ( return (
<> <>
<ToolTitle icon="%" fallback="Delegating..." when={props.input.subagent_type ?? props.input.description}> <ToolTitle icon="" fallback="Delegating..." when={props.input.subagent_type ?? props.input.description}>
Task [{props.input.subagent_type ?? "unknown"}] {props.input.description} {Locale.titlecase(props.input.subagent_type ?? "unknown")} Task "{props.input.description}"
</ToolTitle> </ToolTitle>
<Show when={props.metadata.summary?.length}> <Show when={props.metadata.summary?.length}>
<box> <box>
<For each={props.metadata.summary ?? []}> <For each={props.metadata.summary ?? []}>
{(task) => ( {(task) => (
<text style={{ fg: theme.textMuted }}> <text style={{ fg: theme.textMuted }}>
{task.tool} {task.state.status === "completed" ? task.state.title : ""} {Locale.titlecase(task.tool)} {task.state.status === "completed" ? task.state.title : ""}
</text> </text>
)} )}
</For> </For>
@@ -1471,84 +1470,17 @@ ToolRegistry.register<typeof EditTool>({
const ctx = use() const ctx = use()
const { theme, syntax } = useTheme() const { theme, syntax } = useTheme()
const style = createMemo(() => { const view = createMemo(() => {
const diffStyle = ctx.sync.data.config.tui?.diff_style const diffStyle = ctx.sync.data.config.tui?.diff_style
if (diffStyle === "stacked") return "stacked" if (diffStyle === "stacked") return "unified"
// Default to "auto" behavior // Default to "auto" behavior
return ctx.width > 120 ? "split" : "stacked" return ctx.width > 120 ? "split" : "unified"
})
const diff = createMemo(() => {
const diff = props.metadata.diff ?? props.permission["diff"]
if (!diff) return null
try {
const patches = parsePatch(diff)
if (patches.length === 0) return null
const patch = patches[0]
const oldLines: string[] = []
const newLines: string[] = []
for (const hunk of patch.hunks) {
let i = 0
while (i < hunk.lines.length) {
const line = hunk.lines[i]
if (line.startsWith("-")) {
const removedLines: string[] = []
while (i < hunk.lines.length && hunk.lines[i].startsWith("-")) {
removedLines.push("- " + hunk.lines[i].slice(1))
i++
}
const addedLines: string[] = []
while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) {
addedLines.push("+ " + hunk.lines[i].slice(1))
i++
}
const maxLen = Math.max(removedLines.length, addedLines.length)
for (let j = 0; j < maxLen; j++) {
oldLines.push(removedLines[j] ?? "")
newLines.push(addedLines[j] ?? "")
}
} else if (line.startsWith("+")) {
const addedLines: string[] = []
while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) {
addedLines.push("+ " + hunk.lines[i].slice(1))
i++
}
for (const added of addedLines) {
oldLines.push("")
newLines.push(added)
}
} else {
oldLines.push(" " + line.slice(1))
newLines.push(" " + line.slice(1))
i++
}
}
}
return {
oldContent: oldLines.join("\n"),
newContent: newLines.join("\n"),
}
} catch (error) {
return null
}
})
const code = createMemo(() => {
if (!props.metadata.diff) return ""
const text = props.metadata.diff.split("\n").slice(5).join("\n")
return text.trim()
}) })
const ft = createMemo(() => filetype(props.input.filePath)) const ft = createMemo(() => filetype(props.input.filePath))
const diffContent = createMemo(() => props.metadata.diff ?? props.permission["diff"])
const diagnostics = createMemo(() => { const diagnostics = createMemo(() => {
const arr = props.metadata.diagnostics?.[props.input.filePath ?? ""] ?? [] const arr = props.metadata.diagnostics?.[props.input.filePath ?? ""] ?? []
return arr.filter((x) => x.severity === 1).slice(0, 3) return arr.filter((x) => x.severity === 1).slice(0, 3)
@@ -1562,26 +1494,28 @@ ToolRegistry.register<typeof EditTool>({
replaceAll: props.input.replaceAll, replaceAll: props.input.replaceAll,
})} })}
</ToolTitle> </ToolTitle>
<Switch> <Show when={diffContent()}>
<Match when={props.permission["diff"]}> <box paddingLeft={1}>
<text fg={theme.text}>{props.permission["diff"]?.trim()}</text> <diff
</Match> diff={diffContent()}
<Match when={diff() && style() === "split"}> view={view()}
<box paddingLeft={1} flexDirection="row" gap={2}> filetype={ft()}
<box flexGrow={1} flexBasis={0}> syntaxStyle={syntax()}
<code fg={theme.text} filetype={ft()} syntaxStyle={syntax()} content={diff()!.oldContent} /> showLineNumbers={true}
</box> width="100%"
<box flexGrow={1} flexBasis={0}> wrapMode={ctx.diffWrapMode()}
<code fg={theme.text} filetype={ft()} syntaxStyle={syntax()} content={diff()!.newContent} /> addedBg={theme.diffAddedBg}
</box> removedBg={theme.diffRemovedBg}
</box> contextBg={theme.diffContextBg}
</Match> addedSignColor={theme.diffHighlightAdded}
<Match when={code()}> removedSignColor={theme.diffHighlightRemoved}
<box paddingLeft={1}> lineNumberFg={theme.diffLineNumber}
<code fg={theme.text} filetype={ft()} syntaxStyle={syntax()} content={code()} /> lineNumberBg={theme.diffContextBg}
</box> addedLineNumberBg={theme.diffAddedLineNumberBg}
</Match> removedLineNumberBg={theme.diffRemovedLineNumberBg}
</Switch> />
</box>
</Show>
<Show when={diagnostics().length}> <Show when={diagnostics().length}>
<box> <box>
<For each={diagnostics()}> <For each={diagnostics()}>
@@ -55,9 +55,6 @@ export function DialogPrompt(props: DialogPromptProps) {
<text fg={theme.text}> <text fg={theme.text}>
enter <span style={{ fg: theme.textMuted }}>submit</span> enter <span style={{ fg: theme.textMuted }}>submit</span>
</text> </text>
<text fg={theme.text}>
esc <span style={{ fg: theme.textMuted }}>cancel</span>
</text>
</box> </box>
</box> </box>
) )
@@ -13,6 +13,7 @@ import { Locale } from "@/util/locale"
export interface DialogSelectProps<T> { export interface DialogSelectProps<T> {
title: string title: string
placeholder?: string
options: DialogSelectOption<T>[] options: DialogSelectOption<T>[]
ref?: (ref: DialogSelectRef<T>) => void ref?: (ref: DialogSelectRef<T>) => void
onMove?: (option: DialogSelectOption<T>) => void onMove?: (option: DialogSelectOption<T>) => void
@@ -21,6 +22,7 @@ export interface DialogSelectProps<T> {
keybind?: { keybind?: {
keybind: Keybind.Info keybind: Keybind.Info
title: string title: string
disabled?: boolean
onTrigger: (option: DialogSelectOption<T>) => void onTrigger: (option: DialogSelectOption<T>) => void
}[] }[]
current?: T current?: T
@@ -150,6 +152,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
} }
for (const item of props.keybind ?? []) { for (const item of props.keybind ?? []) {
if (item.disabled) continue
if (Keybind.match(item.keybind, keybind.parse(evt))) { if (Keybind.match(item.keybind, keybind.parse(evt))) {
const s = selected() const s = selected()
if (s) { if (s) {
@@ -171,8 +174,10 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
} }
props.ref?.(ref) props.ref?.(ref)
const keybinds = createMemo(() => props.keybind?.filter((x) => !x.disabled) ?? [])
return ( return (
<box gap={1}> <box gap={1} paddingBottom={1}>
<box paddingLeft={4} paddingRight={4}> <box paddingLeft={4} paddingRight={4}>
<box flexDirection="row" justifyContent="space-between"> <box flexDirection="row" justifyContent="space-between">
<text fg={theme.text} attributes={TextAttributes.BOLD}> <text fg={theme.text} attributes={TextAttributes.BOLD}>
@@ -195,7 +200,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
input = r input = r
setTimeout(() => input.focus(), 1) setTimeout(() => input.focus(), 1)
}} }}
placeholder="Enter search term" placeholder={props.placeholder ?? "Search"}
/> />
</box> </box>
</box> </box>
@@ -253,18 +258,20 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
)} )}
</For> </For>
</scrollbox> </scrollbox>
<box paddingRight={2} paddingLeft={4} flexDirection="row" paddingBottom={1} gap={2}> <Show when={keybinds().length} fallback={<box flexShrink={0} />}>
<For each={props.keybind ?? []}> <box paddingRight={2} paddingLeft={4} flexDirection="row" gap={2} flexShrink={0} paddingTop={1}>
{(item) => ( <For each={keybinds()}>
<text> {(item) => (
<span style={{ fg: theme.text }}> <text>
<b>{item.title}</b>{" "} <span style={{ fg: theme.text }}>
</span> <b>{item.title}</b>{" "}
<span style={{ fg: theme.textMuted }}>{Keybind.toString(item.keybind)}</span> </span>
</text> <span style={{ fg: theme.textMuted }}>{Keybind.toString(item.keybind)}</span>
)} </text>
</For> )}
</box> </For>
</box>
</Show>
</box> </box>
) )
} }
+1
View File
@@ -527,6 +527,7 @@ export namespace Config {
plan: Agent.optional(), plan: Agent.optional(),
build: Agent.optional(), build: Agent.optional(),
general: Agent.optional(), general: Agent.optional(),
explore: Agent.optional(),
}) })
.catchall(Agent) .catchall(Agent)
.optional() .optional()
@@ -246,3 +246,12 @@ export const htmlbeautifier: Info = {
return Bun.which("htmlbeautifier") !== null return Bun.which("htmlbeautifier") !== null
}, },
} }
export const dart: Info = {
name: "dart",
command: ["dart", "format", "$FILE"],
extensions: [".dart"],
async enabled() {
return Bun.which("dart") !== null
},
}
+18
View File
@@ -1166,4 +1166,22 @@ export namespace LSPServer {
} }
}, },
} }
export const Dart: Info = {
id: "dart",
extensions: [".dart"],
root: NearestRoot(["pubspec.yaml", "analysis_options.yaml"]),
async spawn(root) {
const dart = Bun.which("dart")
if (!dart) {
log.info("dart not found, please install dart first")
return
}
return {
process: spawn(dart, ["language-server", "--lsp"], {
cwd: root,
}),
}
},
}
} }
@@ -677,6 +677,21 @@ export namespace Provider {
} }
} }
export async function closest(providerID: string, query: string[]) {
const s = await state()
const provider = s.providers[providerID]
if (!provider) return undefined
for (const item of query) {
for (const modelID of Object.keys(provider.info.models)) {
if (modelID.includes(item))
return {
providerID,
modelID,
}
}
}
}
export async function getSmallModel(providerID: string) { export async function getSmallModel(providerID: string) {
const cfg = await Config.get() const cfg = await Config.get()
+1 -1
View File
@@ -666,7 +666,7 @@ export namespace MessageV2 {
} }
} }
return convertToModelMessages(result) return convertToModelMessages(result.filter((msg) => msg.parts.length > 0))
} }
export const stream = fn(Identifier.schema("session"), async function* (sessionID) { export const stream = fn(Identifier.schema("session"), async function* (sessionID) {
+1 -1
View File
@@ -333,7 +333,7 @@ export namespace SessionProcessor {
error: e, error: e,
}) })
const error = MessageV2.fromError(e, { providerID: input.providerID }) const error = MessageV2.fromError(e, { providerID: input.providerID })
if ((error?.name === "APIError" && error.data.isRetryable) || error.data.message.includes("Overloaded")) { if (error?.name === "APIError" && error.data.isRetryable) {
attempt++ attempt++
const delay = SessionRetry.delay(attempt, error.name === "APIError" ? error : undefined) const delay = SessionRetry.delay(attempt, error.name === "APIError" ? error : undefined)
SessionStatus.set(input.sessionID, { SessionStatus.set(input.sessionID, {
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin", "name": "@opencode-ai/plugin",
"version": "1.0.119", "version": "1.0.120",
"type": "module", "type": "module",
"scripts": { "scripts": {
"typecheck": "tsgo --noEmit", "typecheck": "tsgo --noEmit",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk", "name": "@opencode-ai/sdk",
"version": "1.0.119", "version": "1.0.120",
"type": "module", "type": "module",
"scripts": { "scripts": {
"typecheck": "tsgo --noEmit", "typecheck": "tsgo --noEmit",
+1
View File
@@ -1073,6 +1073,7 @@ export type Config = {
plan?: AgentConfig plan?: AgentConfig
build?: AgentConfig build?: AgentConfig
general?: AgentConfig general?: AgentConfig
explore?: AgentConfig
[key: string]: AgentConfig | undefined [key: string]: AgentConfig | undefined
} }
/** /**
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/slack", "name": "@opencode-ai/slack",
"version": "1.0.119", "version": "1.0.120",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "bun run src/index.ts", "dev": "bun run src/index.ts",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@opencode-ai/tauri", "name": "@opencode-ai/tauri",
"private": true, "private": true,
"version": "1.0.119", "version": "1.0.120",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/ui", "name": "@opencode-ai/ui",
"version": "1.0.119", "version": "1.0.120",
"type": "module", "type": "module",
"exports": { "exports": {
"./*": "./src/components/*.tsx", "./*": "./src/components/*.tsx",
+1 -1
View File
@@ -46,7 +46,7 @@ export function Diff<T>(props: DiffProps<T>) {
}) })
onMount(() => { onMount(() => {
if (isServer) return if (isServer || !props.preloadedDiff) return
fileDiffInstance = new FileDiff<T>({ fileDiffInstance = new FileDiff<T>({
...createDefaultOptions(props.diffStyle), ...createDefaultOptions(props.diffStyle),
...others, ...others,
+13 -26
View File
@@ -17,7 +17,8 @@ import { Diff } from "./diff"
import { DiffChanges } from "./diff-changes" import { DiffChanges } from "./diff-changes"
import { Markdown } from "./markdown" import { Markdown } from "./markdown"
import { getDirectory, getFilename } from "@opencode-ai/util/path" import { getDirectory, getFilename } from "@opencode-ai/util/path"
import { sanitize, sanitizePart } from "@opencode-ai/util/sanitize" import { sanitizePart } from "@opencode-ai/util/sanitize"
import { unwrap } from "solid-js/store"
export interface MessageProps { export interface MessageProps {
message: MessageType message: MessageType
@@ -83,15 +84,10 @@ export function UserMessageDisplay(props: { message: UserMessage; parts: PartTyp
export function Part(props: MessagePartProps) { export function Part(props: MessagePartProps) {
const component = createMemo(() => PART_MAPPING[props.part.type]) const component = createMemo(() => PART_MAPPING[props.part.type])
const part = createMemo(() => sanitizePart(unwrap(props.part), props.sanitize))
return ( return (
<Show when={component()}> <Show when={component()}>
<Dynamic <Dynamic component={component()} part={part()} message={props.message} hideDetails={props.hideDetails} />
component={component()}
part={props.part}
message={props.message}
hideDetails={props.hideDetails}
sanitize={props.sanitize}
/>
</Show> </Show>
) )
} }
@@ -102,7 +98,6 @@ export interface ToolProps {
tool: string tool: string
output?: string output?: string
hideDetails?: boolean hideDetails?: boolean
sanitize?: RegExp
} }
export type ToolComponent = Component<ToolProps> export type ToolComponent = Component<ToolProps>
@@ -170,7 +165,6 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
metadata={metadata} metadata={metadata}
output={part.state.status === "completed" ? part.state.output : undefined} output={part.state.status === "completed" ? part.state.output : undefined}
hideDetails={props.hideDetails} hideDetails={props.hideDetails}
sanitize={props.sanitize}
/> />
</Match> </Match>
</Switch> </Switch>
@@ -182,7 +176,7 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
PART_MAPPING["text"] = function TextPartDisplay(props) { PART_MAPPING["text"] = function TextPartDisplay(props) {
const part = props.part as TextPart const part = props.part as TextPart
const sanitized = createMemo(() => (props.sanitize ? (sanitizePart(part, props.sanitize) as TextPart) : part)) const sanitized = createMemo(() => (props.sanitize ? (sanitizePart(unwrap(part), props.sanitize) as TextPart) : part))
return ( return (
<Show when={part.text.trim()}> <Show when={part.text.trim()}>
<div data-component="text-part"> <div data-component="text-part">
@@ -211,7 +205,7 @@ ToolRegistry.register({
icon="glasses" icon="glasses"
trigger={{ trigger={{
title: "Read", title: "Read",
subtitle: props.input.filePath ? getFilename(sanitize(props.input.filePath, props.sanitize)) : "", subtitle: props.input.filePath ? getFilename(props.input.filePath) : "",
}} }}
/> />
) )
@@ -222,12 +216,9 @@ ToolRegistry.register({
name: "list", name: "list",
render(props) { render(props) {
return ( return (
<BasicTool <BasicTool icon="bullet-list" trigger={{ title: "List", subtitle: getDirectory(props.input.path || "/") }}>
icon="bullet-list"
trigger={{ title: "List", subtitle: getDirectory(sanitize(props.input.path, props.sanitize) || "/") }}
>
<Show when={false && props.output}> <Show when={false && props.output}>
<div data-component="tool-output">{sanitize(props.output, props.sanitize)}</div> <div data-component="tool-output">{props.output}</div>
</Show> </Show>
</BasicTool> </BasicTool>
) )
@@ -335,7 +326,7 @@ ToolRegistry.register({
> >
<div data-component="tool-output"> <div data-component="tool-output">
<Markdown <Markdown
text={`\`\`\`command\n$ ${sanitize(props.input.command, props.sanitize)}${props.output ? "\n\n" + props.output : ""}\n\`\`\``} text={`\`\`\`command\n$ ${props.input.command}${props.output ? "\n\n" + props.output : ""}\n\`\`\``}
/> />
</div> </div>
</BasicTool> </BasicTool>
@@ -355,13 +346,9 @@ ToolRegistry.register({
<div data-slot="message-part-title">Edit</div> <div data-slot="message-part-title">Edit</div>
<div data-slot="message-part-path"> <div data-slot="message-part-path">
<Show when={props.input.filePath?.includes("/")}> <Show when={props.input.filePath?.includes("/")}>
<span data-slot="message-part-directory"> <span data-slot="message-part-directory">{getDirectory(props.input.filePath!)}</span>
{getDirectory(sanitize(props.input.filePath!, props.sanitize))}
</span>
</Show> </Show>
<span data-slot="message-part-filename"> <span data-slot="message-part-filename">{getFilename(props.input.filePath ?? "")}</span>
{getFilename(sanitize(props.input.filePath ?? "", props.sanitize))}
</span>
</div> </div>
</div> </div>
<div data-slot="message-part-actions"> <div data-slot="message-part-actions">
@@ -376,11 +363,11 @@ ToolRegistry.register({
<div data-component="edit-content"> <div data-component="edit-content">
<Diff <Diff
before={{ before={{
name: getFilename(sanitize(props.metadata.filediff.path, props.sanitize)), name: getFilename(props.metadata.filediff.path),
contents: props.metadata.filediff.before, contents: props.metadata.filediff.before,
}} }}
after={{ after={{
name: getFilename(sanitize(props.metadata.filediff.path, props.sanitize)), name: getFilename(props.metadata.filediff.path),
contents: props.metadata.filediff.after, contents: props.metadata.filediff.after,
}} }}
/> />
+1 -1
View File
@@ -6,7 +6,7 @@ export function createDefaultOptions<T>(style: FileDiffOptions<T>["diffStyle"])
themeType: "system", themeType: "system",
disableLineNumbers: false, disableLineNumbers: false,
overflow: "wrap", overflow: "wrap",
diffStyle: style, diffStyle: style ?? "unified",
diffIndicators: "bars", diffIndicators: "bars",
disableBackground: false, disableBackground: false,
expansionLineCount: 20, expansionLineCount: 20,
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/util", "name": "@opencode-ai/util",
"version": "1.0.119", "version": "1.0.120",
"private": true, "private": true,
"type": "module", "type": "module",
"exports": { "exports": {
+1 -1
View File
@@ -2,7 +2,7 @@ import type { Part } from "@opencode-ai/sdk/client"
export const sanitize = (text: string | undefined, remove?: RegExp) => (remove ? text?.replace(remove, "") : text) ?? "" export const sanitize = (text: string | undefined, remove?: RegExp) => (remove ? text?.replace(remove, "") : text) ?? ""
export const sanitizePart = (part: Part, remove: RegExp) => { export const sanitizePart = (part: Part, remove: RegExp | undefined) => {
if (part.type === "text") { if (part.type === "text") {
part.text = sanitize(part.text, remove) part.text = sanitize(part.text, remove)
} else if (part.type === "reasoning") { } else if (part.type === "reasoning") {
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@opencode-ai/web", "name": "@opencode-ai/web",
"type": "module", "type": "module",
"version": "1.0.119", "version": "1.0.120",
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
+11 -1
View File
@@ -26,6 +26,7 @@ OpenCode comes with several built-in formatters for popular languages and framew
| standardrb | .rb, .rake, .gemspec, .ru | `standardrb` command available | | standardrb | .rb, .rake, .gemspec, .ru | `standardrb` command available |
| htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available | | htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available |
| air | .R | `air` command available | | air | .R | `air` command available |
| dart | .dart | `dart` command available |
So if your project has `prettier` in your `package.json`, OpenCode will automatically use it. So if your project has `prettier` in your `package.json`, OpenCode will automatically use it.
@@ -69,7 +70,16 @@ Let's look at some examples.
### Disabling formatters ### Disabling formatters
To disable a specific formatter, set `disabled` to `true`: To disable **all** formatters globally, set `formatter` to `false`:
```json title="opencode.json" {3}
{
"$schema": "https://opencode.ai/config.json",
"formatter": false
}
```
To disable a **specific** formatter, set `disabled` to `true`:
```json title="opencode.json" {5} ```json title="opencode.json" {5}
{ {
+11 -1
View File
@@ -32,6 +32,7 @@ OpenCode comes with several built-in LSP servers for popular languages:
| lua-ls | .lua | Auto-installs for Lua projects | | lua-ls | .lua | Auto-installs for Lua projects |
| sourcekit-lsp | .swift, .objc, .objcpp | `swift` installed (`xcode` on macOS) | | sourcekit-lsp | .swift, .objc, .objcpp | `swift` installed (`xcode` on macOS) |
| php intelephense | .php | Auto-installs for PHP projects | | php intelephense | .php | Auto-installs for PHP projects |
| dart | .dart | `dart` command available |
LSP servers are automatically enabled when one of the above file extensions are detected and the requirements are met. LSP servers are automatically enabled when one of the above file extensions are detected and the requirements are met.
@@ -77,7 +78,16 @@ Let's look at some examples.
### Disabling LSP servers ### Disabling LSP servers
To disable a specific LSP server, set `disabled` to `true`: To disable **all** LSP servers globally, set `lsp` to `false`:
```json title="opencode.json" {3}
{
"$schema": "https://opencode.ai/config.json",
"lsp": false
}
```
To disable a **specific** LSP server, set `disabled` to `true`:
```json title="opencode.json" {5} ```json title="opencode.json" {5}
{ {
+8
View File
@@ -228,6 +228,10 @@ Apply patches to files.
This tool applies patch files to your codebase. Useful for applying diffs and patches from various sources. This tool applies patch files to your codebase. Useful for applying diffs and patches from various sources.
:::tip
This tool is disabled for subagents by default, but you can enable it manually. [Learn more](/docs/agents/#tools)
:::
--- ---
### todowrite ### todowrite
@@ -245,6 +249,10 @@ Manage todo lists during coding sessions.
Creates and updates task lists to track progress during complex operations. The LLM uses this to organize multi-step tasks. Creates and updates task lists to track progress during complex operations. The LLM uses this to organize multi-step tasks.
:::tip
This tool is disabled for subagents by default, but you can enable it manually. [Learn more](/docs/agents/#tools)
:::
--- ---
### todoread ### todoread
+2 -2
View File
@@ -108,8 +108,8 @@ We support a pay-as-you-go model. Below are the prices **per 1M tokens**.
| Big Pickle | Free | Free | Free | - | | Big Pickle | Free | Free | Free | - |
| Grok Code Fast 1 | Free | Free | Free | - | | Grok Code Fast 1 | Free | Free | Free | - |
| GLM 4.6 | $0.60 | $2.20 | $0.10 | - | | GLM 4.6 | $0.60 | $2.20 | $0.10 | - |
| Kimi K2 | $0.45 | $2.50 | - | - | | Kimi K2 | $0.40 | $2.50 | - | - |
| Kimi K2 Thinking | $0.60 | $2.50 | - | - | | Kimi K2 Thinking | $0.40 | $2.50 | - | - |
| Qwen3 Coder 480B | $0.45 | $1.50 | - | - | | Qwen3 Coder 480B | $0.45 | $1.50 | - | - |
| Claude Sonnet 4.5 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 | | Claude Sonnet 4.5 (≤ 200K tokens) | $3.00 | $15.00 | $0.30 | $3.75 |
| Claude Sonnet 4.5 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 | | Claude Sonnet 4.5 (> 200K tokens) | $6.00 | $22.50 | $0.60 | $7.50 |
+38
View File
@@ -73,6 +73,44 @@ if (!Script.preview) {
console.log(notes.join("\n")) console.log(notes.join("\n"))
console.log("-----------------------------") console.log("-----------------------------")
opencode.server.close() opencode.server.close()
// Get contributors
const team = [
"actions-user",
"opencode",
"rekram1-node",
"thdxr",
"kommander",
"jayair",
"fwang",
"adamdotdevin",
"opencode-agent[bot]",
]
const compare =
await $`gh api "/repos/sst/opencode/compare/v${previous}...HEAD" --jq '.commits[] | {login: .author.login, message: .commit.message}'`.text()
const contributors = new Map<string, string[]>()
for (const line of compare.split("\n").filter(Boolean)) {
const { login, message } = JSON.parse(line) as { login: string | null; message: string }
const title = message.split("\n")[0] ?? ""
if (title.match(/^(ignore:|test:|chore:|ci:|release:)/i)) continue
if (login && !team.includes(login)) {
if (!contributors.has(login)) contributors.set(login, [])
contributors.get(login)?.push(title)
}
}
if (contributors.size > 0) {
notes.push("")
notes.push(`**Thank you to ${contributors.size} community contributor${contributors.size > 1 ? "s" : ""}:**`)
for (const [username, userCommits] of contributors) {
notes.push(`- @${username}:`)
for (const commit of userCommits) {
notes.push(` - ${commit}`)
}
}
}
} }
const pkgjsons = await Array.fromAsync( const pkgjsons = await Array.fromAsync(
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "opencode", "name": "opencode",
"displayName": "opencode", "displayName": "opencode",
"description": "opencode for VS Code", "description": "opencode for VS Code",
"version": "1.0.119", "version": "1.0.120",
"publisher": "sst-dev", "publisher": "sst-dev",
"repository": { "repository": {
"type": "git", "type": "git",