mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-21 17:16:14 +00:00
Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fdb104bb5 | ||
|
|
b02419621b | ||
|
|
95f098a721 | ||
|
|
8c39e13b77 | ||
|
|
ed11c895c6 | ||
|
|
8bc87d3c6a | ||
|
|
1ec78c705a | ||
|
|
ede6c4f1a6 | ||
|
|
c34171845e | ||
|
|
4a7a497597 | ||
|
|
b7f72b1bd4 | ||
|
|
cff8ba313a | ||
|
|
836fcfb742 | ||
|
|
03d1099933 | ||
|
|
e41e416beb | ||
|
|
2d16759723 | ||
|
|
a41ed1cdf2 | ||
|
|
24479e2df9 | ||
|
|
365a95c91b | ||
|
|
49ca92621d | ||
|
|
92aa12a609 | ||
|
|
484b662665 | ||
|
|
e8d955a40c | ||
|
|
7adfe50b9a | ||
|
|
5613ebb514 | ||
|
|
c0ecd5da0e | ||
|
|
be5f5dbc29 | ||
|
|
3cf9cfb6f3 | ||
|
|
fcc21f3dc5 | ||
|
|
fa40659b7f | ||
|
|
fdf14a8462 | ||
|
|
db43ee0b05 | ||
|
|
2b53cf0f4a | ||
|
|
dffa1045d3 | ||
|
|
d862d02d0e | ||
|
|
4abac2a431 |
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
@@ -96,7 +96,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"bin": {
|
||||
"lildax": "./bin/lildax.cjs",
|
||||
},
|
||||
@@ -144,7 +144,7 @@
|
||||
},
|
||||
"packages/codemode": {
|
||||
"name": "@opencode-ai/codemode",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"acorn": "8.15.0",
|
||||
"effect": "catalog:",
|
||||
@@ -158,7 +158,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"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.181",
|
||||
"@ai-sdk/google-vertex": "4.0.128",
|
||||
"@ai-sdk/groq": "3.0.31",
|
||||
"@ai-sdk/mistral": "3.0.51",
|
||||
"@ai-sdk/openai": "3.0.84",
|
||||
@@ -381,7 +381,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -449,7 +449,7 @@
|
||||
},
|
||||
"packages/effect-sqlite-node": {
|
||||
"name": "@opencode-ai/effect-sqlite-node",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"effect": "catalog:",
|
||||
},
|
||||
@@ -461,7 +461,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"@hono/standard-validator": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
@@ -493,7 +493,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@smithy/eventstream-codec": "4.2.14",
|
||||
@@ -559,7 +559,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.181",
|
||||
"@ai-sdk/google-vertex": "4.0.128",
|
||||
"@ai-sdk/groq": "3.0.31",
|
||||
"@ai-sdk/mistral": "3.0.51",
|
||||
"@ai-sdk/openai": "3.0.84",
|
||||
@@ -690,7 +690,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"cross-spawn": "catalog:",
|
||||
},
|
||||
@@ -781,7 +781,7 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@opencode-ai/server",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/protocol": "workspace:*",
|
||||
@@ -796,7 +796,7 @@
|
||||
},
|
||||
"packages/session-ui": {
|
||||
"name": "@opencode-ai/session-ui",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -883,7 +883,7 @@
|
||||
},
|
||||
"packages/stats/core": {
|
||||
"name": "@opencode-ai/stats-core",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
@@ -971,7 +971,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/diffs": "catalog:",
|
||||
@@ -1022,7 +1022,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -1197,7 +1197,7 @@
|
||||
|
||||
"@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.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/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/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=="],
|
||||
|
||||
@@ -5691,15 +5691,13 @@
|
||||
|
||||
"@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.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/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/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/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/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": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
|
||||
|
||||
"@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/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/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=="],
|
||||
|
||||
@@ -6183,6 +6181,8 @@
|
||||
|
||||
"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/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-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/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=="],
|
||||
@@ -6615,8 +6615,6 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -7017,6 +7015,12 @@
|
||||
|
||||
"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/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-gateway-provider/@ai-sdk/google-vertex/@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-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-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=="],
|
||||
@@ -7459,6 +7463,10 @@
|
||||
|
||||
"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-vertex/@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-vertex/@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=="],
|
||||
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"nodeModules": {
|
||||
"x86_64-linux": "sha256-yQ8EIxxYkzlEWIMY/UiIR+7lbGBuizsoehMPZzFA65Y=",
|
||||
"aarch64-linux": "sha256-JF9VVgnl5QUZ430fqb5Qu8y0kchYJ00LO3FbYcd0lBM=",
|
||||
"aarch64-darwin": "sha256-f3Tu6eu463NWcHgr7dupfD/zUTh26bJ6N2vVXuEyi6c=",
|
||||
"x86_64-darwin": "sha256-miv9Sv4KdhD0UIi2O5LVS1wQOfq29IV+9S+BvIzAvgo="
|
||||
"x86_64-linux": "sha256-kDCnJMnaK/Jq7ckcpPB7Vl9v98EMSdcehZAtf8jNjTs=",
|
||||
"aarch64-linux": "sha256-0aR+OJGXS5HMlXbe/BHybjIRvdNJJw6gjW+jr6Dk7Pk=",
|
||||
"aarch64-darwin": "sha256-loLrV6xiorhwS/N2hlpiKSKX172Qxy+auNiPzFBhQSc=",
|
||||
"x86_64-darwin": "sha256-PNEpQBLAz8M274bSyTpp0jofETn2L+D0uBiJHUV7nB0="
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,39 +60,6 @@ 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) {
|
||||
@@ -107,10 +74,7 @@ async function readProbe(page: Page) {
|
||||
.evaluate((el) => (el as Probed).__e2eProbe)
|
||||
}
|
||||
|
||||
async function setup(
|
||||
page: Page,
|
||||
findFiles?: (input: { query: string; dirs?: string; limit?: number }) => unknown | Promise<unknown>,
|
||||
) {
|
||||
async function setup(page: Page) {
|
||||
await mockOpenCodeServer(page, {
|
||||
directory,
|
||||
project: {
|
||||
@@ -155,7 +119,6 @@ async function setup(
|
||||
}))
|
||||
},
|
||||
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 | Promise<unknown>
|
||||
findFiles?: (input: { query: string; dirs?: string; limit?: number }) => unknown
|
||||
sessionStatus?: Record<string, unknown> | (() => Record<string, unknown>)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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")}
|
||||
defaultValue={props.name}
|
||||
value={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")}
|
||||
defaultValue={props.username}
|
||||
value={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")}
|
||||
defaultValue={props.password}
|
||||
value={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 max-h-[calc(100dvh-56px)] bg-background-strong rounded-xl overflow-hidden"
|
||||
class="tabs 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 max-h-[calc(100dvh-56px)] bg-background-strong rounded-xl overflow-hidden"
|
||||
class="tabs bg-background-strong rounded-xl overflow-hidden"
|
||||
data-component="tabs"
|
||||
data-active={settings.general.newLayoutDesigns() ? "mcp" : "servers"}
|
||||
defaultValue={settings.general.newLayoutDesigns() ? "mcp" : "servers"}
|
||||
|
||||
@@ -972,6 +972,17 @@ 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"),
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
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,6 +790,25 @@ 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({
|
||||
@@ -813,6 +832,36 @@ 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
|
||||
@@ -862,7 +911,7 @@ export function MessageTimeline(props: {
|
||||
}
|
||||
}
|
||||
|
||||
sessionArchive.navigateAfterRemoval(sessionID, session.parentID, nextSession?.id)
|
||||
navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id)
|
||||
|
||||
sync().set(
|
||||
produce((draft) => {
|
||||
@@ -1544,7 +1593,7 @@ export function MessageTimeline(props: {
|
||||
<DropdownMenu.Item onSelect={() => exportSession(id)}>
|
||||
<DropdownMenu.ItemLabel>{language.t("common.export")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onSelect={() => void sessionArchive.archive(id)}>
|
||||
<DropdownMenu.Item onSelect={() => void archiveSession(id)}>
|
||||
<DropdownMenu.ItemLabel>{language.t("common.archive")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
@@ -1618,7 +1667,7 @@ export function MessageTimeline(props: {
|
||||
<MenuV2.Item onSelect={() => exportSession(id)}>
|
||||
{language.t("common.export")}...
|
||||
</MenuV2.Item>
|
||||
<MenuV2.Item onSelect={() => void sessionArchive.archive(id)}>
|
||||
<MenuV2.Item onSelect={() => void archiveSession(id)}>
|
||||
{language.t("common.archive")}
|
||||
</MenuV2.Item>
|
||||
<MenuV2.Separator />
|
||||
|
||||
@@ -18,7 +18,6 @@ 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"
|
||||
|
||||
@@ -53,7 +52,6 @@ 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()
|
||||
@@ -499,16 +497,6 @@ 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, keepPreviousData } from "@tanstack/solid-query"
|
||||
import { createQuery } 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,7 +50,6 @@ 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.20",
|
||||
"version": "1.18.18",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/codemode",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -252,7 +252,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "الاستثناءات التالية",
|
||||
|
||||
"go.title": "OpenCode Go | نماذج برمجة منخفضة التكلفة للجميع",
|
||||
"go.banner.text": "Ox Alpha Free متاح على Go لفترة محدودة",
|
||||
"go.meta.description":
|
||||
"يبدأ Go بسعر $5 للشهر الأول، ثم $10/شهر، مع حدود استخدام سخية ووصول موثوق إلى نماذج البرمجة الرائدة.",
|
||||
"go.hero.title": "نماذج برمجة منخفضة التكلفة للجميع",
|
||||
@@ -270,8 +269,6 @@ 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}}",
|
||||
|
||||
@@ -344,13 +341,10 @@ 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":
|
||||
@@ -391,9 +385,6 @@ 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}}",
|
||||
|
||||
@@ -673,7 +664,6 @@ 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,6 @@ 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": "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,8 +273,6 @@ 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}}",
|
||||
|
||||
@@ -351,13 +348,10 @@ 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.",
|
||||
@@ -400,9 +394,6 @@ 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}}",
|
||||
|
||||
@@ -684,7 +675,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "følgende undtagelser",
|
||||
|
||||
"go.title": "OpenCode Go | Kodningsmodeller til lav pris for alle",
|
||||
"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,8 +271,6 @@ 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}}",
|
||||
|
||||
@@ -348,13 +345,10 @@ 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.",
|
||||
@@ -397,9 +391,6 @@ 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}}",
|
||||
|
||||
@@ -681,7 +672,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "folgenden Ausnahmen",
|
||||
|
||||
"go.title": "OpenCode Go | Kostengünstige Coding-Modelle für alle",
|
||||
"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,8 +273,6 @@ 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}}",
|
||||
|
||||
@@ -350,13 +347,10 @@ 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.",
|
||||
@@ -399,9 +393,6 @@ 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}}",
|
||||
|
||||
@@ -683,7 +674,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "following exceptions",
|
||||
|
||||
"go.title": "OpenCode Go | Low cost coding models for everyone",
|
||||
"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,8 +269,6 @@ 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}}",
|
||||
@@ -346,13 +343,10 @@ 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",
|
||||
@@ -396,9 +390,6 @@ 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}}",
|
||||
|
||||
@@ -680,8 +671,7 @@ 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.allowTraining": "Allow models that train on request data",
|
||||
"workspace.lite.providers.useChina": "Allow models hosted in China",
|
||||
"workspace.lite.providers.useChina": "Enable 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "siguientes excepciones",
|
||||
|
||||
"go.title": "OpenCode Go | Modelos de programación de bajo coste para todos",
|
||||
"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,8 +274,6 @@ 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}}",
|
||||
|
||||
@@ -351,13 +348,10 @@ 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.",
|
||||
@@ -400,9 +394,6 @@ 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}}",
|
||||
|
||||
@@ -684,7 +675,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "exceptions suivantes",
|
||||
|
||||
"go.title": "OpenCode Go | Modèles de code à faible coût pour tous",
|
||||
"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,8 +275,6 @@ 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}}",
|
||||
|
||||
@@ -352,13 +349,10 @@ 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.",
|
||||
@@ -401,9 +395,6 @@ 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}}",
|
||||
|
||||
@@ -691,7 +682,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "seguenti eccezioni",
|
||||
|
||||
"go.title": "OpenCode Go | Modelli di coding a basso costo per tutti",
|
||||
"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,8 +271,6 @@ 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}}",
|
||||
|
||||
@@ -347,7 +344,6 @@ 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":
|
||||
@@ -355,8 +351,6 @@ 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",
|
||||
@@ -396,9 +390,6 @@ 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}}",
|
||||
|
||||
@@ -682,7 +673,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "以下の例外",
|
||||
|
||||
"go.title": "OpenCode Go | すべての人のための低価格なコーディングモデル",
|
||||
"go.banner.text": "Ox Alpha Freeは期間限定でGoで利用できます",
|
||||
"go.meta.description":
|
||||
"Goは最初の月$5、その後$10/月で、主要なコーディングモデルへのゆとりある利用上限と安定したアクセスを提供します。",
|
||||
"go.hero.title": "すべての人のための低価格なコーディングモデル",
|
||||
@@ -271,8 +270,6 @@ 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}}",
|
||||
|
||||
@@ -347,12 +344,9 @@ 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":
|
||||
@@ -395,9 +389,6 @@ 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}}",
|
||||
|
||||
@@ -680,7 +671,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "다음 예외",
|
||||
|
||||
"go.title": "OpenCode Go | 모두를 위한 저비용 코딩 모델",
|
||||
"go.banner.text": "Ox Alpha Free가 한정된 기간 동안 Go에서 제공됩니다",
|
||||
"go.meta.description":
|
||||
"Go는 첫 달 $5, 이후 $10/월로 시작하며, 넉넉한 사용 한도와 주요 코딩 모델에 대한 안정적인 액세스를 제공합니다.",
|
||||
"go.hero.title": "모두를 위한 저비용 코딩 모델",
|
||||
@@ -268,8 +267,6 @@ 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}}",
|
||||
|
||||
@@ -343,12 +340,9 @@ 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":
|
||||
@@ -389,9 +383,6 @@ 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,7 +663,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "følgende unntak",
|
||||
|
||||
"go.title": "OpenCode Go | Rimelige kodemodeller for alle",
|
||||
"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,8 +271,6 @@ 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}}",
|
||||
|
||||
@@ -348,7 +345,6 @@ 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":
|
||||
@@ -356,8 +352,6 @@ 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",
|
||||
@@ -398,9 +392,6 @@ 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}}",
|
||||
|
||||
@@ -682,7 +673,6 @@ 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,6 @@ 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": "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,8 +272,6 @@ 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}}",
|
||||
|
||||
@@ -349,15 +346,12 @@ 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",
|
||||
@@ -398,9 +392,6 @@ 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}}",
|
||||
|
||||
@@ -682,7 +673,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "следующими исключениями",
|
||||
|
||||
"go.title": "OpenCode Go | Недорогие модели для кодинга для всех",
|
||||
"go.banner.text": "Ox Alpha Free доступна в Go в течение ограниченного времени",
|
||||
"go.meta.description":
|
||||
"Go стоит $5 за первый месяц, затем $10/месяц и предлагает щедрые лимиты использования и надежный доступ к ведущим моделям для кодинга.",
|
||||
"go.hero.title": "Недорогие модели для кодинга для всех",
|
||||
@@ -276,8 +275,6 @@ 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}}",
|
||||
|
||||
@@ -353,7 +350,6 @@ 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":
|
||||
@@ -361,8 +357,6 @@ export const dict = {
|
||||
"go.faq.a5.learnMore": "Подробнее",
|
||||
"go.faq.a5.deepseekRetention":
|
||||
"Соглашение ZDR продлевается ежемесячно. Текущее соглашение действует до 31 августа 2026 года.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Значительно сниженная стоимость токенов в обмен на разрешение использовать ваши промпты и ответы для обучения будущих моделей Meta.",
|
||||
|
||||
"go.faq.a5.beforeExceptions":
|
||||
"Модели Go размещены в США. Провайдеры следуют политике нулевого хранения и не используют ваши данные для обучения моделей, за",
|
||||
@@ -403,9 +397,6 @@ 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}}",
|
||||
|
||||
@@ -689,7 +680,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "ข้อยกเว้นดังนี้",
|
||||
|
||||
"go.title": "OpenCode Go | โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน",
|
||||
"go.banner.text": "Ox Alpha Free พร้อมใช้งานบน Go ในช่วงเวลาจำกัด",
|
||||
"go.meta.description":
|
||||
"Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน พร้อมขีดจำกัดการใช้งานที่เอื้อเฟื้อและการเข้าถึงโมเดลเขียนโค้ดชั้นนำอย่างเชื่อถือได้",
|
||||
"go.hero.title": "โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน",
|
||||
@@ -270,8 +269,6 @@ 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}}",
|
||||
|
||||
@@ -345,13 +342,10 @@ 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",
|
||||
|
||||
@@ -393,9 +387,6 @@ 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}}",
|
||||
|
||||
@@ -677,7 +668,6 @@ 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,6 @@ 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": "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,8 +273,6 @@ 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}}",
|
||||
|
||||
@@ -351,7 +348,6 @@ 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":
|
||||
@@ -359,8 +355,6 @@ 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",
|
||||
@@ -401,9 +395,6 @@ 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}}",
|
||||
|
||||
@@ -685,7 +676,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "такими винятками",
|
||||
|
||||
"go.title": "OpenCode Go | Недорогі моделі кодування для всіх",
|
||||
"go.banner.text": "Ox Alpha Free доступна в Go протягом обмеженого часу",
|
||||
"go.meta.description":
|
||||
"Go починається від $5 за перший місяць, потім $10/місяць, зі щедрими лімітами використання та надійним доступом до провідних моделей для кодування.",
|
||||
"go.hero.title": "Недорогі моделі кодування для всіх",
|
||||
@@ -273,8 +272,6 @@ 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}}",
|
||||
|
||||
@@ -349,15 +346,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.learnMore": "Докладніше",
|
||||
"go.faq.a5.deepseekRetention": "Угода ZDR поновлюється щомісяця. Поточна угода дійсна до 31 серпня 2026 року.",
|
||||
"go.faq.a5.museRetention":
|
||||
"Значно знижені ціни на токени в обмін на дозвіл використовувати ваші промпти та відповіді для навчання майбутніх моделей Meta.",
|
||||
|
||||
"go.faq.a5.beforeExceptions":
|
||||
"Моделі Go розміщені в США. Провайдери дотримуються політики нульового зберігання та не використовують ваші дані для навчання моделей, за",
|
||||
@@ -397,9 +391,6 @@ 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}}",
|
||||
|
||||
@@ -680,7 +671,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "以下例外情况除外",
|
||||
|
||||
"go.title": "OpenCode Go | 人人可用的低成本编程模型",
|
||||
"go.banner.text": "Ox Alpha Free 限时加入 Go",
|
||||
"go.meta.description": "Go 首月 $5,之后 $10/月,提供充裕的使用限额,并可可靠访问领先的编程模型。",
|
||||
"go.hero.title": "人人可用的低成本编程模型",
|
||||
"go.hero.body":
|
||||
@@ -260,8 +259,6 @@ 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}}",
|
||||
|
||||
@@ -331,11 +328,9 @@ 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 模型托管在美国。提供商遵循零留存政策,不使用您的数据进行模型训练,",
|
||||
@@ -373,8 +368,6 @@ 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 | 访问全球顶尖编程模型",
|
||||
@@ -652,7 +645,6 @@ 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,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "以下例外情況",
|
||||
|
||||
"go.title": "OpenCode Go | 低成本全民編碼模型",
|
||||
"go.banner.text": "Ox Alpha Free 限時加入 Go",
|
||||
"go.meta.description": "Go 首月 $5,之後 $10/月,提供充裕的使用限額,並可穩定存取領先的編碼模型。",
|
||||
"go.hero.title": "低成本全民編碼模型",
|
||||
"go.hero.body":
|
||||
@@ -260,8 +259,6 @@ 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}}",
|
||||
|
||||
@@ -331,11 +328,9 @@ 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 模型託管在美國。供應商遵循零留存政策,不會將你的資料用於模型訓練,但有",
|
||||
@@ -373,8 +368,6 @@ 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 | 存取全球最佳編碼模型",
|
||||
@@ -652,7 +645,6 @@ 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,38 +1,5 @@
|
||||
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,16 +592,6 @@ 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;
|
||||
@@ -698,11 +688,6 @@ 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;
|
||||
|
||||
@@ -39,7 +39,6 @@ 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" },
|
||||
@@ -73,14 +72,12 @@ function LimitsGraph(props: { href: string }) {
|
||||
{ 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-flash", name: "DeepSeek V4 Flash", req: 3800, 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: "deepseek-v4-flash", name: "DeepSeek V4 Flash", req: 7600, d: "330ms" },
|
||||
{ id: "mimo-v2.5", name: "MiMo-V2.5", req: 30100, 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" },
|
||||
{ id: "hy3", name: "Hy3", req: 4300, d: "320ms" },
|
||||
{ id: "mimo-v2.5", name: "MiMo-V2.5", req: 30100, edge: true, d: "340ms" },
|
||||
]
|
||||
|
||||
const w = 1040
|
||||
@@ -90,10 +87,9 @@ 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.filter((m) => !("infinite" in m)).map((m) => ratio(m.req)))
|
||||
const rmax = Math.max(1, ...graph.map((m) => ratio(m.req)))
|
||||
const log = (n: number) => Math.log10(Math.max(n, 1))
|
||||
const base = 24
|
||||
const p = 2.2
|
||||
@@ -155,10 +151,10 @@ function LimitsGraph(props: { href: string }) {
|
||||
<rect
|
||||
x={left}
|
||||
y={gy(i()) - bh / 2}
|
||||
width={Math.max(0, ("infinite" in m ? infiniteX : x(ratio(m.baseReq ?? m.req))) - left)}
|
||||
width={Math.max(0, x(ratio(m.baseReq ?? m.req)) - left)}
|
||||
height={bh}
|
||||
data-bar
|
||||
data-kind={"infinite" in m ? "infinite" : "go"}
|
||||
data-kind="go"
|
||||
data-model={m.id}
|
||||
/>
|
||||
{m.baseReq && (
|
||||
@@ -194,7 +190,7 @@ function LimitsGraph(props: { href: string }) {
|
||||
</For>
|
||||
</div>
|
||||
|
||||
<div data-slot="pills">
|
||||
<div data-slot="pills" aria-hidden="true">
|
||||
<For each={graph}>
|
||||
{(m, i) => (
|
||||
<span
|
||||
@@ -202,24 +198,11 @@ function LimitsGraph(props: { href: string }) {
|
||||
data-kind="go"
|
||||
data-model={m.id}
|
||||
data-edge={"edge" in m ? "" : undefined}
|
||||
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
|
||||
}
|
||||
style={{ "--x": px(x(ratio(m.req))), "--y": py(gy(i())), "--d": m.d } as any}
|
||||
>
|
||||
<span data-value>{"infinite" in m ? "∞" : m.req.toLocaleString()}</span>
|
||||
<span data-value>{m.req.toLocaleString()}</span>
|
||||
<span data-name>{m.name}</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>}
|
||||
{m.baseReq && <span data-bonus>2x usage</span>}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
@@ -269,12 +252,6 @@ export default function Home() {
|
||||
|
||||
<div data-component="content">
|
||||
<section data-component="hero">
|
||||
<div data-component="desktop-app-banner">
|
||||
<span data-slot="badge">{i18n.t("home.banner.badge")}</span>
|
||||
<div data-slot="content">
|
||||
<span data-slot="text">{i18n.t("go.banner.text")}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div data-slot="hero-copy">
|
||||
<img data-slot="zen logo light" src={goLogoLight} alt="" />
|
||||
<img data-slot="zen logo dark" src={goLogoDark} alt="" />
|
||||
@@ -528,13 +505,6 @@ 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,7 +106,6 @@
|
||||
|
||||
[data-slot="setting-row"] {
|
||||
margin-top: 0;
|
||||
padding-block: var(--space-2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ 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))
|
||||
@@ -55,7 +54,6 @@ 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({
|
||||
@@ -156,24 +154,6 @@ 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()
|
||||
|
||||
@@ -206,7 +186,6 @@ 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,
|
||||
@@ -285,20 +264,6 @@ 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} />
|
||||
@@ -349,18 +314,15 @@ 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,7 +4,6 @@ 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,7 +22,6 @@ import {
|
||||
UserLimitError,
|
||||
ModelError,
|
||||
RegionError,
|
||||
DataPolicyError,
|
||||
RateLimitError,
|
||||
FreeUsageLimitError,
|
||||
GoUsageLimitError,
|
||||
@@ -52,7 +51,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, isModelCountryRestricted } from "~/lib/request-country"
|
||||
import { countryFromRequest } from "~/lib/request-country"
|
||||
|
||||
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
|
||||
type RetryOptions = {
|
||||
@@ -122,8 +121,6 @@ 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
|
||||
@@ -131,23 +128,12 @@ 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 }),
|
||||
Workspace.setDefaultRegion({ country: countryFromRequest(input.request) }),
|
||||
)
|
||||
})()
|
||||
if (
|
||||
@@ -261,7 +247,7 @@ export async function handler(
|
||||
headers.delete("host")
|
||||
headers.delete("content-length")
|
||||
headers.delete("x-opencode-request")
|
||||
if (!isNewInference) headers.delete("x-opencode-session")
|
||||
headers.delete("x-opencode-session")
|
||||
headers.delete("x-opencode-project")
|
||||
headers.delete("x-opencode-client")
|
||||
return headers
|
||||
@@ -491,7 +477,7 @@ export async function handler(
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (error instanceof RegionError || error instanceof DataPolicyError)
|
||||
if (error instanceof RegionError)
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
type: "error",
|
||||
@@ -722,7 +708,6 @@ 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,
|
||||
@@ -835,7 +820,6 @@ 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,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE `workspace` ADD `allow_non_zdr` boolean;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
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.20",
|
||||
"version": "1.18.18",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -53,7 +53,6 @@ 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, index, mysqlTable, primaryKey, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||
import { bigint, mysqlTable, primaryKey, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||
import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types"
|
||||
import { workspaceIndexes } from "./workspace.sql"
|
||||
|
||||
@@ -31,8 +31,5 @@ export const ReferralRewardTable = mysqlTable(
|
||||
amount: bigint("amount", { mode: "number" }).notNull(),
|
||||
timeApplied: utc("time_applied"),
|
||||
},
|
||||
(table) => [
|
||||
primaryKey({ columns: [table.workspaceID, table.referralID] }),
|
||||
index("referral_id").on(table.referralID),
|
||||
],
|
||||
(table) => [primaryKey({ columns: [table.workspaceID, table.referralID] })],
|
||||
)
|
||||
|
||||
@@ -8,7 +8,6 @@ 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,7 +62,6 @@ 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()
|
||||
@@ -73,7 +72,6 @@ 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.20",
|
||||
"version": "1.18.18",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.181",
|
||||
"@ai-sdk/google-vertex": "4.0.128",
|
||||
"@ai-sdk/groq": "3.0.31",
|
||||
"@ai-sdk/mistral": "3.0.51",
|
||||
"@ai-sdk/openai": "3.0.84",
|
||||
|
||||
@@ -202,7 +202,6 @@ export const make = (dependencies: Dependencies) => {
|
||||
.stream(
|
||||
LLM.request({
|
||||
model: input.model,
|
||||
http: input.request.http,
|
||||
messages: [Message.user(summaryPrompt)],
|
||||
tools: [],
|
||||
generation: { maxTokens: summaryOutput },
|
||||
|
||||
@@ -204,13 +204,6 @@ 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)
|
||||
|
||||
@@ -1101,16 +1101,6 @@ 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>")
|
||||
@@ -2518,43 +2508,6 @@ 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.20",
|
||||
"version": "1.18.18",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://opencode.ai",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"name": "@opencode-ai/effect-sqlite-node",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"$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.20",
|
||||
"version": "1.18.18",
|
||||
"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.20",
|
||||
"version": "1.18.18",
|
||||
"name": "@opencode-ai/llm",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"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.181",
|
||||
"@ai-sdk/google-vertex": "4.0.128",
|
||||
"@ai-sdk/groq": "3.0.31",
|
||||
"@ai-sdk/mistral": "3.0.51",
|
||||
"@ai-sdk/openai": "3.0.84",
|
||||
|
||||
@@ -696,14 +696,9 @@ 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 &&
|
||||
@@ -800,7 +795,7 @@ export const RunCommand = effectCmd({
|
||||
|
||||
if (event.type === "permission.asked") {
|
||||
const permission = event.properties
|
||||
if (!sessions.has(permission.sessionID)) continue
|
||||
if (permission.sessionID !== sessionID) continue
|
||||
|
||||
if (auto) {
|
||||
await client.permission.reply({
|
||||
|
||||
@@ -1,6 +1,70 @@
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||
import type { Provider } from "@opencode-ai/sdk/v2"
|
||||
import { Schema } from "effect"
|
||||
import { OAUTH_DUMMY_KEY } from "../auth"
|
||||
|
||||
const AZURE_COGNITIVE_SERVICES_SCOPE = "https://cognitiveservices.azure.com/.default"
|
||||
const AZURE_FOUNDRY_SCOPE = "https://ai.azure.com/.default"
|
||||
const AZURE_TOKEN_REFRESH_BUFFER = 60_000
|
||||
|
||||
const AzureCliToken = Schema.Struct({
|
||||
accessToken: Schema.NonEmptyString,
|
||||
expires_on: Schema.Number,
|
||||
})
|
||||
const decodeAzureCliToken = Schema.decodeUnknownPromise(AzureCliToken)
|
||||
|
||||
const decodeAzureAccounts = Schema.decodeUnknownPromise(
|
||||
Schema.Array(
|
||||
Schema.Struct({
|
||||
name: Schema.NonEmptyString,
|
||||
resourceGroup: Schema.NonEmptyString,
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
const decodeAzureDeployments = Schema.decodeUnknownPromise(
|
||||
Schema.Array(
|
||||
Schema.Struct({
|
||||
name: Schema.NonEmptyString,
|
||||
properties: Schema.Struct({
|
||||
model: Schema.Struct({
|
||||
name: Schema.NonEmptyString,
|
||||
}),
|
||||
provisioningState: Schema.NonEmptyString,
|
||||
}),
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
type AzureCommand = {
|
||||
quiet(): AzureCommand
|
||||
json(): Promise<unknown>
|
||||
}
|
||||
|
||||
type AzureShell = (strings: TemplateStringsArray, ...values: string[]) => AzureCommand
|
||||
|
||||
export async function AzureAuthPlugin(input: PluginInput): Promise<Hooks> {
|
||||
return createAzureAuthHooks(input.$)
|
||||
}
|
||||
|
||||
export function createAzureAuthHooks(
|
||||
shell: AzureShell,
|
||||
request: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response> = fetch,
|
||||
): Hooks {
|
||||
const tokens = new Map<string, { token: string; expires: number }>()
|
||||
async function token(scope: string) {
|
||||
const cached = tokens.get(scope)
|
||||
if (cached && cached.expires - Date.now() > AZURE_TOKEN_REFRESH_BUFFER) return cached.token
|
||||
|
||||
const result = await decodeAzureCliToken(
|
||||
await shell`az account get-access-token --scope ${scope} --output json`.quiet().json(),
|
||||
)
|
||||
const token = { token: result.accessToken, expires: result.expires_on * 1000 }
|
||||
tokens.set(scope, token)
|
||||
return token.token
|
||||
}
|
||||
|
||||
export async function AzureAuthPlugin(_input: PluginInput): Promise<Hooks> {
|
||||
const prompts = []
|
||||
if (!process.env.AZURE_RESOURCE_NAME) {
|
||||
prompts.push({
|
||||
@@ -12,15 +76,102 @@ export async function AzureAuthPlugin(_input: PluginInput): Promise<Hooks> {
|
||||
}
|
||||
|
||||
return {
|
||||
provider: {
|
||||
id: "azure",
|
||||
async models(provider, context) {
|
||||
if (context.auth?.type !== "oauth") return provider.models
|
||||
if (!context.auth.accountId) return {}
|
||||
return discoverAzureModels(provider.models, context.auth.accountId, shell).catch(() => ({}))
|
||||
},
|
||||
},
|
||||
auth: {
|
||||
provider: "azure",
|
||||
async loader(getAuth) {
|
||||
if ((await getAuth()).type !== "oauth") return {}
|
||||
|
||||
return {
|
||||
apiKey: OAUTH_DUMMY_KEY,
|
||||
async fetch(input: RequestInfo | URL, init?: RequestInit) {
|
||||
const headers = new Headers(input instanceof Request ? input.headers : undefined)
|
||||
new Headers(init?.headers).forEach((value, key) => headers.set(key, value))
|
||||
headers.delete("api-key")
|
||||
headers.delete("x-api-key")
|
||||
headers.set("authorization", `Bearer ${await token(scopeForRequest(input))}`)
|
||||
headers.set("User-Agent", `opencode/${InstallationVersion}`)
|
||||
return request(input, { ...init, headers })
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: [
|
||||
{
|
||||
type: "api",
|
||||
label: "API key",
|
||||
prompts,
|
||||
},
|
||||
{
|
||||
type: "oauth",
|
||||
label: "Microsoft Entra ID (Azure CLI)",
|
||||
prompts,
|
||||
async authorize(inputs) {
|
||||
return {
|
||||
url: "",
|
||||
instructions: "Sign in with `az login` before continuing.",
|
||||
method: "auto",
|
||||
callback: async () => {
|
||||
const resourceName = inputs?.resourceName ?? process.env.AZURE_RESOURCE_NAME
|
||||
if (!resourceName) throw new Error("Azure Resource Name is required")
|
||||
|
||||
await token(AZURE_COGNITIVE_SERVICES_SCOPE)
|
||||
return {
|
||||
type: "success",
|
||||
access: OAUTH_DUMMY_KEY,
|
||||
refresh: OAUTH_DUMMY_KEY,
|
||||
expires: Date.now() + 365 * 24 * 60 * 60 * 1000,
|
||||
accountId: resourceName,
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async function discoverAzureModels(models: Provider["models"], resourceName: string, shell: AzureShell) {
|
||||
const accounts = await decodeAzureAccounts(
|
||||
await shell`az cognitiveservices account list --output json --only-show-errors`.quiet().json(),
|
||||
)
|
||||
const account = accounts.find((account) => account.name.toLowerCase() === resourceName.toLowerCase())
|
||||
if (!account) return {}
|
||||
|
||||
const deployments = await decodeAzureDeployments(
|
||||
await shell`az cognitiveservices account deployment list --name ${account.name} --resource-group ${account.resourceGroup} --output json --only-show-errors`
|
||||
.quiet()
|
||||
.json(),
|
||||
)
|
||||
const found = new Map<string, Provider["models"][string]>()
|
||||
deployments.forEach((deployment) => {
|
||||
if (deployment.properties.provisioningState !== "Succeeded") return
|
||||
const modelID = Object.keys(models).find(
|
||||
(modelID) => modelID.toLowerCase() === deployment.properties.model.name.toLowerCase(),
|
||||
)
|
||||
if (!modelID) return
|
||||
found.set(modelID, {
|
||||
...models[modelID],
|
||||
api: {
|
||||
...models[modelID].api,
|
||||
id: deployment.name,
|
||||
},
|
||||
})
|
||||
})
|
||||
return Object.fromEntries(found)
|
||||
}
|
||||
|
||||
function scopeForRequest(input: RequestInfo | URL) {
|
||||
const url = new URL(input instanceof Request ? input.url : input)
|
||||
if (url.hostname.endsWith(".services.ai.azure.com") && !url.pathname.startsWith("/models")) {
|
||||
return AZURE_FOUNDRY_SCOPE
|
||||
}
|
||||
return AZURE_COGNITIVE_SERVICES_SCOPE
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
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
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ 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"
|
||||
@@ -81,7 +80,6 @@ function internalPlugins(flags: RuntimeFlags.Info): PluginInstance[] {
|
||||
DigitalOceanAuthPlugin,
|
||||
SnowflakeCortexAuthPlugin,
|
||||
XaiAuthPlugin,
|
||||
CerebrasPlugin,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -37,12 +37,10 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,14 +75,6 @@ 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",
|
||||
@@ -416,12 +406,10 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
|
||||
requestInput instanceof URL
|
||||
? requestInput
|
||||
: new URL(typeof requestInput === "string" ? requestInput : requestInput.url)
|
||||
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 url =
|
||||
parsed.pathname.includes("/v1/responses") || parsed.pathname.includes("/chat/completions")
|
||||
? new URL(codexApiEndpoint)
|
||||
: parsed
|
||||
|
||||
const requestInit = {
|
||||
...init,
|
||||
|
||||
@@ -110,11 +110,10 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
invalidate(entry)
|
||||
}
|
||||
},
|
||||
onConnectionInvalid: (_error, closeCode) => {
|
||||
onConnectionInvalid: (error) => {
|
||||
entry.busy = false
|
||||
entry.lastUsedAt = Date.now()
|
||||
if (closeCode === OpenAIWebSocket.MESSAGE_TOO_BIG_CLOSE_CODE) entry.fallback = true
|
||||
else if (!entry.fallback) recordStreamFailure(entry)
|
||||
if (!entry.fallback) recordStreamFailure(entry)
|
||||
invalidate(entry)
|
||||
resolveFirstEvent(false)
|
||||
},
|
||||
|
||||
@@ -9,7 +9,6 @@ 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
|
||||
@@ -27,7 +26,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, closeCode?: number) => void
|
||||
onConnectionInvalid?: (error: ProviderError.ResponseStreamError) => void
|
||||
onAbort?: (error: Error) => void
|
||||
}
|
||||
|
||||
@@ -163,11 +162,11 @@ export function streamResponsesWebSocket(options: StreamResponsesWebSocketOption
|
||||
controller?.close()
|
||||
}
|
||||
|
||||
function invalidate(error: ProviderError.ResponseStreamError, closeCode?: number) {
|
||||
function invalidate(error: ProviderError.ResponseStreamError) {
|
||||
if (completed) return
|
||||
completed = true
|
||||
cleanup()
|
||||
options.onConnectionInvalid?.(error, closeCode)
|
||||
options.onConnectionInvalid?.(error)
|
||||
controller?.error(error)
|
||||
}
|
||||
|
||||
@@ -275,7 +274,6 @@ export function streamResponsesWebSocket(options: StreamResponsesWebSocketOption
|
||||
if (completed) return
|
||||
invalidate(
|
||||
new ProviderError.ResponseStreamError(closeMessage("WebSocket closed before response.completed", code, reason)),
|
||||
code,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -375,7 +373,7 @@ function abortError(signal: AbortSignal | undefined) {
|
||||
|
||||
function closeMessage(message: string, code: number, reason: Buffer) {
|
||||
const details = [`code ${code}`]
|
||||
if (code === MESSAGE_TOO_BIG_CLOSE_CODE) details.push("message too big")
|
||||
if (code === 1009) details.push("message too big")
|
||||
if (reason.length > 0) details.push(reason.toString())
|
||||
return `${message} (${details.join(": ")})`
|
||||
}
|
||||
|
||||
@@ -144,13 +144,6 @@ 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,12 +98,6 @@ 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
|
||||
@@ -250,6 +244,7 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
|
||||
return [
|
||||
provider.options?.resourceName,
|
||||
auth?.type === "api" ? auth.metadata?.resourceName : undefined,
|
||||
auth?.type === "oauth" ? auth.accountId : undefined,
|
||||
env["AZURE_RESOURCE_NAME"],
|
||||
].find((name) => typeof name === "string" && name.trim() !== "")
|
||||
})
|
||||
@@ -525,10 +520,11 @@ 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: googleVertexEndpoint(location),
|
||||
GOOGLE_VERTEX_ENDPOINT: endpoint,
|
||||
}
|
||||
},
|
||||
options: {
|
||||
|
||||
@@ -528,6 +528,7 @@ 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
|
||||
@@ -546,6 +547,7 @@ 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,7 +2,6 @@ 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"
|
||||
@@ -37,7 +36,6 @@ 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()
|
||||
@@ -49,7 +47,6 @@ 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,
|
||||
@@ -57,7 +54,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider"
|
||||
return {
|
||||
all: Object.values(providers).map(Provider.toPublicInfo),
|
||||
default: Provider.defaultModelIDs(providers),
|
||||
connected: Object.keys(providers).filter((id) => id in connected || credentials[id]),
|
||||
connected: Object.keys(connected),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ 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
|
||||
@@ -86,8 +85,6 @@ 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", "unknown"].includes(lastAssistant.finish) &&
|
||||
!["tool-calls"].includes(lastAssistant.finish) &&
|
||||
!hasToolCalls &&
|
||||
lastAssistant.parentID === lastUser.id
|
||||
) {
|
||||
|
||||
@@ -34,10 +34,9 @@ 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[-_\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,
|
||||
/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,
|
||||
/^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,8 +336,10 @@ export function plan(input: { slug: string; time: { created: number } }, instanc
|
||||
}
|
||||
|
||||
export const getUsage = (input: { model: Provider.Model; usage: Usage; metadata?: ProviderMetadata }) => {
|
||||
const finite = (value: number) => (Number.isFinite(value) ? value : 0)
|
||||
const safe = (value: number) => Math.max(0, finite(value))
|
||||
const safe = (value: number) => {
|
||||
if (!Number.isFinite(value)) return 0
|
||||
return Math.max(0, value)
|
||||
}
|
||||
const inputTokens = safe(input.usage.inputTokens ?? 0)
|
||||
const outputTokens = safe(input.usage.outputTokens ?? 0)
|
||||
const reasoningTokens = safe(input.usage.reasoningTokens ?? 0)
|
||||
@@ -391,13 +393,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(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))
|
||||
.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))
|
||||
// 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(finite(costInfo?.output ?? 0)).div(1_000_000))
|
||||
.add(new Decimal(tokens.reasoning).mul(costInfo?.output ?? 0).div(1_000_000))
|
||||
.toNumber(),
|
||||
),
|
||||
tokens,
|
||||
|
||||
@@ -210,17 +210,6 @@ 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 ?? ""
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
import { afterEach, describe, expect, test } from "bun:test"
|
||||
import type { Hooks } from "@opencode-ai/plugin"
|
||||
import type { Auth, Provider } from "@opencode-ai/sdk/v2"
|
||||
import { OAUTH_DUMMY_KEY } from "../../src/auth"
|
||||
import { createAzureAuthHooks } from "../../src/plugin/azure"
|
||||
|
||||
const resourceName = process.env.AZURE_RESOURCE_NAME
|
||||
|
||||
afterEach(() => {
|
||||
if (resourceName === undefined) delete process.env.AZURE_RESOURCE_NAME
|
||||
else process.env.AZURE_RESOURCE_NAME = resourceName
|
||||
})
|
||||
|
||||
const oauth: Auth = {
|
||||
type: "oauth",
|
||||
access: OAUTH_DUMMY_KEY,
|
||||
refresh: OAUTH_DUMMY_KEY,
|
||||
expires: Date.now() + 60 * 60 * 1000,
|
||||
accountId: "test-resource",
|
||||
}
|
||||
|
||||
const provider: Provider = {
|
||||
id: "azure",
|
||||
name: "Azure",
|
||||
source: "custom",
|
||||
env: [],
|
||||
options: {},
|
||||
models: {},
|
||||
}
|
||||
|
||||
function oauthMethod(hooks: Hooks) {
|
||||
const method = hooks.auth?.methods.find((method) => method.type === "oauth")
|
||||
if (!method || method.type !== "oauth") throw new Error("Azure OAuth method is missing")
|
||||
return method
|
||||
}
|
||||
|
||||
function loader(hooks: Hooks) {
|
||||
if (!hooks.auth?.loader) throw new Error("Azure auth loader is missing")
|
||||
return hooks.auth.loader
|
||||
}
|
||||
|
||||
function customFetch(options: Record<string, unknown>) {
|
||||
const result = options["fetch"]
|
||||
if (typeof result !== "function") throw new Error("Azure custom fetch is missing")
|
||||
return async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const response: unknown = await Reflect.apply(result, undefined, [input, init])
|
||||
if (!(response instanceof Response)) throw new Error("Azure custom fetch returned an invalid response")
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
function models(...ids: string[]): Provider["models"] {
|
||||
return Object.fromEntries(
|
||||
ids.map((id) => [
|
||||
id,
|
||||
{
|
||||
id,
|
||||
providerID: "azure",
|
||||
name: id,
|
||||
family: "",
|
||||
api: { id, url: "", npm: "@ai-sdk/azure" },
|
||||
status: "active",
|
||||
headers: {},
|
||||
options: {},
|
||||
cost: { input: 0, output: 0, cache: { read: 0, write: 0 } },
|
||||
limit: { context: 0, output: 0 },
|
||||
capabilities: {
|
||||
temperature: true,
|
||||
reasoning: false,
|
||||
attachment: false,
|
||||
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,
|
||||
},
|
||||
release_date: "",
|
||||
variants: {},
|
||||
},
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
function azureShell(scopes: string[]) {
|
||||
return (_strings: TemplateStringsArray, ...values: string[]) => {
|
||||
const output = {
|
||||
quiet: () => output,
|
||||
json: async () => {
|
||||
const scope = values[0]
|
||||
scopes.push(scope)
|
||||
return {
|
||||
accessToken: `${scope}-token`,
|
||||
expires_on: Math.floor((Date.now() + 60 * 60 * 1000) / 1000),
|
||||
}
|
||||
},
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
function discoveryShell(accounts: unknown, deployments: unknown, commands: string[]) {
|
||||
return (strings: TemplateStringsArray, ...values: string[]) => {
|
||||
const command = String.raw(strings, ...values)
|
||||
commands.push(command)
|
||||
const output = {
|
||||
quiet: () => output,
|
||||
json: async () => (command.includes("deployment list") ? deployments : accounts),
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
describe("plugin.azure", () => {
|
||||
test("keeps the existing API-key method and adds Entra ID", () => {
|
||||
delete process.env.AZURE_RESOURCE_NAME
|
||||
const hooks = createAzureAuthHooks(azureShell([]))
|
||||
|
||||
expect(hooks.auth?.provider).toBe("azure")
|
||||
expect(hooks.provider?.id).toBe("azure")
|
||||
expect(hooks.auth?.methods.map((method) => [method.type, method.label])).toEqual([
|
||||
["api", "API key"],
|
||||
["oauth", "Microsoft Entra ID (Azure CLI)"],
|
||||
])
|
||||
expect(hooks.auth?.methods[0]).toEqual({
|
||||
type: "api",
|
||||
label: "API key",
|
||||
prompts: [
|
||||
{
|
||||
type: "text",
|
||||
key: "resourceName",
|
||||
message: "Enter Azure Resource Name",
|
||||
placeholder: "e.g. my-models",
|
||||
},
|
||||
],
|
||||
})
|
||||
expect(hooks.auth?.methods[1].prompts).toEqual(hooks.auth?.methods[0].prompts)
|
||||
})
|
||||
|
||||
test("checks Azure CLI and stores the resource name", async () => {
|
||||
const scopes: string[] = []
|
||||
const hooks = createAzureAuthHooks(azureShell(scopes))
|
||||
const authorization = await oauthMethod(hooks).authorize({ resourceName: "test-resource" })
|
||||
if (authorization.method !== "auto") throw new Error("Unexpected Azure authorization method")
|
||||
|
||||
expect(await authorization.callback()).toMatchObject({
|
||||
type: "success",
|
||||
access: OAUTH_DUMMY_KEY,
|
||||
refresh: OAUTH_DUMMY_KEY,
|
||||
accountId: "test-resource",
|
||||
})
|
||||
expect(scopes).toEqual(["https://cognitiveservices.azure.com/.default"])
|
||||
})
|
||||
|
||||
test("discovers deployed models through Azure CLI", async () => {
|
||||
const commands: string[] = []
|
||||
const hooks = createAzureAuthHooks(
|
||||
discoveryShell(
|
||||
[{ name: "test-resource", resourceGroup: "test-group" }],
|
||||
[
|
||||
{
|
||||
name: "gpt-production",
|
||||
properties: { model: { name: "gpt-5-mini" }, provisioningState: "Succeeded" },
|
||||
},
|
||||
{
|
||||
name: "DeepSeek-V4-Flash",
|
||||
properties: { model: { name: "DeepSeek-V4-Flash" }, provisioningState: "Succeeded" },
|
||||
},
|
||||
{
|
||||
name: "phi-production",
|
||||
properties: { model: { name: "Phi-4-mini-instruct" }, provisioningState: "Succeeded" },
|
||||
},
|
||||
{
|
||||
name: "gpt-5-nano",
|
||||
properties: { model: { name: "gpt-5-nano" }, provisioningState: "Creating" },
|
||||
},
|
||||
],
|
||||
commands,
|
||||
),
|
||||
)
|
||||
const list = hooks.provider?.models
|
||||
if (!list) throw new Error("Azure provider model hook is missing")
|
||||
|
||||
const result = await list(
|
||||
{
|
||||
...provider,
|
||||
models: models("gpt-5-mini", "deepseek-v4-flash", "phi-4-mini", "phi-4-mini-instruct", "gpt-5-nano"),
|
||||
},
|
||||
{ auth: oauth },
|
||||
)
|
||||
|
||||
expect(Object.keys(result)).toEqual(["gpt-5-mini", "deepseek-v4-flash", "phi-4-mini-instruct"])
|
||||
expect(result["gpt-5-mini"].api.id).toBe("gpt-production")
|
||||
expect(result["deepseek-v4-flash"].api.id).toBe("DeepSeek-V4-Flash")
|
||||
expect(result["phi-4-mini-instruct"].api.id).toBe("phi-production")
|
||||
expect(commands).toEqual([
|
||||
"az cognitiveservices account list --output json --only-show-errors",
|
||||
"az cognitiveservices account deployment list --name test-resource --resource-group test-group --output json --only-show-errors",
|
||||
])
|
||||
})
|
||||
|
||||
test("keeps startup running when Azure discovery fails", async () => {
|
||||
const hooks = createAzureAuthHooks(() => {
|
||||
const output = {
|
||||
quiet: () => output,
|
||||
json: async () => {
|
||||
throw new Error("Azure CLI failed")
|
||||
},
|
||||
}
|
||||
return output
|
||||
})
|
||||
const list = hooks.provider?.models
|
||||
if (!list) throw new Error("Azure provider model hook is missing")
|
||||
|
||||
expect(await list({ ...provider, models: models("gpt-5-mini") }, { auth: oauth })).toEqual({})
|
||||
})
|
||||
|
||||
test("does not change API-key loading", async () => {
|
||||
const scopes: string[] = []
|
||||
const hooks = createAzureAuthHooks(azureShell(scopes))
|
||||
const catalog = models("gpt-5-mini")
|
||||
const list = hooks.provider?.models
|
||||
if (!list) throw new Error("Azure provider model hook is missing")
|
||||
|
||||
expect(await loader(hooks)(async () => ({ type: "api", key: "test-key" }), provider)).toEqual({})
|
||||
expect(await list({ ...provider, models: catalog }, { auth: { type: "api", key: "test-key" } })).toBe(catalog)
|
||||
expect(scopes).toEqual([])
|
||||
})
|
||||
|
||||
test("uses Azure CLI bearer tokens for Azure inference endpoints", async () => {
|
||||
const scopes: string[] = []
|
||||
const requests: Headers[] = []
|
||||
const hooks = createAzureAuthHooks(azureShell(scopes), async (_input, init) => {
|
||||
requests.push(new Headers(init?.headers))
|
||||
return new Response(null, { status: 200 })
|
||||
})
|
||||
const options = await loader(hooks)(async () => oauth, provider)
|
||||
const request = customFetch(options)
|
||||
|
||||
await request("https://test-resource.openai.azure.com/openai/v1/responses", {
|
||||
headers: { "api-key": OAUTH_DUMMY_KEY, "x-keep": "yes" },
|
||||
})
|
||||
await request("https://test-resource.services.ai.azure.com/models/chat/completions", {
|
||||
headers: { Authorization: `Bearer ${OAUTH_DUMMY_KEY}` },
|
||||
})
|
||||
await request("https://test-resource.services.ai.azure.com/anthropic/v1/messages", {
|
||||
headers: { "x-api-key": OAUTH_DUMMY_KEY },
|
||||
})
|
||||
|
||||
expect(scopes).toEqual(["https://cognitiveservices.azure.com/.default", "https://ai.azure.com/.default"])
|
||||
expect(requests.map((headers) => headers.get("authorization"))).toEqual([
|
||||
"Bearer https://cognitiveservices.azure.com/.default-token",
|
||||
"Bearer https://cognitiveservices.azure.com/.default-token",
|
||||
"Bearer https://ai.azure.com/.default-token",
|
||||
])
|
||||
expect(requests[0].get("api-key")).toBeNull()
|
||||
expect(requests[0].get("x-keep")).toBe("yes")
|
||||
expect(requests[2].get("x-api-key")).toBeNull()
|
||||
expect(requests.every((headers) => headers.get("user-agent")?.startsWith("opencode/"))).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,47 +0,0 @@
|
||||
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)
|
||||
})
|
||||
})
|
||||
@@ -1,13 +1,9 @@
|
||||
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"
|
||||
@@ -135,69 +131,6 @@ 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 })
|
||||
@@ -216,73 +149,6 @@ 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 }
|
||||
@@ -327,9 +193,6 @@ 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",
|
||||
@@ -344,7 +207,7 @@ describe("plugin.codex", () => {
|
||||
resolveRefresh = resolve
|
||||
})
|
||||
let refreshRequests = 0
|
||||
const apiRequests: { authorization: string | null; accountId: string | null; residency: string | null }[] = []
|
||||
const apiRequests: { authorization: string | null; accountId: string | null }[] = []
|
||||
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
@@ -356,7 +219,7 @@ describe("plugin.codex", () => {
|
||||
await refreshReady
|
||||
return Response.json({
|
||||
id_token: createTestJwt({ chatgpt_account_id: "acc-123" }),
|
||||
access_token: refreshedAccess,
|
||||
access_token: "access-new",
|
||||
refresh_token: "refresh-new",
|
||||
expires_in: 3600,
|
||||
})
|
||||
@@ -366,7 +229,6 @@ 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 })
|
||||
}
|
||||
@@ -419,11 +281,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(refreshedAccess)
|
||||
expect(authUpdates[0]?.body.access).toBe("access-new")
|
||||
expect(authUpdates[0]?.body.accountId).toBe("acc-123")
|
||||
expect(apiRequests).toEqual([
|
||||
{ authorization: `Bearer ${refreshedAccess}`, accountId: "acc-123", residency: "eu" },
|
||||
{ authorization: `Bearer ${refreshedAccess}`, accountId: "acc-123", residency: "eu" },
|
||||
{ authorization: "Bearer access-new", accountId: "acc-123" },
|
||||
{ authorization: "Bearer access-new", accountId: "acc-123" },
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -435,29 +297,3 @@ 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,18 +17,13 @@ describe("plugin.openai.ws", () => {
|
||||
|
||||
const socket = await OpenAIWebSocket.connectResponsesWebSocket({
|
||||
url: server.wsUrl,
|
||||
headers: {
|
||||
authorization: "Bearer test",
|
||||
"content-length": "123",
|
||||
"x-openai-internal-codex-residency": "eu",
|
||||
},
|
||||
headers: { authorization: "Bearer test", "content-length": "123" },
|
||||
})
|
||||
|
||||
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()
|
||||
})
|
||||
@@ -237,26 +232,6 @@ 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,24 +0,0 @@
|
||||
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,32 +1909,6 @@ 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,19 +3215,6 @@ 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,22 +1782,6 @@ 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,
|
||||
|
||||
@@ -27,7 +27,6 @@ import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
|
||||
import { ProviderError } from "@/provider/error"
|
||||
|
||||
type ConfigModel = NonNullable<NonNullable<ConfigV1.Info["provider"]>[string]["models"]>[string]
|
||||
|
||||
@@ -833,70 +832,6 @@ describe("session.llm.stream", () => {
|
||||
},
|
||||
)
|
||||
|
||||
it.instance(
|
||||
"surfaces network_error finish reasons as retryable stream failures",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const fixture = loadFixture(vivgridFixture.providerID, vivgridFixture.modelID)
|
||||
const request = waitRequest(
|
||||
"/chat/completions",
|
||||
createEventResponse(
|
||||
[
|
||||
{
|
||||
id: "chatcmpl-network-error",
|
||||
object: "chat.completion.chunk",
|
||||
choices: [{ index: 0, delta: { role: "assistant", content: "" }, finish_reason: "network_error" }],
|
||||
},
|
||||
],
|
||||
true,
|
||||
),
|
||||
)
|
||||
const resolved = yield* Provider.use.getModel(
|
||||
ProviderV2.ID.make(vivgridFixture.providerID),
|
||||
ModelV2.ID.make(fixture.model.id),
|
||||
)
|
||||
const sessionID = SessionID.make("session-test-network-error")
|
||||
const agent = {
|
||||
name: "test",
|
||||
mode: "primary",
|
||||
options: {},
|
||||
permission: [{ permission: "*", pattern: "*", action: "allow" }],
|
||||
} satisfies Agent.Info
|
||||
const user = {
|
||||
id: MessageID.make("msg_user-network-error"),
|
||||
sessionID,
|
||||
role: "user",
|
||||
time: { created: Date.now() },
|
||||
agent: agent.name,
|
||||
model: { providerID: ProviderV2.ID.make(vivgridFixture.providerID), modelID: resolved.id },
|
||||
} satisfies SessionV1.User
|
||||
|
||||
const error = yield* drain({
|
||||
user,
|
||||
sessionID,
|
||||
model: resolved,
|
||||
agent,
|
||||
system: ["You are a helpful assistant."],
|
||||
messages: [{ role: "user", content: "Hello" }],
|
||||
tools: {},
|
||||
}).pipe(Effect.flip)
|
||||
yield* Effect.promise(() => request)
|
||||
|
||||
if (!(error instanceof ProviderError.ResponseStreamError)) throw error
|
||||
expect(error.message).toBe("Provider finish_reason: network_error")
|
||||
}),
|
||||
{
|
||||
config: () => ({
|
||||
enabled_providers: [vivgridFixture.providerID],
|
||||
provider: {
|
||||
[vivgridFixture.providerID]: {
|
||||
options: { apiKey: "test-key", baseURL: `${state.server!.url.origin}/v1` },
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
const cerebrasFixture = { providerID: "cerebras", modelID: "gpt-oss-120b" }
|
||||
it.instance(
|
||||
"replays Cerebras assistant reasoning using the provider-supported field",
|
||||
|
||||
@@ -651,63 +651,6 @@ it.live("session.processor effect tests retry OpenAI-compatible midstream server
|
||||
),
|
||||
)
|
||||
|
||||
it.live("session.processor effect tests retry network_error finish reasons", () =>
|
||||
provideTmpdirServer(
|
||||
({ dir, llm }) =>
|
||||
Effect.gen(function* () {
|
||||
const { processors, session, provider } = yield* boot()
|
||||
|
||||
yield* llm.push(
|
||||
raw({
|
||||
chunks: [
|
||||
{
|
||||
id: "chatcmpl-network-error",
|
||||
object: "chat.completion.chunk",
|
||||
choices: [{ index: 0, delta: { role: "assistant", content: "" }, finish_reason: "network_error" }],
|
||||
},
|
||||
],
|
||||
}),
|
||||
)
|
||||
yield* llm.text("after retry")
|
||||
|
||||
const chat = yield* session.create({})
|
||||
const parent = yield* user(chat.id, "retry network error")
|
||||
const msg = yield* assistant(chat.id, parent.id, path.resolve(dir))
|
||||
const mdl = yield* provider.getModel(ref.providerID, ref.modelID)
|
||||
const handle = yield* processors.create({
|
||||
assistantMessage: msg,
|
||||
sessionID: chat.id,
|
||||
model: mdl,
|
||||
})
|
||||
|
||||
const value = yield* handle.process({
|
||||
user: {
|
||||
id: parent.id,
|
||||
sessionID: chat.id,
|
||||
role: "user",
|
||||
time: parent.time,
|
||||
agent: parent.agent,
|
||||
model: { providerID: ref.providerID, modelID: ref.modelID },
|
||||
} satisfies SessionV1.User,
|
||||
sessionID: chat.id,
|
||||
model: mdl,
|
||||
agent: agent(),
|
||||
system: [],
|
||||
messages: [{ role: "user", content: "retry network error" }],
|
||||
tools: {},
|
||||
})
|
||||
|
||||
const parts = yield* MessageV2.parts(msg.id)
|
||||
|
||||
expect(value).toBe("continue")
|
||||
expect(yield* llm.calls).toBe(2)
|
||||
expect(parts.some((part) => part.type === "text" && part.text === "after retry")).toBe(true)
|
||||
expect(handle.message.error).toBeUndefined()
|
||||
}),
|
||||
{ config: (url) => providerCfg(url) },
|
||||
),
|
||||
)
|
||||
|
||||
it.live("session.processor effect tests publish retry status updates", () =>
|
||||
provideTmpdirServer(
|
||||
({ dir, llm }) =>
|
||||
|
||||
@@ -850,34 +850,6 @@ it.instance("loop continues when finish is tool-calls", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("loop continues when finish is unknown", () =>
|
||||
Effect.gen(function* () {
|
||||
const { llm } = yield* useServerConfig(providerCfg)
|
||||
const prompt = yield* SessionPrompt.Service
|
||||
const sessions = yield* Session.Service
|
||||
const session = yield* sessions.create({
|
||||
title: "Pinned",
|
||||
permission: [{ permission: "*", pattern: "*", action: "allow" }],
|
||||
})
|
||||
yield* prompt.prompt({
|
||||
sessionID: session.id,
|
||||
agent: "build",
|
||||
noReply: true,
|
||||
parts: [{ type: "text", text: "hello" }],
|
||||
})
|
||||
yield* llm.push(reply())
|
||||
yield* llm.text("second")
|
||||
|
||||
const result = yield* prompt.loop({ sessionID: session.id })
|
||||
expect(yield* llm.calls).toBe(2)
|
||||
expect(result.info.role).toBe("assistant")
|
||||
if (result.info.role === "assistant") {
|
||||
expect(result.parts.some((part) => part.type === "text" && part.text === "second")).toBe(true)
|
||||
expect(result.info.finish).toBe("stop")
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("glob tool keeps instance context during prompt runs", () =>
|
||||
Effect.gen(function* () {
|
||||
const { dir, llm } = yield* useServerConfig(providerCfg)
|
||||
|
||||
@@ -207,9 +207,6 @@ describe("session.retry.retryable", () => {
|
||||
"provider-returned-error",
|
||||
"terminated",
|
||||
"fetch failed",
|
||||
"network error",
|
||||
"network-error",
|
||||
"network_error",
|
||||
"connection refused",
|
||||
"connect ECONNREFUSED",
|
||||
"request ETIMEDOUT",
|
||||
@@ -218,9 +215,6 @@ describe("session.retry.retryable", () => {
|
||||
"response timed out",
|
||||
"Please retry your request",
|
||||
"try your request again",
|
||||
"Please try again in a few minutes",
|
||||
"The model is currently at capacity due to high demand",
|
||||
"The service is temporarily at capacity",
|
||||
"upstream returned status 524",
|
||||
])("retries matching API error text: %s", (message) => {
|
||||
expect(SessionRetry.retryable(wrap(message), retryProvider)).toEqual({ message })
|
||||
|
||||
@@ -3,7 +3,7 @@ import { SessionV1 } from "@opencode-ai/core/v1/session"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { SessionProjector } from "@opencode-ai/core/session/projector"
|
||||
import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect"
|
||||
import { Deferred, Effect, Exit, Fiber, Layer } from "effect"
|
||||
import { Agent } from "../../src/agent/agent"
|
||||
import { BackgroundJob } from "@/background/job"
|
||||
import { EventV2Bridge } from "@/event-v2-bridge"
|
||||
@@ -96,29 +96,19 @@ const seed = Effect.fn("TaskToolTest.seed")(function* (title = "Pinned") {
|
||||
return { chat, assistant }
|
||||
})
|
||||
|
||||
function stubOps(opts?: {
|
||||
onPrompt?: (input: SessionPrompt.PromptInput) => void
|
||||
text?: string
|
||||
error?: NonNullable<SessionV1.Assistant["error"]>
|
||||
toolError?: string
|
||||
}): TaskPromptOps {
|
||||
function stubOps(opts?: { onPrompt?: (input: SessionPrompt.PromptInput) => void; text?: string }): TaskPromptOps {
|
||||
return {
|
||||
cancel: () => Effect.void,
|
||||
resolvePromptParts: (template) => Effect.succeed([{ type: "text" as const, text: template }]),
|
||||
prompt: (input) =>
|
||||
Effect.sync(() => {
|
||||
opts?.onPrompt?.(input)
|
||||
return reply(input, opts?.text ?? "done", opts?.error, opts?.toolError)
|
||||
return reply(input, opts?.text ?? "done")
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
function reply(
|
||||
input: SessionPrompt.PromptInput,
|
||||
text: string,
|
||||
error?: NonNullable<SessionV1.Assistant["error"]>,
|
||||
toolError?: string,
|
||||
): SessionV1.WithParts {
|
||||
function reply(input: SessionPrompt.PromptInput, text: string): SessionV1.WithParts {
|
||||
const id = MessageID.ascending()
|
||||
return {
|
||||
info: {
|
||||
@@ -135,7 +125,6 @@ function reply(
|
||||
providerID: input.model?.providerID ?? ref.providerID,
|
||||
time: { created: Date.now() },
|
||||
finish: "stop",
|
||||
error,
|
||||
},
|
||||
parts: [
|
||||
{
|
||||
@@ -145,24 +134,6 @@ function reply(
|
||||
type: "text",
|
||||
text,
|
||||
},
|
||||
...(toolError
|
||||
? [
|
||||
{
|
||||
id: PartID.ascending(),
|
||||
messageID: id,
|
||||
sessionID: input.sessionID,
|
||||
type: "tool" as const,
|
||||
tool: "read",
|
||||
callID: "call-1",
|
||||
state: {
|
||||
status: "error" as const,
|
||||
input: { filePath: "/external" },
|
||||
error: toolError,
|
||||
time: { start: Date.now(), end: Date.now() },
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -284,93 +255,6 @@ describe("tool.task", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("execute surfaces child errors with a resumable task_id", () =>
|
||||
Effect.gen(function* () {
|
||||
const sessions = yield* Session.Service
|
||||
const { chat, assistant } = yield* seed()
|
||||
const tool = yield* TaskTool
|
||||
const def = yield* tool.init()
|
||||
|
||||
const exit = yield* def
|
||||
.execute(
|
||||
{
|
||||
description: "inspect bug",
|
||||
prompt: "look into the cache key path",
|
||||
subagent_type: "general",
|
||||
},
|
||||
{
|
||||
sessionID: chat.id,
|
||||
messageID: assistant.id,
|
||||
agent: "build",
|
||||
abort: new AbortController().signal,
|
||||
extra: {
|
||||
promptOps: stubOps({
|
||||
text: "",
|
||||
error: new SessionV1.APIError({ message: "Network connection lost", isRetryable: false }).toObject(),
|
||||
}),
|
||||
},
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask: () => Effect.void,
|
||||
},
|
||||
)
|
||||
.pipe(Effect.exit)
|
||||
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
if (Exit.isSuccess(exit)) throw new Error("expected task failure")
|
||||
const child = (yield* sessions.children(chat.id))[0]
|
||||
expect(child).toBeDefined()
|
||||
const failure = Cause.squash(exit.cause)
|
||||
expect(failure).toBeInstanceOf(Error)
|
||||
if (!(failure instanceof Error)) throw new Error("expected Error defect")
|
||||
expect(failure.message).toBe(`Subagent failed (task_id: ${child?.id}): Network connection lost`)
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("execute surfaces terminal child tool errors with a resumable task_id", () =>
|
||||
Effect.gen(function* () {
|
||||
const sessions = yield* Session.Service
|
||||
const { chat, assistant } = yield* seed()
|
||||
const tool = yield* TaskTool
|
||||
const def = yield* tool.init()
|
||||
|
||||
const exit = yield* def
|
||||
.execute(
|
||||
{
|
||||
description: "inspect external directory",
|
||||
prompt: "read the external directory",
|
||||
subagent_type: "general",
|
||||
},
|
||||
{
|
||||
sessionID: chat.id,
|
||||
messageID: assistant.id,
|
||||
agent: "build",
|
||||
abort: new AbortController().signal,
|
||||
extra: {
|
||||
promptOps: stubOps({
|
||||
text: "I will inspect the directory.",
|
||||
toolError: "The user rejected permission to use this specific tool call.",
|
||||
}),
|
||||
},
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask: () => Effect.void,
|
||||
},
|
||||
)
|
||||
.pipe(Effect.exit)
|
||||
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
if (Exit.isSuccess(exit)) throw new Error("expected task failure")
|
||||
const child = (yield* sessions.children(chat.id))[0]
|
||||
const failure = Cause.squash(exit.cause)
|
||||
expect(failure).toBeInstanceOf(Error)
|
||||
if (!(failure instanceof Error)) throw new Error("expected Error defect")
|
||||
expect(failure.message).toBe(
|
||||
`Subagent failed (task_id: ${child?.id}): The user rejected permission to use this specific tool call.`,
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("execute asks by default and skips checks when bypassed", () =>
|
||||
Effect.gen(function* () {
|
||||
const { chat, assistant } = yield* seed()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/server",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/session-ui",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/stats-app",
|
||||
"version": "1.18.20",
|
||||
"version": "1.18.18",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -20,7 +20,7 @@ import { dict as zht } from "./i18n/zht"
|
||||
const en = {
|
||||
"app.title": "AI Model Usage Rankings | OpenCode Data",
|
||||
"app.description":
|
||||
"Explore OpenCode usage across AI models, including token volume, rankings, market share, token pricing, session cost, cache ratio, and geo breakdowns.",
|
||||
"Explore OpenCode Go usage across AI models, including token volume, rankings, market share, token pricing, session cost, cache ratio, and geo breakdowns.",
|
||||
"app.unfurlAlt": "OpenCode Data wordmark on a dark patterned background",
|
||||
"nav.dataHome": "Data Home",
|
||||
"nav.topModels": "Top Models",
|
||||
@@ -104,7 +104,7 @@ const en = {
|
||||
"home.loadingTitle": "Loading data",
|
||||
"home.loadingDescription": "Reading model aggregates.",
|
||||
"home.usageTitle": "Usage",
|
||||
"home.topModelsDescription": "Usage of models across OpenCode.",
|
||||
"home.topModelsDescription": "Usage of models across OpenCode Go.",
|
||||
"home.noUsageTitle": "No usage data",
|
||||
"home.noUsageDescription": "No model rows matched this product and range.",
|
||||
"home.noLeaderboardTitle": "No leaderboard data",
|
||||
@@ -116,7 +116,7 @@ const en = {
|
||||
"home.modelUsers": "model users",
|
||||
"home.new": "New",
|
||||
"home.uniqueUsersTitle": "Unique Users",
|
||||
"home.uniqueUsersDescription": "Daily unique OpenCode users by model.",
|
||||
"home.uniqueUsersDescription": "Daily unique OpenCode Go users by model.",
|
||||
"home.noUserDataTitle": "No user data",
|
||||
"home.noUserDataDescription": "No user-bearing model rows matched this window.",
|
||||
"home.uniqueUsersChart": "Stacked unique user chart by model",
|
||||
@@ -148,7 +148,7 @@ const en = {
|
||||
"home.costSession": "Cost/Session",
|
||||
"lab.title": "{{lab}} AI Model Usage & Rankings | OpenCode Data",
|
||||
"lab.description":
|
||||
"Compare {{lab}} models used in OpenCode, including token usage, model rankings, context windows, release dates, costs, and model-specific data.",
|
||||
"Compare {{lab}} models used in OpenCode Go, including token usage, model rankings, context windows, release dates, costs, and model-specific data.",
|
||||
"lab.loadingTitle": "Model Lab",
|
||||
"lab.loadingDescription": "Reading model availability and recent OpenCode usage.",
|
||||
"lab.notFound": "No models matched this lab.",
|
||||
@@ -174,7 +174,7 @@ const en = {
|
||||
"lab.share": "Share",
|
||||
"model.title": "{{model}} Usage, Cost & Rank | OpenCode Data",
|
||||
"model.description":
|
||||
"View {{model}} OpenCode usage data, including token volume, weekly rank, token mix, costs, cache ratio, sessions, geo breakdowns, and peer models.",
|
||||
"View {{model}} OpenCode Go usage data, including token volume, weekly rank, token mix, costs, cache ratio, sessions, geo breakdowns, and peer models.",
|
||||
"model.loadingTitle": "Model Data",
|
||||
"model.loadingDescription": "Reading model aggregates.",
|
||||
"model.loadingProfile": "Reading the model profile.",
|
||||
@@ -183,15 +183,15 @@ const en = {
|
||||
"model.noMatched": "No model facts or usage rows matched {{id}}.",
|
||||
"model.fallback": "Model",
|
||||
"model.catalogFallback":
|
||||
"Model facts from the shared model index. OpenCode usage appears once this model has activity.",
|
||||
"model.unranked": "Unranked across last week's OpenCode usage",
|
||||
"model.ranked": "Ranked #{{rank}} across last week's OpenCode usage",
|
||||
"Model facts from the shared model index. OpenCode Go usage appears once this model has activity.",
|
||||
"model.unranked": "Unranked across last week's OpenCode Go usage",
|
||||
"model.ranked": "Ranked #{{rank}} across last week's OpenCode Go usage",
|
||||
"model.observedVolume": "with {{share}} of observed 2M volume.",
|
||||
"model.weights": "Model weights: {{label}}",
|
||||
"model.rank": "7D Rank",
|
||||
"model.profile": "Model Profile",
|
||||
"model.listed": "Listed",
|
||||
"model.noCurrentUsage": "No OpenCode usage in the current data window.",
|
||||
"model.noCurrentUsage": "No OpenCode Go usage in the current data window.",
|
||||
"model.facts": "Model facts",
|
||||
"model.context": "Context",
|
||||
"model.output": "Output",
|
||||
@@ -200,7 +200,7 @@ const en = {
|
||||
"model.inputs": "Inputs",
|
||||
"model.overviewDescription": "Recent tokens, unique users, and market position.",
|
||||
"model.noSummaryTitle": "No usage summary",
|
||||
"model.noSummaryDescription": "This model has no OpenCode usage rows yet.",
|
||||
"model.noSummaryDescription": "This model has no OpenCode Go usage rows yet.",
|
||||
"model.tokens": "Tokens",
|
||||
"model.uniqueUsers": "Unique Users",
|
||||
"model.lastTwoMonths": "last two months",
|
||||
@@ -236,11 +236,11 @@ const en = {
|
||||
"model.averageTokensSession": "Average tokens / session",
|
||||
"model.cacheRatio": "Cache Ratio",
|
||||
"model.inputTokens": "input tokens",
|
||||
"model.geoDescription": "OpenCode model tokens used by country.",
|
||||
"model.geoDescription": "OpenCode Go model tokens used by country.",
|
||||
"model.noGeoTitle": "No geo data",
|
||||
"model.noGeoDescription": "No OpenCode geo rows matched this model.",
|
||||
"model.noGeoDescription": "No OpenCode Go geo rows matched this model.",
|
||||
"model.worldMap": "World map of model token usage by country",
|
||||
"model.peersDescription": "Nearby models by recent OpenCode token volume.",
|
||||
"model.peersDescription": "Nearby models by recent OpenCode Go token volume.",
|
||||
"model.noPeersTitle": "No peers",
|
||||
"model.noPeersDescription": "Peer rankings appear after usage lands.",
|
||||
"model.noUsageLastWeek": "No usage last week",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user