mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-22 09:36:17 +00:00
Compare commits
95
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f50ab1363a | ||
|
|
57fa34f235 | ||
|
|
0af9dd61d2 | ||
|
|
8ecd4c21bf | ||
|
|
361a71ffad | ||
|
|
fa8f17014c | ||
|
|
813e6f3cec | ||
|
|
ba72a6ff2b | ||
|
|
b6a1f95858 | ||
|
|
f357e70779 | ||
|
|
a9cac91d60 | ||
|
|
35fe5b7212 | ||
|
|
62cb3f77bd | ||
|
|
e0b9e68a68 | ||
|
|
1e4c153aed | ||
|
|
c9dc1a1b3d | ||
|
|
40282c1d4d | ||
|
|
e11dbd0206 | ||
|
|
7b11ef5a5e | ||
|
|
2859603cbb | ||
|
|
c313504c82 | ||
|
|
e49772a8b4 | ||
|
|
08faeb3893 | ||
|
|
5e75e5e990 | ||
|
|
5cc5b622f0 | ||
|
|
11e8110f9e | ||
|
|
82dee24302 | ||
|
|
ad192a59b5 | ||
|
|
71d08e94d5 | ||
|
|
b155b15694 | ||
|
|
d06bf487a5 | ||
|
|
ccccd75328 | ||
|
|
03d65f36d3 | ||
|
|
f4a89683da | ||
|
|
f715478a13 | ||
|
|
e0e9bd7d5f | ||
|
|
5ccb1e88e1 | ||
|
|
be469f328c | ||
|
|
fc80874f45 | ||
|
|
3d26b12251 | ||
|
|
1b77242d40 | ||
|
|
4c5960dc4b | ||
|
|
e2505d434a | ||
|
|
a466a357b6 | ||
|
|
6386e67949 | ||
|
|
16af134053 | ||
|
|
d545d8fba5 | ||
|
|
da4730e4a4 | ||
|
|
18fc3eea7c | ||
|
|
101ff6d1a2 | ||
|
|
20ad497bc8 | ||
|
|
1b18a50418 | ||
|
|
0a6397272c | ||
|
|
3477d28a87 | ||
|
|
fb772e8a02 | ||
|
|
4e29e04f24 | ||
|
|
fbcd4faa61 | ||
|
|
da3ba63872 | ||
|
|
e2a2bc301d | ||
|
|
72824fe65d | ||
|
|
64b4f7df4c | ||
|
|
7774461bbf | ||
|
|
8b65fa2ef6 | ||
|
|
0033bb3559 | ||
|
|
ad905f8e6c | ||
|
|
9b0dd36cda | ||
|
|
4e81a0b73f | ||
|
|
32320409b1 | ||
|
|
040b856140 | ||
|
|
65c35977bd | ||
|
|
57075d8cdc | ||
|
|
e14acea581 | ||
|
|
7af274a921 | ||
|
|
a97fec8af5 | ||
|
|
2cba7e227d | ||
|
|
4d68d30b48 | ||
|
|
5a0e07efcf | ||
|
|
cba6b5f2f7 | ||
|
|
1c965451b5 | ||
|
|
a0f8dccbfe | ||
|
|
fb8344f3c2 | ||
|
|
3fd77ae980 | ||
|
|
976c185172 | ||
|
|
4643e65ad6 | ||
|
|
e23586af26 | ||
|
|
92d29ba4a3 | ||
|
|
886fd98f52 | ||
|
|
722e717e99 | ||
|
|
6d635007ab | ||
|
|
0e3474509a | ||
|
|
d8bf79225f | ||
|
|
8a55ba75b5 | ||
|
|
2449581543 | ||
|
|
3e25e80f7a | ||
|
|
f06e9491e1 |
@@ -1,37 +0,0 @@
|
||||
name: beta
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 * * * *"
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup-bun
|
||||
|
||||
- name: Setup Git Committer
|
||||
id: setup-git-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
|
||||
run: bun i -g opencode-ai
|
||||
|
||||
- name: Sync beta branch
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.setup-git-committer.outputs.token }}
|
||||
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||
run: bun script/beta.ts
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
@@ -96,7 +96,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"bin": {
|
||||
"lildax": "./bin/lildax.cjs",
|
||||
},
|
||||
@@ -144,7 +144,7 @@
|
||||
},
|
||||
"packages/codemode": {
|
||||
"name": "@opencode-ai/codemode",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"acorn": "8.15.0",
|
||||
"effect": "catalog:",
|
||||
@@ -158,7 +158,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -194,7 +194,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -221,7 +221,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "3.0.82",
|
||||
"@ai-sdk/openai": "3.0.48",
|
||||
@@ -243,7 +243,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -267,7 +267,7 @@
|
||||
},
|
||||
"packages/console/support": {
|
||||
"name": "@opencode-ai/console-support",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
@@ -287,7 +287,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@opencode-ai/core",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -301,7 +301,7 @@
|
||||
"@ai-sdk/deepinfra": "2.0.41",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
"@ai-sdk/google": "3.0.73",
|
||||
"@ai-sdk/google-vertex": "4.0.128",
|
||||
"@ai-sdk/google-vertex": "4.0.181",
|
||||
"@ai-sdk/groq": "3.0.31",
|
||||
"@ai-sdk/mistral": "3.0.51",
|
||||
"@ai-sdk/openai": "3.0.84",
|
||||
@@ -332,7 +332,7 @@
|
||||
"@opentelemetry/sdk-trace-base": "2.6.1",
|
||||
"@parcel/watcher": "2.5.1",
|
||||
"@silvia-odwyer/photon-node": "0.3.4",
|
||||
"ai-gateway-provider": "3.1.2",
|
||||
"ai-gateway-provider": "3.2.0",
|
||||
"bun-pty": "0.4.8",
|
||||
"cross-spawn": "catalog:",
|
||||
"diff": "catalog:",
|
||||
@@ -381,7 +381,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"drizzle-orm": "catalog:",
|
||||
@@ -435,7 +435,7 @@
|
||||
},
|
||||
"packages/effect-drizzle-sqlite": {
|
||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -449,7 +449,7 @@
|
||||
},
|
||||
"packages/effect-sqlite-node": {
|
||||
"name": "@opencode-ai/effect-sqlite-node",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"effect": "catalog:",
|
||||
},
|
||||
@@ -461,7 +461,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@hono/standard-validator": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
@@ -493,7 +493,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -509,7 +509,7 @@
|
||||
},
|
||||
"packages/http-recorder": {
|
||||
"name": "@opencode-ai/http-recorder",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "4.0.0-beta.83",
|
||||
"@effect/platform-node-shared": "4.0.0-beta.83",
|
||||
@@ -540,7 +540,7 @@
|
||||
},
|
||||
"packages/llm": {
|
||||
"name": "@opencode-ai/llm",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@smithy/eventstream-codec": "4.2.14",
|
||||
@@ -559,7 +559,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -576,7 +576,7 @@
|
||||
"@ai-sdk/deepinfra": "2.0.41",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
"@ai-sdk/google": "3.0.73",
|
||||
"@ai-sdk/google-vertex": "4.0.128",
|
||||
"@ai-sdk/google-vertex": "4.0.181",
|
||||
"@ai-sdk/groq": "3.0.31",
|
||||
"@ai-sdk/mistral": "3.0.51",
|
||||
"@ai-sdk/openai": "3.0.84",
|
||||
@@ -623,7 +623,7 @@
|
||||
"@types/ws": "8.18.1",
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"ai": "catalog:",
|
||||
"ai-gateway-provider": "3.1.2",
|
||||
"ai-gateway-provider": "3.2.0",
|
||||
"bonjour-service": "1.3.0",
|
||||
"chokidar": "4.0.3",
|
||||
"cross-spawn": "catalog:",
|
||||
@@ -690,7 +690,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "3.0.8",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -766,7 +766,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"cross-spawn": "catalog:",
|
||||
},
|
||||
@@ -781,7 +781,7 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@opencode-ai/server",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/protocol": "workspace:*",
|
||||
@@ -796,7 +796,7 @@
|
||||
},
|
||||
"packages/session-ui": {
|
||||
"name": "@opencode-ai/session-ui",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/client": "file:../app/vendor/opencode-ai-client-1.17.13-v2.tgz",
|
||||
@@ -836,7 +836,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -849,7 +849,7 @@
|
||||
},
|
||||
"packages/stats/app": {
|
||||
"name": "@opencode-ai/stats-app",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -883,7 +883,7 @@
|
||||
},
|
||||
"packages/stats/core": {
|
||||
"name": "@opencode-ai/stats-core",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-athena": "3.933.0",
|
||||
"@planetscale/database": "1.19.0",
|
||||
@@ -902,7 +902,7 @@
|
||||
},
|
||||
"packages/stats/server": {
|
||||
"name": "@opencode-ai/stats-server",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-firehose": "3.933.0",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -944,7 +944,7 @@
|
||||
},
|
||||
"packages/tui": {
|
||||
"name": "@opencode-ai/tui",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
@@ -971,7 +971,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/diffs": "catalog:",
|
||||
@@ -1022,7 +1022,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -1183,21 +1183,21 @@
|
||||
|
||||
"@ai-sdk/cohere": ["@ai-sdk/cohere@3.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OqcCq2PiFY1dbK/0Ck45KuvE8jfdxRuuAE9Y5w46dAk6U+9vPOeg1CDcmR+ncqmrYrhRl3nmyDttyDahyjCzAw=="],
|
||||
|
||||
"@ai-sdk/deepgram": ["@ai-sdk/deepgram@2.0.33", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-VscTV68g6sXRY4O1yl72/O8y6+tBDvSQax6bqX06hRKWBGxsJ8Jr3LZsNmZnK9Od5Icx565ijK0QgrlNaN4TdQ=="],
|
||||
"@ai-sdk/deepgram": ["@ai-sdk/deepgram@2.0.51", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DPoKiCXDwzopJI/pHcZnXaycZ5qqCL4xCWcfsb1s8M8OUPchK2GUHcCXt6v056fAqtKWLF/hrW+RcHFFY0qyHQ=="],
|
||||
|
||||
"@ai-sdk/deepinfra": ["@ai-sdk/deepinfra@2.0.41", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-y6RoOP7DGWmDSiSxrUSt5p18sbz+Ixe5lMVPmdE7x+Tr5rlrzvftyHhjWHfqlAtoYERZTGFbP6tPW1OfQcrb4A=="],
|
||||
|
||||
"@ai-sdk/deepseek": ["@ai-sdk/deepseek@2.0.47", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MzcQ321JO8OY+TVLFI81A7cIIuoeLLxrLCDD+8C1E3Ro6UFyfMtRXo9bw9OhTMRSDMo6hgSDOo4Fekz8aJtQYQ=="],
|
||||
|
||||
"@ai-sdk/elevenlabs": ["@ai-sdk/elevenlabs@2.0.33", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-EtvsWfGrqx3OhzJdoi82qH+4yzEPPKZr2utyQ+w8cHKoFeg0+8Lou9Z3uixy73WEwz8Z1+AR8QT9fZ64AWGYPA=="],
|
||||
"@ai-sdk/elevenlabs": ["@ai-sdk/elevenlabs@2.0.51", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XFONX6rAsu6d13cJVUfZfkq4a+qdThlxvoEfzYlSRa1AvALlzwX7Y6bunXxevuifT3n882+nDdWrdiYvFP0+Fw=="],
|
||||
|
||||
"@ai-sdk/fireworks": ["@ai-sdk/fireworks@2.0.53", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.48", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HjeiGsdxSzrCkOf2l2V+K+opzlqxBtduBq6BCiohAdgQk2KdZmI/67SMkBM6Kdze/BjUXiZlv0d7zNICPhxVDA=="],
|
||||
"@ai-sdk/fireworks": ["@ai-sdk/fireworks@2.0.76", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.67", "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yg1ulgemh6BLMrs2vBNbtjV70NhenFI3Z7psB7s94FOEcGevvyV1qdFoqsgBZ7QyuUGwnC645c+eBFFtPAr5SQ=="],
|
||||
|
||||
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.104", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZKX5n74io8VIRlhIMSLWVlvT3sXC8Z7cZ9GHuWBWZDVi96+62AIsWuLGvMfcBA1STYuSoDrp6rIziZmvrTq0TA=="],
|
||||
|
||||
"@ai-sdk/google": ["@ai-sdk/google@3.0.73", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-o2MuIeyvZrFIeIbnbA8Thrr63irdyUBh0uWBZ2lY6yFeXuE/tcwyXF74bDKS4KvTu84uFpQfpbS/LXHGKKXz+g=="],
|
||||
|
||||
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.128", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.77", "@ai-sdk/google": "3.0.73", "@ai-sdk/openai-compatible": "2.0.47", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-jK8fixb4km2yfgvb9DUFQRpV/jiDB0v9gyxHoHfPydaQvz+CpAz8DTt1quyaM+Wg9G2R8Zo68CYmHbIkUqW2AA=="],
|
||||
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.181", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.110", "@ai-sdk/google": "3.0.108", "@ai-sdk/openai-compatible": "2.0.67", "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-57b5Qor8V53vubkxCj09tbHWpzpCLUbzmll2FwShuLvyEAsCH6mh3sAowDhiwUWPXnLzU+rC3RVMKCPscqICcg=="],
|
||||
|
||||
"@ai-sdk/groq": ["@ai-sdk/groq@3.0.31", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XbbugpnFmXGu2TlXiq8KUJskP6/VVbuFcnFIGDzDIB/Chg6XHsNnqrTF80Zxkh0Pd3+NvbM+2Uqrtsndk6bDAg=="],
|
||||
|
||||
@@ -3063,7 +3063,7 @@
|
||||
|
||||
"ai": ["ai@6.0.168", "", { "dependencies": { "@ai-sdk/gateway": "3.0.104", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2HqCJuO+1V2aV7vfYs5LFEUfxbkGX+5oa54q/gCCTL7KLTdbxcCu5D7TdLA5kwsrs3Szgjah9q6D9tpjHM3hUQ=="],
|
||||
|
||||
"ai-gateway-provider": ["ai-gateway-provider@3.1.2", "", { "optionalDependencies": { "@ai-sdk/amazon-bedrock": "^4.0.62", "@ai-sdk/anthropic": "^3.0.46", "@ai-sdk/azure": "^3.0.31", "@ai-sdk/cerebras": "^2.0.34", "@ai-sdk/cohere": "^3.0.21", "@ai-sdk/deepgram": "^2.0.20", "@ai-sdk/deepseek": "^2.0.20", "@ai-sdk/elevenlabs": "^2.0.20", "@ai-sdk/fireworks": "^2.0.34", "@ai-sdk/google": "^3.0.30", "@ai-sdk/google-vertex": "^4.0.61", "@ai-sdk/groq": "^3.0.24", "@ai-sdk/mistral": "^3.0.20", "@ai-sdk/openai": "^3.0.30", "@ai-sdk/perplexity": "^3.0.19", "@ai-sdk/xai": "^3.0.57", "@openrouter/ai-sdk-provider": "^2.2.3" }, "peerDependencies": { "@ai-sdk/openai-compatible": "^2.0.0", "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^4.0.0", "ai": "^6.0.0" } }, "sha512-krGNnJSoO/gJ7Hbe5nQDlsBpDUGIBGtMQTRUaW7s1MylsfvLduba0TLWzQaGtOmNRkP0pGhtGlwsnS6FNQMlyw=="],
|
||||
"ai-gateway-provider": ["ai-gateway-provider@3.2.0", "", { "optionalDependencies": { "@ai-sdk/amazon-bedrock": "^4.0.117", "@ai-sdk/anthropic": "^3.0.84", "@ai-sdk/azure": "^3.0.74", "@ai-sdk/cerebras": "^2.0.56", "@ai-sdk/cohere": "^3.0.38", "@ai-sdk/deepgram": "^2.0.35", "@ai-sdk/deepseek": "^2.0.38", "@ai-sdk/elevenlabs": "^2.0.35", "@ai-sdk/fireworks": "^2.0.56", "@ai-sdk/google": "^3.0.82", "@ai-sdk/google-vertex": "^4.0.145", "@ai-sdk/groq": "^3.0.41", "@ai-sdk/mistral": "^3.0.39", "@ai-sdk/openai": "^3.0.71", "@ai-sdk/perplexity": "^3.0.35", "@ai-sdk/xai": "^3.0.95", "@openrouter/ai-sdk-provider": "^2.10.0" }, "peerDependencies": { "@ai-sdk/openai-compatible": "^2.0.0", "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^4.0.0", "ai": "^6.0.0" } }, "sha512-IGSV96IqAfiZd20CWSMVQk5sVeLcJR2uQcoWLB8GdkxyvQrsU3x4U0o1Ok6bfVJug7SrlX6I8ibz9cHIkUyRtg=="],
|
||||
|
||||
"ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
|
||||
|
||||
@@ -5667,9 +5667,9 @@
|
||||
|
||||
"@ai-sdk/cohere/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="],
|
||||
|
||||
"@ai-sdk/deepgram/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
|
||||
"@ai-sdk/deepgram/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"@ai-sdk/deepgram/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
|
||||
"@ai-sdk/deepgram/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="],
|
||||
|
||||
"@ai-sdk/deepinfra/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="],
|
||||
|
||||
@@ -5677,27 +5677,29 @@
|
||||
|
||||
"@ai-sdk/deepseek/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.38", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g=="],
|
||||
|
||||
"@ai-sdk/elevenlabs/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
|
||||
"@ai-sdk/elevenlabs/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"@ai-sdk/elevenlabs/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
|
||||
"@ai-sdk/elevenlabs/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="],
|
||||
|
||||
"@ai-sdk/fireworks/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.48", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z9MC6M4Oh/yUY/F/eszOtO8wc2nMz99XmZQKd2gWTtyIfe716xTfrKe3aYZKg20NZDtyjqPPKPSR+wqz7q1T7Q=="],
|
||||
"@ai-sdk/fireworks/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.67", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-glcEJC2mBXJKj7joFI0fRhcbdDYKTBgXMPcT6Vcnlym67tTzuNG9pFx3zblxVv8TdOxhojJja5zGG19yeGJxuA=="],
|
||||
|
||||
"@ai-sdk/fireworks/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
|
||||
"@ai-sdk/fireworks/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"@ai-sdk/fireworks/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
|
||||
"@ai-sdk/fireworks/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="],
|
||||
|
||||
"@ai-sdk/google/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
|
||||
|
||||
"@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
|
||||
|
||||
"@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.77", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ML8C2M1YvPA1ulEx4TiyF0k1xvC2ikEiPBIC1PPQ0a5xELUGrO2lAaEzsTEoJ+eCeDd8PSBuFJjs+r+9yIwQXA=="],
|
||||
"@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.110", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rNkamQCeAUOUGr5Npg5pXZyYFH4fS1U6Mbdy3dF/NNBEI3D2Chc/ruRrwNegP0gfpX3cllP3O4jSibGBbWPZ7A=="],
|
||||
|
||||
"@ai-sdk/google-vertex/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.47", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Enm5UlL0zUCrW3792opk5h7hRWxZOZzDe6eQYVFqX9LUOGGCe1h8MZWAGim765nwzgnjlpeYOsuzZmLtRsTPlg=="],
|
||||
"@ai-sdk/google-vertex/@ai-sdk/google": ["@ai-sdk/google@3.0.108", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kwvYpRNghqt0VRKE7Hx1UWZQCUJJFqUITj24baxy+ApS0Hru0PkBJHD75a36Wc+e6e+wHcKR2MconTeJiBZigA=="],
|
||||
|
||||
"@ai-sdk/google-vertex/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
|
||||
"@ai-sdk/google-vertex/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.67", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-glcEJC2mBXJKj7joFI0fRhcbdDYKTBgXMPcT6Vcnlym67tTzuNG9pFx3zblxVv8TdOxhojJja5zGG19yeGJxuA=="],
|
||||
|
||||
"@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
|
||||
"@ai-sdk/google-vertex/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="],
|
||||
|
||||
"@ai-sdk/groq/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="],
|
||||
|
||||
@@ -6171,21 +6173,23 @@
|
||||
|
||||
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.107", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.78", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8nT08pGPy25rleJNk56ep00UHK6kCtCmu+ZNqVVSSPDieADlIZqcaN1iRXAFBoCH0Fb9F6C2EjFDaySdsargfQ=="],
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.153", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.110", "@ai-sdk/openai": "3.0.96", "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iEXrLgWylCHJmznqlKLU3CqRh8UWibv+illrwmsk136FVBBvyXiGnpQrI1pGWCScVLQjBQSFQu7GJDkUEomf/A=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.78", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-0OY12G20cUt6iU6htpEA1491Oz++NVxZxlmWGX4B7rSbeZ5pnDmOu6YtW9BKzdZlNx5Gn23i6WMxyZFoMKNcgA=="],
|
||||
"ai-gateway-provider/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.110", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rNkamQCeAUOUGr5Npg5pXZyYFH4fS1U6Mbdy3dF/NNBEI3D2Chc/ruRrwNegP0gfpX3cllP3O4jSibGBbWPZ7A=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/azure": ["@ai-sdk/azure@3.0.49", "", { "dependencies": { "@ai-sdk/openai": "3.0.48", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-wskgAL+OmrHG7by/iWIxEBQCEdc1mDudha/UZav46i0auzdFfsDB/k2rXZaC4/3nWSgMZkxr0W3ncyouEGX/eg=="],
|
||||
"ai-gateway-provider/@ai-sdk/cerebras": ["@ai-sdk/cerebras@2.0.60", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.54", "@ai-sdk/provider": "3.0.12", "@ai-sdk/provider-utils": "4.0.33" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Rnok3cThg6awBwaDSyiZpgRpbV7pqxGYrA89LODCo5cuEHeP2h0AM0lLHP7zIkclAdXfOm4wldKi/S2T/DGCOw=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/deepseek": ["@ai-sdk/deepseek@2.0.35", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9DhYurbAvcurOEGN6u2myYDybrrzGfcrkG8hwmFjwTrePW6KCMggm0YxP7e8RkLYcQKqCEMgFlyEB4BM6EmiKg=="],
|
||||
"ai-gateway-provider/@ai-sdk/cohere": ["@ai-sdk/cohere@3.0.54", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cXLjIsSzUriPHe704IH6d+ipJ/OvczTB700p9Zma7DPgQzvxG/diyr8q/2LEsbTRiTopiKhky8dn1PJNQcJToQ=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/mistral": ["@ai-sdk/mistral@3.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZXe7nZQgliDdjz5ufH5RKpHWxbN72AzmzzKGbF/z+0K9GN5tUCnftrQRvTRFHA5jAzTapcm2BEevmGLVbMkW+A=="],
|
||||
"ai-gateway-provider/@ai-sdk/google": ["@ai-sdk/google@3.0.108", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kwvYpRNghqt0VRKE7Hx1UWZQCUJJFqUITj24baxy+ApS0Hru0PkBJHD75a36Wc+e6e+wHcKR2MconTeJiBZigA=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/openai": ["@ai-sdk/openai@3.0.53", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Wld+Rbc05KaUn08uBt06eEuwcgalcIFtIl32Yp+GxuZXUQwOb6YeAuq+C6da4ch6BurFoqEaLemJVwjBb7x+PQ=="],
|
||||
"ai-gateway-provider/@ai-sdk/groq": ["@ai-sdk/groq@3.0.59", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-X4h60TGq4pIOXPsthatUr+bfTaYCaKGX597hG9JgcueEl4+nboCdw99ixjFKGkvYlBJwLCCfI957EmGA2QlF0w=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/xai": ["@ai-sdk/xai@3.0.82", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-A0VFMufnVf4wODcT3SPQUUzvYXiIO1VhFuXj9r6z/vP4rlo+QRDPw3WSTchcz93ROQWSfBE3I6Szqz342OHi5w=="],
|
||||
"ai-gateway-provider/@ai-sdk/openai": ["@ai-sdk/openai@3.0.84", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cmgbeJL0bbY0yTJH4/AdmP5E7MjWRL9G8UdhIi0JlV/So03o82ORJofW8OzwCZPTORVQblFbpZXYGDcUd9NdUQ=="],
|
||||
|
||||
"ai-gateway-provider/@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.8.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Y6j3yivgoEUf/kutD/k5GX/mzZfioRFoSx0gbQ+mIOzMaH/vJv1rCkztiuvlLw5xRYQil7oxHUZvmSfXqOx1NQ=="],
|
||||
"ai-gateway-provider/@ai-sdk/perplexity": ["@ai-sdk/perplexity@3.0.53", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bL3SWrPltTuxVNg/bZ5APbJLVe0+BIU+BYAbT8Yo0eSAZ5eMTImN2murQHZa08Wi8lUMm8MNolaYBQkb0JDbFw=="],
|
||||
|
||||
"ai-gateway-provider/@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.10.0", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-FMsAEjLUt5pWuRE2LDC/LCvVrFjLlrEzUITH5+5SZtfq7KZ2wrOHjQVxzz92sju8S9ltpzW87CLW8/b0oBXVCw=="],
|
||||
|
||||
"ajv-keywords/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="],
|
||||
|
||||
@@ -6595,16 +6599,24 @@
|
||||
|
||||
"@ai-sdk/deepgram/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/deepgram/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"@ai-sdk/deepinfra/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/deepseek/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/elevenlabs/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/elevenlabs/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"@ai-sdk/fireworks/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/fireworks/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"@ai-sdk/google-vertex/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/google-vertex/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"@ai-sdk/google/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/groq/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
@@ -6977,29 +6989,45 @@
|
||||
|
||||
"accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/openai": ["@ai-sdk/openai@3.0.96", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Pex8vOj1y05j7jtBS39cJJRDjJbMIyCY9+01cSIp1hwEJTKImrFejMgsAazMWXSi/HU+B9ZE6ElftCOwvg4mmQ=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.14", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.1", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
|
||||
"ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
|
||||
"ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/azure/@ai-sdk/openai": ["@ai-sdk/openai@3.0.48", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ALmj/53EXpcRqMbGpPJPP4UOSWw0q4VGpnDo7YctvsynjkrKDmoneDG/1a7VQnSPYHnJp6tTRMf5ZdxZ5whulg=="],
|
||||
"ai-gateway-provider/@ai-sdk/cerebras/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.54", "", { "dependencies": { "@ai-sdk/provider": "3.0.12", "@ai-sdk/provider-utils": "4.0.33" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OyXt0zK8y2/ZIyWlbxTv2r1M7AK227S+Gl4BYOEF42q0wz1n5m4fwR8L4Fy/MQ4Ho6xje47MPsFcRdIqIyP6Rw=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/azure/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="],
|
||||
"ai-gateway-provider/@ai-sdk/cerebras/@ai-sdk/provider": ["@ai-sdk/provider@3.0.12", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-sj9DWTJ2Ze0WR9qsiOPqoqzNx3OxL6iMxHImbhvoe9qOspekbzxNDMiJ4TIGfYHYh9w4OmBjz3prvqhzTi96+Q=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/deepseek/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
|
||||
"ai-gateway-provider/@ai-sdk/cerebras/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.33", "", { "dependencies": { "@ai-sdk/provider": "3.0.12", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-nJ0bAfegMAIJtrzMJtbzer1cS3nb7c7DsyU1S4nrPm7ZU0Mn6SBBZv5IGZZGTbpWTJwqKTSPeZJTXalbAxt1BA=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/deepseek/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
|
||||
"ai-gateway-provider/@ai-sdk/cohere/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/mistral/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="],
|
||||
"ai-gateway-provider/@ai-sdk/cohere/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
|
||||
"ai-gateway-provider/@ai-sdk/google/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/groq/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/groq/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/openai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.38", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/perplexity/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/perplexity/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="],
|
||||
|
||||
"ajv-keywords/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
|
||||
@@ -7419,13 +7447,31 @@
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/azure/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
"ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/deepseek/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
"ai-gateway-provider/@ai-sdk/cerebras/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/mistral/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
"ai-gateway-provider/@ai-sdk/cohere/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/cohere/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/google/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/google/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/groq/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/groq/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/openai/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/perplexity/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/perplexity/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
|
||||
"ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"nodeModules": {
|
||||
"x86_64-linux": "sha256-TNwKfqxD83UpZuCKN8FdEWN+CcQUP9CkCQSLGNqR/sA=",
|
||||
"aarch64-linux": "sha256-qzvOJZzmq2QhlauElw8GwgQnCPHdhexI52L0md5zrxQ=",
|
||||
"aarch64-darwin": "sha256-ZzoyLayOFfcYUAg35ZbZ2WapxDdd9IUWqy2xkxZH4QM=",
|
||||
"x86_64-darwin": "sha256-maP/qLeaC3q8VcmNIPyIKlnplxFXJ7ULho3v21/16Mw="
|
||||
"x86_64-linux": "sha256-yQ8EIxxYkzlEWIMY/UiIR+7lbGBuizsoehMPZzFA65Y=",
|
||||
"aarch64-linux": "sha256-JF9VVgnl5QUZ430fqb5Qu8y0kchYJ00LO3FbYcd0lBM=",
|
||||
"aarch64-darwin": "sha256-f3Tu6eu463NWcHgr7dupfD/zUTh26bJ6N2vVXuEyi6c=",
|
||||
"x86_64-darwin": "sha256-miv9Sv4KdhD0UIi2O5LVS1wQOfq29IV+9S+BvIzAvgo="
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,39 @@ test("keeps the file-browser sidebar mounted when switching file tabs", async ({
|
||||
await expect.poll(() => viewport.evaluate((element) => element.scrollTop)).toBe(scrolled)
|
||||
})
|
||||
|
||||
test("keeps previous file search results visible while the next search loads", async ({ page }) => {
|
||||
const searchPending = Promise.withResolvers<void>()
|
||||
await setup(page, async ({ query }) => {
|
||||
if (query === "file-0") return ["file-00.ts"]
|
||||
if (query === "file-7") {
|
||||
await searchPending.promise
|
||||
return ["file-79.ts"]
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
||||
await page.goto(`/server/${base64Encode(server)}/session/${sessionID}`)
|
||||
await expectSessionTitle(page, title)
|
||||
|
||||
const panel = page.locator("#review-panel")
|
||||
await panel.getByRole("button", { name: "Open file" }).click()
|
||||
const filter = panel.getByRole("combobox", { name: "Filter files" })
|
||||
await filter.fill("file-0")
|
||||
await expect(panel.getByRole("option", { name: "file-00.ts" })).toBeVisible()
|
||||
|
||||
const nextSearch = page.waitForRequest((request) => {
|
||||
const url = new URL(request.url())
|
||||
return url.pathname === "/find/file" && url.searchParams.get("query") === "file-7"
|
||||
})
|
||||
await filter.fill("file-7")
|
||||
await nextSearch
|
||||
await expect(panel.getByRole("option", { name: "file-00.ts" })).toBeVisible()
|
||||
|
||||
searchPending.resolve()
|
||||
await expect(panel.getByRole("option", { name: "file-79.ts" })).toBeVisible()
|
||||
await expect(panel.getByRole("option", { name: "file-00.ts" })).toBeHidden()
|
||||
})
|
||||
|
||||
type Probed = HTMLElement & { __e2eProbe?: string }
|
||||
|
||||
async function writeProbe(page: Page) {
|
||||
@@ -74,7 +107,10 @@ async function readProbe(page: Page) {
|
||||
.evaluate((el) => (el as Probed).__e2eProbe)
|
||||
}
|
||||
|
||||
async function setup(page: Page) {
|
||||
async function setup(
|
||||
page: Page,
|
||||
findFiles?: (input: { query: string; dirs?: string; limit?: number }) => unknown | Promise<unknown>,
|
||||
) {
|
||||
await mockOpenCodeServer(page, {
|
||||
directory,
|
||||
project: {
|
||||
@@ -119,6 +155,7 @@ async function setup(page: Page) {
|
||||
}))
|
||||
},
|
||||
fileContent: (path) => ({ type: "text", content: `contents:${path}` }),
|
||||
findFiles,
|
||||
pageMessages: () => ({ items: [] }),
|
||||
})
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface MockServerConfig {
|
||||
questions?: unknown[] | (() => unknown[])
|
||||
fileList?: (path: string) => unknown | Promise<unknown>
|
||||
fileContent?: (path: string) => unknown | Promise<unknown>
|
||||
findFiles?: (input: { query: string; dirs?: string; limit?: number }) => unknown
|
||||
findFiles?: (input: { query: string; dirs?: string; limit?: number }) => unknown | Promise<unknown>
|
||||
sessionStatus?: Record<string, unknown> | (() => Record<string, unknown>)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -145,7 +145,7 @@ function ServerForm(props: ServerFormProps) {
|
||||
type="text"
|
||||
label={language.t("dialog.server.add.name")}
|
||||
placeholder={language.t("dialog.server.add.namePlaceholder")}
|
||||
value={props.name}
|
||||
defaultValue={props.name}
|
||||
disabled={props.busy}
|
||||
onChange={props.onNameChange}
|
||||
onKeyDown={keyDown}
|
||||
@@ -155,7 +155,7 @@ function ServerForm(props: ServerFormProps) {
|
||||
type="text"
|
||||
label={language.t("dialog.server.add.username")}
|
||||
placeholder={language.t("dialog.server.add.usernamePlaceholder")}
|
||||
value={props.username}
|
||||
defaultValue={props.username}
|
||||
disabled={props.busy}
|
||||
onChange={props.onUsernameChange}
|
||||
onKeyDown={keyDown}
|
||||
@@ -164,7 +164,7 @@ function ServerForm(props: ServerFormProps) {
|
||||
type="password"
|
||||
label={language.t("dialog.server.add.password")}
|
||||
placeholder={language.t("dialog.server.add.passwordPlaceholder")}
|
||||
value={props.password}
|
||||
defaultValue={props.password}
|
||||
disabled={props.busy}
|
||||
onChange={props.onPasswordChange}
|
||||
onKeyDown={keyDown}
|
||||
|
||||
@@ -175,7 +175,7 @@ function ServerStatusPopoverView(props: { state: ServerStatusState }) {
|
||||
<div class="flex items-center gap-1 w-[360px] rounded-xl shadow-[var(--shadow-lg-border-base)]">
|
||||
<Tabs
|
||||
aria-label={props.state.ariaLabel}
|
||||
class="tabs bg-background-strong rounded-xl overflow-hidden"
|
||||
class="tabs max-h-[calc(100dvh-56px)] bg-background-strong rounded-xl overflow-hidden"
|
||||
data-component="tabs"
|
||||
data-active="servers"
|
||||
defaultValue="servers"
|
||||
@@ -301,7 +301,7 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
|
||||
<div class="flex items-center gap-1 w-[360px] rounded-xl shadow-[var(--shadow-lg-border-base)]">
|
||||
<Tabs
|
||||
aria-label={language.t("status.popover.ariaLabel")}
|
||||
class="tabs bg-background-strong rounded-xl overflow-hidden"
|
||||
class="tabs max-h-[calc(100dvh-56px)] bg-background-strong rounded-xl overflow-hidden"
|
||||
data-component="tabs"
|
||||
data-active={settings.general.newLayoutDesigns() ? "mcp" : "servers"}
|
||||
defaultValue={settings.general.newLayoutDesigns() ? "mcp" : "servers"}
|
||||
|
||||
@@ -972,17 +972,6 @@ export default function LegacyLayout(props: ParentProps) {
|
||||
keybind: "shift+alt+arrowdown",
|
||||
onSelect: () => navigateSessionByUnseen(1),
|
||||
},
|
||||
{
|
||||
id: "session.archive",
|
||||
title: language.t("command.session.archive"),
|
||||
category: language.t("command.category.session"),
|
||||
keybind: "mod+shift+backspace",
|
||||
disabled: !params.dir || !params.id,
|
||||
onSelect: () => {
|
||||
const session = currentSessions().find((s) => s.id === params.id)
|
||||
if (session) void archiveSession(session)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "workspace.new",
|
||||
title: language.t("workspace.new"),
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { useNavigate } from "@solidjs/router"
|
||||
import { produce } from "solid-js/store"
|
||||
import { notifySessionTabsRemoved } from "@/components/titlebar-session-events"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useSDK } from "@/context/sdk"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { useTabs } from "@/context/tabs"
|
||||
import { errorMessage } from "@/pages/layout/helpers"
|
||||
import { useSessionKey } from "@/pages/session/session-layout"
|
||||
import { legacySessionHref, requireServerKey, sessionHref } from "@/utils/session-route"
|
||||
import { showToast } from "@/utils/toast"
|
||||
|
||||
export function useSessionArchive() {
|
||||
const language = useLanguage()
|
||||
const navigate = useNavigate()
|
||||
const sdk = useSDK()
|
||||
const sync = useSync()
|
||||
const tabs = useTabs()
|
||||
const { params } = useSessionKey()
|
||||
|
||||
const navigateAfterRemoval = (sessionID: string, parentID?: string, nextSessionID?: string) => {
|
||||
if (params.id !== sessionID) return
|
||||
const href = (id: string) =>
|
||||
params.serverKey ? sessionHref(requireServerKey(params.serverKey), id) : legacySessionHref(sdk().directory, id)
|
||||
if (parentID) {
|
||||
navigate(href(parentID))
|
||||
return
|
||||
}
|
||||
if (nextSessionID) {
|
||||
navigate(href(nextSessionID))
|
||||
return
|
||||
}
|
||||
if (params.serverKey) {
|
||||
tabs.newDraft({ server: requireServerKey(params.serverKey), directory: sdk().directory })
|
||||
return
|
||||
}
|
||||
navigate(`/${params.dir}/session`)
|
||||
}
|
||||
|
||||
const archive = async (sessionID: string) => {
|
||||
const session = sync().session.get(sessionID)
|
||||
if (!session) return
|
||||
if ((await sdk().protocol) !== "v1") return
|
||||
|
||||
const sessions = sync().data.session ?? []
|
||||
const index = sessions.findIndex((s) => s.id === sessionID)
|
||||
const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1])
|
||||
|
||||
await sdk()
|
||||
.client.session.update({ sessionID, directory: sdk().directory, time: { archived: Date.now() } })
|
||||
.then(() => {
|
||||
sync().set(
|
||||
produce((draft) => {
|
||||
const index = draft.session.findIndex((s) => s.id === sessionID)
|
||||
if (index !== -1) draft.session.splice(index, 1)
|
||||
}),
|
||||
)
|
||||
sync().session.evict(sessionID)
|
||||
navigateAfterRemoval(sessionID, session.parentID, nextSession?.id)
|
||||
notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [sessionID] })
|
||||
})
|
||||
.catch((err) => {
|
||||
showToast({
|
||||
title: language.t("common.requestFailed"),
|
||||
description: errorMessage(err, language.t("common.requestFailed")),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return { archive, navigateAfterRemoval }
|
||||
}
|
||||
@@ -63,10 +63,10 @@ import { SessionContextUsage } from "@/components/session-context-usage"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useSessionKey } from "@/pages/session/session-layout"
|
||||
import { useSessionArchive } from "@/pages/session/session-archive"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { useTabs } from "@/context/tabs"
|
||||
import { legacySessionHref, requireServerKey, sessionHref } from "@/utils/session-route"
|
||||
import { useSDK } from "@/context/sdk"
|
||||
import { useSync } from "@/context/sync"
|
||||
@@ -263,8 +263,8 @@ export function MessageTimeline(props: {
|
||||
const sdk = useSDK()
|
||||
const sync = useSync()
|
||||
const settings = useSettings()
|
||||
const tabs = useTabs()
|
||||
const dialog = useDialog()
|
||||
const sessionArchive = useSessionArchive()
|
||||
const language = useLanguage()
|
||||
const { params, sessionKey } = useSessionKey()
|
||||
const ownerSessionKey = sessionKey()
|
||||
@@ -790,25 +790,6 @@ export function MessageTimeline(props: {
|
||||
titleMutation.mutate({ id, title: next })
|
||||
}
|
||||
|
||||
const navigateAfterSessionRemoval = (sessionID: string, parentID?: string, nextSessionID?: string) => {
|
||||
if (params.id !== sessionID) return
|
||||
const href = (id: string) =>
|
||||
params.serverKey ? sessionHref(requireServerKey(params.serverKey), id) : legacySessionHref(sdk().directory, id)
|
||||
if (parentID) {
|
||||
navigate(href(parentID))
|
||||
return
|
||||
}
|
||||
if (nextSessionID) {
|
||||
navigate(href(nextSessionID))
|
||||
return
|
||||
}
|
||||
if (params.serverKey) {
|
||||
tabs.newDraft({ server: requireServerKey(params.serverKey), directory: sdk().directory })
|
||||
return
|
||||
}
|
||||
navigate(`/${params.dir}/session`)
|
||||
}
|
||||
|
||||
const exportSession = async (sessionID: string) => {
|
||||
try {
|
||||
const data = await fetchSessionExport({
|
||||
@@ -832,36 +813,6 @@ export function MessageTimeline(props: {
|
||||
}
|
||||
}
|
||||
|
||||
const archiveSession = async (sessionID: string) => {
|
||||
const session = sync().session.get(sessionID)
|
||||
if (!session) return
|
||||
if ((await sdk().protocol) !== "v1") return
|
||||
|
||||
const sessions = sync().data.session ?? []
|
||||
const index = sessions.findIndex((s) => s.id === sessionID)
|
||||
const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1])
|
||||
|
||||
await sdk()
|
||||
.client.session.update({ sessionID, directory: sdk().directory, time: { archived: Date.now() } })
|
||||
.then(() => {
|
||||
sync().set(
|
||||
produce((draft) => {
|
||||
const index = draft.session.findIndex((s) => s.id === sessionID)
|
||||
if (index !== -1) draft.session.splice(index, 1)
|
||||
}),
|
||||
)
|
||||
sync().session.evict(sessionID)
|
||||
navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id)
|
||||
notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [sessionID] })
|
||||
})
|
||||
.catch((err) => {
|
||||
showToast({
|
||||
title: language.t("common.requestFailed"),
|
||||
description: errorMessage(err),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const deleteSession = async (sessionID: string) => {
|
||||
const session = sync().session.get(sessionID)
|
||||
if (!session) return false
|
||||
@@ -911,7 +862,7 @@ export function MessageTimeline(props: {
|
||||
}
|
||||
}
|
||||
|
||||
navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id)
|
||||
sessionArchive.navigateAfterRemoval(sessionID, session.parentID, nextSession?.id)
|
||||
|
||||
sync().set(
|
||||
produce((draft) => {
|
||||
@@ -1593,7 +1544,7 @@ export function MessageTimeline(props: {
|
||||
<DropdownMenu.Item onSelect={() => exportSession(id)}>
|
||||
<DropdownMenu.ItemLabel>{language.t("common.export")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onSelect={() => void archiveSession(id)}>
|
||||
<DropdownMenu.Item onSelect={() => void sessionArchive.archive(id)}>
|
||||
<DropdownMenu.ItemLabel>{language.t("common.archive")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
@@ -1667,7 +1618,7 @@ export function MessageTimeline(props: {
|
||||
<MenuV2.Item onSelect={() => exportSession(id)}>
|
||||
{language.t("common.export")}...
|
||||
</MenuV2.Item>
|
||||
<MenuV2.Item onSelect={() => void archiveSession(id)}>
|
||||
<MenuV2.Item onSelect={() => void sessionArchive.archive(id)}>
|
||||
{language.t("common.archive")}
|
||||
</MenuV2.Item>
|
||||
<MenuV2.Separator />
|
||||
|
||||
@@ -18,6 +18,7 @@ import { createSessionTabs } from "@/pages/session/helpers"
|
||||
import { extractPromptFromParts } from "@/utils/prompt"
|
||||
import { Message, Part, UserMessage } from "@opencode-ai/sdk/v2"
|
||||
import { useSessionLayout } from "@/pages/session/session-layout"
|
||||
import { useSessionArchive } from "@/pages/session/session-archive"
|
||||
import { createSessionOwnership } from "./session-ownership"
|
||||
import { useLocal } from "@/context/local"
|
||||
|
||||
@@ -52,6 +53,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
||||
const navigate = useNavigate()
|
||||
const { params, sessionKey, tabs, view } = useSessionLayout()
|
||||
const sessionOwnership = createSessionOwnership(sessionKey)
|
||||
const sessionArchive = useSessionArchive()
|
||||
const openDialog = async <T,>(load: () => Promise<T>, show: (value: T) => void) => {
|
||||
const owner = sessionOwnership.capture()
|
||||
const value = await load()
|
||||
@@ -497,6 +499,16 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
||||
disabled: !params.id,
|
||||
onSelect: exportSession,
|
||||
}),
|
||||
sessionCommand({
|
||||
id: "session.archive",
|
||||
title: language.t("command.session.archive"),
|
||||
keybind: "mod+shift+backspace",
|
||||
disabled: !params.id,
|
||||
onSelect: () => {
|
||||
const id = params.id
|
||||
if (id) void sessionArchive.archive(id)
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
const fileCmds = () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createMemo, createSignal, createUniqueId, Show } from "solid-js"
|
||||
import { createQuery } from "@tanstack/solid-query"
|
||||
import { createQuery, keepPreviousData } from "@tanstack/solid-query"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { SessionFilePanelV2, SessionFilePanelV2Empty } from "@opencode-ai/session-ui/v2/session-file-panel-v2"
|
||||
import { SessionReviewV2Sidebar } from "@opencode-ai/session-ui/v2/session-review-v2"
|
||||
@@ -50,6 +50,7 @@ export function SessionFileBrowserTab(props: {
|
||||
queryKey: ["session-open-file", workspaceKey(), value] as const,
|
||||
enabled: value.length > 0,
|
||||
queryFn: ({ signal }) => file.searchFiles(value, { limit: 200, signal }),
|
||||
placeholderData: keepPreviousData,
|
||||
}
|
||||
})
|
||||
const files = createMemo(() => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/codemode",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"description": "Effect-native confined code execution over schema-described tools",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -252,7 +252,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "الاستثناءات التالية",
|
||||
|
||||
"go.title": "OpenCode Go | نماذج برمجة منخفضة التكلفة للجميع",
|
||||
"go.banner.text": "يحصل DeepSeek V4 Flash على حدود استخدام مضاعفة لفترة محدودة",
|
||||
"go.banner.text": "Ox Alpha Free متاح على Go لفترة محدودة",
|
||||
"go.meta.description":
|
||||
"يبدأ Go بسعر $5 للشهر الأول، ثم $10/شهر، مع حدود استخدام سخية ووصول موثوق إلى نماذج البرمجة الرائدة.",
|
||||
"go.hero.title": "نماذج برمجة منخفضة التكلفة للجميع",
|
||||
@@ -270,6 +270,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle ونماذج مجانية",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "الطلبات كل 5 ساعات",
|
||||
"go.graph.limitedRegions": "مناطق محدودة",
|
||||
"go.graph.limitedTime": "لفترة محدودة",
|
||||
"go.graph.usageLimits": "حدود الاستخدام",
|
||||
"go.graph.aria": "الطلبات كل 5 ساعات: {{free}} مقابل {{go}}",
|
||||
|
||||
@@ -342,10 +344,13 @@ export const dict = {
|
||||
"go.faq.a5.used": "مستخدَمة",
|
||||
"go.faq.a5.notUsed": "غير مستخدَمة",
|
||||
"go.faq.a5.noAgreement": "لا توجد اتفاقية",
|
||||
"go.faq.a5.notZdr": "ليست ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"تعطّل ZDR ميزات API مهمة تعتمد على البيانات المخزنة، بما في ذلك Responses API ذات الحالة، وFiles and Collections، وBatch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
"تُنشأ سجلات مراقبة إساءة الاستخدام لكل استخدام لميزات API، ويُحتفظ بها لمدة تصل إلى 30 يومًا.",
|
||||
"go.faq.a5.museRetention":
|
||||
"أسعار توكنات مخفّضة للغاية مقابل منح الإذن باستخدام مطالباتك وإكمالات النموذج لتدريب نماذج Meta المستقبلية.",
|
||||
"go.faq.a5.learnMore": "اعرف المزيد",
|
||||
"go.faq.a5.deepseekRetention": "تُجدَّد اتفاقية ZDR شهريًا. الاتفاقية الحالية سارية حتى 31 أغسطس 2026.",
|
||||
"go.faq.a5.beforeExceptions":
|
||||
@@ -386,6 +391,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "النموذج معطل",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"لا يتوفر أحدث إصدار من هذا النموذج إلا مستضافًا في الصين، ويتطلب تفعيلًا صريحًا: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "هذا النموذج غير متاح في بلدك.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"يجمع هذا النموذج بيانات تُستخدم لتحسين جودته، ويتطلب موافقتك الصريحة: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"انتهى العرض المجاني لـ {{model}}. يمكنك مواصلة استخدام النموذج بالاشتراك في OpenCode Go - {{link}}",
|
||||
|
||||
@@ -665,6 +673,7 @@ export const dict = {
|
||||
'اختر "OpenCode Go" كمزود في إعدادات opencode الخاصة بك لاستخدام نماذج Go.',
|
||||
"workspace.lite.providers.title": "المزودون",
|
||||
"workspace.lite.providers.description": "تحكم في المزودين المستخدمين للتوجيه.",
|
||||
"workspace.lite.providers.allowTraining": "السماح بالنماذج التي تتدرب على بيانات الطلبات",
|
||||
"workspace.lite.providers.useChina": "تفعيل النماذج المستضافة في الصين",
|
||||
"workspace.lite.black.message":
|
||||
"أنت مشترك حاليًا في OpenCode Black أو في قائمة الانتظار. يرجى إلغاء الاشتراك أولاً إذا كنت ترغب في التبديل إلى Go.",
|
||||
|
||||
@@ -256,7 +256,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "seguintes exceções",
|
||||
|
||||
"go.title": "OpenCode Go | Modelos de codificação de baixo custo para todos",
|
||||
"go.banner.text": "DeepSeek V4 Flash tem limites de uso 2x maiores por tempo limitado",
|
||||
"go.banner.text": "Ox Alpha Free está disponível no Go por tempo limitado",
|
||||
"go.meta.description":
|
||||
"O Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos de uso e acesso confiável aos principais modelos de codificação.",
|
||||
"go.hero.title": "Modelos de codificação de baixo custo para todos",
|
||||
@@ -274,6 +274,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle e modelos gratuitos",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Requisições por 5 horas",
|
||||
"go.graph.limitedRegions": "regiões limitadas",
|
||||
"go.graph.limitedTime": "tempo limitado",
|
||||
"go.graph.usageLimits": "Limites de uso",
|
||||
"go.graph.aria": "Requisições por 5h: {{free}} vs {{go}}",
|
||||
|
||||
@@ -349,10 +351,13 @@ export const dict = {
|
||||
"go.faq.a5.used": "Usado",
|
||||
"go.faq.a5.notUsed": "Não usado",
|
||||
"go.faq.a5.noAgreement": "Sem acordo",
|
||||
"go.faq.a5.notZdr": "Não é ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"O ZDR desativa recursos importantes da API que dependem de dados armazenados, incluindo a Responses API com estado, Files and Collections e a Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
"Logs de monitoramento de abuso são gerados para todo uso de recursos da API e retidos por até 30 dias.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Preços de tokens com grandes descontos em troca da permissão para usar seus prompts e respostas geradas para treinar futuros modelos da Meta.",
|
||||
"go.faq.a5.learnMore": "Saiba mais",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"O acordo de ZDR é renovado mensalmente. O acordo atual é válido até 31 de agosto de 2026.",
|
||||
@@ -395,6 +400,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "O modelo está desabilitado",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"A versão mais recente deste modelo está disponível apenas com hospedagem na China e requer adesão explícita: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Este modelo não está disponível no seu país.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Este modelo coleta dados usados para melhorar sua qualidade e exige seu consentimento explícito: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"A promoção gratuita do {{model}} terminou. Você pode continuar usando o modelo assinando o OpenCode Go - {{link}}",
|
||||
|
||||
@@ -676,6 +684,7 @@ export const dict = {
|
||||
'Selecione "OpenCode Go" como provedor na sua configuração do opencode para usar os modelos Go.',
|
||||
"workspace.lite.providers.title": "Provedores",
|
||||
"workspace.lite.providers.description": "Controle quais provedores são usados para roteamento.",
|
||||
"workspace.lite.providers.allowTraining": "Permitir modelos que usam dados de solicitações para treinamento",
|
||||
"workspace.lite.providers.useChina": "Ativar modelos hospedados na China",
|
||||
"workspace.lite.black.message":
|
||||
"Você está atualmente inscrito no OpenCode Black ou na lista de espera. Por favor, cancele a assinatura primeiro se desejar mudar para o Go.",
|
||||
|
||||
@@ -254,7 +254,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "følgende undtagelser",
|
||||
|
||||
"go.title": "OpenCode Go | Kodningsmodeller til lav pris for alle",
|
||||
"go.banner.text": "DeepSeek V4 Flash får fordoblet brugsgrænse i en begrænset periode",
|
||||
"go.banner.text": "Ox Alpha Free er tilgængelig på Go i en begrænset periode",
|
||||
"go.meta.description":
|
||||
"Go starter ved $5 for den første måned, derefter $10/måned, med generøse brugsgrænser og pålidelig adgang til førende kodningsmodeller.",
|
||||
"go.hero.title": "Kodningsmodeller til lav pris for alle",
|
||||
@@ -272,6 +272,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle og gratis modeller",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Forespørgsler pr. 5 timer",
|
||||
"go.graph.limitedRegions": "begrænsede regioner",
|
||||
"go.graph.limitedTime": "begrænset periode",
|
||||
"go.graph.usageLimits": "Brugsgrænser",
|
||||
"go.graph.aria": "Forespørgsler pr. 5t: {{free}} vs {{go}}",
|
||||
|
||||
@@ -346,10 +348,13 @@ export const dict = {
|
||||
"go.faq.a5.used": "Brugt",
|
||||
"go.faq.a5.notUsed": "Ikke brugt",
|
||||
"go.faq.a5.noAgreement": "Ingen aftale",
|
||||
"go.faq.a5.notZdr": "Ikke ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR deaktiverer vigtige API-funktioner, der afhænger af lagrede data, herunder den tilstandsbevarende Responses API, Files and Collections og Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
"Logfiler til overvågning af misbrug genereres ved al brug af API-funktioner og opbevares i op til 30 dage.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Kraftigt nedsatte tokenpriser til gengæld for tilladelse til at bruge dine prompts og modelsvar til at træne fremtidige Meta-modeller.",
|
||||
"go.faq.a5.learnMore": "Læs mere",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"ZDR-aftalen fornyes månedligt. Den nuværende aftale er gyldig til og med 31. august 2026.",
|
||||
@@ -392,6 +397,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "Modellen er deaktiveret",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"Den nyeste version af denne model er kun tilgængelig som hostet i Kina og kræver, at du aktivt tilvælger den: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Denne model er ikke tilgængelig i dit land.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Denne model indsamler data, der bruges til at forbedre dens kvalitet, og kræver, at du aktivt tilvælger den: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"Den gratis kampagne for {{model}} er afsluttet. Du kan fortsætte med at bruge modellen ved at abonnere på OpenCode Go - {{link}}",
|
||||
|
||||
@@ -673,6 +681,7 @@ export const dict = {
|
||||
'Vælg "OpenCode Go" som udbyder i din opencode-konfiguration for at bruge Go-modeller.',
|
||||
"workspace.lite.providers.title": "Udbydere",
|
||||
"workspace.lite.providers.description": "Styr, hvilke udbydere der bruges til routing.",
|
||||
"workspace.lite.providers.allowTraining": "Tillad modeller, der træner på anmodningsdata",
|
||||
"workspace.lite.providers.useChina": "Aktivér modeller hostet i Kina",
|
||||
"workspace.lite.black.message":
|
||||
"Du abonnerer i øjeblikket på OpenCode Black eller er på venteliste. Afmeld venligst først, hvis du vil skifte til Go.",
|
||||
|
||||
@@ -256,7 +256,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "folgenden Ausnahmen",
|
||||
|
||||
"go.title": "OpenCode Go | Kostengünstige Coding-Modelle für alle",
|
||||
"go.banner.text": "DeepSeek V4 Flash erhält für begrenzte Zeit 2x Nutzungslimits",
|
||||
"go.banner.text": "Ox Alpha Free ist für begrenzte Zeit auf Go verfügbar",
|
||||
"go.meta.description":
|
||||
"Go beginnt bei $5 für deinen ersten Monat, danach $10/Monat, mit großzügigen Nutzungslimits und zuverlässigem Zugang zu führenden Coding-Modellen.",
|
||||
"go.hero.title": "Kostengünstige Coding-Modelle für alle",
|
||||
@@ -274,6 +274,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle und kostenlose Modelle",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Anfragen pro 5 Stunden",
|
||||
"go.graph.limitedRegions": "begrenzte Regionen",
|
||||
"go.graph.limitedTime": "begrenzte Zeit",
|
||||
"go.graph.usageLimits": "Nutzungslimits",
|
||||
"go.graph.aria": "Anfragen pro 5h: {{free}} vs {{go}}",
|
||||
|
||||
@@ -348,10 +350,13 @@ export const dict = {
|
||||
"go.faq.a5.used": "Verwendet",
|
||||
"go.faq.a5.notUsed": "Nicht verwendet",
|
||||
"go.faq.a5.noAgreement": "Keine Vereinbarung",
|
||||
"go.faq.a5.notZdr": "Kein ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR deaktiviert wichtige API-Funktionen, die von gespeicherten Daten abhängen, einschließlich der zustandsbehafteten Responses API, Files and Collections und der Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
"Für die Nutzung aller API-Funktionen werden Protokolle zur Missbrauchsüberwachung erstellt und bis zu 30 Tage lang aufbewahrt.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Stark vergünstigte Tokenpreise im Gegenzug für die Erlaubnis, deine Prompts und Vervollständigungen zum Trainieren zukünftiger Meta-Modelle zu verwenden.",
|
||||
"go.faq.a5.learnMore": "Mehr erfahren",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"Die ZDR-Vereinbarung wird monatlich erneuert. Die aktuelle Vereinbarung gilt bis einschließlich 31. August 2026.",
|
||||
@@ -394,6 +399,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "Modell ist deaktiviert",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"Die neueste Version dieses Modells ist nur verfügbar, wenn sie in China gehostet wird, und muss ausdrücklich aktiviert werden: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Dieses Modell ist in deinem Land nicht verfügbar.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Dieses Modell erfasst Daten, die zur Verbesserung seiner Qualität verwendet werden, und erfordert deine ausdrückliche Zustimmung: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"Die kostenlose Aktion für {{model}} ist beendet. Du kannst das Modell weiterhin nutzen, indem du OpenCode Go abonnierst - {{link}}",
|
||||
|
||||
@@ -675,6 +683,7 @@ export const dict = {
|
||||
'Wähle "OpenCode Go" als Anbieter in deiner opencode-Konfiguration, um Go-Modelle zu verwenden.',
|
||||
"workspace.lite.providers.title": "Anbieter",
|
||||
"workspace.lite.providers.description": "Steuere, welche Anbieter für das Routing verwendet werden.",
|
||||
"workspace.lite.providers.allowTraining": "Modelle zulassen, die mit Anfragedaten trainieren",
|
||||
"workspace.lite.providers.useChina": "In China gehostete Modelle aktivieren",
|
||||
"workspace.lite.black.message":
|
||||
"Du hast derzeit OpenCode Black abonniert oder stehst auf der Warteliste. Bitte kündige zuerst, wenn du zu Go wechseln möchtest.",
|
||||
|
||||
@@ -253,7 +253,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "following exceptions",
|
||||
|
||||
"go.title": "OpenCode Go | Low cost coding models for everyone",
|
||||
"go.banner.text": "DeepSeek V4 Flash gets 2× usage limits for a limited time",
|
||||
"go.banner.text": "Ox Alpha Free is available on Go for a limited time",
|
||||
"go.meta.description":
|
||||
"Go starts at $5 for your first month, then $10/month, with generous usage limits and reliable access to leading coding models.",
|
||||
"go.hero.title": "Low cost coding models for everyone",
|
||||
@@ -270,6 +270,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle and free models",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Requests per 5 hour",
|
||||
"go.graph.limitedRegions": "limited regions",
|
||||
"go.graph.limitedTime": "limited time",
|
||||
"go.graph.tick": "{{n}}x",
|
||||
"go.graph.usageLimits": "Usage limits",
|
||||
"go.graph.aria": "Requests per 5h: {{free}} vs {{go}}",
|
||||
@@ -344,10 +346,13 @@ export const dict = {
|
||||
"go.faq.a5.used": "Used",
|
||||
"go.faq.a5.notUsed": "Not used",
|
||||
"go.faq.a5.noAgreement": "No agreement",
|
||||
"go.faq.a5.notZdr": "Not ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR disables important API features that depend on stored data, including the stateful Responses API, Files and Collections, and the Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
"Abuse monitoring logs are generated for all API feature usage and retained for up to 30 days.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Heavily discounted token pricing in exchange for permission to use your prompts and completions to train future Meta models.",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"ZDR agreement is renewed monthly. The current agreement is valid through August 31, 2026.",
|
||||
"go.faq.a5.learnMore": "Learn more",
|
||||
@@ -391,6 +396,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "Model is disabled",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"The latest version of this model is only available hosted in China and requires explicit opt in: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "This model is not available in your country.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"This model collects data used to improve its quality and requires explicit opt in: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"Free promotion has ended for {{model}}. You can continue using the model by subscribing to OpenCode Go - {{link}}",
|
||||
|
||||
@@ -672,7 +680,8 @@ export const dict = {
|
||||
'Select "OpenCode Go" as the provider in your opencode configuration to use Go models.',
|
||||
"workspace.lite.providers.title": "Providers",
|
||||
"workspace.lite.providers.description": "Control which providers are used for routing.",
|
||||
"workspace.lite.providers.useChina": "Enable models hosted in China",
|
||||
"workspace.lite.providers.allowTraining": "Allow models that train on request data",
|
||||
"workspace.lite.providers.useChina": "Allow models hosted in China",
|
||||
"workspace.lite.black.message":
|
||||
"You're currently subscribed to OpenCode Black or on the waitlist. Please unsubscribe first if you'd like to switch to Go.",
|
||||
"workspace.lite.other.message":
|
||||
|
||||
@@ -257,7 +257,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "siguientes excepciones",
|
||||
|
||||
"go.title": "OpenCode Go | Modelos de programación de bajo coste para todos",
|
||||
"go.banner.text": "DeepSeek V4 Flash tiene límites de uso 2x mayores por tiempo limitado",
|
||||
"go.banner.text": "Ox Alpha Free está disponible en Go por tiempo limitado",
|
||||
"go.meta.description":
|
||||
"Go comienza en $5 el primer mes, luego 10 $/mes, con límites de uso generosos y acceso fiable a modelos de programación líderes.",
|
||||
"go.hero.title": "Modelos de programación de bajo coste para todos",
|
||||
@@ -275,6 +275,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle y modelos gratuitos",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Solicitudes por 5 horas",
|
||||
"go.graph.limitedRegions": "regiones limitadas",
|
||||
"go.graph.limitedTime": "tiempo limitado",
|
||||
"go.graph.usageLimits": "Límites de uso",
|
||||
"go.graph.aria": "Solicitudes por 5h: {{free}} vs {{go}}",
|
||||
|
||||
@@ -349,10 +351,13 @@ export const dict = {
|
||||
"go.faq.a5.used": "Utilizado",
|
||||
"go.faq.a5.notUsed": "No utilizado",
|
||||
"go.faq.a5.noAgreement": "Sin acuerdo",
|
||||
"go.faq.a5.notZdr": "Sin ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR deshabilita funciones importantes de la API que dependen de datos almacenados, incluidas la Responses API con estado, Files and Collections y la Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
"Se generan registros de supervisión de abusos para todo el uso de funciones de la API y se conservan durante un máximo de 30 días.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Precios de tokens muy reducidos a cambio de permitir que tus prompts y las respuestas generadas se utilicen para entrenar futuros modelos de Meta.",
|
||||
"go.faq.a5.learnMore": "Más información",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"El acuerdo de ZDR se renueva mensualmente. El acuerdo actual es válido hasta el 31 de agosto de 2026.",
|
||||
@@ -395,6 +400,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "El modelo está deshabilitado",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"La versión más reciente de este modelo solo está disponible alojada en China y requiere una aceptación explícita: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Este modelo no está disponible en tu país.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Este modelo recopila datos que se utilizan para mejorar su calidad y requiere tu consentimiento explícito: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"La promoción gratuita de {{model}} ha finalizado. Puedes seguir usando el modelo suscribiéndote a OpenCode Go - {{link}}",
|
||||
|
||||
@@ -676,6 +684,7 @@ export const dict = {
|
||||
'Selecciona "OpenCode Go" como proveedor en tu configuración de opencode para usar los modelos Go.',
|
||||
"workspace.lite.providers.title": "Proveedores",
|
||||
"workspace.lite.providers.description": "Controla qué proveedores se usan para el enrutamiento.",
|
||||
"workspace.lite.providers.allowTraining": "Permitir modelos que se entrenan con los datos de las solicitudes",
|
||||
"workspace.lite.providers.useChina": "Activar modelos alojados en China",
|
||||
"workspace.lite.black.message":
|
||||
"Actualmente estás suscrito a OpenCode Black o estás en la lista de espera. Por favor, cancela la suscripción primero si deseas cambiar a Go.",
|
||||
|
||||
@@ -258,7 +258,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "exceptions suivantes",
|
||||
|
||||
"go.title": "OpenCode Go | Modèles de code à faible coût pour tous",
|
||||
"go.banner.text": "DeepSeek V4 Flash bénéficie de limites d’utilisation 2x supérieures pour une durée limitée",
|
||||
"go.banner.text": "Ox Alpha Free est disponible sur Go pour une durée limitée",
|
||||
"go.meta.description":
|
||||
"Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites d'utilisation généreuses et un accès fiable aux principaux modèles de codage.",
|
||||
"go.hero.title": "Modèles de code à faible coût pour tous",
|
||||
@@ -276,6 +276,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle et modèles gratuits",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Requêtes par tranche de 5 heures",
|
||||
"go.graph.limitedRegions": "régions limitées",
|
||||
"go.graph.limitedTime": "durée limitée",
|
||||
"go.graph.usageLimits": "Limites d'utilisation",
|
||||
"go.graph.aria": "Requêtes par 5h : {{free}} vs {{go}}",
|
||||
|
||||
@@ -350,10 +352,13 @@ export const dict = {
|
||||
"go.faq.a5.used": "Utilisé",
|
||||
"go.faq.a5.notUsed": "Non utilisé",
|
||||
"go.faq.a5.noAgreement": "Aucun accord",
|
||||
"go.faq.a5.notZdr": "Pas de ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"Le ZDR désactive d'importantes fonctionnalités API qui dépendent des données stockées, notamment Responses API avec état, Files and Collections et Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
"Des journaux de surveillance des abus sont générés pour toute utilisation des fonctionnalités API et conservés pendant un maximum de 30 jours.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Des tarifs de tokens fortement réduits en échange de l’autorisation d’utiliser vos prompts et vos complétions pour entraîner de futurs modèles Meta.",
|
||||
"go.faq.a5.learnMore": "En savoir plus",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"L’accord ZDR est renouvelé chaque mois. L’accord actuel est valable jusqu’au 31 août 2026.",
|
||||
@@ -396,6 +401,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "Le modèle est désactivé",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"La dernière version de ce modèle est uniquement disponible avec un hébergement en Chine et nécessite votre consentement explicite : {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Ce modèle n’est pas disponible dans votre pays.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Ce modèle collecte des données utilisées pour améliorer sa qualité et nécessite votre consentement explicite : {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"La promotion gratuite de {{model}} est terminée. Vous pouvez continuer à utiliser le modèle en vous abonnant à OpenCode Go - {{link}}",
|
||||
|
||||
@@ -683,6 +691,7 @@ export const dict = {
|
||||
'Sélectionnez "OpenCode Go" comme fournisseur dans votre configuration opencode pour utiliser les modèles Go.',
|
||||
"workspace.lite.providers.title": "Fournisseurs",
|
||||
"workspace.lite.providers.description": "Contrôlez les fournisseurs utilisés pour le routage.",
|
||||
"workspace.lite.providers.allowTraining": "Autoriser les modèles qui s’entraînent sur les données des requêtes",
|
||||
"workspace.lite.providers.useChina": "Activer les modèles hébergés en Chine",
|
||||
"workspace.lite.black.message":
|
||||
"Vous êtes actuellement abonné à OpenCode Black ou sur liste d'attente. Veuillez d'abord vous désabonner si vous souhaitez passer à Go.",
|
||||
|
||||
@@ -254,7 +254,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "seguenti eccezioni",
|
||||
|
||||
"go.title": "OpenCode Go | Modelli di coding a basso costo per tutti",
|
||||
"go.banner.text": "DeepSeek V4 Flash offre limiti di utilizzo 2x superiori per un periodo limitato",
|
||||
"go.banner.text": "Ox Alpha Free è disponibile su Go per un periodo limitato",
|
||||
"go.meta.description":
|
||||
"Go inizia a $5 per il primo mese, poi $10/mese, con limiti di utilizzo generosi e un accesso affidabile ai principali modelli di coding.",
|
||||
"go.hero.title": "Modelli di coding a basso costo per tutti",
|
||||
@@ -272,6 +272,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle e modelli gratuiti",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Richieste ogni 5 ore",
|
||||
"go.graph.limitedRegions": "regioni limitate",
|
||||
"go.graph.limitedTime": "periodo limitato",
|
||||
"go.graph.usageLimits": "Limiti di utilizzo",
|
||||
"go.graph.aria": "Richieste ogni 5h: {{free}} vs {{go}}",
|
||||
|
||||
@@ -345,6 +347,7 @@ export const dict = {
|
||||
"go.faq.a5.used": "Utilizzato",
|
||||
"go.faq.a5.notUsed": "Non utilizzato",
|
||||
"go.faq.a5.noAgreement": "Nessun accordo",
|
||||
"go.faq.a5.notZdr": "Non ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR disabilita importanti funzionalità API che dipendono dai dati archiviati, tra cui la Responses API con stato, Files and Collections e Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
@@ -352,6 +355,8 @@ export const dict = {
|
||||
"go.faq.a5.learnMore": "Scopri di più",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"L'accordo ZDR viene rinnovato mensilmente. L'accordo attuale è valido fino al 31 agosto 2026.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Prezzi dei token fortemente scontati in cambio dell'autorizzazione a utilizzare i tuoi prompt e completamenti per addestrare futuri modelli Meta.",
|
||||
"go.faq.a5.beforeExceptions":
|
||||
"I modelli Go sono ospitati negli Stati Uniti. I provider seguono una policy di zero-retention e non usano i tuoi dati per l'addestramento dei modelli, con le",
|
||||
"go.faq.a5.exceptionsLink": "seguenti eccezioni",
|
||||
@@ -391,6 +396,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "Il modello è disabilitato",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"La versione più recente di questo modello è disponibile solo con hosting in Cina e richiede un consenso esplicito: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Questo modello non è disponibile nel tuo paese.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Questo modello raccoglie dati utilizzati per migliorarne la qualità e richiede il tuo consenso esplicito: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"La promozione gratuita di {{model}} è terminata. Puoi continuare a usare il modello abbonandoti a OpenCode Go - {{link}}",
|
||||
|
||||
@@ -674,6 +682,7 @@ export const dict = {
|
||||
'Seleziona "OpenCode Go" come provider nella tua configurazione opencode per utilizzare i modelli Go.',
|
||||
"workspace.lite.providers.title": "Provider",
|
||||
"workspace.lite.providers.description": "Controlla quali provider vengono usati per il routing.",
|
||||
"workspace.lite.providers.allowTraining": "Consenti i modelli che si addestrano sui dati delle richieste",
|
||||
"workspace.lite.providers.useChina": "Abilita modelli ospitati in Cina",
|
||||
"workspace.lite.black.message":
|
||||
"Attualmente sei abbonato a OpenCode Black o sei in lista d'attesa. Annulla l'iscrizione prima se desideri passare a Go.",
|
||||
|
||||
@@ -253,7 +253,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "以下の例外",
|
||||
|
||||
"go.title": "OpenCode Go | すべての人のための低価格なコーディングモデル",
|
||||
"go.banner.text": "DeepSeek V4 Flashの利用上限が期間限定で2倍に",
|
||||
"go.banner.text": "Ox Alpha Freeは期間限定でGoで利用できます",
|
||||
"go.meta.description":
|
||||
"Goは最初の月$5、その後$10/月で、主要なコーディングモデルへのゆとりある利用上限と安定したアクセスを提供します。",
|
||||
"go.hero.title": "すべての人のための低価格なコーディングモデル",
|
||||
@@ -271,6 +271,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickleと無料モデル",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "5時間あたりのリクエスト数",
|
||||
"go.graph.limitedRegions": "一部の地域に限定",
|
||||
"go.graph.limitedTime": "期間限定",
|
||||
"go.graph.usageLimits": "利用制限",
|
||||
"go.graph.aria": "5時間あたりのリクエスト数: {{free}} 対 {{go}}",
|
||||
|
||||
@@ -345,9 +347,12 @@ export const dict = {
|
||||
"go.faq.a5.used": "使用あり",
|
||||
"go.faq.a5.notUsed": "使用なし",
|
||||
"go.faq.a5.noAgreement": "契約なし",
|
||||
"go.faq.a5.notZdr": "ZDRではない",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDRでは、保存データに依存する重要なAPI機能(ステートフルなResponses API、Files and Collections、Batch APIなど)が無効になります。",
|
||||
"go.faq.a5.gptRetention": "不正使用監視ログはすべてのAPI機能の使用時に生成され、最大30日間保持されます。",
|
||||
"go.faq.a5.museRetention":
|
||||
"将来のMetaモデルのトレーニングにプロンプトと生成結果を使用する許可と引き換えに、トークン料金が大幅に割引されます。",
|
||||
"go.faq.a5.learnMore": "詳しく見る",
|
||||
"go.faq.a5.deepseekRetention": "ZDR契約は毎月更新されます。現在の契約は2026年8月31日まで有効です。",
|
||||
"go.faq.a5.beforeExceptions":
|
||||
@@ -390,6 +395,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "モデルが無効です",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"このモデルの最新バージョンは中国でのみホスト提供されており、利用するには明示的なオプトインが必要です: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "このモデルはお住まいの国では利用できません。",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"このモデルは品質向上のためのデータを収集します。利用するには明示的な同意が必要です: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"{{model}} の無料プロモーションは終了しました。OpenCode Go を購読するとモデルを引き続き使用できます - {{link}}",
|
||||
|
||||
@@ -672,6 +680,7 @@ export const dict = {
|
||||
"Go モデルを使用するには、opencode の設定で「OpenCode Go」をプロバイダーとして選択してください。",
|
||||
"workspace.lite.providers.title": "プロバイダー",
|
||||
"workspace.lite.providers.description": "ルーティングに使用するプロバイダーを管理します。",
|
||||
"workspace.lite.providers.allowTraining": "リクエストデータを学習に使用するモデルを許可",
|
||||
"workspace.lite.providers.useChina": "中国でホストされているモデルを有効にする",
|
||||
"workspace.lite.black.message":
|
||||
"現在 OpenCode Black を購読中、またはウェイティングリストに登録されています。Go に切り替える場合は、先に登録を解除してください。",
|
||||
|
||||
@@ -250,7 +250,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "다음 예외",
|
||||
|
||||
"go.title": "OpenCode Go | 모두를 위한 저비용 코딩 모델",
|
||||
"go.banner.text": "DeepSeek V4 Flash 사용 한도가 한시적으로 2배 확대됩니다",
|
||||
"go.banner.text": "Ox Alpha Free가 한정된 기간 동안 Go에서 제공됩니다",
|
||||
"go.meta.description":
|
||||
"Go는 첫 달 $5, 이후 $10/월로 시작하며, 넉넉한 사용 한도와 주요 코딩 모델에 대한 안정적인 액세스를 제공합니다.",
|
||||
"go.hero.title": "모두를 위한 저비용 코딩 모델",
|
||||
@@ -268,6 +268,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle 및 무료 모델",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "5시간당 요청 수",
|
||||
"go.graph.limitedRegions": "일부 지역에서만 제공",
|
||||
"go.graph.limitedTime": "한정된 기간",
|
||||
"go.graph.usageLimits": "사용 한도",
|
||||
"go.graph.aria": "5시간당 요청 수: {{free}} 대 {{go}}",
|
||||
|
||||
@@ -341,9 +343,12 @@ export const dict = {
|
||||
"go.faq.a5.used": "사용됨",
|
||||
"go.faq.a5.notUsed": "사용되지 않음",
|
||||
"go.faq.a5.noAgreement": "합의 없음",
|
||||
"go.faq.a5.notZdr": "ZDR 아님",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR은 저장된 데이터에 의존하는 중요한 API 기능(상태 저장형 Responses API, Files and Collections, Batch API 포함)을 비활성화합니다.",
|
||||
"go.faq.a5.gptRetention": "모든 API 기능 사용에 대해 악용 모니터링 로그가 생성되며 최대 30일 동안 보존됩니다.",
|
||||
"go.faq.a5.museRetention":
|
||||
"향후 Meta 모델 학습에 사용자의 프롬프트와 생성 결과를 사용할 수 있도록 허용하는 대신 토큰 가격이 대폭 할인됩니다.",
|
||||
"go.faq.a5.learnMore": "자세히 알아보기",
|
||||
"go.faq.a5.deepseekRetention": "ZDR 계약은 매월 갱신됩니다. 현재 계약은 2026년 8월 31일까지 유효합니다.",
|
||||
"go.faq.a5.beforeExceptions":
|
||||
@@ -384,6 +389,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "모델이 비활성화되었습니다",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"이 모델의 최신 버전은 중국에서 호스팅되는 경우에만 사용할 수 있으며, 명시적으로 사용에 동의해야 합니다: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "이 모델은 거주 국가에서 사용할 수 없습니다.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"이 모델은 품질 개선에 사용되는 데이터를 수집하며, 사용하려면 명시적으로 동의해야 합니다: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"{{model}}의 무료 프로모션이 종료되었습니다. OpenCode Go를 구독하면 모델을 계속 사용할 수 있습니다 - {{link}}",
|
||||
|
||||
@@ -664,6 +672,7 @@ export const dict = {
|
||||
'Go 모델을 사용하려면 opencode 설정에서 "OpenCode Go"를 공급자로 선택하세요.',
|
||||
"workspace.lite.providers.title": "공급자",
|
||||
"workspace.lite.providers.description": "라우팅에 사용할 공급자를 제어합니다.",
|
||||
"workspace.lite.providers.allowTraining": "요청 데이터를 학습에 사용하는 모델 허용",
|
||||
"workspace.lite.providers.useChina": "중국에서 호스팅되는 모델 활성화",
|
||||
"workspace.lite.black.message":
|
||||
"현재 OpenCode Black을 구독 중이거나 대기 명단에 등록되어 있습니다. Go로 전환하려면 먼저 구독을 취소해 주세요.",
|
||||
|
||||
@@ -254,7 +254,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "følgende unntak",
|
||||
|
||||
"go.title": "OpenCode Go | Rimelige kodemodeller for alle",
|
||||
"go.banner.text": "DeepSeek V4 Flash får 2x bruksgrense i en begrenset periode",
|
||||
"go.banner.text": "Ox Alpha Free er tilgjengelig på Go i en begrenset periode",
|
||||
"go.meta.description":
|
||||
"Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse bruksgrenser og pålitelig tilgang til ledende kodemodeller.",
|
||||
"go.hero.title": "Rimelige kodemodeller for alle",
|
||||
@@ -272,6 +272,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle og gratis modeller",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Forespørsler per 5 timer",
|
||||
"go.graph.limitedRegions": "begrensede regioner",
|
||||
"go.graph.limitedTime": "begrenset periode",
|
||||
"go.graph.usageLimits": "Bruksgrenser",
|
||||
"go.graph.aria": "Forespørsler per 5t: {{free}} vs {{go}}",
|
||||
|
||||
@@ -346,6 +348,7 @@ export const dict = {
|
||||
"go.faq.a5.used": "Brukes",
|
||||
"go.faq.a5.notUsed": "Brukes ikke",
|
||||
"go.faq.a5.noAgreement": "Ingen avtale",
|
||||
"go.faq.a5.notZdr": "Ikke ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR deaktiverer viktige API-funksjoner som er avhengige av lagrede data, inkludert den tilstandsbaserte Responses API, Files and Collections og Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
@@ -353,6 +356,8 @@ export const dict = {
|
||||
"go.faq.a5.learnMore": "Les mer",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"ZDR-avtalen fornyes månedlig. Den gjeldende avtalen er gyldig til og med 31. august 2026.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Kraftig rabatterte tokenpriser i bytte mot tillatelse til å bruke ledetekstene og fullføringene dine til å trene fremtidige Meta-modeller.",
|
||||
|
||||
"go.faq.a5.beforeExceptions":
|
||||
"Go-modeller hostes i USA. Leverandører følger en policy om null oppbevaring og bruker ikke dataene dine til modelltrening, med",
|
||||
@@ -393,6 +398,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "Modellen er deaktivert",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"Den nyeste versjonen av denne modellen er bare tilgjengelig som en tjeneste driftet i Kina og krever at du uttrykkelig samtykker: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Denne modellen er ikke tilgjengelig i landet ditt.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Denne modellen samler inn data som brukes til å forbedre modellens kvalitet, og krever at du uttrykkelig samtykker: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"Den gratis kampanjen for {{model}} er avsluttet. Du kan fortsette å bruke modellen ved å abonnere på OpenCode Go - {{link}}",
|
||||
|
||||
@@ -674,6 +682,7 @@ export const dict = {
|
||||
'Velg "OpenCode Go" som leverandør i opencode-konfigurasjonen din for å bruke Go-modeller.',
|
||||
"workspace.lite.providers.title": "Leverandører",
|
||||
"workspace.lite.providers.description": "Kontroller hvilke leverandører som brukes til ruting.",
|
||||
"workspace.lite.providers.allowTraining": "Tillat modeller som trener på forespørselsdata",
|
||||
"workspace.lite.providers.useChina": "Aktiver modeller hostet i Kina",
|
||||
"workspace.lite.black.message":
|
||||
"Du abonnerer for øyeblikket på OpenCode Black eller står på venteliste. Vennligst avslutt abonnementet først hvis du vil bytte til Go.",
|
||||
|
||||
@@ -255,7 +255,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "następującymi wyjątkami",
|
||||
|
||||
"go.title": "OpenCode Go | Niskokosztowe modele do kodowania dla każdego",
|
||||
"go.banner.text": "DeepSeek V4 Flash oferuje 2x wyższe limity użycia przez ograniczony czas",
|
||||
"go.banner.text": "Ox Alpha Free jest dostępny w Go przez ograniczony czas",
|
||||
"go.meta.description":
|
||||
"Go kosztuje $5 za pierwszy miesiąc, a następnie $10/miesiąc, oferując hojne limity użycia i niezawodny dostęp do wiodących modeli do kodowania.",
|
||||
"go.hero.title": "Niskokosztowe modele do kodowania dla każdego",
|
||||
@@ -273,6 +273,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle i darmowe modele",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Żądania na 5 godzin",
|
||||
"go.graph.limitedRegions": "ograniczone regiony",
|
||||
"go.graph.limitedTime": "ograniczony czas",
|
||||
"go.graph.usageLimits": "Limity użycia",
|
||||
"go.graph.aria": "Żądania na 5h: {{free}} vs {{go}}",
|
||||
|
||||
@@ -347,12 +349,15 @@ export const dict = {
|
||||
"go.faq.a5.used": "Wykorzystywane",
|
||||
"go.faq.a5.notUsed": "Niewykorzystywane",
|
||||
"go.faq.a5.noAgreement": "Brak umowy",
|
||||
"go.faq.a5.notZdr": "Nie ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR wyłącza ważne funkcje API zależne od przechowywanych danych, w tym stanowy Responses API, Files and Collections oraz Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
"Dzienniki monitorowania nadużyć są generowane dla każdego użycia funkcji API i przechowywane przez maksymalnie 30 dni.",
|
||||
"go.faq.a5.learnMore": "Dowiedz się więcej",
|
||||
"go.faq.a5.deepseekRetention": "Umowa ZDR jest odnawiana co miesiąc. Obecna umowa obowiązuje do 31 sierpnia 2026 r.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Znacznie obniżone ceny tokenów w zamian za zgodę na wykorzystanie Twoich promptów i odpowiedzi do trenowania przyszłych modeli Meta.",
|
||||
|
||||
"go.faq.a5.beforeExceptions":
|
||||
"Modele Go są hostowane w USA. Dostawcy stosują politykę zerowej retencji i nie używają Twoich danych do trenowania modeli, z",
|
||||
@@ -393,6 +398,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "Model jest wyłączony",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"Najnowsza wersja tego modelu jest dostępna wyłącznie jako usługa hostowana w Chinach i wymaga jawnej zgody użytkownika: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Ten model nie jest dostępny w Twoim kraju.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Ten model zbiera dane wykorzystywane do poprawy jego jakości i wymaga wyraźnej zgody użytkownika: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"Bezpłatna promocja {{model}} dobiegła końca. Możesz dalej korzystać z modelu, subskrybując OpenCode Go - {{link}}",
|
||||
|
||||
@@ -674,6 +682,7 @@ export const dict = {
|
||||
'Wybierz "OpenCode Go" jako dostawcę w konfiguracji opencode, aby używać modeli Go.',
|
||||
"workspace.lite.providers.title": "Dostawcy",
|
||||
"workspace.lite.providers.description": "Kontroluj, którzy dostawcy są używani do routingu.",
|
||||
"workspace.lite.providers.allowTraining": "Zezwalaj na modele wykorzystujące dane żądań do trenowania",
|
||||
"workspace.lite.providers.useChina": "Włącz modele hostowane w Chinach",
|
||||
"workspace.lite.black.message":
|
||||
"Obecnie subskrybujesz OpenCode Black lub jesteś na liście oczekujących. Jeśli chcesz przejść na Go, najpierw anuluj subskrypcję.",
|
||||
|
||||
@@ -258,7 +258,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "следующими исключениями",
|
||||
|
||||
"go.title": "OpenCode Go | Недорогие модели для кодинга для всех",
|
||||
"go.banner.text": "DeepSeek V4 Flash получает 2x лимиты использования на ограниченное время",
|
||||
"go.banner.text": "Ox Alpha Free доступна в Go в течение ограниченного времени",
|
||||
"go.meta.description":
|
||||
"Go стоит $5 за первый месяц, затем $10/месяц и предлагает щедрые лимиты использования и надежный доступ к ведущим моделям для кодинга.",
|
||||
"go.hero.title": "Недорогие модели для кодинга для всех",
|
||||
@@ -276,6 +276,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle и бесплатные модели",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Запросов за 5 часов",
|
||||
"go.graph.limitedRegions": "доступно в отдельных регионах",
|
||||
"go.graph.limitedTime": "ограниченное время",
|
||||
"go.graph.usageLimits": "Лимиты использования",
|
||||
"go.graph.aria": "Запросов за 5ч: {{free}} против {{go}}",
|
||||
|
||||
@@ -351,6 +353,7 @@ export const dict = {
|
||||
"go.faq.a5.used": "Используется",
|
||||
"go.faq.a5.notUsed": "Не используется",
|
||||
"go.faq.a5.noAgreement": "Нет соглашения",
|
||||
"go.faq.a5.notZdr": "Не ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR отключает важные функции API, зависящие от сохраненных данных, включая Responses API с сохранением состояния, Files and Collections и Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
@@ -358,6 +361,8 @@ export const dict = {
|
||||
"go.faq.a5.learnMore": "Подробнее",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"Соглашение ZDR продлевается ежемесячно. Текущее соглашение действует до 31 августа 2026 года.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Значительно сниженная стоимость токенов в обмен на разрешение использовать ваши промпты и ответы для обучения будущих моделей Meta.",
|
||||
|
||||
"go.faq.a5.beforeExceptions":
|
||||
"Модели Go размещены в США. Провайдеры следуют политике нулевого хранения и не используют ваши данные для обучения моделей, за",
|
||||
@@ -398,6 +403,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "Модель отключена",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"Последняя версия этой модели размещена только в Китае. Чтобы использовать её, необходимо явно подтвердить согласие: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Эта модель недоступна в вашей стране.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Эта модель собирает данные, которые используются для улучшения её качества, и требует явного согласия: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"Бесплатная акция для {{model}} завершена. Вы можете продолжить использование модели, подписавшись на OpenCode Go - {{link}}",
|
||||
|
||||
@@ -681,6 +689,7 @@ export const dict = {
|
||||
'Выберите "OpenCode Go" в качестве провайдера в настройках opencode для использования моделей Go.',
|
||||
"workspace.lite.providers.title": "Провайдеры",
|
||||
"workspace.lite.providers.description": "Управляйте провайдерами, используемыми для маршрутизации.",
|
||||
"workspace.lite.providers.allowTraining": "Разрешить модели, обучающиеся на данных запросов",
|
||||
"workspace.lite.providers.useChina": "Включить модели, размещенные в Китае",
|
||||
"workspace.lite.black.message":
|
||||
"Вы подписаны на OpenCode Black или находитесь в списке ожидания. Пожалуйста, сначала отмените подписку, если хотите перейти на Go.",
|
||||
|
||||
@@ -253,7 +253,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "ข้อยกเว้นดังนี้",
|
||||
|
||||
"go.title": "OpenCode Go | โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน",
|
||||
"go.banner.text": "DeepSeek V4 Flash เพิ่มโควตาการใช้งานเป็น 2 เท่าในช่วงเวลาจำกัด",
|
||||
"go.banner.text": "Ox Alpha Free พร้อมใช้งานบน Go ในช่วงเวลาจำกัด",
|
||||
"go.meta.description":
|
||||
"Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน พร้อมขีดจำกัดการใช้งานที่เอื้อเฟื้อและการเข้าถึงโมเดลเขียนโค้ดชั้นนำอย่างเชื่อถือได้",
|
||||
"go.hero.title": "โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน",
|
||||
@@ -270,6 +270,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle และโมเดลฟรี",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "คำขอต่อ 5 ชั่วโมง",
|
||||
"go.graph.limitedRegions": "เฉพาะบางภูมิภาค",
|
||||
"go.graph.limitedTime": "ช่วงเวลาจำกัด",
|
||||
"go.graph.usageLimits": "ขีดจำกัดการใช้งาน",
|
||||
"go.graph.aria": "คำขอต่อ 5 ชม.: {{free}} vs {{go}}",
|
||||
|
||||
@@ -343,10 +345,13 @@ export const dict = {
|
||||
"go.faq.a5.used": "นำไปใช้",
|
||||
"go.faq.a5.notUsed": "ไม่นำไปใช้",
|
||||
"go.faq.a5.noAgreement": "ไม่มีข้อตกลง",
|
||||
"go.faq.a5.notZdr": "ไม่ใช่ ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR ปิดใช้งานฟีเจอร์ API สำคัญที่ต้องอาศัยข้อมูลที่จัดเก็บไว้ ซึ่งรวมถึง Responses API แบบมีสถานะ, Files and Collections และ Batch API",
|
||||
"go.faq.a5.gptRetention":
|
||||
"ระบบจะสร้างบันทึกการตรวจสอบการใช้งานในทางที่ผิดสำหรับการใช้งานฟีเจอร์ API ทั้งหมด และเก็บรักษาไว้นานสูงสุด 30 วัน",
|
||||
"go.faq.a5.museRetention":
|
||||
"ราคาของ token ลดลงอย่างมาก โดยแลกกับการอนุญาตให้นำพรอมต์และผลลัพธ์ที่สร้างขึ้นของคุณไปใช้ฝึกโมเดล Meta ในอนาคต",
|
||||
"go.faq.a5.learnMore": "ดูข้อมูลเพิ่มเติม",
|
||||
"go.faq.a5.deepseekRetention": "ข้อตกลง ZDR จะต่ออายุทุกเดือน ข้อตกลงปัจจุบันมีผลใช้ถึงวันที่ 31 สิงหาคม 2026",
|
||||
|
||||
@@ -388,6 +393,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "โมเดลถูกปิดใช้งาน",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"โมเดลเวอร์ชันล่าสุดนี้ให้บริการเฉพาะผ่านระบบที่โฮสต์ในประเทศจีน และต้องให้ความยินยอมอย่างชัดแจ้งก่อนใช้งาน: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "โมเดลนี้ไม่พร้อมให้บริการในประเทศของคุณ",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"โมเดลนี้เก็บรวบรวมข้อมูลที่ใช้เพื่อปรับปรุงคุณภาพของโมเดล และต้องได้รับความยินยอมอย่างชัดแจ้งก่อนใช้งาน: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"โปรโมชันฟรีสำหรับ {{model}} สิ้นสุดแล้ว คุณสามารถใช้โมเดลต่อได้โดยสมัครสมาชิก OpenCode Go - {{link}}",
|
||||
|
||||
@@ -669,6 +677,7 @@ export const dict = {
|
||||
'เลือก "OpenCode Go" เป็นผู้ให้บริการในการตั้งค่า opencode ของคุณเพื่อใช้โมเดล Go',
|
||||
"workspace.lite.providers.title": "ผู้ให้บริการ",
|
||||
"workspace.lite.providers.description": "ควบคุมผู้ให้บริการที่ใช้สำหรับการกำหนดเส้นทาง",
|
||||
"workspace.lite.providers.allowTraining": "อนุญาตโมเดลที่ฝึกด้วยข้อมูลคำขอ",
|
||||
"workspace.lite.providers.useChina": "เปิดใช้โมเดลที่โฮสต์ในจีน",
|
||||
"workspace.lite.black.message":
|
||||
"ขณะนี้คุณสมัครสมาชิก OpenCode Black หรืออยู่ในรายการรอ โปรดยกเลิกการสมัครก่อนหากต้องการเปลี่ยนไปใช้ Go",
|
||||
|
||||
@@ -256,7 +256,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "aşağıdaki istisnalar",
|
||||
|
||||
"go.title": "OpenCode Go | Herkes için düşük maliyetli kodlama modelleri",
|
||||
"go.banner.text": "DeepSeek V4 Flash sınırlı bir süre için 2x kullanım limiti sunuyor",
|
||||
"go.banner.text": "Ox Alpha Free sınırlı bir süre için Go'da kullanılabilir",
|
||||
"go.meta.description":
|
||||
"Go ilk ay $5, sonrasında ayda 10$ fiyatıyla başlar; cömert kullanım limitleri ve önde gelen kodlama modellerine güvenilir erişim sunar.",
|
||||
"go.hero.title": "Herkes için düşük maliyetli kodlama modelleri",
|
||||
@@ -274,6 +274,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle ve ücretsiz modeller",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "5 saat başına istekler",
|
||||
"go.graph.limitedRegions": "sınırlı bölgeler",
|
||||
"go.graph.limitedTime": "sınırlı süre",
|
||||
"go.graph.usageLimits": "Kullanım limitleri",
|
||||
"go.graph.aria": "5 saatlik istekler: {{free}} vs {{go}}",
|
||||
|
||||
@@ -349,6 +351,7 @@ export const dict = {
|
||||
"go.faq.a5.used": "Kullanılır",
|
||||
"go.faq.a5.notUsed": "Kullanılmaz",
|
||||
"go.faq.a5.noAgreement": "Anlaşma yok",
|
||||
"go.faq.a5.notZdr": "ZDR değil",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR, durum bilgisi tutan Responses API, Files and Collections ve Batch API dahil olmak üzere saklanan verilere bağlı önemli API özelliklerini devre dışı bırakır.",
|
||||
"go.faq.a5.gptRetention":
|
||||
@@ -356,6 +359,8 @@ export const dict = {
|
||||
"go.faq.a5.learnMore": "Daha fazla bilgi",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"ZDR anlaşması aylık olarak yenilenir. Mevcut anlaşma 31 Ağustos 2026 tarihine kadar geçerlidir.",
|
||||
"go.faq.a5.museRetention":
|
||||
"İstemlerinizi ve tamamlamalarınızı gelecekteki Meta modellerini eğitmek için kullanma izni karşılığında büyük ölçüde indirimli token fiyatları.",
|
||||
|
||||
"go.faq.a5.beforeExceptions":
|
||||
"Go modelleri ABD'de barındırılmaktadır. Sağlayıcılar sıfır saklama politikası izler ve verilerinizi model eğitimi için kullanmaz; şu",
|
||||
@@ -396,6 +401,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "Model devre dışı",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"Bu modelin en son sürümü yalnızca Çin'de barındırılıyor ve kullanabilmek için açıkça onay vermeniz gerekiyor: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Bu model ülkenizde kullanılamıyor.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Bu model, kalitesini artırmak için kullanılan verileri toplar. Modeli kullanabilmek için açıkça onay vermeniz gerekir: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"{{model}} için ücretsiz promosyon sona erdi. OpenCode Go'ya abone olarak modeli kullanmaya devam edebilirsiniz - {{link}}",
|
||||
|
||||
@@ -677,6 +685,7 @@ export const dict = {
|
||||
'Go modellerini kullanmak için opencode yapılandırmanızda "OpenCode Go"\'yu sağlayıcı olarak seçin.',
|
||||
"workspace.lite.providers.title": "Sağlayıcılar",
|
||||
"workspace.lite.providers.description": "Yönlendirme için hangi sağlayıcıların kullanılacağını kontrol edin.",
|
||||
"workspace.lite.providers.allowTraining": "İstek verileriyle eğitilen modellere izin ver",
|
||||
"workspace.lite.providers.useChina": "Çin'de barındırılan modelleri etkinleştir",
|
||||
"workspace.lite.black.message":
|
||||
"Şu anda OpenCode Black abonesisiniz veya bekleme listesindesiniz. Go'ya geçmek istiyorsanız lütfen önce aboneliğinizi iptal edin.",
|
||||
|
||||
@@ -255,7 +255,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "такими винятками",
|
||||
|
||||
"go.title": "OpenCode Go | Недорогі моделі кодування для всіх",
|
||||
"go.banner.text": "DeepSeek V4 Flash отримує 2x ліміти використання протягом обмеженого часу",
|
||||
"go.banner.text": "Ox Alpha Free доступна в Go протягом обмеженого часу",
|
||||
"go.meta.description":
|
||||
"Go починається від $5 за перший місяць, потім $10/місяць, зі щедрими лімітами використання та надійним доступом до провідних моделей для кодування.",
|
||||
"go.hero.title": "Недорогі моделі кодування для всіх",
|
||||
@@ -273,6 +273,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle та безкоштовні моделі",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "Запитів за 5 годин",
|
||||
"go.graph.limitedRegions": "обмежені регіони",
|
||||
"go.graph.limitedTime": "обмежений час",
|
||||
"go.graph.usageLimits": "Ліміти використання",
|
||||
"go.graph.aria": "Запитів за 5 год: {{free}} vs {{go}}",
|
||||
|
||||
@@ -347,12 +349,15 @@ export const dict = {
|
||||
"go.faq.a5.used": "Використовується",
|
||||
"go.faq.a5.notUsed": "Не використовується",
|
||||
"go.faq.a5.noAgreement": "Немає угоди",
|
||||
"go.faq.a5.notZdr": "Не ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR вимикає важливі функції API, які залежать від збережених даних, зокрема Responses API зі збереженням стану, Files and Collections та Batch API.",
|
||||
"go.faq.a5.gptRetention":
|
||||
"Журнали моніторингу зловживань створюються для всіх випадків використання функцій API та зберігаються до 30 днів.",
|
||||
"go.faq.a5.learnMore": "Докладніше",
|
||||
"go.faq.a5.deepseekRetention": "Угода ZDR поновлюється щомісяця. Поточна угода дійсна до 31 серпня 2026 року.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Значно знижені ціни на токени в обмін на дозвіл використовувати ваші промпти та відповіді для навчання майбутніх моделей Meta.",
|
||||
|
||||
"go.faq.a5.beforeExceptions":
|
||||
"Моделі Go розміщені в США. Провайдери дотримуються політики нульового зберігання та не використовують ваші дані для навчання моделей, за",
|
||||
@@ -392,6 +397,9 @@ export const dict = {
|
||||
"zen.api.error.modelDisabled": "Модель вимкнено",
|
||||
"zen.api.error.regionNotAllowed":
|
||||
"Остання версія цієї моделі доступна лише на серверах у Китаї, і для її використання потрібно надати явну згоду: {{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "Ця модель недоступна у вашій країні.",
|
||||
"zen.api.error.trainingNotAllowed":
|
||||
"Ця модель збирає дані, які використовуються для покращення її якості; для її використання потрібна ваша явна згода: {{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded":
|
||||
"Безкоштовна акція для {{model}} закінчилася. Ви можете продовжити використання, підписавшись на OpenCode Go — {{link}}",
|
||||
|
||||
@@ -672,6 +680,7 @@ export const dict = {
|
||||
"workspace.lite.subscription.selectProvider": 'Виберіть "OpenCode Go" як провайдера в конфігурації opencode.',
|
||||
"workspace.lite.providers.title": "Провайдери",
|
||||
"workspace.lite.providers.description": "Керуйте провайдерами, які використовуються для маршрутизації.",
|
||||
"workspace.lite.providers.allowTraining": "Дозволити моделі, що навчаються на даних запитів",
|
||||
"workspace.lite.providers.useChina": "Увімкнути моделі, розміщені в Китаї",
|
||||
"workspace.lite.black.message":
|
||||
"Ви вже підписані на OpenCode Black або в списку очікування. Спочатку скасуйте підписку, якщо хочете перейти на Go.",
|
||||
|
||||
@@ -244,7 +244,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "以下例外情况除外",
|
||||
|
||||
"go.title": "OpenCode Go | 人人可用的低成本编程模型",
|
||||
"go.banner.text": "DeepSeek V4 Flash 限时享受 2 倍使用额度",
|
||||
"go.banner.text": "Ox Alpha Free 限时加入 Go",
|
||||
"go.meta.description": "Go 首月 $5,之后 $10/月,提供充裕的使用限额,并可可靠访问领先的编程模型。",
|
||||
"go.hero.title": "人人可用的低成本编程模型",
|
||||
"go.hero.body":
|
||||
@@ -260,6 +260,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle 和免费模型",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "每 5 小时请求数",
|
||||
"go.graph.limitedRegions": "仅限部分地区",
|
||||
"go.graph.limitedTime": "限时",
|
||||
"go.graph.usageLimits": "使用限制",
|
||||
"go.graph.aria": "每 5 小时请求数: {{free}} vs {{go}}",
|
||||
|
||||
@@ -329,9 +331,11 @@ export const dict = {
|
||||
"go.faq.a5.used": "使用",
|
||||
"go.faq.a5.notUsed": "不使用",
|
||||
"go.faq.a5.noAgreement": "无协议",
|
||||
"go.faq.a5.notZdr": "非 ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR 会禁用依赖所存储数据的重要 API 功能,包括有状态的 Responses API、Files and Collections 和 Batch API。",
|
||||
"go.faq.a5.gptRetention": "所有 API 功能的使用都会生成滥用监控日志,并最多保留 30 天。",
|
||||
"go.faq.a5.museRetention": "以允许使用你的提示词和补全结果训练未来的 Meta 模型为交换,token 价格可获得大幅折扣。",
|
||||
"go.faq.a5.learnMore": "了解更多",
|
||||
"go.faq.a5.deepseekRetention": "ZDR 协议每月续签。当前协议有效期至 2026 年 8 月 31 日。",
|
||||
"go.faq.a5.beforeExceptions": "Go 模型托管在美国。提供商遵循零留存政策,不使用您的数据进行模型训练,",
|
||||
@@ -369,6 +373,8 @@ export const dict = {
|
||||
"zen.api.error.userMonthlyLimitReached": "您已达到每月支出限额 ${{amount}}。请在此处管理您的限额:{{membersUrl}}",
|
||||
"zen.api.error.modelDisabled": "模型已禁用",
|
||||
"zen.api.error.regionNotAllowed": "该模型的最新版本仅在中国提供托管服务,需明确选择启用:{{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "该模型在你所在的国家或地区不可用。",
|
||||
"zen.api.error.trainingNotAllowed": "该模型会收集用于提升其质量的数据,使用前需要你明确同意:{{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded": "{{model}} 的限免活动已结束。您可以订阅 OpenCode Go 继续使用该模型 - {{link}}",
|
||||
|
||||
"black.meta.title": "OpenCode Black | 访问全球顶尖编程模型",
|
||||
@@ -646,6 +652,7 @@ export const dict = {
|
||||
"在你的 opencode 配置中选择「OpenCode Go」作为提供商,即可使用 Go 模型。",
|
||||
"workspace.lite.providers.title": "提供商",
|
||||
"workspace.lite.providers.description": "控制用于路由的提供商。",
|
||||
"workspace.lite.providers.allowTraining": "允许使用请求数据进行训练的模型",
|
||||
"workspace.lite.providers.useChina": "启用部署在中国的模型",
|
||||
"workspace.lite.black.message": "您当前已订阅 OpenCode Black 或在候补名单中。如需切换到 Go,请先取消订阅。",
|
||||
"workspace.lite.other.message": "此工作区中的另一位成员已经订阅了 OpenCode Go。每个工作区只有一名成员可以订阅。",
|
||||
|
||||
@@ -244,7 +244,7 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "以下例外情況",
|
||||
|
||||
"go.title": "OpenCode Go | 低成本全民編碼模型",
|
||||
"go.banner.text": "DeepSeek V4 Flash 限時享有 2 倍使用額度",
|
||||
"go.banner.text": "Ox Alpha Free 限時加入 Go",
|
||||
"go.meta.description": "Go 首月 $5,之後 $10/月,提供充裕的使用限額,並可穩定存取領先的編碼模型。",
|
||||
"go.hero.title": "低成本全民編碼模型",
|
||||
"go.hero.body":
|
||||
@@ -260,6 +260,8 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle 與免費模型",
|
||||
"go.graph.go": "Go",
|
||||
"go.graph.label": "每 5 小時請求數",
|
||||
"go.graph.limitedRegions": "僅限部分地區",
|
||||
"go.graph.limitedTime": "限時",
|
||||
"go.graph.usageLimits": "使用限制",
|
||||
"go.graph.aria": "每 5 小時請求數:{{free}} vs {{go}}",
|
||||
|
||||
@@ -329,9 +331,11 @@ export const dict = {
|
||||
"go.faq.a5.used": "使用",
|
||||
"go.faq.a5.notUsed": "不使用",
|
||||
"go.faq.a5.noAgreement": "無協議",
|
||||
"go.faq.a5.notZdr": "非 ZDR",
|
||||
"go.faq.a5.grokRetention":
|
||||
"ZDR 會停用依賴儲存資料的重要 API 功能,包括具狀態的 Responses API、Files and Collections 與 Batch API。",
|
||||
"go.faq.a5.gptRetention": "所有 API 功能的使用都會產生濫用監控日誌,並保留最多 30 天。",
|
||||
"go.faq.a5.museRetention": "以允許使用您的提示詞和生成結果來訓練未來的 Meta 模型為交換,token 價格可享大幅折扣。",
|
||||
"go.faq.a5.learnMore": "了解更多",
|
||||
"go.faq.a5.deepseekRetention": "ZDR 協議每月續簽。目前的協議有效至 2026 年 8 月 31 日。",
|
||||
"go.faq.a5.beforeExceptions": "Go 模型託管在美國。供應商遵循零留存政策,不會將你的資料用於模型訓練,但有",
|
||||
@@ -369,6 +373,8 @@ export const dict = {
|
||||
"zen.api.error.userMonthlyLimitReached": "你已達到每月支出限額 ${{amount}}。請在此處管理你的限額:{{membersUrl}}",
|
||||
"zen.api.error.modelDisabled": "模型已停用",
|
||||
"zen.api.error.regionNotAllowed": "此模型的最新版本僅於中國託管,且需明確選擇啟用:{{consoleGoUrl}}",
|
||||
"zen.api.error.countryNotAllowed": "此模型無法在你所在的國家或地區使用。",
|
||||
"zen.api.error.trainingNotAllowed": "此模型會收集用於提升其品質的資料,使用前須經你明確同意:{{consoleGoUrl}}",
|
||||
"zen.api.error.trialEnded": "{{model}} 的限免活动已結束。您可以訂閱 OpenCode Go 繼續使用該模型 - {{link}}",
|
||||
|
||||
"black.meta.title": "OpenCode Black | 存取全球最佳編碼模型",
|
||||
@@ -646,6 +652,7 @@ export const dict = {
|
||||
"在您的 opencode 設定中選擇「OpenCode Go」作為提供商,即可使用 Go 模型。",
|
||||
"workspace.lite.providers.title": "提供商",
|
||||
"workspace.lite.providers.description": "控制用於路由的提供商。",
|
||||
"workspace.lite.providers.allowTraining": "允許使用請求資料進行訓練的模型",
|
||||
"workspace.lite.providers.useChina": "啟用部署在中國的模型",
|
||||
"workspace.lite.black.message": "您目前已訂閱 OpenCode Black 或在候補名單中。若要切換至 Go,請先取消訂閱。",
|
||||
"workspace.lite.other.message": "此工作區中的另一位成員已訂閱 OpenCode Go。每個工作區只能有一位成員訂閱。",
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
const MUSE_SPARK_BLOCKED_COUNTRIES = new Set([
|
||||
"AF",
|
||||
"BY",
|
||||
"CN",
|
||||
"CU",
|
||||
"EH",
|
||||
"ER",
|
||||
"ET",
|
||||
"HK",
|
||||
"HT",
|
||||
"IQ",
|
||||
"IR",
|
||||
"KH",
|
||||
"KP",
|
||||
"LY",
|
||||
"MM",
|
||||
"MO",
|
||||
"NI",
|
||||
"PK",
|
||||
"RU",
|
||||
"SO",
|
||||
"SY",
|
||||
"VE",
|
||||
])
|
||||
|
||||
export function countryFromRequest(request: Request | undefined) {
|
||||
if (!request) return undefined
|
||||
const cloudflareRequest = request as Request & { cf?: { country?: string } }
|
||||
return cloudflareRequest.cf?.country ?? request.headers.get("cf-ipcountry") ?? undefined
|
||||
}
|
||||
|
||||
export function isModelCountryRestricted(model: string, country: string | undefined) {
|
||||
return (
|
||||
["muse-spark-1.2-contributor", "muse-spark-1.2-contributor-free"].includes(model) &&
|
||||
country !== undefined &&
|
||||
MUSE_SPARK_BLOCKED_COUNTRIES.has(country.toUpperCase())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -592,6 +592,16 @@ body {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:is([data-regions], [data-limited]) {
|
||||
color: var(--color-text-weak);
|
||||
white-space: nowrap;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
[data-value] {
|
||||
color: var(--color-text-strong);
|
||||
font-weight: 600;
|
||||
@@ -688,6 +698,11 @@ body {
|
||||
fill: color-mix(in srgb, var(--bar-go) 50%, transparent);
|
||||
}
|
||||
|
||||
[data-item][data-infinite] [data-value] {
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
[data-val] {
|
||||
fill: var(--color-text-strong);
|
||||
font-size: 13px;
|
||||
|
||||
@@ -25,6 +25,7 @@ const checkLoggedIn = query(async () => {
|
||||
const models = [
|
||||
{ name: "Grok 4.5", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention30" },
|
||||
{ name: "GPT 5.6 Luna", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention30" },
|
||||
{ name: "GLM-5.3", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||
{ name: "GLM-5.2", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||
{ name: "GLM-5.1", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||
{ name: "Kimi K3", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||
@@ -38,6 +39,7 @@ const models = [
|
||||
{ name: "Qwen3.6 Plus", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||
{ name: "MiniMax M3", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||
{ name: "MiniMax M2.7", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||
{ name: "Muse Spark 1.2 Contributor", training: "go.faq.a5.used", retention: "go.faq.a5.notZdr" },
|
||||
{ name: "DeepSeek V4 Pro", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||
{ name: "DeepSeek V4 Flash", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||
{ name: "Hy3", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||
@@ -66,24 +68,19 @@ function LimitsGraph(props: { href: string }) {
|
||||
|
||||
const baseline = 100
|
||||
const graph = [
|
||||
{ id: "grok-4.5", name: "Grok 4.5", req: 120, d: "50ms" },
|
||||
{ id: "kimi-k3", name: "Kimi K3", req: 110, d: "75ms" },
|
||||
{ id: "kimi-k3", name: "Kimi K3", req: 110, d: "50ms" },
|
||||
{ id: "grok-4.5", name: "Grok 4.5", req: 120, d: "75ms" },
|
||||
{ id: "qwen3.8-max", name: "Qwen3.8 Max", req: 160, d: "90ms" },
|
||||
{ id: "glm-5.2", name: "GLM-5.2", req: 880, d: "100ms" },
|
||||
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", req: 1050, d: "150ms" },
|
||||
{ id: "gpt-5.6-luna", name: "GPT 5.6 Luna", req: 2050, d: "290ms" },
|
||||
{ id: "minimax-m3", name: "MiniMax M3", req: 3200, d: "210ms" },
|
||||
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", req: 3450, d: "270ms" },
|
||||
{ id: "gpt-5.6-luna", name: "GPT 5.6 Luna", req: 4100, baseReq: 2050, d: "290ms" },
|
||||
{ id: "qwen3.7-plus", name: "Qwen3.7 Plus", req: 4300, d: "300ms" },
|
||||
{ id: "hy3", name: "Hy3", req: 4300, d: "320ms" },
|
||||
{ id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", req: 7600, d: "330ms" },
|
||||
{ id: "mimo-v2.5", name: "MiMo-V2.5", req: 30100, d: "340ms" },
|
||||
{
|
||||
id: "deepseek-v4-flash",
|
||||
name: "DeepSeek V4 Flash",
|
||||
req: 63300,
|
||||
baseReq: 31650,
|
||||
edge: true,
|
||||
d: "340ms",
|
||||
},
|
||||
{ id: "hy3", name: "Hy3", req: 34400, baseReq: 4300, d: "320ms" },
|
||||
{ id: "muse-spark-1.2-contributor", name: "Muse Spark 1.2 Contributor", req: 45300, edge: true, d: "360ms" },
|
||||
{ id: "ox-alpha-free", name: "Ox Alpha Free", req: Infinity, infinite: true, edge: true, d: "400ms" },
|
||||
]
|
||||
|
||||
const w = 1040
|
||||
@@ -93,9 +90,10 @@ function LimitsGraph(props: { href: string }) {
|
||||
const top = 18
|
||||
const bottom = 44
|
||||
const plot = chartW - left - right
|
||||
const infiniteX = w - 180
|
||||
|
||||
const ratio = (n: number) => n / baseline
|
||||
const rmax = Math.max(1, ...graph.map((m) => ratio(m.req)))
|
||||
const rmax = Math.max(1, ...graph.filter((m) => !("infinite" in m)).map((m) => ratio(m.req)))
|
||||
const log = (n: number) => Math.log10(Math.max(n, 1))
|
||||
const base = 24
|
||||
const p = 2.2
|
||||
@@ -157,10 +155,10 @@ function LimitsGraph(props: { href: string }) {
|
||||
<rect
|
||||
x={left}
|
||||
y={gy(i()) - bh / 2}
|
||||
width={Math.max(0, x(ratio(m.baseReq ?? m.req)) - left)}
|
||||
width={Math.max(0, ("infinite" in m ? infiniteX : x(ratio(m.baseReq ?? m.req))) - left)}
|
||||
height={bh}
|
||||
data-bar
|
||||
data-kind="go"
|
||||
data-kind={"infinite" in m ? "infinite" : "go"}
|
||||
data-model={m.id}
|
||||
/>
|
||||
{m.baseReq && (
|
||||
@@ -196,7 +194,7 @@ function LimitsGraph(props: { href: string }) {
|
||||
</For>
|
||||
</div>
|
||||
|
||||
<div data-slot="pills" aria-hidden="true">
|
||||
<div data-slot="pills">
|
||||
<For each={graph}>
|
||||
{(m, i) => (
|
||||
<span
|
||||
@@ -204,11 +202,24 @@ function LimitsGraph(props: { href: string }) {
|
||||
data-kind="go"
|
||||
data-model={m.id}
|
||||
data-edge={"edge" in m ? "" : undefined}
|
||||
style={{ "--x": px(x(ratio(m.req))), "--y": py(gy(i())), "--d": m.d } as any}
|
||||
data-infinite={"infinite" in m ? "" : undefined}
|
||||
style={
|
||||
{ "--x": px("infinite" in m ? infiniteX : x(ratio(m.req))), "--y": py(gy(i())), "--d": m.d } as any
|
||||
}
|
||||
>
|
||||
<span data-value>{m.req.toLocaleString()}</span>
|
||||
<span data-value>{"infinite" in m ? "∞" : m.req.toLocaleString()}</span>
|
||||
<span data-name>{m.name}</span>
|
||||
{m.baseReq && <span data-bonus>2x usage</span>}
|
||||
{m.id === "muse-spark-1.2-contributor" && (
|
||||
<span data-regions>
|
||||
(
|
||||
<a href="https://ai.developer.meta.com/legal/geographic-use-policy">
|
||||
{i18n.t("go.graph.limitedRegions")}
|
||||
</a>
|
||||
)
|
||||
</span>
|
||||
)}
|
||||
{"infinite" in m && <span data-limited>({i18n.t("go.graph.limitedTime")})</span>}
|
||||
{m.baseReq && <span data-bonus>8x usage</span>}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
@@ -517,6 +528,13 @@ export default function Home() {
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Muse Spark 1.2 Contributor:</strong> {i18n.t("go.faq.a5.museRetention")}{" "}
|
||||
<a href="https://dev.meta.ai/docs/pricing-rate-limits#contributor-tier">
|
||||
{i18n.t("go.faq.a5.learnMore")}
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
<strong>DeepSeek V4 Flash:</strong> {i18n.t("go.faq.a5.deepseekRetention")}
|
||||
</p>
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
|
||||
[data-slot="setting-row"] {
|
||||
margin-top: 0;
|
||||
padding-block: var(--space-2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ export const queryLiteSubscription = query(async (workspaceID: string) => {
|
||||
timeCreated: LiteTable.timeCreated,
|
||||
lite: BillingTable.lite,
|
||||
region: WorkspaceTable.region,
|
||||
allowTraining: WorkspaceTable.allow_training,
|
||||
})
|
||||
.from(BillingTable)
|
||||
.innerJoin(LiteTable, eq(LiteTable.workspaceID, BillingTable.workspaceID))
|
||||
@@ -54,6 +55,7 @@ export const queryLiteSubscription = query(async (workspaceID: string) => {
|
||||
return {
|
||||
mine,
|
||||
useBalance: row.lite?.useBalance ?? false,
|
||||
allowTraining: row.allowTraining ?? false,
|
||||
region:
|
||||
row.region ?? (await Workspace.setDefaultRegion({ country: countryFromRequest(getRequestEvent()?.request) })),
|
||||
rollingUsage: Subscription.analyzeRollingUsage({
|
||||
@@ -154,6 +156,24 @@ const setGoProviderRouting = action(async (form: FormData) => {
|
||||
)
|
||||
}, "go.providerRouting.set")
|
||||
|
||||
const setGoAllowTraining = action(async (form: FormData) => {
|
||||
"use server"
|
||||
const workspaceID = form.get("workspaceID") as string | null
|
||||
if (!workspaceID) return { error: formError.workspaceRequired }
|
||||
const allowTraining = (form.get("allowTraining") as string | null) === "true"
|
||||
|
||||
return json(
|
||||
await withActor(
|
||||
() =>
|
||||
Workspace.update({ allow_training: allowTraining })
|
||||
.then(() => ({ error: undefined }))
|
||||
.catch((e) => ({ error: e.message as string })),
|
||||
workspaceID,
|
||||
),
|
||||
{ revalidate: queryLiteSubscription.key },
|
||||
)
|
||||
}, "go.allowTraining.set")
|
||||
|
||||
function LiteUsageItem(props: { label: string; usage: { usagePercent: number; resetInSec: number } }) {
|
||||
const i18n = useI18n()
|
||||
|
||||
@@ -186,6 +206,7 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
|
||||
const checkoutSubmission = useSubmission(createLiteCheckoutUrl)
|
||||
const useBalanceSubmission = useSubmission(setLiteUseBalance)
|
||||
const providerRoutingSubmission = useSubmission(setGoProviderRouting)
|
||||
const allowTrainingSubmission = useSubmission(setGoAllowTraining)
|
||||
const [store, setStore] = createStore({
|
||||
loading: undefined as undefined | "session" | "checkout" | "alipay" | "upi",
|
||||
showModal: false,
|
||||
@@ -264,6 +285,20 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
|
||||
<h3>{i18n.t("workspace.lite.providers.title")}</h3>
|
||||
<p>{i18n.t("workspace.lite.providers.description")}</p>
|
||||
</div>
|
||||
<form action={setGoAllowTraining} method="post" data-slot="setting-row">
|
||||
<p>{i18n.t("workspace.lite.providers.allowTraining")}</p>
|
||||
<input type="hidden" name="workspaceID" value={params.id} />
|
||||
<input type="hidden" name="allowTraining" value={sub().allowTraining ? "false" : "true"} />
|
||||
<label data-slot="toggle-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={sub().allowTraining}
|
||||
disabled={allowTrainingSubmission.pending}
|
||||
onChange={(e) => e.currentTarget.form?.requestSubmit()}
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</form>
|
||||
<form action={setGoProviderRouting} method="post" data-slot="setting-row">
|
||||
<p>{i18n.t("workspace.lite.providers.useChina")}</p>
|
||||
<input type="hidden" name="workspaceID" value={params.id} />
|
||||
@@ -306,6 +341,7 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
|
||||
<ul data-slot="promo-models">
|
||||
<li>Grok 4.5</li>
|
||||
<li>GPT 5.6 Luna</li>
|
||||
<li>GLM-5.3</li>
|
||||
<li>GLM-5.2</li>
|
||||
<li>GLM-5.1</li>
|
||||
<li>Kimi K3</li>
|
||||
@@ -313,15 +349,18 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
|
||||
<li>Kimi K2.6</li>
|
||||
<li>MiniMax M3</li>
|
||||
<li>MiniMax M2.7</li>
|
||||
<li>Muse Spark 1.2 Contributor</li>
|
||||
<li>Qwen3.8 Max</li>
|
||||
<li>Qwen3.7 Max</li>
|
||||
<li>Qwen3.7 Plus</li>
|
||||
<li>Qwen3.6 Plus</li>
|
||||
<li>DeepSeek V4 Pro</li>
|
||||
<li>DeepSeek V4 Flash</li>
|
||||
<li>DeepSeek V4 Flash Vision Exp</li>
|
||||
<li>MiMo-V2.5</li>
|
||||
<li>MiMo-V2.5-Pro</li>
|
||||
<li>Hy3</li>
|
||||
<li>Ox Alpha Free</li>
|
||||
</ul>
|
||||
<p data-slot="promo-description">{i18n.t("workspace.lite.promo.footer")}</p>
|
||||
<div data-slot="subscribe-actions">
|
||||
|
||||
@@ -4,6 +4,7 @@ export class MonthlyLimitError extends Error {}
|
||||
export class UserLimitError extends Error {}
|
||||
export class ModelError extends Error {}
|
||||
export class RegionError extends Error {}
|
||||
export class DataPolicyError extends Error {}
|
||||
|
||||
class LimitError extends Error {
|
||||
retryAfter?: number
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
UserLimitError,
|
||||
ModelError,
|
||||
RegionError,
|
||||
DataPolicyError,
|
||||
RateLimitError,
|
||||
FreeUsageLimitError,
|
||||
GoUsageLimitError,
|
||||
@@ -51,7 +52,7 @@ import { createModelTpsLimiter } from "./modelTpsLimiter"
|
||||
import { createProviderBudgetTracker } from "./providerBudgetTracker"
|
||||
import { accumulateUsage, HOT_WORKSPACES } from "./usageBatcher"
|
||||
import { Workspace } from "@opencode-ai/console-core/workspace.js"
|
||||
import { countryFromRequest } from "~/lib/request-country"
|
||||
import { countryFromRequest, isModelCountryRestricted } from "~/lib/request-country"
|
||||
|
||||
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
|
||||
type RetryOptions = {
|
||||
@@ -121,6 +122,8 @@ export async function handler(
|
||||
})
|
||||
const zenData = ZenData.list(opts.modelList)
|
||||
const modelInfo = validateModel(zenData, model)
|
||||
const country = countryFromRequest(input.request)
|
||||
if (isModelCountryRestricted(modelInfo.id, country)) throw new RegionError(t("zen.api.error.countryNotAllowed"))
|
||||
const trialLimiter = createTrialLimiter(modelInfo.trialProvider, ip)
|
||||
const trialProviders = await trialLimiter?.check()
|
||||
const rateLimiter = modelInfo.allowAnonymous
|
||||
@@ -128,12 +131,23 @@ export async function handler(
|
||||
: createKeyRateLimiter(modelInfo.id, modelInfo.rateLimit, zenApiKey, input.request)
|
||||
await rateLimiter?.check()
|
||||
const authInfo = await authenticate(modelInfo, zenApiKey)
|
||||
if (
|
||||
authInfo &&
|
||||
opts.modelList === "lite" &&
|
||||
modelInfo.id === "muse-spark-1.2-contributor" &&
|
||||
!authInfo.allowTraining
|
||||
)
|
||||
throw new DataPolicyError(
|
||||
t("zen.api.error.trainingNotAllowed", {
|
||||
consoleGoUrl: `https://opencode.ai/workspace/${authInfo.workspaceID}/go`,
|
||||
}),
|
||||
)
|
||||
const allowedRegions = authInfo?.region
|
||||
? authInfo.region
|
||||
: await (async () => {
|
||||
if (!authInfo) return
|
||||
return Actor.provide("system", { workspaceID: authInfo.workspaceID }, () =>
|
||||
Workspace.setDefaultRegion({ country: countryFromRequest(input.request) }),
|
||||
Workspace.setDefaultRegion({ country }),
|
||||
)
|
||||
})()
|
||||
if (
|
||||
@@ -247,7 +261,7 @@ export async function handler(
|
||||
headers.delete("host")
|
||||
headers.delete("content-length")
|
||||
headers.delete("x-opencode-request")
|
||||
headers.delete("x-opencode-session")
|
||||
if (!isNewInference) headers.delete("x-opencode-session")
|
||||
headers.delete("x-opencode-project")
|
||||
headers.delete("x-opencode-client")
|
||||
return headers
|
||||
@@ -477,7 +491,7 @@ export async function handler(
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (error instanceof RegionError)
|
||||
if (error instanceof RegionError || error instanceof DataPolicyError)
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
type: "error",
|
||||
@@ -708,6 +722,7 @@ export async function handler(
|
||||
workspace: {
|
||||
id: WorkspaceTable.id,
|
||||
region: WorkspaceTable.region,
|
||||
allowTraining: WorkspaceTable.allow_training,
|
||||
isBlocked: WorkspaceTable.is_blocked,
|
||||
isFlaggedByAnthropic: WorkspaceTable.is_flagged_by_anthropic,
|
||||
isFlaggedByOpenAI: WorkspaceTable.is_flagged_by_openai,
|
||||
@@ -820,6 +835,7 @@ export async function handler(
|
||||
apiKeyId: data.apiKey,
|
||||
workspaceID: data.workspace.id,
|
||||
region: data.workspace.region,
|
||||
allowTraining: data.workspace.allowTraining ?? false,
|
||||
billing: data.billing,
|
||||
user: data.user,
|
||||
black: data.black,
|
||||
@@ -1032,11 +1048,14 @@ export async function handler(
|
||||
const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } =
|
||||
usageInfo
|
||||
|
||||
const hour = new Date().getUTCHours()
|
||||
const modelCost =
|
||||
modelInfo.cost200K &&
|
||||
inputTokens + (cacheReadTokens ?? 0) + (cacheWrite5mTokens ?? 0) + (cacheWrite1hTokens ?? 0) > 200_000
|
||||
? modelInfo.cost200K
|
||||
: modelInfo.cost
|
||||
modelInfo.costPeak && ((hour >= 1 && hour < 4) || (hour >= 6 && hour < 10))
|
||||
? modelInfo.costPeak
|
||||
: modelInfo.cost200K &&
|
||||
inputTokens + (cacheReadTokens ?? 0) + (cacheWrite5mTokens ?? 0) + (cacheWrite1hTokens ?? 0) > 200_000
|
||||
? modelInfo.cost200K
|
||||
: modelInfo.cost
|
||||
|
||||
const inputCost = modelCost.input * inputTokens * 100
|
||||
const outputCost = modelCost.output * outputTokens * 100
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `workspace` ADD `allow_non_zdr` boolean;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
ALTER TABLE `workspace` RENAME COLUMN `allow_non_zdr` TO `allow_training`;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -24,6 +24,7 @@ export namespace ZenData {
|
||||
cost: ModelCostSchema,
|
||||
costMultiplier: z.number().default(1),
|
||||
cost200K: ModelCostSchema.optional(),
|
||||
costPeak: ModelCostSchema.optional(),
|
||||
allowAnonymous: z.boolean().optional(),
|
||||
byokProvider: z.enum(["openai", "anthropic", "google"]).optional(),
|
||||
stickyProvider: z.enum(["strict", "prefer"]).optional(),
|
||||
|
||||
@@ -53,6 +53,7 @@ export const BillingTable = mysqlTable(
|
||||
...workspaceIndexes(table),
|
||||
uniqueIndex("global_customer_id").on(table.customerID),
|
||||
uniqueIndex("global_subscription_id").on(table.subscriptionID),
|
||||
uniqueIndex("global_lite_subscription_id").on(table.liteSubscriptionID),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { bigint, mysqlTable, primaryKey, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||
import { bigint, index, mysqlTable, primaryKey, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||
import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types"
|
||||
import { workspaceIndexes } from "./workspace.sql"
|
||||
|
||||
@@ -31,5 +31,8 @@ export const ReferralRewardTable = mysqlTable(
|
||||
amount: bigint("amount", { mode: "number" }).notNull(),
|
||||
timeApplied: utc("time_applied"),
|
||||
},
|
||||
(table) => [primaryKey({ columns: [table.workspaceID, table.referralID] })],
|
||||
(table) => [
|
||||
primaryKey({ columns: [table.workspaceID, table.referralID] }),
|
||||
index("referral_id").on(table.referralID),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ export const WorkspaceTable = mysqlTable(
|
||||
slug: varchar("slug", { length: 255 }),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
region: json("region").$type<("us" | "eu" | "sg" | "cn")[]>(),
|
||||
allow_training: boolean(),
|
||||
is_blocked: boolean(),
|
||||
is_flagged_by_anthropic: boolean(),
|
||||
is_flagged_by_openai: boolean(),
|
||||
|
||||
@@ -62,6 +62,7 @@ export namespace Workspace {
|
||||
z.object({
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
region: z.array(Region).min(1).optional(),
|
||||
allow_training: z.boolean().optional(),
|
||||
}),
|
||||
async (input) => {
|
||||
Actor.assertAdmin()
|
||||
@@ -72,6 +73,7 @@ export namespace Workspace {
|
||||
.set({
|
||||
...("name" in input ? { name: input.name } : {}),
|
||||
...("region" in input ? { region: input.region } : {}),
|
||||
...("allow_training" in input ? { allow_training: input.allow_training } : {}),
|
||||
})
|
||||
.where(eq(WorkspaceTable.id, workspaceID)),
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-support",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"name": "@opencode-ai/core",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
@@ -70,7 +70,7 @@
|
||||
"@ai-sdk/deepinfra": "2.0.41",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
"@ai-sdk/google": "3.0.73",
|
||||
"@ai-sdk/google-vertex": "4.0.128",
|
||||
"@ai-sdk/google-vertex": "4.0.181",
|
||||
"@ai-sdk/groq": "3.0.31",
|
||||
"@ai-sdk/mistral": "3.0.51",
|
||||
"@ai-sdk/openai": "3.0.84",
|
||||
@@ -101,7 +101,7 @@
|
||||
"@parcel/watcher": "2.5.1",
|
||||
"@silvia-odwyer/photon-node": "0.3.4",
|
||||
"@openrouter/ai-sdk-provider": "2.9.0",
|
||||
"ai-gateway-provider": "3.1.2",
|
||||
"ai-gateway-provider": "3.2.0",
|
||||
"bun-pty": "0.4.8",
|
||||
"cross-spawn": "catalog:",
|
||||
"diff": "catalog:",
|
||||
|
||||
@@ -24,9 +24,15 @@ export const CloudflareAIGatewayPlugin = define({
|
||||
apiKey: config.apiKey,
|
||||
options: gatewayOptions(evt.options, metadata),
|
||||
} as any)
|
||||
const unified = createUnified({ apiKey: config.apiKey })
|
||||
evt.sdk = {
|
||||
languageModel(modelID: string) {
|
||||
// Workers AI is the only first-party provider whose upstream is Cloudflare itself, so it is
|
||||
// the only one that should receive the Cloudflare token as its upstream Authorization header.
|
||||
// The Unified API addresses Workers AI both with the explicit "workers-ai/" prefix and as
|
||||
// bare "@cf/..." ids. Third-party providers must not receive the token; they rely on the
|
||||
// gateway's stored/BYOK keys instead.
|
||||
const isWorkersAi = modelID.startsWith("workers-ai/") || modelID.startsWith("@cf/")
|
||||
const unified = createUnified(isWorkersAi ? { apiKey: config.apiKey } : {})
|
||||
return gateway(unified(modelID))
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { ConfigProviderV1 } from "../../v1/config/provider"
|
||||
import { ConfigProviderOptionsV1 } from "../../v1/config/provider-options"
|
||||
import { ConfigV1 } from "../../v1/config/config"
|
||||
|
||||
const defaultServer = "https://console.opencode.ai"
|
||||
const defaultServer = "https://opencode.ai/console"
|
||||
const clientID = "opencode-cli"
|
||||
const methodID = Integration.MethodID.make("device")
|
||||
const RemoteResponse = Schema.Struct({ config: ConfigV1.Info })
|
||||
|
||||
@@ -202,6 +202,7 @@ export const make = (dependencies: Dependencies) => {
|
||||
.stream(
|
||||
LLM.request({
|
||||
model: input.model,
|
||||
http: input.request.http,
|
||||
messages: [Message.user(summaryPrompt)],
|
||||
tools: [],
|
||||
generation: { maxTokens: summaryOutput },
|
||||
|
||||
@@ -12,7 +12,6 @@ import { SessionMessage } from "./message"
|
||||
import { SessionMessageUpdater } from "./message-updater"
|
||||
import { SessionInput } from "./input"
|
||||
import { WorkspaceV2 } from "../workspace"
|
||||
import { SessionContextEpoch } from "./context-epoch"
|
||||
import { MessageTable, PartTable, SessionInputTable, SessionMessageTable, SessionTable } from "./sql"
|
||||
import type { DeepMutable } from "../schema"
|
||||
|
||||
@@ -253,7 +252,6 @@ const layer = Layer.effectDiscard(
|
||||
.where(eq(SessionTable.id, event.data.sessionID))
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
yield* SessionContextEpoch.reset(db, event.data.sessionID)
|
||||
}),
|
||||
)
|
||||
yield* events.project(SessionV1.Event.Deleted, (event) =>
|
||||
@@ -449,7 +447,6 @@ const layer = Layer.effectDiscard(
|
||||
.where(eq(SessionTable.id, event.data.sessionID))
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
yield* SessionContextEpoch.reset(db, event.data.sessionID)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
|
||||
@@ -204,6 +204,13 @@ const layer = Layer.effect(
|
||||
const promptCacheKey = /^ses_[0-9a-f]{64}$/.test(session.id) ? session.id.slice(4) : session.id
|
||||
const request = LLM.request({
|
||||
model,
|
||||
http: {
|
||||
headers: {
|
||||
"x-session-affinity": session.id,
|
||||
"X-Session-Id": session.id,
|
||||
...(session.parentID ? { "x-parent-session-id": session.parentID } : {}),
|
||||
},
|
||||
},
|
||||
providerOptions: { openai: { promptCacheKey } },
|
||||
system: [agent.info?.system, system.baseline]
|
||||
.filter((part): part is string => part !== undefined && part.length > 0)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect } from "bun:test"
|
||||
import { DateTime, Effect, Schema } from "effect"
|
||||
import { asc, eq } from "drizzle-orm"
|
||||
import { asc, eq, sql } from "drizzle-orm"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
@@ -22,6 +22,7 @@ import { SessionInput } from "@opencode-ai/core/session/input"
|
||||
import { SessionInputTable, SessionMessageTable, SessionTable } from "@opencode-ai/core/session/sql"
|
||||
import { testEffect } from "./lib/effect"
|
||||
import { Snapshot } from "@opencode-ai/core/snapshot"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
|
||||
const it = testEffect(AppNodeBuilder.build(LayerNode.group([Database.node, EventV2.node, SessionProjector.node])))
|
||||
const sessionsLayer = AppNodeBuilder.build(SessionV2.node, [[SessionExecution.node, SessionExecution.noopLayer]])
|
||||
@@ -44,6 +45,39 @@ const assistantRow = (
|
||||
}
|
||||
|
||||
describe("SessionProjector", () => {
|
||||
it.effect("projects moved sessions without the transitional context epoch table", () =>
|
||||
Effect.gen(function* () {
|
||||
const { db } = yield* Database.Service
|
||||
const events = yield* EventV2.Service
|
||||
yield* db
|
||||
.insert(ProjectTable)
|
||||
.values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
|
||||
.run()
|
||||
yield* db
|
||||
.insert(SessionTable)
|
||||
.values({
|
||||
id: sessionID,
|
||||
project_id: Project.ID.global,
|
||||
slug: "test",
|
||||
directory: "/project",
|
||||
title: "test",
|
||||
version: "test",
|
||||
})
|
||||
.run()
|
||||
yield* db.run(sql`DROP TABLE session_context_epoch`)
|
||||
|
||||
yield* events.publish(SessionEvent.Moved, {
|
||||
sessionID,
|
||||
timestamp: DateTime.makeUnsafe(1),
|
||||
location: Location.Ref.make({ directory: AbsolutePath.make("/project/subdir") }),
|
||||
})
|
||||
|
||||
expect(yield* db.select({ directory: SessionTable.directory }).from(SessionTable).get()).toEqual({
|
||||
directory: "/project/subdir",
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("projects staged, cleared, and committed reverts", () =>
|
||||
Effect.gen(function* () {
|
||||
const db = (yield* Database.Service).db
|
||||
|
||||
@@ -703,13 +703,6 @@ describe("SessionRunnerLLM", () => {
|
||||
timestamp: DateTime.makeUnsafe(1),
|
||||
location: Location.Ref.make({ directory: AbsolutePath.make("/moved") }),
|
||||
})
|
||||
expect(
|
||||
yield* db
|
||||
.select()
|
||||
.from(SessionContextEpochTable)
|
||||
.where(eq(SessionContextEpochTable.session_id, sessionID))
|
||||
.get(),
|
||||
).toBeUndefined()
|
||||
|
||||
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
|
||||
const exit = yield* session.resume(sessionID).pipe(Effect.exit)
|
||||
@@ -1108,6 +1101,16 @@ describe("SessionRunnerLLM", () => {
|
||||
yield* session.resume(sessionID)
|
||||
|
||||
expect(requests).toHaveLength(2)
|
||||
expect(requests.map((request) => request.http?.headers)).toEqual([
|
||||
{
|
||||
"x-session-affinity": sessionID,
|
||||
"X-Session-Id": sessionID,
|
||||
},
|
||||
{
|
||||
"x-session-affinity": sessionID,
|
||||
"X-Session-Id": sessionID,
|
||||
},
|
||||
])
|
||||
expect(userTexts(requests[0])[0]).toContain("## Objective")
|
||||
expect(userTexts(requests[1])).toHaveLength(1)
|
||||
expect(userTexts(requests[1])[0]).toContain("<summary>\n## Objective\n- Preserve the task\n</summary>")
|
||||
@@ -2515,6 +2518,43 @@ describe("SessionRunnerLLM", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("adds session correlation headers to model requests", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* setup
|
||||
const session = yield* SessionV2.Service
|
||||
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Run correlated request" }), resume: false })
|
||||
|
||||
requests.length = 0
|
||||
yield* session.resume(sessionID)
|
||||
|
||||
expect(requests[0]?.http?.headers).toEqual({
|
||||
"x-session-affinity": sessionID,
|
||||
"X-Session-Id": sessionID,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("adds the parent session header to child model requests", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* setup
|
||||
const session = yield* SessionV2.Service
|
||||
const parentID = SessionV2.ID.make("ses_runner_parent")
|
||||
const { db } = yield* Database.Service
|
||||
yield* db
|
||||
.update(SessionTable)
|
||||
.set({ parent_id: parentID })
|
||||
.where(eq(SessionTable.id, sessionID))
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Run child request" }), resume: false })
|
||||
|
||||
requests.length = 0
|
||||
yield* session.resume(sessionID)
|
||||
|
||||
expect(requests[0]?.http?.headers?.["x-parent-session-id"]).toBe(parentID)
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("bounds 64-character session prompt cache keys", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* setup
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"private": true,
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://opencode.ai",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"name": "@opencode-ai/effect-sqlite-node",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"name": "@opencode-ai/http-recorder",
|
||||
"description": "Record and replay Effect HTTP client traffic with deterministic cassettes",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"name": "@opencode-ai/llm",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.18",
|
||||
"version": "1.18.20",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
@@ -64,7 +64,7 @@
|
||||
"@ai-sdk/deepinfra": "2.0.41",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
"@ai-sdk/google": "3.0.73",
|
||||
"@ai-sdk/google-vertex": "4.0.128",
|
||||
"@ai-sdk/google-vertex": "4.0.181",
|
||||
"@ai-sdk/groq": "3.0.31",
|
||||
"@ai-sdk/mistral": "3.0.51",
|
||||
"@ai-sdk/openai": "3.0.84",
|
||||
@@ -111,7 +111,7 @@
|
||||
"@types/ws": "8.18.1",
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"ai": "catalog:",
|
||||
"ai-gateway-provider": "3.1.2",
|
||||
"ai-gateway-provider": "3.2.0",
|
||||
"bonjour-service": "1.3.0",
|
||||
"chokidar": "4.0.3",
|
||||
"cross-spawn": "catalog:",
|
||||
|
||||
@@ -15,7 +15,7 @@ const dim = (value: string) => UI.Style.TEXT_DIM + value + UI.Style.TEXT_NORMAL
|
||||
|
||||
const activeSuffix = (isActive: boolean) => (isActive ? dim(" (active)") : "")
|
||||
|
||||
export const defaultConsoleUrl = "https://console.opencode.ai"
|
||||
export const defaultConsoleUrl = "https://opencode.ai/console"
|
||||
|
||||
export const formatAccountLabel = (account: { email: string; url: string }, isActive: boolean) =>
|
||||
`${account.email} ${dim(account.url)}${activeSuffix(isActive)}`
|
||||
|
||||
@@ -696,9 +696,14 @@ export const RunCommand = effectCmd({
|
||||
// created, and replies issued from inside the loop must use that client.
|
||||
async function loop(client: OpencodeClient, events: Awaited<ReturnType<typeof sdk.event.subscribe>>) {
|
||||
const toggles = new Map<string, boolean>()
|
||||
const sessions = new Set([sessionID])
|
||||
let error: string | undefined
|
||||
|
||||
for await (const event of events.stream) {
|
||||
if (event.type === "session.created" && event.properties.info.parentID) {
|
||||
if (sessions.has(event.properties.info.parentID)) sessions.add(event.properties.info.id)
|
||||
}
|
||||
|
||||
if (
|
||||
event.type === "message.updated" &&
|
||||
event.properties.sessionID === sessionID &&
|
||||
@@ -795,7 +800,7 @@ export const RunCommand = effectCmd({
|
||||
|
||||
if (event.type === "permission.asked") {
|
||||
const permission = event.properties
|
||||
if (permission.sessionID !== sessionID) continue
|
||||
if (!sessions.has(permission.sessionID)) continue
|
||||
|
||||
if (auto) {
|
||||
await client.permission.reply({
|
||||
|
||||
@@ -714,6 +714,7 @@ const layer = Layer.effect(
|
||||
})
|
||||
|
||||
const list = Effect.fn("Workspace.list")(function* (project: Project.Info) {
|
||||
if (!flags.experimentalWorkspaces) return []
|
||||
return (yield* db
|
||||
.select()
|
||||
.from(WorkspaceTable)
|
||||
@@ -851,6 +852,7 @@ const layer = Layer.effect(
|
||||
})
|
||||
|
||||
const startWorkspaceSyncing = Effect.fn("Workspace.startWorkspaceSyncing")(function* (projectID: ProjectV2.ID) {
|
||||
if (!flags.experimentalWorkspaces) return
|
||||
const rows = yield* db
|
||||
.selectDistinct({ workspace: WorkspaceTable })
|
||||
.from(WorkspaceTable)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||
|
||||
export async function CerebrasPlugin(_input: PluginInput): Promise<Hooks> {
|
||||
return {
|
||||
"chat.params": async (input, output) => {
|
||||
if (input.model.api.npm !== "@ai-sdk/cerebras") return
|
||||
if (output.options.max_completion_tokens === undefined) return
|
||||
output.maxOutputTokens = undefined
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -61,16 +61,5 @@ export async function CloudflareAIGatewayAuthPlugin(_input: PluginInput): Promis
|
||||
},
|
||||
],
|
||||
},
|
||||
"chat.params": async (input, output) => {
|
||||
if (input.model.providerID !== "cloudflare-ai-gateway") return
|
||||
// The unified gateway routes through @ai-sdk/openai-compatible, which
|
||||
// always emits max_tokens. OpenAI reasoning models (gpt-5.x, o-series)
|
||||
// reject that field and require max_completion_tokens instead, and the
|
||||
// compatible SDK has no way to rename it. Drop the cap so OpenAI falls
|
||||
// back to the model's default output budget.
|
||||
if (!input.model.api.id.toLowerCase().startsWith("openai/")) return
|
||||
if (!input.model.capabilities.reasoning) return
|
||||
output.maxOutputTokens = undefined
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import { CloudflareAIGatewayAuthPlugin, CloudflareWorkersAuthPlugin } from "./cl
|
||||
import { AzureAuthPlugin } from "./azure"
|
||||
import { DigitalOceanAuthPlugin } from "./digitalocean"
|
||||
import { XaiAuthPlugin } from "./xai"
|
||||
import { CerebrasPlugin } from "./cerebras"
|
||||
import { SnowflakeCortexAuthPlugin } from "./snowflake-cortex"
|
||||
import { Effect, Layer, Context } from "effect"
|
||||
import { EffectBridge } from "@/effect/bridge"
|
||||
@@ -80,6 +81,7 @@ function internalPlugins(flags: RuntimeFlags.Info): PluginInstance[] {
|
||||
DigitalOceanAuthPlugin,
|
||||
SnowflakeCortexAuthPlugin,
|
||||
XaiAuthPlugin,
|
||||
CerebrasPlugin,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,12 @@ function base64UrlEncode(buffer: ArrayBuffer): string {
|
||||
|
||||
export interface IdTokenClaims {
|
||||
chatgpt_account_id?: string
|
||||
chatgpt_compute_residency?: string
|
||||
organizations?: Array<{ id: string }>
|
||||
email?: string
|
||||
"https://api.openai.com/auth"?: {
|
||||
chatgpt_account_id?: string
|
||||
chatgpt_compute_residency?: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +77,14 @@ export function extractAccountId(tokens: TokenResponse): string | undefined {
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function extractResidency(token: string): string | undefined {
|
||||
const claims = parseJwtClaims(token)
|
||||
const residency =
|
||||
claims?.["https://api.openai.com/auth"]?.chatgpt_compute_residency ?? claims?.chatgpt_compute_residency
|
||||
if (!residency || residency === "no_constraint") return undefined
|
||||
return residency
|
||||
}
|
||||
|
||||
function buildAuthorizeUrl(redirectUri: string, pkce: PkceCodes, state: string): string {
|
||||
const params = new URLSearchParams({
|
||||
response_type: "code",
|
||||
@@ -299,16 +309,11 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
|
||||
output: 0,
|
||||
cache: { read: 0, write: 0 },
|
||||
},
|
||||
limit: model.id.includes("gpt-5.5")
|
||||
? {
|
||||
context: 400_000,
|
||||
input: 272_000,
|
||||
output: 128_000,
|
||||
}
|
||||
: model.id.includes("gpt-5.6")
|
||||
limit:
|
||||
model.id.includes("gpt-5.5") || model.id.includes("gpt-5.6")
|
||||
? {
|
||||
context: 500_000,
|
||||
input: 372_000,
|
||||
context: 400_000,
|
||||
input: 272_000,
|
||||
output: 128_000,
|
||||
}
|
||||
: model.limit,
|
||||
@@ -411,10 +416,12 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
|
||||
requestInput instanceof URL
|
||||
? requestInput
|
||||
: new URL(typeof requestInput === "string" ? requestInput : requestInput.url)
|
||||
const url =
|
||||
parsed.pathname.includes("/v1/responses") || parsed.pathname.includes("/chat/completions")
|
||||
? new URL(codexApiEndpoint)
|
||||
: parsed
|
||||
const rewrite = parsed.pathname.includes("/v1/responses") || parsed.pathname.includes("/chat/completions")
|
||||
const url = rewrite ? new URL(codexApiEndpoint) : parsed
|
||||
if (rewrite) {
|
||||
const residency = extractResidency(currentAuth.access)
|
||||
if (residency) headers.set("x-openai-internal-codex-residency", residency)
|
||||
}
|
||||
|
||||
const requestInit = {
|
||||
...init,
|
||||
|
||||
@@ -110,10 +110,11 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
invalidate(entry)
|
||||
}
|
||||
},
|
||||
onConnectionInvalid: (error) => {
|
||||
onConnectionInvalid: (_error, closeCode) => {
|
||||
entry.busy = false
|
||||
entry.lastUsedAt = Date.now()
|
||||
if (!entry.fallback) recordStreamFailure(entry)
|
||||
if (closeCode === OpenAIWebSocket.MESSAGE_TOO_BIG_CLOSE_CODE) entry.fallback = true
|
||||
else if (!entry.fallback) recordStreamFailure(entry)
|
||||
invalidate(entry)
|
||||
resolveFirstEvent(false)
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ProxyEnv } from "@/util/proxy-env"
|
||||
import { isRecord } from "@/util/record"
|
||||
|
||||
export const PROTOCOL_HEADER = "responses_websockets=2026-02-06"
|
||||
export const MESSAGE_TOO_BIG_CLOSE_CODE = 1009
|
||||
|
||||
export interface ConnectResponsesWebSocketOptions {
|
||||
url: string
|
||||
@@ -26,7 +27,7 @@ export interface StreamResponsesWebSocketOptions {
|
||||
onComplete?: (event: Record<string, unknown>) => void
|
||||
onTerminal?: (event: Record<string, unknown>) => void
|
||||
onRetryableTerminal?: (event: Record<string, unknown>) => Promise<WebSocket | undefined>
|
||||
onConnectionInvalid?: (error: ProviderError.ResponseStreamError) => void
|
||||
onConnectionInvalid?: (error: ProviderError.ResponseStreamError, closeCode?: number) => void
|
||||
onAbort?: (error: Error) => void
|
||||
}
|
||||
|
||||
@@ -162,11 +163,11 @@ export function streamResponsesWebSocket(options: StreamResponsesWebSocketOption
|
||||
controller?.close()
|
||||
}
|
||||
|
||||
function invalidate(error: ProviderError.ResponseStreamError) {
|
||||
function invalidate(error: ProviderError.ResponseStreamError, closeCode?: number) {
|
||||
if (completed) return
|
||||
completed = true
|
||||
cleanup()
|
||||
options.onConnectionInvalid?.(error)
|
||||
options.onConnectionInvalid?.(error, closeCode)
|
||||
controller?.error(error)
|
||||
}
|
||||
|
||||
@@ -274,6 +275,7 @@ export function streamResponsesWebSocket(options: StreamResponsesWebSocketOption
|
||||
if (completed) return
|
||||
invalidate(
|
||||
new ProviderError.ResponseStreamError(closeMessage("WebSocket closed before response.completed", code, reason)),
|
||||
code,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -373,7 +375,7 @@ function abortError(signal: AbortSignal | undefined) {
|
||||
|
||||
function closeMessage(message: string, code: number, reason: Buffer) {
|
||||
const details = [`code ${code}`]
|
||||
if (code === 1009) details.push("message too big")
|
||||
if (code === MESSAGE_TOO_BIG_CLOSE_CODE) details.push("message too big")
|
||||
if (reason.length > 0) details.push(reason.toString())
|
||||
return `${message} (${details.join(": ")})`
|
||||
}
|
||||
|
||||
@@ -144,6 +144,13 @@ export function parseStreamError(input: unknown): ParsedStreamError | undefined
|
||||
responseBody,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: "api_error",
|
||||
message: typeof body?.error?.message === "string" ? body.error.message : "Server error.",
|
||||
isRetryable: true,
|
||||
responseBody,
|
||||
}
|
||||
}
|
||||
|
||||
export type ParsedAPICallError =
|
||||
|
||||
@@ -98,6 +98,12 @@ function googleVertexAnthropicBaseURL(project: string | undefined, location: str
|
||||
return `https://aiplatform.${location}.rep.googleapis.com/v1/projects/${project}/locations/${location}/publishers/anthropic/models`
|
||||
}
|
||||
|
||||
function googleVertexEndpoint(location: string) {
|
||||
if (location === "global") return "aiplatform.googleapis.com"
|
||||
if (location === "eu" || location === "us") return `aiplatform.${location}.rep.googleapis.com`
|
||||
return `${location}-aiplatform.googleapis.com`
|
||||
}
|
||||
|
||||
type BundledSDK = {
|
||||
languageModel(modelId: string): LanguageModelV3
|
||||
chat?: (modelId: string) => LanguageModelV3
|
||||
@@ -519,11 +525,10 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
|
||||
return {
|
||||
autoload: true,
|
||||
vars(_options: Record<string, any>) {
|
||||
const endpoint = location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com`
|
||||
return {
|
||||
...(project && { GOOGLE_VERTEX_PROJECT: project }),
|
||||
GOOGLE_VERTEX_LOCATION: location,
|
||||
GOOGLE_VERTEX_ENDPOINT: endpoint,
|
||||
GOOGLE_VERTEX_ENDPOINT: googleVertexEndpoint(location),
|
||||
}
|
||||
},
|
||||
options: {
|
||||
@@ -800,9 +805,10 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
|
||||
)
|
||||
}
|
||||
|
||||
// Use official ai-gateway-provider package (v2.x for AI SDK v5 compatibility)
|
||||
const { createAiGateway } = yield* Effect.promise(() => import("ai-gateway-provider"))
|
||||
const { createUnified } = yield* Effect.promise(() => import("ai-gateway-provider/providers/unified"))
|
||||
const { createOpenAI } = yield* Effect.promise(() => import("ai-gateway-provider/providers/openai"))
|
||||
const { createAnthropic } = yield* Effect.promise(() => import("ai-gateway-provider/providers/anthropic"))
|
||||
|
||||
const metadata = iife(() => {
|
||||
if (input.options?.metadata) return input.options.metadata
|
||||
@@ -829,12 +835,23 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
|
||||
apiKey: apiToken,
|
||||
...(Object.values(opts).some((v) => v !== undefined) ? { options: opts } : {}),
|
||||
})
|
||||
const unified = createUnified({ apiKey: apiToken })
|
||||
|
||||
return {
|
||||
autoload: true,
|
||||
async getModel(_sdk: any, modelID: string, _options?: Record<string, any>) {
|
||||
// Model IDs use Unified API format: provider/model (e.g., "anthropic/claude-sonnet-4-5")
|
||||
// Model IDs use Unified API format: provider/model (e.g., "anthropic/claude-sonnet-4-5").
|
||||
// OpenAI and Anthropic ride their native passthrough routes so agents get the Responses
|
||||
// and Messages APIs; new OpenAI models reject tools+reasoning_effort on chat completions.
|
||||
// The passthrough wrappers inject a CF_TEMP_TOKEN sentinel that the gateway strips before
|
||||
// dispatch, so upstream billing stays on the gateway (Unified Billing / stored BYOK).
|
||||
if (modelID.startsWith("openai/")) return aigateway(createOpenAI()(modelID.slice("openai/".length)))
|
||||
if (modelID.startsWith("anthropic/")) return aigateway(createAnthropic()(modelID.slice("anthropic/".length)))
|
||||
// Workers AI is the only first-party provider whose upstream is Cloudflare itself, so it is
|
||||
// the only one that should receive the Cloudflare token as its upstream Authorization header.
|
||||
// The Unified API addresses Workers AI both with the explicit "workers-ai/" prefix and as
|
||||
// bare "@cf/..." ids. Third-party providers must not receive the token; they rely on the
|
||||
// gateway's stored/BYOK keys instead.
|
||||
const isWorkersAi = modelID.startsWith("workers-ai/") || modelID.startsWith("@cf/")
|
||||
const unified = createUnified(isWorkersAi ? { apiKey: apiToken } : {})
|
||||
return aigateway(unified(modelID))
|
||||
},
|
||||
options: {},
|
||||
@@ -1209,6 +1226,17 @@ function cost(c: ModelsDev.Model["cost"]): Model["cost"] {
|
||||
return result
|
||||
}
|
||||
|
||||
// Cloudflare AI Gateway routes OpenAI and Anthropic models through their native
|
||||
// passthrough SDKs (Responses / Messages APIs). Resolving the native npm before
|
||||
// variants are computed makes reasoning variants produce payloads the native
|
||||
// SDKs understand (e.g. anthropic `effort` instead of compat `reasoningEffort`).
|
||||
function cloudflareGatewayNpm(providerID: string, modelID: string) {
|
||||
if (providerID !== "cloudflare-ai-gateway") return undefined
|
||||
if (modelID.startsWith("openai/")) return "@ai-sdk/openai"
|
||||
if (modelID.startsWith("anthropic/")) return "@ai-sdk/anthropic"
|
||||
return undefined
|
||||
}
|
||||
|
||||
function fromModelsDevModel(provider: ModelsDev.Provider, model: ModelsDev.Model): Model {
|
||||
const base: Model = {
|
||||
id: ModelV2.ID.make(model.id),
|
||||
@@ -1218,7 +1246,11 @@ function fromModelsDevModel(provider: ModelsDev.Provider, model: ModelsDev.Model
|
||||
api: {
|
||||
id: model.id,
|
||||
url: model.provider?.api ?? provider.api ?? "",
|
||||
npm: model.provider?.npm ?? provider.npm ?? "@ai-sdk/openai-compatible",
|
||||
npm:
|
||||
cloudflareGatewayNpm(provider.id, model.id) ??
|
||||
model.provider?.npm ??
|
||||
provider.npm ??
|
||||
"@ai-sdk/openai-compatible",
|
||||
},
|
||||
status: model.status ?? "active",
|
||||
headers: {},
|
||||
@@ -1440,6 +1472,9 @@ const layer = Layer.effect(
|
||||
model.provider?.npm ??
|
||||
provider.npm ??
|
||||
existingModel?.api.npm ??
|
||||
// Config-defined gateway models bypass fromModelsDevModel, so resolve the
|
||||
// native passthrough npm here before falling back to the catalog default.
|
||||
cloudflareGatewayNpm(providerID, apiID) ??
|
||||
modelsDev[providerID]?.npm ??
|
||||
"@ai-sdk/openai-compatible"
|
||||
const name = iife(() => {
|
||||
@@ -1619,6 +1654,7 @@ const layer = Layer.effect(
|
||||
|
||||
for (const [modelID, model] of Object.entries(provider.models)) {
|
||||
model.api.id = model.api.id ?? model.id ?? modelID
|
||||
|
||||
if (
|
||||
// These chat aliases are invalid for the special handling in the
|
||||
// built-in providers below, but custom providers may support them.
|
||||
|
||||
@@ -528,7 +528,6 @@ const GEMINI_MODELS_WITH_SAMPLING_DEFAULTS = [
|
||||
export function temperature(model: Provider.Model) {
|
||||
const id = model.api.id.toLowerCase()
|
||||
if (id.includes("north-mini-code")) return 1.0
|
||||
if (id.includes("qwen")) return 0.55
|
||||
if (id.includes("claude")) return undefined
|
||||
if (id.includes("gemini"))
|
||||
return GEMINI_MODELS_WITH_SAMPLING_DEFAULTS.some((model) => model.test(id)) ? 1.0 : undefined
|
||||
@@ -547,7 +546,6 @@ export function temperature(model: Provider.Model) {
|
||||
|
||||
export function topP(model: Provider.Model) {
|
||||
const id = model.api.id.toLowerCase()
|
||||
if (id.includes("qwen")) return 1
|
||||
if (id.includes("gemini"))
|
||||
return GEMINI_MODELS_WITH_SAMPLING_DEFAULTS.some((model) => model.test(id)) ? 0.95 : undefined
|
||||
if (["minimax-m2", "kimi-k2.5", "kimi-k2p5", "kimi-k2-5"].some((s) => id.includes(s))) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ProviderAuth } from "@/provider/auth"
|
||||
import { Config } from "@/config/config"
|
||||
import { ModelsDev } from "@opencode-ai/core/models-dev"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { Auth } from "@/auth"
|
||||
|
||||
import { mapValues } from "remeda"
|
||||
import { Effect, Schema } from "effect"
|
||||
@@ -36,6 +37,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider"
|
||||
const cfg = yield* Config.Service
|
||||
const provider = yield* Provider.Service
|
||||
const svc = yield* ProviderAuth.Service
|
||||
const authStore = yield* Auth.Service
|
||||
|
||||
const list = Effect.fn("ProviderHttpApi.list")(function* () {
|
||||
const config = yield* cfg.get()
|
||||
@@ -47,6 +49,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider"
|
||||
if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) filtered[key] = value
|
||||
}
|
||||
const connected = yield* provider.list()
|
||||
const credentials = yield* authStore.all().pipe(Effect.orDie)
|
||||
const providers = Object.assign(
|
||||
mapValues(filtered, (item) => Provider.fromModelsDevProvider(item)),
|
||||
connected,
|
||||
@@ -54,7 +57,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider"
|
||||
return {
|
||||
all: Object.values(providers).map(Provider.toPublicInfo),
|
||||
default: Provider.defaultModelIDs(providers),
|
||||
connected: Object.keys(connected),
|
||||
connected: Object.keys(providers).filter((id) => id in connected || credentials[id]),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { FinishReason, LLMEvent, ProviderMetadata, ToolResultValue } from "@open
|
||||
import { Effect, Schema } from "effect"
|
||||
import { type streamText } from "ai"
|
||||
import { errorMessage } from "@/util/error"
|
||||
import { ProviderError } from "@/provider/error"
|
||||
|
||||
type Result = Awaited<ReturnType<typeof streamText>>
|
||||
type AISDKEvent = Result["fullStream"] extends AsyncIterable<infer T> ? T : never
|
||||
@@ -85,6 +86,8 @@ export function toLLMEvents(
|
||||
return Effect.succeed([LLMEvent.stepStart({ index: state.step })])
|
||||
|
||||
case "finish-step":
|
||||
if (event.rawFinishReason === "network_error")
|
||||
return Effect.fail(new ProviderError.ResponseStreamError("Provider finish_reason: network_error"))
|
||||
return Effect.sync(() => {
|
||||
const original = providerMetadata(event.providerMetadata)
|
||||
const metadata =
|
||||
|
||||
@@ -1110,7 +1110,7 @@ const layer = Layer.effect(
|
||||
|
||||
if (
|
||||
lastAssistant?.finish &&
|
||||
!["tool-calls"].includes(lastAssistant.finish) &&
|
||||
!["tool-calls", "unknown"].includes(lastAssistant.finish) &&
|
||||
!hasToolCalls &&
|
||||
lastAssistant.parentID === lastUser.id
|
||||
) {
|
||||
|
||||
@@ -34,9 +34,10 @@ const RETRYABLE_MESSAGE_PATTERNS = [
|
||||
/429|500|502|503|504|524/i,
|
||||
/rate increased too quickly|rate limit|rate-limit|rate_limit|too many requests/i,
|
||||
/overloaded|service unavailable|service_unavailable|service-unavailable|internal error|internal_error|internal server error|server error|server_error|server-error|provider returned error|provider_returned_error|provider-returned-error/i,
|
||||
/terminated|fetch failed|failed to fetch|network error|upstream connect|connection error|connection refused|connection lost|socket connection was closed|socket hang up|reset before headers|getaddrinfo|enotfound|eai_again|econnrefused|econnreset|etimedout/i,
|
||||
/terminated|fetch failed|failed to fetch|network[-_\s]error|upstream connect|connection error|connection refused|connection lost|socket connection was closed|socket hang up|reset before headers|getaddrinfo|enotfound|eai_again|econnrefused|econnreset|etimedout/i,
|
||||
/^timeout$|\b(?:request|response|connection|network|stream|read) (?:timeout|timed out|time out)\b/i,
|
||||
/try your request again|retry your request|resource exhausted|resource_exhausted/i,
|
||||
/\btry again (?:later|in\b)|\b(?:currently|temporarily) at capacity\b/i,
|
||||
]
|
||||
|
||||
function cap(ms: number) {
|
||||
|
||||
@@ -336,10 +336,8 @@ export function plan(input: { slug: string; time: { created: number } }, instanc
|
||||
}
|
||||
|
||||
export const getUsage = (input: { model: Provider.Model; usage: Usage; metadata?: ProviderMetadata }) => {
|
||||
const safe = (value: number) => {
|
||||
if (!Number.isFinite(value)) return 0
|
||||
return Math.max(0, value)
|
||||
}
|
||||
const finite = (value: number) => (Number.isFinite(value) ? value : 0)
|
||||
const safe = (value: number) => Math.max(0, finite(value))
|
||||
const inputTokens = safe(input.usage.inputTokens ?? 0)
|
||||
const outputTokens = safe(input.usage.outputTokens ?? 0)
|
||||
const reasoningTokens = safe(input.usage.reasoningTokens ?? 0)
|
||||
@@ -393,13 +391,13 @@ export const getUsage = (input: { model: Provider.Model; usage: Usage; metadata?
|
||||
? new Decimal(totalNanoAiu).div(100_000_000_000).toNumber()
|
||||
: safe(
|
||||
new Decimal(0)
|
||||
.add(new Decimal(tokens.input).mul(costInfo?.input ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.output).mul(costInfo?.output ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.read).mul(costInfo?.cache?.read ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.write).mul(costInfo?.cache?.write ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.input).mul(finite(costInfo?.input ?? 0)).div(1_000_000))
|
||||
.add(new Decimal(tokens.output).mul(finite(costInfo?.output ?? 0)).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.read).mul(finite(costInfo?.cache?.read ?? 0)).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.write).mul(finite(costInfo?.cache?.write ?? 0)).div(1_000_000))
|
||||
// TODO: update models.dev to have better pricing model, for now:
|
||||
// charge reasoning tokens at the same rate as output tokens
|
||||
.add(new Decimal(tokens.reasoning).mul(costInfo?.output ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.reasoning).mul(finite(costInfo?.output ?? 0)).div(1_000_000))
|
||||
.toNumber(),
|
||||
),
|
||||
tokens,
|
||||
|
||||
@@ -56,7 +56,12 @@ import { PermissionV1 } from "@opencode-ai/core/v1/permission"
|
||||
import { McpCatalog } from "@/mcp/catalog"
|
||||
|
||||
export function webSearchEnabled(providerID: ProviderV2.ID, flags = { exa: false, parallel: false }) {
|
||||
return providerID === ProviderV2.ID.opencode || flags.exa || flags.parallel
|
||||
return (
|
||||
providerID === ProviderV2.ID.opencode ||
|
||||
providerID === ProviderV2.ID.make("opencode-go") ||
|
||||
flags.exa ||
|
||||
flags.parallel
|
||||
)
|
||||
}
|
||||
|
||||
type TaskDef = Tool.InferDef<typeof TaskTool>
|
||||
|
||||
@@ -210,6 +210,17 @@ export const TaskTool = Tool.define(
|
||||
agent: next.name,
|
||||
parts,
|
||||
})
|
||||
if (result.info.role === "assistant" && result.info.error) {
|
||||
const message =
|
||||
"message" in result.info.error.data && typeof result.info.error.data.message === "string"
|
||||
? result.info.error.data.message
|
||||
: result.info.error.name
|
||||
return yield* Effect.fail(new Error(`Subagent failed (task_id: ${nextSession.id}): ${message}`))
|
||||
}
|
||||
const failed = result.parts.findLast((item) => item.type === "tool" && item.state.status === "error")
|
||||
if (failed?.type === "tool" && failed.state.status === "error") {
|
||||
return yield* Effect.fail(new Error(`Subagent failed (task_id: ${nextSession.id}): ${failed.state.error}`))
|
||||
}
|
||||
return result.parts.findLast((item) => item.type === "text")?.text ?? ""
|
||||
})
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import stripAnsi from "strip-ansi"
|
||||
import { defaultConsoleUrl, formatAccountLabel, formatOrgLine } from "../../src/cli/cmd/account"
|
||||
|
||||
describe("console account display", () => {
|
||||
test("uses console.opencode.ai as the default login URL", () => {
|
||||
expect(defaultConsoleUrl).toBe("https://console.opencode.ai")
|
||||
test("uses opencode.ai/console as the default login URL", () => {
|
||||
expect(defaultConsoleUrl).toBe("https://opencode.ai/console")
|
||||
})
|
||||
|
||||
test("includes the account url in account labels", () => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Effect, Exit, Fiber, Layer, Schema } from "effect"
|
||||
import { HttpServer, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { GlobalBus, type GlobalEvent } from "@/bus/global"
|
||||
import { Project } from "@/project/project"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { ProjectV2 } from "@opencode-ai/core/project"
|
||||
import { ProjectTable } from "@opencode-ai/core/project/sql"
|
||||
@@ -133,6 +134,9 @@ const startWorkspaceSyncingWithFlag = (projectID: ProjectV2.ID, experimentalWork
|
||||
Workspace.use.startWorkspaceSyncing(projectID).pipe(Effect.provide(workspaceLayer(experimentalWorkspaces))),
|
||||
)
|
||||
|
||||
const listWithFlag = (project: Project.Info, experimentalWorkspaces: boolean) =>
|
||||
Effect.runPromise(Workspace.use.list(project).pipe(Effect.provide(workspaceLayer(experimentalWorkspaces))))
|
||||
|
||||
function captureGlobalEvents() {
|
||||
const events: GlobalEvent[] = []
|
||||
const handler = (event: GlobalEvent) => events.push(event)
|
||||
@@ -417,6 +421,18 @@ describe("workspace CRUD", () => {
|
||||
{ git: true },
|
||||
)
|
||||
|
||||
it.instance(
|
||||
"list is disabled by the experimental workspace flag",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const instance = yield* requireInstance
|
||||
yield* insertWorkspace(workspaceInfo(instance.project.id, "manual"))
|
||||
|
||||
expect(yield* Effect.promise(() => listWithFlag(instance.project, false))).toEqual([])
|
||||
}),
|
||||
{ git: true },
|
||||
)
|
||||
|
||||
it.instance(
|
||||
"create configures, persists, creates, starts local sync, and passes environment",
|
||||
() =>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||
import { CerebrasPlugin } from "../../src/plugin/cerebras"
|
||||
|
||||
type ChatParams = NonNullable<Hooks["chat.params"]>
|
||||
|
||||
function input(npm: string) {
|
||||
return {
|
||||
model: { api: { npm } },
|
||||
} as Parameters<ChatParams>[0]
|
||||
}
|
||||
|
||||
function output(options: Record<string, unknown>) {
|
||||
return {
|
||||
maxOutputTokens: 32_000,
|
||||
options,
|
||||
} as Parameters<ChatParams>[1]
|
||||
}
|
||||
|
||||
describe("CerebrasPlugin", () => {
|
||||
test("omits the generic output cap when max_completion_tokens is configured", async () => {
|
||||
const hook = (await CerebrasPlugin({} as PluginInput))["chat.params"]!
|
||||
const params = output({ max_completion_tokens: 64 })
|
||||
|
||||
await hook(input("@ai-sdk/cerebras"), params)
|
||||
|
||||
expect(params.maxOutputTokens).toBeUndefined()
|
||||
})
|
||||
|
||||
test("preserves the generic output cap without max_completion_tokens", async () => {
|
||||
const hook = (await CerebrasPlugin({} as PluginInput))["chat.params"]!
|
||||
const params = output({})
|
||||
|
||||
await hook(input("@ai-sdk/cerebras"), params)
|
||||
|
||||
expect(params.maxOutputTokens).toBe(32_000)
|
||||
})
|
||||
|
||||
test("does not change other providers", async () => {
|
||||
const hook = (await CerebrasPlugin({} as PluginInput))["chat.params"]!
|
||||
const params = output({ max_completion_tokens: 64 })
|
||||
|
||||
await hook(input("@ai-sdk/openai"), params)
|
||||
|
||||
expect(params.maxOutputTokens).toBe(32_000)
|
||||
})
|
||||
})
|
||||
@@ -13,56 +13,13 @@ const pluginInput = {
|
||||
$: {} as never,
|
||||
}
|
||||
|
||||
function makeHookInput(overrides: { providerID?: string; apiId?: string; reasoning?: boolean }) {
|
||||
return {
|
||||
sessionID: "s",
|
||||
agent: "a",
|
||||
provider: {} as never,
|
||||
message: {} as never,
|
||||
model: {
|
||||
providerID: overrides.providerID ?? "cloudflare-ai-gateway",
|
||||
api: { id: overrides.apiId ?? "openai/gpt-5.2-codex", url: "", npm: "ai-gateway-provider" },
|
||||
capabilities: {
|
||||
reasoning: overrides.reasoning ?? true,
|
||||
temperature: false,
|
||||
attachment: true,
|
||||
toolcall: true,
|
||||
input: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
interleaved: false,
|
||||
},
|
||||
} as never,
|
||||
}
|
||||
}
|
||||
|
||||
function makeHookOutput() {
|
||||
return { temperature: 0, topP: 1, topK: 0, maxOutputTokens: 32_000 as number | undefined, options: {} }
|
||||
}
|
||||
|
||||
test("omits maxOutputTokens for openai reasoning models on cloudflare-ai-gateway", async () => {
|
||||
test("registers the cloudflare-ai-gateway auth method", async () => {
|
||||
const hooks = await CloudflareAIGatewayAuthPlugin(pluginInput)
|
||||
const out = makeHookOutput()
|
||||
await hooks["chat.params"]!(makeHookInput({ apiId: "openai/gpt-5.2-codex", reasoning: true }), out)
|
||||
expect(out.maxOutputTokens).toBeUndefined()
|
||||
expect(hooks.auth?.provider).toBe("cloudflare-ai-gateway")
|
||||
expect(hooks.auth?.methods).toHaveLength(1)
|
||||
})
|
||||
|
||||
test("keeps maxOutputTokens for openai non-reasoning models", async () => {
|
||||
test("no longer drops maxOutputTokens; OpenAI models ride the Responses API passthrough", async () => {
|
||||
const hooks = await CloudflareAIGatewayAuthPlugin(pluginInput)
|
||||
const out = makeHookOutput()
|
||||
await hooks["chat.params"]!(makeHookInput({ apiId: "openai/gpt-4-turbo", reasoning: false }), out)
|
||||
expect(out.maxOutputTokens).toBe(32_000)
|
||||
})
|
||||
|
||||
test("keeps maxOutputTokens for non-openai reasoning models on cloudflare-ai-gateway", async () => {
|
||||
const hooks = await CloudflareAIGatewayAuthPlugin(pluginInput)
|
||||
const out = makeHookOutput()
|
||||
await hooks["chat.params"]!(makeHookInput({ apiId: "anthropic/claude-sonnet-4-5", reasoning: true }), out)
|
||||
expect(out.maxOutputTokens).toBe(32_000)
|
||||
})
|
||||
|
||||
test("ignores non-cloudflare-ai-gateway providers", async () => {
|
||||
const hooks = await CloudflareAIGatewayAuthPlugin(pluginInput)
|
||||
const out = makeHookOutput()
|
||||
await hooks["chat.params"]!(makeHookInput({ providerID: "openai", apiId: "gpt-5.2-codex", reasoning: true }), out)
|
||||
expect(out.maxOutputTokens).toBe(32_000)
|
||||
expect(hooks["chat.params"]).toBeUndefined()
|
||||
})
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { createServer, type IncomingMessage } from "node:http"
|
||||
import { type AddressInfo } from "node:net"
|
||||
import { WebSocketServer } from "ws"
|
||||
import {
|
||||
CodexAuthPlugin,
|
||||
parseJwtClaims,
|
||||
extractAccountIdFromClaims,
|
||||
extractAccountId,
|
||||
extractResidency,
|
||||
renderOAuthError,
|
||||
type IdTokenClaims,
|
||||
} from "../../src/plugin/openai/codex"
|
||||
@@ -131,6 +135,69 @@ describe("plugin.codex", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("extractResidency", () => {
|
||||
test("extracts compute residency from the namespaced auth claims", () => {
|
||||
expect(
|
||||
extractResidency(
|
||||
createTestJwt({
|
||||
"https://api.openai.com/auth": { chatgpt_compute_residency: "eu" },
|
||||
}),
|
||||
),
|
||||
).toBe("eu")
|
||||
})
|
||||
|
||||
test("falls back to a root compute residency claim", () => {
|
||||
expect(extractResidency(createTestJwt({ chatgpt_compute_residency: "us" }))).toBe("us")
|
||||
})
|
||||
|
||||
test("supports compute residency values without maintaining a region list", () => {
|
||||
expect(
|
||||
extractResidency(
|
||||
createTestJwt({
|
||||
"https://api.openai.com/auth": { chatgpt_compute_residency: "ae" },
|
||||
}),
|
||||
),
|
||||
).toBe("ae")
|
||||
expect(
|
||||
extractResidency(
|
||||
createTestJwt({
|
||||
"https://api.openai.com/auth": { chatgpt_compute_residency: "future-region_1" },
|
||||
}),
|
||||
),
|
||||
).toBe("future-region_1")
|
||||
})
|
||||
|
||||
test("ignores unconstrained and data residency values", () => {
|
||||
expect(
|
||||
extractResidency(
|
||||
createTestJwt({
|
||||
"https://api.openai.com/auth": { chatgpt_compute_residency: "no_constraint" },
|
||||
}),
|
||||
),
|
||||
).toBeUndefined()
|
||||
expect(
|
||||
extractResidency(
|
||||
createTestJwt({
|
||||
"https://api.openai.com/auth": { chatgpt_data_residency: "gb" },
|
||||
}),
|
||||
),
|
||||
).toBeUndefined()
|
||||
expect(extractResidency(createTestJwt({ chatgpt_compute_residency: "" }))).toBeUndefined()
|
||||
expect(extractResidency("not-a-jwt")).toBeUndefined()
|
||||
})
|
||||
|
||||
test("prefers a namespaced unconstrained value over a root residency", () => {
|
||||
expect(
|
||||
extractResidency(
|
||||
createTestJwt({
|
||||
chatgpt_compute_residency: "eu",
|
||||
"https://api.openai.com/auth": { chatgpt_compute_residency: "no_constraint" },
|
||||
}),
|
||||
),
|
||||
).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
test("installs websocket transport only when experimental websockets are enabled", async () => {
|
||||
const disabled = await CodexAuthPlugin({} as never)
|
||||
const enabled = await CodexAuthPlugin({} as never, { experimentalWebSockets: true })
|
||||
@@ -149,6 +216,73 @@ describe("plugin.codex", () => {
|
||||
await enabled.dispose?.()
|
||||
})
|
||||
|
||||
test("sends token residency only to the ChatGPT Codex backend", async () => {
|
||||
const requests: Array<{ path: string; residency: string | null }> = []
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch(request) {
|
||||
requests.push({
|
||||
path: new URL(request.url).pathname,
|
||||
residency: request.headers.get("x-openai-internal-codex-residency"),
|
||||
})
|
||||
return new Response("{}")
|
||||
},
|
||||
})
|
||||
const hooks = await CodexAuthPlugin({} as never, {
|
||||
codexApiEndpoint: new URL("/backend-api/codex/responses", server.url).toString(),
|
||||
})
|
||||
const loaded = await hooks.auth!.loader!(
|
||||
async () =>
|
||||
({
|
||||
type: "oauth",
|
||||
refresh: "refresh",
|
||||
access: createTestJwt({
|
||||
"https://api.openai.com/auth": { chatgpt_compute_residency: "eu" },
|
||||
}),
|
||||
expires: Date.now() + 60_000,
|
||||
}) as never,
|
||||
{} as never,
|
||||
)
|
||||
|
||||
await loaded.fetch!("https://api.openai.com/v1/responses")
|
||||
await loaded.fetch!(new URL("/other", server.url))
|
||||
|
||||
expect(requests).toEqual([
|
||||
{ path: "/backend-api/codex/responses", residency: "eu" },
|
||||
{ path: "/other", residency: null },
|
||||
])
|
||||
})
|
||||
|
||||
test("sends token residency through the WebSocket transport", async () => {
|
||||
await using server = await createCodexWebSocketServer()
|
||||
const hooks = await CodexAuthPlugin({} as never, {
|
||||
codexApiEndpoint: server.url,
|
||||
experimentalWebSockets: true,
|
||||
})
|
||||
const loaded = await hooks.auth!.loader!(
|
||||
async () =>
|
||||
({
|
||||
type: "oauth",
|
||||
refresh: "refresh",
|
||||
access: createTestJwt({
|
||||
"https://api.openai.com/auth": { chatgpt_compute_residency: "eu" },
|
||||
}),
|
||||
expires: Date.now() + 60_000,
|
||||
}) as never,
|
||||
{} as never,
|
||||
)
|
||||
|
||||
const response = await loaded.fetch!("https://api.openai.com/v1/responses", {
|
||||
method: "POST",
|
||||
headers: { "session-id": "session-1" },
|
||||
body: JSON.stringify({ stream: true, input: "hi" }),
|
||||
})
|
||||
|
||||
expect(await response.text()).toContain("data: [DONE]")
|
||||
expect(server.headers()?.["x-openai-internal-codex-residency"]).toBe("eu")
|
||||
await hooks.dispose?.()
|
||||
})
|
||||
|
||||
test("filters unsupported modes and uses Codex context limits for OAuth GPT models", async () => {
|
||||
const hooks = await CodexAuthPlugin({} as never)
|
||||
const limit = { context: 1_050_000, input: 922_000, output: 128_000 }
|
||||
@@ -181,9 +315,9 @@ describe("plugin.codex", () => {
|
||||
|
||||
expect(models["gpt-5.4"]?.limit).toEqual(limit)
|
||||
expect(models["gpt-5.5"]?.limit).toEqual({ context: 400_000, input: 272_000, output: 128_000 })
|
||||
expect(models["gpt-5.6-sol"]?.limit).toEqual({ context: 500_000, input: 372_000, output: 128_000 })
|
||||
expect(models["gpt-5.6-terra"]?.limit).toEqual({ context: 500_000, input: 372_000, output: 128_000 })
|
||||
expect(models["gpt-5.6-luna"]?.limit).toEqual({ context: 500_000, input: 372_000, output: 128_000 })
|
||||
expect(models["gpt-5.6-sol"]?.limit).toEqual({ context: 400_000, input: 272_000, output: 128_000 })
|
||||
expect(models["gpt-5.6-terra"]?.limit).toEqual({ context: 400_000, input: 272_000, output: 128_000 })
|
||||
expect(models["gpt-5.6-luna"]?.limit).toEqual({ context: 400_000, input: 272_000, output: 128_000 })
|
||||
expect(models["gpt-5.4-pro"]).toBeUndefined()
|
||||
expect(models["gpt-5.7-pro"]).toBeDefined()
|
||||
expect(models["gpt-5.6-sol-high"]).toBeDefined()
|
||||
@@ -193,6 +327,9 @@ describe("plugin.codex", () => {
|
||||
})
|
||||
|
||||
test("deduplicates concurrent Codex token refreshes", async () => {
|
||||
const refreshedAccess = createTestJwt({
|
||||
"https://api.openai.com/auth": { chatgpt_compute_residency: "eu" },
|
||||
})
|
||||
let auth = {
|
||||
type: "oauth" as const,
|
||||
refresh: "refresh-old",
|
||||
@@ -207,7 +344,7 @@ describe("plugin.codex", () => {
|
||||
resolveRefresh = resolve
|
||||
})
|
||||
let refreshRequests = 0
|
||||
const apiRequests: { authorization: string | null; accountId: string | null }[] = []
|
||||
const apiRequests: { authorization: string | null; accountId: string | null; residency: string | null }[] = []
|
||||
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
@@ -219,7 +356,7 @@ describe("plugin.codex", () => {
|
||||
await refreshReady
|
||||
return Response.json({
|
||||
id_token: createTestJwt({ chatgpt_account_id: "acc-123" }),
|
||||
access_token: "access-new",
|
||||
access_token: refreshedAccess,
|
||||
refresh_token: "refresh-new",
|
||||
expires_in: 3600,
|
||||
})
|
||||
@@ -229,6 +366,7 @@ describe("plugin.codex", () => {
|
||||
apiRequests.push({
|
||||
authorization: request.headers.get("authorization"),
|
||||
accountId: request.headers.get("ChatGPT-Account-Id"),
|
||||
residency: request.headers.get("x-openai-internal-codex-residency"),
|
||||
})
|
||||
return new Response("{}", { status: 200 })
|
||||
}
|
||||
@@ -281,11 +419,11 @@ describe("plugin.codex", () => {
|
||||
expect(refreshRequests).toBe(1)
|
||||
expect(authUpdates).toHaveLength(1)
|
||||
expect(authUpdates[0]?.body.refresh).toBe("refresh-new")
|
||||
expect(authUpdates[0]?.body.access).toBe("access-new")
|
||||
expect(authUpdates[0]?.body.access).toBe(refreshedAccess)
|
||||
expect(authUpdates[0]?.body.accountId).toBe("acc-123")
|
||||
expect(apiRequests).toEqual([
|
||||
{ authorization: "Bearer access-new", accountId: "acc-123" },
|
||||
{ authorization: "Bearer access-new", accountId: "acc-123" },
|
||||
{ authorization: `Bearer ${refreshedAccess}`, accountId: "acc-123", residency: "eu" },
|
||||
{ authorization: `Bearer ${refreshedAccess}`, accountId: "acc-123", residency: "eu" },
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -297,3 +435,29 @@ async function waitFor(predicate: () => boolean) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1))
|
||||
}
|
||||
}
|
||||
|
||||
async function createCodexWebSocketServer() {
|
||||
let headers: IncomingMessage["headers"] | undefined
|
||||
const server = createServer()
|
||||
const sockets = new WebSocketServer({ server })
|
||||
sockets.on("connection", (socket, request) => {
|
||||
headers = request.headers
|
||||
socket.once("message", () => {
|
||||
socket.send(JSON.stringify({ type: "response.completed", response: { id: "resp_123" } }))
|
||||
})
|
||||
})
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
server.once("error", reject)
|
||||
server.listen(0, "127.0.0.1", resolve)
|
||||
})
|
||||
const address = server.address() as AddressInfo
|
||||
return {
|
||||
url: `http://127.0.0.1:${address.port}/backend-api/codex/responses`,
|
||||
headers: () => headers,
|
||||
async [Symbol.asyncDispose]() {
|
||||
for (const socket of sockets.clients) socket.terminate()
|
||||
sockets.close()
|
||||
server.close()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,18 @@ describe("plugin.openai.ws", () => {
|
||||
|
||||
const socket = await OpenAIWebSocket.connectResponsesWebSocket({
|
||||
url: server.wsUrl,
|
||||
headers: { authorization: "Bearer test", "content-length": "123" },
|
||||
headers: {
|
||||
authorization: "Bearer test",
|
||||
"content-length": "123",
|
||||
"x-openai-internal-codex-residency": "eu",
|
||||
},
|
||||
})
|
||||
|
||||
expect(OpenAIWebSocket.toWebSocketUrl("http://example.com/v1/responses")).toBe("ws://example.com/v1/responses")
|
||||
expect(OpenAIWebSocket.toWebSocketUrl("https://example.com/v1/responses")).toBe("wss://example.com/v1/responses")
|
||||
expect(headers?.authorization).toBe("Bearer test")
|
||||
expect(headers?.["openai-beta"]).toBe(OpenAIWebSocket.PROTOCOL_HEADER)
|
||||
expect(headers?.["x-openai-internal-codex-residency"]).toBe("eu")
|
||||
expect(headers?.["content-length"]).toBeUndefined()
|
||||
socket.terminate()
|
||||
})
|
||||
@@ -232,6 +237,26 @@ describe("plugin.openai.ws-pool", () => {
|
||||
fetch.close()
|
||||
})
|
||||
|
||||
test("falls back immediately to HTTP when a websocket request is too large", async () => {
|
||||
let connections = 0
|
||||
await using server = await createWebSocketServer((socket) => {
|
||||
connections += 1
|
||||
socket.once("message", () => socket.close(1009, "payload too large"))
|
||||
})
|
||||
const fetch = OpenAIWebSocketPool.createWebSocketFetch({
|
||||
url: server.url,
|
||||
})
|
||||
|
||||
const first = await fetch(server.url, streamRequest())
|
||||
const second = await fetch(server.url, streamRequest())
|
||||
|
||||
expect(await first.text()).toBe("http")
|
||||
expect(await second.text()).toBe("http")
|
||||
expect(connections).toBe(1)
|
||||
expect(server.httpRequests).toHaveLength(2)
|
||||
fetch.close()
|
||||
})
|
||||
|
||||
test("removes HTTP fallback when its session is deleted", async () => {
|
||||
let websocketAttempts = 0
|
||||
await using server = await createRejectingWebSocketServer(() => websocketAttempts++)
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
// End-to-end regression test for opencode#24432.
|
||||
// End-to-end regression tests for opencode#24432 and opencode#32051/#32052.
|
||||
//
|
||||
// Routes through the actual ai-gateway-provider + @ai-sdk/openai-compatible
|
||||
// chain that provider.ts:811 builds at runtime, with only the network boundary
|
||||
// stubbed. Asserts that `reasoning_effort` (and other provider options the
|
||||
// transform emits) actually land in the body Cloudflare AI Gateway forwards
|
||||
// upstream, which is the only place the bug was observable.
|
||||
// Routes through the actual ai-gateway-provider chain that provider.ts builds at
|
||||
// runtime, with only the network boundary stubbed:
|
||||
// - openai/* -> native OpenAI passthrough (Responses API)
|
||||
// - anthropic/* -> native Anthropic passthrough (Messages API)
|
||||
// - everything else -> unified /compat (openai-compatible chat completions)
|
||||
// Asserts what actually lands in the envelope body Cloudflare AI Gateway
|
||||
// forwards upstream, which is the only place these bugs were observable.
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
|
||||
import type { JSONValue } from "ai"
|
||||
import { generateText } from "ai"
|
||||
import { createAiGateway } from "ai-gateway-provider"
|
||||
import { createUnified } from "ai-gateway-provider/providers/unified"
|
||||
import { createOpenAI } from "ai-gateway-provider/providers/openai"
|
||||
import { createAnthropic } from "ai-gateway-provider/providers/anthropic"
|
||||
import { ProviderTransform } from "@/provider/transform"
|
||||
import type * as Provider from "@/provider/provider"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
|
||||
type Captured = { url: string; outerBody: unknown }
|
||||
type Captured = { url: string; outerBody: unknown; headers: Record<string, string> }
|
||||
type ProviderOptions = Record<string, Record<string, JSONValue>>
|
||||
|
||||
const realFetch = globalThis.fetch
|
||||
@@ -26,24 +30,76 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
}
|
||||
|
||||
// The gateway returns the upstream provider's response body verbatim, so the
|
||||
// mock must answer in the wire format of the step's target provider.
|
||||
function upstreamResponseBody(provider: string | undefined) {
|
||||
if (provider === "openai")
|
||||
return {
|
||||
id: "resp_test",
|
||||
object: "response",
|
||||
created_at: 0,
|
||||
model: "gpt-5.4",
|
||||
status: "completed",
|
||||
error: null,
|
||||
incomplete_details: null,
|
||||
output: [
|
||||
{
|
||||
type: "message",
|
||||
role: "assistant",
|
||||
id: "msg_1",
|
||||
status: "completed",
|
||||
content: [{ type: "output_text", text: "ok", annotations: [] }],
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
input_tokens: 1,
|
||||
input_tokens_details: { cached_tokens: 0 },
|
||||
output_tokens: 1,
|
||||
output_tokens_details: { reasoning_tokens: 0 },
|
||||
total_tokens: 2,
|
||||
},
|
||||
}
|
||||
if (provider === "anthropic")
|
||||
return {
|
||||
id: "msg_test",
|
||||
type: "message",
|
||||
role: "assistant",
|
||||
model: "claude-sonnet-4-6",
|
||||
content: [{ type: "text", text: "ok" }],
|
||||
stop_reason: "end_turn",
|
||||
stop_sequence: null,
|
||||
usage: { input_tokens: 1, output_tokens: 1 },
|
||||
}
|
||||
return {
|
||||
id: "chatcmpl-test",
|
||||
object: "chat.completion",
|
||||
created: 0,
|
||||
model: "test",
|
||||
choices: [{ index: 0, message: { role: "assistant", content: "ok" }, finish_reason: "stop" }],
|
||||
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
captured = null
|
||||
const handle = async (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]): Promise<Response> => {
|
||||
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url
|
||||
if (url.startsWith("https://gateway.ai.cloudflare.com/")) {
|
||||
const bodyText = typeof init?.body === "string" ? init.body : ""
|
||||
captured = { url, outerBody: bodyText ? JSON.parse(bodyText) : null }
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
id: "chatcmpl-test",
|
||||
object: "chat.completion",
|
||||
created: 0,
|
||||
model: "openai/gpt-5.4",
|
||||
choices: [{ index: 0, message: { role: "assistant", content: "ok" }, finish_reason: "stop" }],
|
||||
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
|
||||
}),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } },
|
||||
)
|
||||
const outerBody = bodyText ? JSON.parse(bodyText) : null
|
||||
captured = {
|
||||
url,
|
||||
outerBody,
|
||||
headers: Object.fromEntries(new Headers(init?.headers).entries()),
|
||||
}
|
||||
const provider =
|
||||
Array.isArray(outerBody) && isRecord(outerBody[0]) && typeof outerBody[0].provider === "string"
|
||||
? outerBody[0].provider
|
||||
: undefined
|
||||
return new Response(JSON.stringify(upstreamResponseBody(provider)), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
})
|
||||
}
|
||||
return realFetch(input, init)
|
||||
}
|
||||
@@ -56,11 +112,19 @@ afterEach(() => {
|
||||
globalThis.fetch = realFetch
|
||||
})
|
||||
|
||||
// Mirrors the runtime npm rewrite in provider.ts: openai/anthropic models carry
|
||||
// their native SDK package so transforms key provider options correctly.
|
||||
const cfNpm = (apiId: string) => {
|
||||
if (apiId.startsWith("openai/")) return "@ai-sdk/openai"
|
||||
if (apiId.startsWith("anthropic/")) return "@ai-sdk/anthropic"
|
||||
return "ai-gateway-provider"
|
||||
}
|
||||
|
||||
const cfModel = (apiId: string, releaseDate = "2026-03-05"): Provider.Model => ({
|
||||
id: ModelV2.ID.make(`cloudflare-ai-gateway/${apiId}`),
|
||||
providerID: ProviderV2.ID.make("cloudflare-ai-gateway"),
|
||||
name: apiId,
|
||||
api: { id: apiId, url: "https://gateway.ai.cloudflare.com/v1/compat", npm: "ai-gateway-provider" },
|
||||
api: { id: apiId, url: "https://gateway.ai.cloudflare.com/v1/compat", npm: cfNpm(apiId) },
|
||||
capabilities: {
|
||||
reasoning: true,
|
||||
temperature: false,
|
||||
@@ -80,53 +144,165 @@ const cfModel = (apiId: string, releaseDate = "2026-03-05"): Provider.Model => (
|
||||
|
||||
// ai-gateway-provider sends an array of step descriptors; each entry's `query`
|
||||
// is the body forwarded to the upstream provider.
|
||||
function extractUpstreamQuery(body: unknown): Record<string, unknown> | undefined {
|
||||
function firstStep(body: unknown): Record<string, unknown> | undefined {
|
||||
if (!Array.isArray(body) || body.length === 0) return undefined
|
||||
const first = body[0]
|
||||
if (!isRecord(first)) return undefined
|
||||
const query = first.query
|
||||
return isRecord(first) ? first : undefined
|
||||
}
|
||||
|
||||
function extractUpstreamQuery(body: unknown): Record<string, unknown> | undefined {
|
||||
const query = firstStep(body)?.query
|
||||
return isRecord(query) ? query : undefined
|
||||
}
|
||||
|
||||
async function callThroughGateway(apiId: string, providerOptions: ProviderOptions) {
|
||||
const aigateway = createAiGateway({ accountId: "test", gateway: "test", apiKey: "test" })
|
||||
const unified = createUnified()
|
||||
await generateText({ model: aigateway(unified(apiId)), prompt: "hi", providerOptions })
|
||||
// Each step descriptor also carries the `headers` forwarded to the upstream provider.
|
||||
function extractUpstreamHeaders(body: unknown): Record<string, unknown> | undefined {
|
||||
const headers = firstStep(body)?.headers
|
||||
return isRecord(headers) ? headers : undefined
|
||||
}
|
||||
|
||||
// Mirrors the runtime routing in provider.ts getModel.
|
||||
function gatewayModel(apiId: string, gatewayToken = "test") {
|
||||
const aigateway = createAiGateway({ accountId: "test", gateway: "test", apiKey: gatewayToken })
|
||||
if (apiId.startsWith("openai/")) return aigateway(createOpenAI()(apiId.slice("openai/".length)))
|
||||
if (apiId.startsWith("anthropic/")) return aigateway(createAnthropic()(apiId.slice("anthropic/".length)))
|
||||
const isWorkersAi = apiId.startsWith("workers-ai/") || apiId.startsWith("@cf/")
|
||||
const unified = createUnified(isWorkersAi ? { apiKey: gatewayToken } : {})
|
||||
return aigateway(unified(apiId))
|
||||
}
|
||||
|
||||
async function callThroughGateway(apiId: string, providerOptions: ProviderOptions, gatewayToken = "test") {
|
||||
await generateText({ model: gatewayModel(apiId, gatewayToken), prompt: "hi", providerOptions })
|
||||
return extractUpstreamQuery(captured?.outerBody)
|
||||
}
|
||||
|
||||
describe("cf-ai-gateway routing", () => {
|
||||
test("openai/* rides the native OpenAI passthrough on the Responses API", async () => {
|
||||
await callThroughGateway("openai/gpt-5.4", {})
|
||||
const step = firstStep(captured?.outerBody)
|
||||
expect(step?.provider).toBe("openai")
|
||||
expect(step?.endpoint).toBe("v1/responses")
|
||||
const upstream = extractUpstreamQuery(captured?.outerBody)
|
||||
expect(upstream?.model).toBe("gpt-5.4")
|
||||
})
|
||||
|
||||
test("anthropic/* rides the native Anthropic passthrough on the Messages API", async () => {
|
||||
await callThroughGateway("anthropic/claude-sonnet-4-6", {})
|
||||
const step = firstStep(captured?.outerBody)
|
||||
expect(step?.provider).toBe("anthropic")
|
||||
expect(step?.endpoint).toBe("v1/messages")
|
||||
const upstream = extractUpstreamQuery(captured?.outerBody)
|
||||
expect(upstream?.model).toBe("claude-sonnet-4-6")
|
||||
})
|
||||
|
||||
test("workers-ai models stay on the unified /compat route", async () => {
|
||||
await callThroughGateway("workers-ai/@cf/moonshotai/kimi-k2.6", {})
|
||||
const step = firstStep(captured?.outerBody)
|
||||
expect(step?.provider).toBe("compat")
|
||||
expect(step?.endpoint).toBe("chat/completions")
|
||||
const upstream = extractUpstreamQuery(captured?.outerBody)
|
||||
expect(upstream?.model).toBe("workers-ai/@cf/moonshotai/kimi-k2.6")
|
||||
})
|
||||
})
|
||||
|
||||
describe("cf-ai-gateway end-to-end (regression: #24432)", () => {
|
||||
test("ProviderTransform.providerOptions output puts reasoning_effort on the wire", async () => {
|
||||
// The full chain the runtime exercises:
|
||||
// transform.providerOptions() -> openaiCompatible key
|
||||
// -> @ai-sdk/openai-compatible reads it as compatibleOptions
|
||||
// -> emits body.reasoning_effort
|
||||
test("ProviderTransform.providerOptions output puts reasoning effort on the Responses wire", async () => {
|
||||
// The full chain the runtime exercises for OpenAI models:
|
||||
// transform.providerOptions() -> "openai" key (npm rewritten to @ai-sdk/openai)
|
||||
// -> OpenAIResponsesLanguageModel emits body.reasoning.effort
|
||||
// -> ai-gateway-provider wraps the body and forwards to gateway.ai.cloudflare.com
|
||||
const opts = ProviderTransform.providerOptions(cfModel("openai/gpt-5.4"), { reasoningEffort: "xhigh" })
|
||||
expect(opts).toEqual({ openaiCompatible: { reasoningEffort: "xhigh" } })
|
||||
expect(Object.keys(opts)).toEqual(["openai"])
|
||||
expect(opts.openai.reasoningEffort).toBe("xhigh")
|
||||
|
||||
const upstream = await callThroughGateway("openai/gpt-5.4", opts)
|
||||
expect(upstream?.reasoning_effort).toBe("xhigh")
|
||||
expect((upstream?.reasoning as Record<string, unknown> | undefined)?.effort).toBe("xhigh")
|
||||
})
|
||||
|
||||
test("variants() output for openai/gpt-5.4 lands xhigh on the wire", async () => {
|
||||
// The other half of the bug: workflow `variant: xhigh` flows through variants()
|
||||
// and must reach the wire. variants() returns the providerOptions payload
|
||||
// unwrapped; providerOptions() wraps it under the SDK key.
|
||||
// fromModelsDevModel resolves the native npm before computing variants, so
|
||||
// OpenAI models get full Responses-flavored payloads (summary + encrypted
|
||||
// reasoning include for stateless multi-turn reasoning).
|
||||
const variants = ProviderTransform.variants(cfModel("openai/gpt-5.4"))
|
||||
expect(variants.xhigh).toEqual({ reasoningEffort: "xhigh" })
|
||||
expect(variants.xhigh).toEqual({
|
||||
reasoningEffort: "xhigh",
|
||||
reasoningSummary: "auto",
|
||||
include: ["reasoning.encrypted_content"],
|
||||
})
|
||||
|
||||
const opts = ProviderTransform.providerOptions(cfModel("openai/gpt-5.4"), variants.xhigh)
|
||||
const upstream = await callThroughGateway("openai/gpt-5.4", opts)
|
||||
expect(upstream?.reasoning_effort).toBe("xhigh")
|
||||
const reasoning = upstream?.reasoning as Record<string, unknown> | undefined
|
||||
expect(reasoning?.effort).toBe("xhigh")
|
||||
expect(reasoning?.summary).toBe("auto")
|
||||
})
|
||||
|
||||
test("reasoning effort variants for anthropic models land as native adaptive thinking", async () => {
|
||||
// Mirrors the runtime catalog path: models.dev reasoning_options -> reasoningVariants
|
||||
// computed on the native @ai-sdk/anthropic npm -> adaptive thinking + output_config.effort.
|
||||
const model = cfModel("anthropic/claude-sonnet-4-6")
|
||||
const variants = ProviderTransform.reasoningVariants(
|
||||
{ reasoning_options: [{ type: "effort", values: ["low", "medium", "high"] }] } as never,
|
||||
model,
|
||||
)
|
||||
expect(variants?.high).toMatchObject({ effort: "high" })
|
||||
|
||||
const opts = ProviderTransform.providerOptions(model, variants!.high)
|
||||
expect(Object.keys(opts)).toEqual(["anthropic"])
|
||||
|
||||
const upstream = await callThroughGateway("anthropic/claude-sonnet-4-6", opts)
|
||||
expect((upstream?.thinking as Record<string, unknown> | undefined)?.type).toBe("adaptive")
|
||||
expect((upstream?.output_config as Record<string, unknown> | undefined)?.effort).toBe("high")
|
||||
})
|
||||
|
||||
test("reasoning_effort still reaches the /compat wire for workers-ai models", async () => {
|
||||
const model = cfModel("workers-ai/@cf/moonshotai/kimi-k2.6")
|
||||
const opts = ProviderTransform.providerOptions(model, { reasoningEffort: "high" })
|
||||
expect(opts).toEqual({ openaiCompatible: { reasoningEffort: "high" } })
|
||||
|
||||
const upstream = await callThroughGateway("workers-ai/@cf/moonshotai/kimi-k2.6", opts)
|
||||
expect(upstream?.reasoning_effort).toBe("high")
|
||||
})
|
||||
|
||||
test("legacy buggy key 'cloudflare-ai-gateway' does NOT reach the wire (proves the bug)", async () => {
|
||||
// Sanity: confirms the bug class. If a future change accidentally restores
|
||||
// providerID-keyed providerOptions, this test fails before users notice.
|
||||
const upstream = await callThroughGateway("openai/gpt-5.4", {
|
||||
const upstream = await callThroughGateway("workers-ai/@cf/moonshotai/kimi-k2.6", {
|
||||
"cloudflare-ai-gateway": { reasoningEffort: "high" },
|
||||
})
|
||||
expect(upstream?.reasoning_effort).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("cf-ai-gateway token scoping (regression: #32051/#32052)", () => {
|
||||
test("openai passthrough does NOT forward the Cloudflare token upstream", async () => {
|
||||
await callThroughGateway("openai/gpt-5.4", {}, "cf-gateway-secret")
|
||||
|
||||
expect(captured?.headers["cf-aig-authorization"]).toBe("Bearer cf-gateway-secret")
|
||||
// Security invariant: the Cloudflare token must never become the upstream provider's Authorization.
|
||||
expect(extractUpstreamHeaders(captured?.outerBody)?.["authorization"]).toBeUndefined()
|
||||
expect(JSON.stringify(captured?.outerBody)).not.toContain("cf-gateway-secret")
|
||||
})
|
||||
|
||||
test("anthropic passthrough does NOT forward the Cloudflare token upstream", async () => {
|
||||
await callThroughGateway("anthropic/claude-sonnet-4-6", {}, "cf-gateway-secret")
|
||||
|
||||
expect(captured?.headers["cf-aig-authorization"]).toBe("Bearer cf-gateway-secret")
|
||||
expect(extractUpstreamHeaders(captured?.outerBody)?.["x-api-key"]).toBeUndefined()
|
||||
expect(JSON.stringify(captured?.outerBody)).not.toContain("cf-gateway-secret")
|
||||
})
|
||||
|
||||
test("workers-ai models DO forward the Cloudflare token upstream", async () => {
|
||||
await callThroughGateway("workers-ai/@cf/google/gemma-4-26b-a4b-it", {}, "cf-gateway-secret")
|
||||
|
||||
expect(captured?.headers["cf-aig-authorization"]).toBe("Bearer cf-gateway-secret")
|
||||
expect(extractUpstreamHeaders(captured?.outerBody)?.["authorization"]).toBe("Bearer cf-gateway-secret")
|
||||
})
|
||||
|
||||
test("bare @cf/ Workers AI models DO forward the Cloudflare token upstream", async () => {
|
||||
await callThroughGateway("@cf/meta/llama-3.1-8b-instruct", {}, "cf-gateway-secret")
|
||||
|
||||
expect(captured?.headers["cf-aig-authorization"]).toBe("Bearer cf-gateway-secret")
|
||||
expect(extractUpstreamHeaders(captured?.outerBody)?.["authorization"]).toBe("Bearer cf-gateway-secret")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { ProviderError } from "@/provider/error"
|
||||
|
||||
describe("provider stream errors", () => {
|
||||
test("retries provider stream errors without a code", () => {
|
||||
const messages = [
|
||||
"The model is currently at capacity due to high demand. Please try again in a few minutes, or use a higher service tier for priority processing: https://docs.x.ai/developers/advanced-api-usage/priority-processing",
|
||||
"The model is temporarily unavailable.",
|
||||
]
|
||||
|
||||
for (const message of messages)
|
||||
expect(
|
||||
ProviderError.parseStreamError({
|
||||
type: "error",
|
||||
error: { message },
|
||||
}),
|
||||
).toEqual({
|
||||
type: "api_error",
|
||||
message,
|
||||
isRetryable: true,
|
||||
responseBody: JSON.stringify({ type: "error", error: { message } }),
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1909,6 +1909,32 @@ it.instance("Google Vertex: keeps regional Claude endpoints unchanged", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("Google Vertex: uses REP endpoint for Gemini continental multi-regions", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
|
||||
yield* set("VERTEX_LOCATION", "eu")
|
||||
const provider = yield* Provider.Service
|
||||
const model = yield* provider.getModel(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini-3.5-flash"))
|
||||
const language = yield* provider.getLanguage(model)
|
||||
expect(languageBaseURL(language)).toBe(
|
||||
"https://aiplatform.eu.rep.googleapis.com/v1beta1/projects/test-project/locations/eu/publishers/google",
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("Google Vertex: keeps regional Gemini endpoints unchanged", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
|
||||
yield* set("VERTEX_LOCATION", "europe-west1")
|
||||
const provider = yield* Provider.Service
|
||||
const model = yield* provider.getModel(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini-3.5-flash"))
|
||||
const language = yield* provider.getLanguage(model)
|
||||
expect(languageBaseURL(language)).toBe(
|
||||
"https://europe-west1-aiplatform.googleapis.com/v1beta1/projects/test-project/locations/europe-west1/publishers/google",
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("cloudflare-ai-gateway loads with env variables", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* set("CLOUDFLARE_ACCOUNT_ID", "test-account")
|
||||
|
||||
@@ -3215,6 +3215,19 @@ describe("ProviderTransform.temperature - Cohere North", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("ProviderTransform sampling defaults - Qwen", () => {
|
||||
test.each(["Qwen3.8-27B", "qwen3-coder-30b-a3b-instruct"])('leaves sampling unset for "%s"', (id) => {
|
||||
const model = {
|
||||
id: `custom/${id}`,
|
||||
api: { id },
|
||||
} as any
|
||||
|
||||
expect(ProviderTransform.temperature(model)).toBeUndefined()
|
||||
expect(ProviderTransform.topP(model)).toBeUndefined()
|
||||
expect(ProviderTransform.topK(model)).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("ProviderTransform sampling defaults - Gemini", () => {
|
||||
const model = (id: string) =>
|
||||
({
|
||||
|
||||
@@ -1782,6 +1782,22 @@ describe("SessionNs.getUsage", () => {
|
||||
expect(Number.isNaN(result.cost)).toBe(false)
|
||||
})
|
||||
|
||||
test("ignores malformed cost fields", () => {
|
||||
const model = createModel({
|
||||
context: 100_000,
|
||||
output: 32_000,
|
||||
cost: { input: 3, output: 15, cache: { read: 0.3, write: 3.75 } },
|
||||
})
|
||||
Object.assign(model.cost, { input: {} })
|
||||
|
||||
const result = SessionNs.getUsage({
|
||||
model,
|
||||
usage: usage({ inputTokens: 1_000_000, outputTokens: 100_000, totalTokens: 1_100_000 }),
|
||||
})
|
||||
|
||||
expect(result.cost).toBe(1.5)
|
||||
})
|
||||
|
||||
test("calculates cost correctly", () => {
|
||||
const model = createModel({
|
||||
context: 100_000,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user