Merge remote-tracking branch 'origin/dev' into nxl/runtime-aware-search-service
# Conflicts: # packages/opencode/src/tool/read.ts
This commit is contained in:
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -83,7 +83,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -117,7 +117,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -144,7 +144,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "3.0.64",
|
||||
"@ai-sdk/openai": "3.0.48",
|
||||
@@ -168,7 +168,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -192,7 +192,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
@@ -225,7 +225,7 @@
|
||||
},
|
||||
"packages/desktop-electron": {
|
||||
"name": "@opencode-ai/desktop-electron",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -269,7 +269,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@opencode-ai/shared": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
@@ -298,7 +298,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -314,7 +314,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -460,7 +460,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"effect": "catalog:",
|
||||
@@ -495,7 +495,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"cross-spawn": "catalog:",
|
||||
},
|
||||
@@ -510,7 +510,7 @@
|
||||
},
|
||||
"packages/shared": {
|
||||
"name": "@opencode-ai/shared",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -534,7 +534,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -569,7 +569,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -618,7 +618,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
|
||||
Generated
+3
-3
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1773909469,
|
||||
"narHash": "sha256-vglVrLfHjFIzIdV9A27Ugul6rh3I1qHbbitGW7dk420=",
|
||||
"lastModified": 1776683584,
|
||||
"narHash": "sha256-NuTLMrr10Tng72hurYG8jYQ4XKK8wnpJmOGcPiis96g=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7149c06513f335be57f26fcbbbe34afda923882b",
|
||||
"rev": "9dd5558b06dbdacbf635a3dd36dce1b1a7ee3a89",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -295,6 +295,19 @@ function createGlobalSync() {
|
||||
const event = e.details
|
||||
const recent = bootingRoot || Date.now() - bootedAt < 1500
|
||||
|
||||
if (event.type === "session.error") {
|
||||
const error = event.properties.error
|
||||
if (error?.name !== "MessageAbortedError") {
|
||||
console.error("[global-sync] session error", {
|
||||
scope: directory === "global" ? "global" : "workspace",
|
||||
directory: directory === "global" ? undefined : directory,
|
||||
project: directory === "global" ? undefined : getFilename(directory),
|
||||
sessionID: event.properties.sessionID,
|
||||
error,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (directory === "global") {
|
||||
applyGlobalEvent({
|
||||
event,
|
||||
|
||||
@@ -259,7 +259,7 @@ export function MessageTimeline(props: {
|
||||
if (!id) return idle
|
||||
return sync.data.session_status[id] ?? idle
|
||||
})
|
||||
const working = createMemo(() => !!pending() || sessionStatus().type !== "idle")
|
||||
const working = createMemo(() => sessionStatus().type !== "idle")
|
||||
const tint = createMemo(() => messageAgentColor(sessionMessages(), sync.data.agent))
|
||||
|
||||
const [timeoutDone, setTimeoutDone] = createSignal(true)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop-electron",
|
||||
"private": true,
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://opencode.ai",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"private": true,
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The open source coding agent."
|
||||
version = "1.14.20"
|
||||
version = "1.14.21"
|
||||
schema_version = 1
|
||||
authors = ["Anomaly"]
|
||||
repository = "https://github.com/anomalyco/opencode"
|
||||
@@ -11,26 +11,26 @@ name = "OpenCode"
|
||||
icon = "./icons/opencode.svg"
|
||||
|
||||
[agent_servers.opencode.targets.darwin-aarch64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.20/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.21/opencode-darwin-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.darwin-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.20/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.21/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.20/opencode-linux-arm64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.21/opencode-linux-arm64.tar.gz"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.20/opencode-linux-x64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.21/opencode-linux-x64.tar.gz"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.windows-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.20/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.14.21/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -23,8 +23,7 @@ const DiagnosticsCommand = cmd({
|
||||
const out = await AppRuntime.runPromise(
|
||||
LSP.Service.use((lsp) =>
|
||||
Effect.gen(function* () {
|
||||
yield* lsp.touchFile(args.file, true)
|
||||
yield* Effect.sleep(1000)
|
||||
yield* lsp.touchFile(args.file, "full")
|
||||
return yield* lsp.diagnostics()
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -14,6 +14,16 @@ import { withTimeout } from "../util/timeout"
|
||||
import { Filesystem } from "../util"
|
||||
|
||||
const DIAGNOSTICS_DEBOUNCE_MS = 150
|
||||
const DIAGNOSTICS_DOCUMENT_WAIT_TIMEOUT_MS = 5_000
|
||||
const DIAGNOSTICS_FULL_WAIT_TIMEOUT_MS = 10_000
|
||||
const DIAGNOSTICS_REQUEST_TIMEOUT_MS = 3_000
|
||||
|
||||
const INITIALIZE_TIMEOUT_MS = 45_000
|
||||
|
||||
// LSP spec constants
|
||||
const FILE_CHANGE_CREATED = 1
|
||||
const FILE_CHANGE_CHANGED = 2
|
||||
const TEXT_DOCUMENT_SYNC_INCREMENTAL = 2
|
||||
|
||||
const log = Log.create({ service: "lsp.client" })
|
||||
|
||||
@@ -38,48 +48,194 @@ export const Event = {
|
||||
),
|
||||
}
|
||||
|
||||
type DocumentDiagnosticReport = {
|
||||
items?: Diagnostic[]
|
||||
relatedDocuments?: Record<string, DocumentDiagnosticReport>
|
||||
}
|
||||
|
||||
type WorkspaceDiagnosticReport = {
|
||||
items?: {
|
||||
uri?: string
|
||||
items?: Diagnostic[]
|
||||
}[]
|
||||
}
|
||||
|
||||
type DiagnosticRequestResult = {
|
||||
handled: boolean
|
||||
matched: boolean
|
||||
byFile: Map<string, Diagnostic[]>
|
||||
}
|
||||
|
||||
type CapabilityRegistration = {
|
||||
id: string
|
||||
method: string
|
||||
registerOptions?: {
|
||||
identifier?: string
|
||||
workspaceDiagnostics?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
type ServerCapabilities = {
|
||||
textDocumentSync?:
|
||||
| number
|
||||
| {
|
||||
change?: number
|
||||
}
|
||||
diagnosticProvider?: unknown
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
function getFilePath(uri: string) {
|
||||
if (!uri.startsWith("file://")) return
|
||||
return Filesystem.normalizePath(fileURLToPath(uri))
|
||||
}
|
||||
|
||||
function getSyncKind(capabilities?: ServerCapabilities) {
|
||||
if (!capabilities) return
|
||||
const sync = capabilities.textDocumentSync
|
||||
if (typeof sync === "number") return sync
|
||||
return sync?.change
|
||||
}
|
||||
|
||||
function endPosition(text: string) {
|
||||
const lines = text.split(/\r\n|\r|\n/)
|
||||
return {
|
||||
line: lines.length - 1,
|
||||
character: lines.at(-1)?.length ?? 0,
|
||||
}
|
||||
}
|
||||
|
||||
function dedupeDiagnostics(items: Diagnostic[]) {
|
||||
const seen = new Set<string>()
|
||||
return items.filter((item) => {
|
||||
const key = JSON.stringify({
|
||||
code: item.code,
|
||||
severity: item.severity,
|
||||
message: item.message,
|
||||
source: item.source,
|
||||
range: item.range,
|
||||
})
|
||||
if (seen.has(key)) return false
|
||||
seen.add(key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
function configurationValue(settings: unknown, section?: string) {
|
||||
if (!section) return settings ?? null
|
||||
const result = section.split(".").reduce<unknown>((acc, key) => {
|
||||
if (!acc || typeof acc !== "object" || !(key in acc)) return undefined
|
||||
return (acc as Record<string, unknown>)[key]
|
||||
}, settings)
|
||||
return result ?? null
|
||||
}
|
||||
|
||||
// TypeScript's built-in LSP pushes diagnostics aggressively on first open.
|
||||
// We seed the push cache on the very first publish so waitForFreshPush can
|
||||
// resolve immediately instead of waiting for a second debounced push.
|
||||
function shouldSeedDiagnosticsOnFirstPush(serverID: string) {
|
||||
return serverID === "typescript"
|
||||
}
|
||||
|
||||
export async function create(input: { serverID: string; server: LSPServer.Handle; root: string; directory: string }) {
|
||||
const l = log.clone().tag("serverID", input.serverID)
|
||||
l.info("starting client")
|
||||
const logger = log.clone().tag("serverID", input.serverID)
|
||||
logger.info("starting client")
|
||||
|
||||
const connection = createMessageConnection(
|
||||
new StreamMessageReader(input.server.process.stdout as any),
|
||||
new StreamMessageWriter(input.server.process.stdin as any),
|
||||
)
|
||||
// Server stderr can contain both real errors and routine informational logs,
|
||||
// which is normal stderr practice for some tools. Keep the raw stream at
|
||||
// debug so users can opt in with --print-logs --log-level DEBUG without
|
||||
// polluting normal logs.
|
||||
input.server.process.stderr?.on("data", (data: Buffer) => {
|
||||
const text = data.toString().trim()
|
||||
if (text) logger.debug("server stderr", { text: text.slice(0, 1000) })
|
||||
})
|
||||
|
||||
// --- Connection state ---
|
||||
|
||||
const pushDiagnostics = new Map<string, Diagnostic[]>()
|
||||
const pullDiagnostics = new Map<string, Diagnostic[]>()
|
||||
const published = new Map<string, { at: number; version?: number }>()
|
||||
const diagnosticRegistrations = new Map<string, CapabilityRegistration>()
|
||||
const registrationListeners = new Set<() => void>()
|
||||
const mergedDiagnostics = (filePath: string) =>
|
||||
dedupeDiagnostics([...(pushDiagnostics.get(filePath) ?? []), ...(pullDiagnostics.get(filePath) ?? [])])
|
||||
const updatePushDiagnostics = (filePath: string, next: Diagnostic[]) => {
|
||||
pushDiagnostics.set(filePath, next)
|
||||
Bus.publish(Event.Diagnostics, { path: filePath, serverID: input.serverID })
|
||||
}
|
||||
const updatePullDiagnostics = (filePath: string, next: Diagnostic[]) => {
|
||||
pullDiagnostics.set(filePath, next)
|
||||
}
|
||||
const emitRegistrationChange = () => {
|
||||
for (const listener of [...registrationListeners]) listener()
|
||||
}
|
||||
|
||||
// --- LSP connection handlers ---
|
||||
|
||||
const diagnostics = new Map<string, Diagnostic[]>()
|
||||
connection.onNotification("textDocument/publishDiagnostics", (params) => {
|
||||
const filePath = Filesystem.normalizePath(fileURLToPath(params.uri))
|
||||
l.info("textDocument/publishDiagnostics", {
|
||||
const filePath = getFilePath(params.uri)
|
||||
if (!filePath) return
|
||||
logger.info("textDocument/publishDiagnostics", {
|
||||
path: filePath,
|
||||
count: params.diagnostics.length,
|
||||
version: params.version,
|
||||
})
|
||||
const exists = diagnostics.has(filePath)
|
||||
diagnostics.set(filePath, params.diagnostics)
|
||||
if (!exists && input.serverID === "typescript") return
|
||||
Bus.publish(Event.Diagnostics, { path: filePath, serverID: input.serverID })
|
||||
published.set(filePath, {
|
||||
at: Date.now(),
|
||||
version: typeof params.version === "number" ? params.version : undefined,
|
||||
})
|
||||
if (shouldSeedDiagnosticsOnFirstPush(input.serverID) && !pushDiagnostics.has(filePath)) {
|
||||
pushDiagnostics.set(filePath, params.diagnostics)
|
||||
return
|
||||
}
|
||||
updatePushDiagnostics(filePath, params.diagnostics)
|
||||
})
|
||||
connection.onRequest("window/workDoneProgress/create", (params) => {
|
||||
l.info("window/workDoneProgress/create", params)
|
||||
logger.info("window/workDoneProgress/create", params)
|
||||
return null
|
||||
})
|
||||
connection.onRequest("workspace/configuration", async () => {
|
||||
// Return server initialization options
|
||||
return [input.server.initialization ?? {}]
|
||||
connection.onRequest("workspace/configuration", async (params) => {
|
||||
const items = (params as { items?: { section?: string }[] }).items ?? []
|
||||
return items.map((item) => configurationValue(input.server.initialization, item.section))
|
||||
})
|
||||
connection.onRequest("client/registerCapability", async (params) => {
|
||||
const registrations = (params as { registrations?: CapabilityRegistration[] }).registrations ?? []
|
||||
let changed = false
|
||||
for (const registration of registrations) {
|
||||
if (registration.method !== "textDocument/diagnostic") continue
|
||||
diagnosticRegistrations.set(registration.id, registration)
|
||||
changed = true
|
||||
}
|
||||
if (changed) emitRegistrationChange()
|
||||
})
|
||||
connection.onRequest("client/unregisterCapability", async (params) => {
|
||||
const registrations = (params as { unregisterations?: { id: string; method: string }[] }).unregisterations ?? []
|
||||
let changed = false
|
||||
for (const registration of registrations) {
|
||||
if (registration.method !== "textDocument/diagnostic") continue
|
||||
diagnosticRegistrations.delete(registration.id)
|
||||
changed = true
|
||||
}
|
||||
if (changed) emitRegistrationChange()
|
||||
})
|
||||
connection.onRequest("client/registerCapability", async () => {})
|
||||
connection.onRequest("client/unregisterCapability", async () => {})
|
||||
connection.onRequest("workspace/workspaceFolders", async () => [
|
||||
{
|
||||
name: "workspace",
|
||||
uri: pathToFileURL(input.root).href,
|
||||
},
|
||||
])
|
||||
connection.onRequest("workspace/diagnostic/refresh", async () => null)
|
||||
connection.listen()
|
||||
|
||||
l.info("sending initialize")
|
||||
await withTimeout(
|
||||
connection.sendRequest("initialize", {
|
||||
// --- Initialize handshake ---
|
||||
|
||||
logger.info("sending initialize")
|
||||
const initialized = await withTimeout(
|
||||
connection.sendRequest<{ capabilities?: ServerCapabilities }>("initialize", {
|
||||
rootUri: pathToFileURL(input.root).href,
|
||||
processId: input.server.process.pid,
|
||||
workspaceFolders: [
|
||||
@@ -100,21 +256,28 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
|
||||
didChangeWatchedFiles: {
|
||||
dynamicRegistration: true,
|
||||
},
|
||||
diagnostics: {
|
||||
refreshSupport: false,
|
||||
},
|
||||
},
|
||||
textDocument: {
|
||||
synchronization: {
|
||||
didOpen: true,
|
||||
didChange: true,
|
||||
},
|
||||
diagnostic: {
|
||||
dynamicRegistration: true,
|
||||
relatedDocumentSupport: true,
|
||||
},
|
||||
publishDiagnostics: {
|
||||
versionSupport: true,
|
||||
versionSupport: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
45_000,
|
||||
INITIALIZE_TIMEOUT_MS,
|
||||
).catch((err) => {
|
||||
l.error("initialize error", { error: err })
|
||||
logger.error("initialize error", { error: err })
|
||||
throw new InitializeError(
|
||||
{ serverID: input.serverID },
|
||||
{
|
||||
@@ -123,6 +286,9 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
|
||||
)
|
||||
})
|
||||
|
||||
const syncKind = getSyncKind(initialized.capabilities)
|
||||
const hasStaticPullDiagnostics = Boolean(initialized.capabilities?.diagnosticProvider)
|
||||
|
||||
await connection.sendNotification("initialized", {})
|
||||
|
||||
if (input.server.initialization) {
|
||||
@@ -131,9 +297,280 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
|
||||
})
|
||||
}
|
||||
|
||||
const files: {
|
||||
[path: string]: number
|
||||
} = {}
|
||||
const files: Record<string, { version: number; text: string }> = {}
|
||||
|
||||
// --- Diagnostic helpers ---
|
||||
|
||||
const mergeResults = (filePath: string, results: DiagnosticRequestResult[]) => {
|
||||
const handled = results.some((result) => result.handled)
|
||||
const matched = results.some((result) => result.matched)
|
||||
if (!handled) return { handled: false, matched: false }
|
||||
|
||||
const merged = new Map<string, Diagnostic[]>()
|
||||
for (const result of results) {
|
||||
for (const [target, items] of result.byFile.entries()) {
|
||||
const existing = merged.get(target) ?? []
|
||||
merged.set(target, existing.concat(items))
|
||||
}
|
||||
}
|
||||
|
||||
if (matched && !merged.has(filePath)) merged.set(filePath, [])
|
||||
for (const [target, items] of merged.entries()) {
|
||||
updatePullDiagnostics(target, dedupeDiagnostics(items))
|
||||
}
|
||||
|
||||
return { handled, matched }
|
||||
}
|
||||
|
||||
async function requestDiagnosticReport(filePath: string, identifier?: string): Promise<DiagnosticRequestResult> {
|
||||
const report = await withTimeout(
|
||||
connection.sendRequest<DocumentDiagnosticReport | null>("textDocument/diagnostic", {
|
||||
...(identifier ? { identifier } : {}),
|
||||
textDocument: {
|
||||
uri: pathToFileURL(filePath).href,
|
||||
},
|
||||
}),
|
||||
DIAGNOSTICS_REQUEST_TIMEOUT_MS,
|
||||
).catch(() => null)
|
||||
if (!report) return { handled: false, matched: false, byFile: new Map<string, Diagnostic[]>() }
|
||||
|
||||
const byFile = new Map<string, Diagnostic[]>()
|
||||
const push = (target: string, items: Diagnostic[]) => {
|
||||
const existing = byFile.get(target) ?? []
|
||||
byFile.set(target, existing.concat(items))
|
||||
}
|
||||
|
||||
let handled = false
|
||||
let matched = false
|
||||
if (Array.isArray(report.items)) {
|
||||
push(filePath, report.items)
|
||||
handled = true
|
||||
matched = true
|
||||
}
|
||||
for (const [uri, related] of Object.entries(report.relatedDocuments ?? {})) {
|
||||
const relatedPath = getFilePath(uri)
|
||||
if (!relatedPath || !Array.isArray(related.items)) continue
|
||||
push(relatedPath, related.items)
|
||||
handled = true
|
||||
matched = matched || relatedPath === filePath
|
||||
}
|
||||
|
||||
return { handled, matched, byFile }
|
||||
}
|
||||
|
||||
async function requestWorkspaceDiagnosticReport(
|
||||
filePath: string,
|
||||
identifier?: string,
|
||||
): Promise<DiagnosticRequestResult> {
|
||||
const report = await withTimeout(
|
||||
connection.sendRequest<WorkspaceDiagnosticReport | null>("workspace/diagnostic", {
|
||||
...(identifier ? { identifier } : {}),
|
||||
previousResultIds: [],
|
||||
}),
|
||||
DIAGNOSTICS_REQUEST_TIMEOUT_MS,
|
||||
).catch(() => null)
|
||||
if (!report) return { handled: false, matched: false, byFile: new Map<string, Diagnostic[]>() }
|
||||
|
||||
const byFile = new Map<string, Diagnostic[]>()
|
||||
let matched = false
|
||||
for (const item of report.items ?? []) {
|
||||
const relatedPath = item.uri ? getFilePath(item.uri) : undefined
|
||||
if (!relatedPath || !Array.isArray(item.items)) continue
|
||||
const existing = byFile.get(relatedPath) ?? []
|
||||
byFile.set(relatedPath, existing.concat(item.items))
|
||||
matched = matched || relatedPath === filePath
|
||||
}
|
||||
|
||||
return { handled: true, matched, byFile }
|
||||
}
|
||||
|
||||
function documentPullState() {
|
||||
const documentRegistrations = [...diagnosticRegistrations.values()].filter(
|
||||
(registration) => registration.registerOptions?.workspaceDiagnostics !== true,
|
||||
)
|
||||
return {
|
||||
documentIdentifiers: [
|
||||
...new Set(documentRegistrations.flatMap((registration) => registration.registerOptions?.identifier ?? [])),
|
||||
],
|
||||
supported: hasStaticPullDiagnostics || documentRegistrations.length > 0,
|
||||
}
|
||||
}
|
||||
|
||||
function workspacePullState() {
|
||||
const workspaceRegistrations = [...diagnosticRegistrations.values()].filter(
|
||||
(registration) => registration.registerOptions?.workspaceDiagnostics === true,
|
||||
)
|
||||
return {
|
||||
workspaceIdentifiers: [
|
||||
...new Set(workspaceRegistrations.flatMap((registration) => registration.registerOptions?.identifier ?? [])),
|
||||
],
|
||||
supported: workspaceRegistrations.length > 0,
|
||||
}
|
||||
}
|
||||
|
||||
const hasCurrentFileDiagnostics = (filePath: string, results: DiagnosticRequestResult[]) =>
|
||||
results.some((result) => (result.byFile.get(filePath)?.length ?? 0) > 0)
|
||||
|
||||
async function requestDiagnostics(
|
||||
filePath: string,
|
||||
requests: Promise<DiagnosticRequestResult>[],
|
||||
done: (results: DiagnosticRequestResult[]) => boolean,
|
||||
) {
|
||||
if (!requests.length) return { handled: false, matched: false }
|
||||
|
||||
const results: DiagnosticRequestResult[] = []
|
||||
return new Promise<{ handled: boolean; matched: boolean }>((resolve) => {
|
||||
let pending = requests.length
|
||||
let resolved = false
|
||||
const finish = (merged: { handled: boolean; matched: boolean }, force = false) => {
|
||||
if (resolved) return
|
||||
if (!force && !done(results)) return
|
||||
resolved = true
|
||||
resolve(merged)
|
||||
}
|
||||
|
||||
for (const request of requests) {
|
||||
request.then((result) => {
|
||||
results.push(result)
|
||||
pending -= 1
|
||||
const merged = mergeResults(filePath, results)
|
||||
finish(merged)
|
||||
if (pending === 0) finish(merged, true)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// LATENCY-CRITICAL: dispatch identifier pulls in parallel and unblock once one
|
||||
// batch already produced diagnostics for the current file. Let slower pulls keep
|
||||
// merging in the background; do not sequence identifier-by-identifier, and do
|
||||
// not add a post-match settle/debounce delay. See PR #23771.
|
||||
async function requestDocumentDiagnostics(filePath: string) {
|
||||
const state = documentPullState()
|
||||
if (!state.supported) return { handled: false, matched: false }
|
||||
return requestDiagnostics(
|
||||
filePath,
|
||||
[
|
||||
requestDiagnosticReport(filePath),
|
||||
...state.documentIdentifiers.map((identifier) => requestDiagnosticReport(filePath, identifier)),
|
||||
],
|
||||
(results) => hasCurrentFileDiagnostics(filePath, results),
|
||||
)
|
||||
}
|
||||
|
||||
async function requestFullDiagnostics(filePath: string) {
|
||||
const documentState = documentPullState()
|
||||
const workspaceState = workspacePullState()
|
||||
if (!documentState.supported && !workspaceState.supported) return { handled: false, matched: false }
|
||||
return mergeResults(
|
||||
filePath,
|
||||
await Promise.all([
|
||||
...(documentState.supported ? [requestDiagnosticReport(filePath)] : []),
|
||||
...documentState.documentIdentifiers.map((identifier) => requestDiagnosticReport(filePath, identifier)),
|
||||
...(workspaceState.supported ? [requestWorkspaceDiagnosticReport(filePath)] : []),
|
||||
...workspaceState.workspaceIdentifiers.map((identifier) =>
|
||||
requestWorkspaceDiagnosticReport(filePath, identifier),
|
||||
),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
function waitForRegistrationChange(timeout: number) {
|
||||
if (timeout <= 0) return Promise.resolve(false)
|
||||
return new Promise<boolean>((resolve) => {
|
||||
let finished = false
|
||||
let timer: ReturnType<typeof setTimeout> | undefined
|
||||
const finish = (result: boolean) => {
|
||||
if (finished) return
|
||||
finished = true
|
||||
if (timer) clearTimeout(timer)
|
||||
registrationListeners.delete(listener)
|
||||
resolve(result)
|
||||
}
|
||||
const listener = () => finish(true)
|
||||
registrationListeners.add(listener)
|
||||
timer = setTimeout(() => finish(false), timeout)
|
||||
})
|
||||
}
|
||||
|
||||
function waitForFreshPush(request: { path: string; version: number; after: number; timeout: number }) {
|
||||
if (request.timeout <= 0) return Promise.resolve(false)
|
||||
return new Promise<boolean>((resolve) => {
|
||||
let finished = false
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | undefined
|
||||
let timeoutTimer: ReturnType<typeof setTimeout> | undefined
|
||||
let unsub: (() => void) | undefined
|
||||
const finish = (result: boolean) => {
|
||||
if (finished) return
|
||||
finished = true
|
||||
if (debounceTimer) clearTimeout(debounceTimer)
|
||||
if (timeoutTimer) clearTimeout(timeoutTimer)
|
||||
unsub?.()
|
||||
resolve(result)
|
||||
}
|
||||
const schedule = () => {
|
||||
const hit = published.get(request.path)
|
||||
if (!hit) return
|
||||
if (typeof hit.version === "number" && hit.version !== request.version) return
|
||||
if (hit.at < request.after && hit.version !== request.version) return
|
||||
if (debounceTimer) clearTimeout(debounceTimer)
|
||||
debounceTimer = setTimeout(() => finish(true), Math.max(0, DIAGNOSTICS_DEBOUNCE_MS - (Date.now() - hit.at)))
|
||||
}
|
||||
|
||||
timeoutTimer = setTimeout(() => finish(false), request.timeout)
|
||||
unsub = Bus.subscribe(Event.Diagnostics, (event) => {
|
||||
if (event.properties.path !== request.path || event.properties.serverID !== input.serverID) return
|
||||
schedule()
|
||||
})
|
||||
schedule()
|
||||
})
|
||||
}
|
||||
|
||||
async function waitForDocumentDiagnostics(request: { path: string; version: number; after?: number }) {
|
||||
const startedAt = request.after ?? Date.now()
|
||||
const pushWait = waitForFreshPush({
|
||||
path: request.path,
|
||||
version: request.version,
|
||||
after: startedAt,
|
||||
timeout: DIAGNOSTICS_DOCUMENT_WAIT_TIMEOUT_MS,
|
||||
})
|
||||
|
||||
while (Date.now() - startedAt < DIAGNOSTICS_DOCUMENT_WAIT_TIMEOUT_MS) {
|
||||
const result = await requestDocumentDiagnostics(request.path)
|
||||
if (result.matched) return
|
||||
const remaining = DIAGNOSTICS_DOCUMENT_WAIT_TIMEOUT_MS - (Date.now() - startedAt)
|
||||
if (remaining <= 0) return
|
||||
const next = await Promise.race([
|
||||
pushWait.then((ready) => (ready ? "push" : ("timeout" as const))),
|
||||
waitForRegistrationChange(remaining).then((changed) => (changed ? "registration" : ("timeout" as const))),
|
||||
])
|
||||
if (next !== "registration") return
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForFullDiagnostics(request: { path: string; version: number; after?: number }) {
|
||||
const startedAt = request.after ?? Date.now()
|
||||
const pushWait = waitForFreshPush({
|
||||
path: request.path,
|
||||
version: request.version,
|
||||
after: startedAt,
|
||||
timeout: DIAGNOSTICS_FULL_WAIT_TIMEOUT_MS,
|
||||
})
|
||||
|
||||
while (Date.now() - startedAt < DIAGNOSTICS_FULL_WAIT_TIMEOUT_MS) {
|
||||
const result = await requestFullDiagnostics(request.path)
|
||||
if (result.handled || result.matched) return
|
||||
const remaining = DIAGNOSTICS_FULL_WAIT_TIMEOUT_MS - (Date.now() - startedAt)
|
||||
if (remaining <= 0) return
|
||||
const next = await Promise.race([
|
||||
pushWait.then((ready) => (ready ? "push" : ("timeout" as const))),
|
||||
waitForRegistrationChange(remaining).then((changed) => (changed ? "registration" : ("timeout" as const))),
|
||||
])
|
||||
if (next !== "registration") return
|
||||
}
|
||||
}
|
||||
|
||||
// --- Public API ---
|
||||
|
||||
const result = {
|
||||
root: input.root,
|
||||
@@ -145,26 +582,32 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
|
||||
},
|
||||
notify: {
|
||||
async open(request: { path: string }) {
|
||||
request.path = path.isAbsolute(request.path) ? request.path : path.resolve(input.directory, request.path)
|
||||
request.path = Filesystem.normalizePath(
|
||||
path.isAbsolute(request.path) ? request.path : path.resolve(input.directory, request.path),
|
||||
)
|
||||
const text = await Filesystem.readText(request.path)
|
||||
const extension = path.extname(request.path)
|
||||
const languageId = LANGUAGE_EXTENSIONS[extension] ?? "plaintext"
|
||||
|
||||
const version = files[request.path]
|
||||
if (version !== undefined) {
|
||||
log.info("workspace/didChangeWatchedFiles", request)
|
||||
const document = files[request.path]
|
||||
if (document !== undefined) {
|
||||
// Do not wipe diagnostics on didChange. Some servers (e.g. clangd) only
|
||||
// re-emit diagnostics when the content actually changes, so clearing
|
||||
// here would lose errors for no-op touchFile calls. Let the server's
|
||||
// next push/pull overwrite naturally.
|
||||
logger.info("workspace/didChangeWatchedFiles", request)
|
||||
await connection.sendNotification("workspace/didChangeWatchedFiles", {
|
||||
changes: [
|
||||
{
|
||||
uri: pathToFileURL(request.path).href,
|
||||
type: 2, // Changed
|
||||
type: FILE_CHANGE_CHANGED,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const next = version + 1
|
||||
files[request.path] = next
|
||||
log.info("textDocument/didChange", {
|
||||
const next = document.version + 1
|
||||
files[request.path] = { version: next, text }
|
||||
logger.info("textDocument/didChange", {
|
||||
path: request.path,
|
||||
version: next,
|
||||
})
|
||||
@@ -173,23 +616,35 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
|
||||
uri: pathToFileURL(request.path).href,
|
||||
version: next,
|
||||
},
|
||||
contentChanges: [{ text }],
|
||||
contentChanges:
|
||||
syncKind === TEXT_DOCUMENT_SYNC_INCREMENTAL
|
||||
? [
|
||||
{
|
||||
range: {
|
||||
start: { line: 0, character: 0 },
|
||||
end: endPosition(document.text),
|
||||
},
|
||||
text,
|
||||
},
|
||||
]
|
||||
: [{ text }],
|
||||
})
|
||||
return
|
||||
return next
|
||||
}
|
||||
|
||||
log.info("workspace/didChangeWatchedFiles", request)
|
||||
logger.info("workspace/didChangeWatchedFiles", request)
|
||||
await connection.sendNotification("workspace/didChangeWatchedFiles", {
|
||||
changes: [
|
||||
{
|
||||
uri: pathToFileURL(request.path).href,
|
||||
type: 1, // Created
|
||||
type: FILE_CHANGE_CREATED,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
log.info("textDocument/didOpen", request)
|
||||
diagnostics.delete(request.path)
|
||||
logger.info("textDocument/didOpen", request)
|
||||
pushDiagnostics.delete(request.path)
|
||||
pullDiagnostics.delete(request.path)
|
||||
await connection.sendNotification("textDocument/didOpen", {
|
||||
textDocument: {
|
||||
uri: pathToFileURL(request.path).href,
|
||||
@@ -198,52 +653,42 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
|
||||
text,
|
||||
},
|
||||
})
|
||||
files[request.path] = 0
|
||||
return
|
||||
files[request.path] = { version: 0, text }
|
||||
return 0
|
||||
},
|
||||
},
|
||||
get diagnostics() {
|
||||
return diagnostics
|
||||
const result = new Map<string, Diagnostic[]>()
|
||||
for (const key of new Set([...pushDiagnostics.keys(), ...pullDiagnostics.keys()])) {
|
||||
result.set(key, mergedDiagnostics(key))
|
||||
}
|
||||
return result
|
||||
},
|
||||
async waitForDiagnostics(request: { path: string }) {
|
||||
async waitForDiagnostics(request: { path: string; version: number; mode?: "document" | "full"; after?: number }) {
|
||||
const normalizedPath = Filesystem.normalizePath(
|
||||
path.isAbsolute(request.path) ? request.path : path.resolve(input.directory, request.path),
|
||||
)
|
||||
log.info("waiting for diagnostics", { path: normalizedPath })
|
||||
let unsub: () => void
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | undefined
|
||||
return await withTimeout(
|
||||
new Promise<void>((resolve) => {
|
||||
unsub = Bus.subscribe(Event.Diagnostics, (event) => {
|
||||
if (event.properties.path === normalizedPath && event.properties.serverID === result.serverID) {
|
||||
// Debounce to allow LSP to send follow-up diagnostics (e.g., semantic after syntax)
|
||||
if (debounceTimer) clearTimeout(debounceTimer)
|
||||
debounceTimer = setTimeout(() => {
|
||||
log.info("got diagnostics", { path: normalizedPath })
|
||||
unsub?.()
|
||||
resolve()
|
||||
}, DIAGNOSTICS_DEBOUNCE_MS)
|
||||
}
|
||||
})
|
||||
}),
|
||||
3000,
|
||||
)
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
if (debounceTimer) clearTimeout(debounceTimer)
|
||||
unsub?.()
|
||||
})
|
||||
logger.info("waiting for diagnostics", {
|
||||
path: normalizedPath,
|
||||
mode: request.mode ?? "full",
|
||||
version: request.version,
|
||||
})
|
||||
if (request.mode === "document") {
|
||||
await waitForDocumentDiagnostics({ path: normalizedPath, version: request.version, after: request.after })
|
||||
return
|
||||
}
|
||||
await waitForFullDiagnostics({ path: normalizedPath, version: request.version, after: request.after })
|
||||
},
|
||||
async shutdown() {
|
||||
l.info("shutting down")
|
||||
logger.info("shutting down")
|
||||
connection.end()
|
||||
connection.dispose()
|
||||
await Process.stop(input.server.process)
|
||||
l.info("shutdown")
|
||||
logger.info("shutdown")
|
||||
},
|
||||
}
|
||||
|
||||
l.info("initialized")
|
||||
logger.info("initialized")
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ export interface Interface {
|
||||
readonly init: () => Effect.Effect<void>
|
||||
readonly status: () => Effect.Effect<Status[]>
|
||||
readonly hasClients: (file: string) => Effect.Effect<boolean>
|
||||
readonly touchFile: (input: string, waitForDiagnostics?: boolean) => Effect.Effect<void>
|
||||
readonly touchFile: (input: string, diagnostics?: "document" | "full") => Effect.Effect<void>
|
||||
readonly diagnostics: () => Effect.Effect<Record<string, LSPClient.Diagnostic[]>>
|
||||
readonly hover: (input: LocInput) => Effect.Effect<any>
|
||||
readonly definition: (input: LocInput) => Effect.Effect<any[]>
|
||||
@@ -358,15 +358,21 @@ export const layer = Layer.effect(
|
||||
})
|
||||
})
|
||||
|
||||
const touchFile = Effect.fn("LSP.touchFile")(function* (input: string, waitForDiagnostics?: boolean) {
|
||||
const touchFile = Effect.fn("LSP.touchFile")(function* (input: string, diagnostics?: "document" | "full") {
|
||||
log.info("touching file", { file: input })
|
||||
const clients = yield* getClients(input)
|
||||
yield* Effect.promise(() =>
|
||||
Promise.all(
|
||||
clients.map(async (client) => {
|
||||
const wait = waitForDiagnostics ? client.waitForDiagnostics({ path: input }) : Promise.resolve()
|
||||
await client.notify.open({ path: input })
|
||||
return wait
|
||||
const after = Date.now()
|
||||
const version = await client.notify.open({ path: input })
|
||||
if (!diagnostics) return
|
||||
return client.waitForDiagnostics({
|
||||
path: input,
|
||||
version,
|
||||
mode: diagnostics,
|
||||
after,
|
||||
})
|
||||
}),
|
||||
).catch((err) => {
|
||||
log.error("failed to touch file", { err, file: input })
|
||||
|
||||
@@ -490,7 +490,7 @@ export const Pyright: Info = {
|
||||
const args = []
|
||||
if (!binary) {
|
||||
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||
const resolved = await Npm.which("pyright")
|
||||
const resolved = await Npm.which("pyright", "pyright-langserver")
|
||||
if (!resolved) return
|
||||
binary = resolved
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export interface Interface {
|
||||
},
|
||||
) => Effect.Effect<void, EffectFlock.LockError | InstallFailedError>
|
||||
readonly outdated: (pkg: string, cachedVersion: string) => Effect.Effect<boolean>
|
||||
readonly which: (pkg: string) => Effect.Effect<Option.Option<string>>
|
||||
readonly which: (pkg: string, bin?: string) => Effect.Effect<Option.Option<string>>
|
||||
}
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Npm") {}
|
||||
@@ -207,7 +207,7 @@ export const layer = Layer.effect(
|
||||
return
|
||||
}, Effect.scoped)
|
||||
|
||||
const which = Effect.fn("Npm.which")(function* (pkg: string) {
|
||||
const which = Effect.fn("Npm.which")(function* (pkg: string, bin?: string) {
|
||||
const dir = directory(pkg)
|
||||
const binDir = path.join(dir, "node_modules", ".bin")
|
||||
|
||||
@@ -215,6 +215,9 @@ export const layer = Layer.effect(
|
||||
const files = yield* fs.readDirectory(binDir).pipe(Effect.catch(() => Effect.succeed([] as string[])))
|
||||
|
||||
if (files.length === 0) return Option.none<string>()
|
||||
// Caller picked a specific bin (e.g. pyright exposes both `pyright` and
|
||||
// `pyright-langserver`); trust the hint if the package provides it.
|
||||
if (bin) return files.includes(bin) ? Option.some(bin) : Option.none<string>()
|
||||
if (files.length === 1) return Option.some(files[0])
|
||||
|
||||
const pkgJson = yield* afs.readJson(path.join(dir, "node_modules", pkg, "package.json")).pipe(Effect.option)
|
||||
@@ -223,11 +226,11 @@ export const layer = Layer.effect(
|
||||
const parsed = pkgJson.value as { bin?: string | Record<string, string> }
|
||||
if (parsed?.bin) {
|
||||
const unscoped = pkg.startsWith("@") ? pkg.split("/")[1] : pkg
|
||||
const bin = parsed.bin
|
||||
if (typeof bin === "string") return Option.some(unscoped)
|
||||
const keys = Object.keys(bin)
|
||||
const parsedBin = parsed.bin
|
||||
if (typeof parsedBin === "string") return Option.some(unscoped)
|
||||
const keys = Object.keys(parsedBin)
|
||||
if (keys.length === 1) return Option.some(keys[0])
|
||||
return bin[unscoped] ? Option.some(unscoped) : Option.some(keys[0])
|
||||
return parsedBin[unscoped] ? Option.some(unscoped) : Option.some(keys[0])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -379,6 +379,8 @@ export async function CodexAuthPlugin(input: PluginInput): Promise<Hooks> {
|
||||
for (const [modelId, model] of Object.entries(provider.models)) {
|
||||
if (modelId.includes("codex")) continue
|
||||
if (allowedModels.has(model.api.id)) continue
|
||||
const match = model.api.id.match(/^gpt-(\d+\.\d+)/)
|
||||
if (match && parseFloat(match[1]) > 5.4) continue
|
||||
delete provider.models[modelId]
|
||||
}
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ export const ApplyPatchTool = Tool.define(
|
||||
for (const change of fileChanges) {
|
||||
if (change.type === "delete") continue
|
||||
const target = change.movePath ?? change.filePath
|
||||
yield* lsp.touchFile(target, true)
|
||||
yield* lsp.touchFile(target, "document")
|
||||
}
|
||||
const diagnostics = yield* lsp.diagnostics()
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ export const EditTool = Tool.define(
|
||||
})
|
||||
|
||||
let output = "Edit applied successfully."
|
||||
yield* lsp.touchFile(filePath, true)
|
||||
yield* lsp.touchFile(filePath, "document")
|
||||
const diagnostics = yield* lsp.diagnostics()
|
||||
const normalizedFilePath = AppFileSystem.normalizePath(filePath)
|
||||
const block = LSP.Diagnostic.report(filePath, diagnostics[normalizedFilePath] ?? [])
|
||||
|
||||
@@ -55,7 +55,7 @@ export const LspTool = Tool.define(
|
||||
const available = yield* lsp.hasClients(file)
|
||||
if (!available) throw new Error("No LSP server available for this file type.")
|
||||
|
||||
yield* lsp.touchFile(file, true)
|
||||
yield* lsp.touchFile(file, "document")
|
||||
|
||||
const result: unknown[] = yield* (() => {
|
||||
switch (args.operation) {
|
||||
|
||||
@@ -78,7 +78,7 @@ export const ReadTool = Tool.define(
|
||||
|
||||
const warm = Effect.fn("ReadTool.warm")(function* (filepath: string) {
|
||||
yield* search.open({ file: filepath }).pipe(Effect.ignore)
|
||||
yield* lsp.touchFile(filepath, false).pipe(Effect.ignore, Effect.forkIn(scope))
|
||||
yield* lsp.touchFile(filepath).pipe(Effect.ignore, Effect.forkIn(scope))
|
||||
})
|
||||
|
||||
const readSample = Effect.fn("ReadTool.readSample")(function* (
|
||||
|
||||
@@ -67,7 +67,7 @@ export const WriteTool = Tool.define(
|
||||
})
|
||||
|
||||
let output = "Wrote file successfully."
|
||||
yield* lsp.touchFile(filepath, true)
|
||||
yield* lsp.touchFile(filepath, "document")
|
||||
const diagnostics = yield* lsp.diagnostics()
|
||||
const normalizedFilepath = AppFileSystem.normalizePath(filepath)
|
||||
let projectDiagnosticsCount = 0
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
// Simple JSON-RPC 2.0 LSP-like fake server over stdio
|
||||
// Implements a minimal LSP handshake and triggers a request upon notification
|
||||
|
||||
let nextId = 1
|
||||
let readBuffer = Buffer.alloc(0)
|
||||
let lastChange = null
|
||||
let initializeParams = null
|
||||
let diagnosticRequestCount = 0
|
||||
let registeredCapability = false
|
||||
const pendingClientRequests = new Map()
|
||||
let pullConfig = {
|
||||
delayMs: 0,
|
||||
registerOn: undefined,
|
||||
registrations: [],
|
||||
documentDiagnostics: [],
|
||||
documentDiagnosticsByIdentifier: {},
|
||||
documentDelayMsByIdentifier: {},
|
||||
workspaceDiagnostics: [],
|
||||
workspaceDiagnosticsByIdentifier: {},
|
||||
workspaceDelayMsByIdentifier: {},
|
||||
}
|
||||
|
||||
function encode(message) {
|
||||
const json = JSON.stringify(message)
|
||||
@@ -14,29 +30,19 @@ function decodeFrames(buffer) {
|
||||
let idx
|
||||
while ((idx = buffer.indexOf("\r\n\r\n")) !== -1) {
|
||||
const header = buffer.slice(0, idx).toString("utf8")
|
||||
const m = /Content-Length:\s*(\d+)/i.exec(header)
|
||||
const len = m ? parseInt(m[1], 10) : 0
|
||||
const match = /Content-Length:\s*(\d+)/i.exec(header)
|
||||
const length = match ? parseInt(match[1], 10) : 0
|
||||
const bodyStart = idx + 4
|
||||
const bodyEnd = bodyStart + len
|
||||
const bodyEnd = bodyStart + length
|
||||
if (buffer.length < bodyEnd) break
|
||||
const body = buffer.slice(bodyStart, bodyEnd).toString("utf8")
|
||||
results.push(body)
|
||||
results.push(buffer.slice(bodyStart, bodyEnd).toString("utf8"))
|
||||
buffer = buffer.slice(bodyEnd)
|
||||
}
|
||||
return { messages: results, rest: buffer }
|
||||
}
|
||||
|
||||
let readBuffer = Buffer.alloc(0)
|
||||
|
||||
process.stdin.on("data", (chunk) => {
|
||||
readBuffer = Buffer.concat([readBuffer, chunk])
|
||||
const { messages, rest } = decodeFrames(readBuffer)
|
||||
readBuffer = rest
|
||||
for (const m of messages) handle(m)
|
||||
})
|
||||
|
||||
function send(msg) {
|
||||
process.stdout.write(encode(msg))
|
||||
function send(message) {
|
||||
process.stdout.write(encode(message))
|
||||
}
|
||||
|
||||
function sendRequest(method, params) {
|
||||
@@ -45,6 +51,50 @@ function sendRequest(method, params) {
|
||||
return id
|
||||
}
|
||||
|
||||
function sendResponse(id, result) {
|
||||
send({ jsonrpc: "2.0", id, result })
|
||||
}
|
||||
|
||||
function sendNotification(method, params) {
|
||||
send({ jsonrpc: "2.0", method, params })
|
||||
}
|
||||
|
||||
function maybeRegister(method) {
|
||||
if (pullConfig.registerOn !== method || registeredCapability) return
|
||||
registeredCapability = true
|
||||
sendRequest("client/registerCapability", {
|
||||
registrations: pullConfig.registrations.map((registration, index) => ({
|
||||
id: registration.id ?? `pull-${index}`,
|
||||
method: registration.method ?? "textDocument/diagnostic",
|
||||
registerOptions: registration.registerOptions ?? registration,
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
function delayed(id, result, delayMs = pullConfig.delayMs) {
|
||||
if (!delayMs) {
|
||||
sendResponse(id, result)
|
||||
return
|
||||
}
|
||||
setTimeout(() => sendResponse(id, result), delayMs)
|
||||
}
|
||||
|
||||
function diagnosticsForIdentifier(identifier) {
|
||||
return pullConfig.documentDiagnosticsByIdentifier[identifier] ?? pullConfig.documentDiagnostics
|
||||
}
|
||||
|
||||
function workspaceDiagnosticsForIdentifier(identifier) {
|
||||
return pullConfig.workspaceDiagnosticsByIdentifier[identifier] ?? pullConfig.workspaceDiagnostics
|
||||
}
|
||||
|
||||
function documentDelayForIdentifier(identifier) {
|
||||
return pullConfig.documentDelayMsByIdentifier[identifier] ?? pullConfig.delayMs
|
||||
}
|
||||
|
||||
function workspaceDelayForIdentifier(identifier) {
|
||||
return pullConfig.workspaceDelayMsByIdentifier[identifier] ?? pullConfig.delayMs
|
||||
}
|
||||
|
||||
function handle(raw) {
|
||||
let data
|
||||
try {
|
||||
@@ -52,24 +102,148 @@ function handle(raw) {
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof data.method === "undefined" && typeof data.id !== "undefined") {
|
||||
const pending = pendingClientRequests.get(data.id)
|
||||
if (!pending) return
|
||||
pendingClientRequests.delete(data.id)
|
||||
sendResponse(pending, data.result ?? null)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "initialize") {
|
||||
send({ jsonrpc: "2.0", id: data.id, result: { capabilities: {} } })
|
||||
initializeParams = data.params
|
||||
sendResponse(data.id, {
|
||||
capabilities: {
|
||||
textDocumentSync: {
|
||||
change: 2,
|
||||
},
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
if (data.method === "initialized") {
|
||||
|
||||
if (data.method === "test/get-initialize-params") {
|
||||
sendResponse(data.id, initializeParams)
|
||||
return
|
||||
}
|
||||
if (data.method === "workspace/didChangeConfiguration") {
|
||||
|
||||
if (data.method === "test/request-configuration") {
|
||||
const id = sendRequest("workspace/configuration", data.params)
|
||||
pendingClientRequests.set(id, data.id)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "initialized" || data.method === "workspace/didChangeConfiguration") {
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "textDocument/didOpen") {
|
||||
maybeRegister("didOpen")
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "textDocument/didChange") {
|
||||
lastChange = data.params
|
||||
maybeRegister("didChange")
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "test/trigger") {
|
||||
const method = data.params && data.params.method
|
||||
if (method === "client/registerCapability") {
|
||||
sendRequest(method, {
|
||||
registrations: [
|
||||
{
|
||||
id: "test-diagnostic-registration",
|
||||
method: "textDocument/diagnostic",
|
||||
registerOptions: { identifier: "syntax" },
|
||||
},
|
||||
],
|
||||
})
|
||||
return
|
||||
}
|
||||
if (method === "client/unregisterCapability") {
|
||||
sendRequest(method, {
|
||||
unregisterations: [{ id: "test-diagnostic-registration", method: "textDocument/diagnostic" }],
|
||||
})
|
||||
return
|
||||
}
|
||||
if (method) sendRequest(method, {})
|
||||
return
|
||||
}
|
||||
if (typeof data.id !== "undefined") {
|
||||
// Respond OK to any request from client to keep transport flowing
|
||||
send({ jsonrpc: "2.0", id: data.id, result: null })
|
||||
|
||||
if (data.method === "test/configure-pull-diagnostics") {
|
||||
pullConfig = {
|
||||
delayMs: data.params?.delayMs ?? 0,
|
||||
registerOn: data.params?.registerOn,
|
||||
registrations: data.params?.registrations ?? [],
|
||||
documentDiagnostics: data.params?.documentDiagnostics ?? [],
|
||||
documentDiagnosticsByIdentifier: data.params?.documentDiagnosticsByIdentifier ?? {},
|
||||
documentDelayMsByIdentifier: data.params?.documentDelayMsByIdentifier ?? {},
|
||||
workspaceDiagnostics: data.params?.workspaceDiagnostics ?? [],
|
||||
workspaceDiagnosticsByIdentifier: data.params?.workspaceDiagnosticsByIdentifier ?? {},
|
||||
workspaceDelayMsByIdentifier: data.params?.workspaceDelayMsByIdentifier ?? {},
|
||||
}
|
||||
registeredCapability = false
|
||||
sendResponse(data.id, null)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "test/register-configured-pull-diagnostics") {
|
||||
maybeRegister(undefined)
|
||||
sendResponse(data.id, null)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "test/publish-diagnostics") {
|
||||
sendNotification("textDocument/publishDiagnostics", data.params)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "test/get-last-change") {
|
||||
sendResponse(data.id, lastChange)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "test/get-diagnostic-request-count") {
|
||||
sendResponse(data.id, diagnosticRequestCount)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "textDocument/diagnostic") {
|
||||
diagnosticRequestCount += 1
|
||||
delayed(
|
||||
data.id,
|
||||
{
|
||||
kind: "full",
|
||||
items: diagnosticsForIdentifier(data.params?.identifier ?? ""),
|
||||
},
|
||||
documentDelayForIdentifier(data.params?.identifier ?? ""),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.method === "workspace/diagnostic") {
|
||||
diagnosticRequestCount += 1
|
||||
delayed(
|
||||
data.id,
|
||||
{
|
||||
items: workspaceDiagnosticsForIdentifier(data.params?.identifier ?? ""),
|
||||
},
|
||||
workspaceDelayForIdentifier(data.params?.identifier ?? ""),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof data.id !== "undefined") {
|
||||
sendResponse(data.id, null)
|
||||
}
|
||||
}
|
||||
|
||||
process.stdin.on("data", (chunk) => {
|
||||
readBuffer = Buffer.concat([readBuffer, chunk])
|
||||
const { messages, rest } = decodeFrames(readBuffer)
|
||||
readBuffer = rest
|
||||
for (const message of messages) handle(message)
|
||||
})
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { describe, expect, test, beforeEach } from "bun:test"
|
||||
import { beforeEach, describe, expect, test } from "bun:test"
|
||||
import path from "path"
|
||||
import { pathToFileURL } from "url"
|
||||
import { tmpdir } from "../fixture/fixture"
|
||||
import { LSPClient } from "../../src/lsp"
|
||||
import { LSPServer } from "../../src/lsp"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { Log } from "../../src/util"
|
||||
|
||||
// Minimal fake LSP server that speaks JSON-RPC over stdio
|
||||
function spawnFakeServer() {
|
||||
const { spawn } = require("child_process")
|
||||
const serverPath = path.join(__dirname, "../fixture/lsp/fake-lsp-server.js")
|
||||
@@ -39,10 +40,8 @@ describe("LSPClient interop", () => {
|
||||
method: "workspace/workspaceFolders",
|
||||
})
|
||||
|
||||
await new Promise((r) => setTimeout(r, 100))
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
expect(client.connection).toBeDefined()
|
||||
|
||||
await client.shutdown()
|
||||
})
|
||||
|
||||
@@ -64,10 +63,8 @@ describe("LSPClient interop", () => {
|
||||
method: "client/registerCapability",
|
||||
})
|
||||
|
||||
await new Promise((r) => setTimeout(r, 100))
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
expect(client.connection).toBeDefined()
|
||||
|
||||
await client.shutdown()
|
||||
})
|
||||
|
||||
@@ -89,10 +86,397 @@ describe("LSPClient interop", () => {
|
||||
method: "client/unregisterCapability",
|
||||
})
|
||||
|
||||
await new Promise((r) => setTimeout(r, 100))
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
expect(client.connection).toBeDefined()
|
||||
await client.shutdown()
|
||||
})
|
||||
|
||||
test("initialize does not overclaim unsupported diagnostics capabilities", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
|
||||
const client = await Instance.provide({
|
||||
directory: process.cwd(),
|
||||
fn: () =>
|
||||
LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: process.cwd(),
|
||||
directory: process.cwd(),
|
||||
}),
|
||||
})
|
||||
|
||||
const params = await client.connection.sendRequest<any>("test/get-initialize-params", {})
|
||||
expect(params.capabilities.workspace.diagnostics.refreshSupport).toBe(false)
|
||||
expect(params.capabilities.textDocument.publishDiagnostics.versionSupport).toBe(false)
|
||||
|
||||
await client.shutdown()
|
||||
})
|
||||
|
||||
test("workspace/configuration returns one result per requested item", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
const initialization = {
|
||||
alpha: {
|
||||
beta: 1,
|
||||
},
|
||||
gamma: true,
|
||||
}
|
||||
|
||||
const client = await Instance.provide({
|
||||
directory: process.cwd(),
|
||||
fn: () =>
|
||||
LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: {
|
||||
...(handle as unknown as LSPServer.Handle),
|
||||
initialization,
|
||||
},
|
||||
root: process.cwd(),
|
||||
directory: process.cwd(),
|
||||
}),
|
||||
})
|
||||
|
||||
const response = await client.connection.sendRequest<any[]>("test/request-configuration", {
|
||||
items: [{ section: "alpha" }, { section: "alpha.beta" }, { section: "missing" }, {}],
|
||||
})
|
||||
|
||||
expect(response).toEqual([{ beta: 1 }, 1, null, initialization])
|
||||
|
||||
await client.shutdown()
|
||||
})
|
||||
|
||||
test("sends ranged didChange for incremental sync servers", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
await using tmp = await tmpdir()
|
||||
const file = path.join(tmp.path, "client.ts")
|
||||
await Bun.write(file, "first\n")
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const client = await LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: tmp.path,
|
||||
directory: tmp.path,
|
||||
})
|
||||
|
||||
await client.notify.open({ path: file })
|
||||
await Bun.write(file, "second\nthird\n")
|
||||
await client.notify.open({ path: file })
|
||||
|
||||
const change = await client.connection.sendRequest<{
|
||||
textDocument: { version: number }
|
||||
contentChanges: {
|
||||
range?: { start: { line: number; character: number }; end: { line: number; character: number } }
|
||||
text: string
|
||||
}[]
|
||||
}>("test/get-last-change", {})
|
||||
expect(change.textDocument.version).toBe(1)
|
||||
expect(change.contentChanges).toEqual([
|
||||
{
|
||||
range: {
|
||||
start: { line: 0, character: 0 },
|
||||
end: { line: 1, character: 0 },
|
||||
},
|
||||
text: "second\nthird\n",
|
||||
},
|
||||
])
|
||||
|
||||
await client.shutdown()
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("document mode falls back to push diagnostics", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
await using tmp = await tmpdir()
|
||||
const file = path.join(tmp.path, "client.ts")
|
||||
await Bun.write(file, "const x = 1\n")
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const client = await LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: tmp.path,
|
||||
directory: tmp.path,
|
||||
})
|
||||
|
||||
const version = await client.notify.open({ path: file })
|
||||
const wait = client.waitForDiagnostics({ path: file, version, mode: "document" })
|
||||
await client.connection.sendNotification("test/publish-diagnostics", {
|
||||
uri: pathToFileURL(file).href,
|
||||
version,
|
||||
diagnostics: [
|
||||
{
|
||||
range: {
|
||||
start: { line: 0, character: 0 },
|
||||
end: { line: 0, character: 5 },
|
||||
},
|
||||
message: "push diagnostic",
|
||||
severity: 1,
|
||||
},
|
||||
],
|
||||
})
|
||||
await wait
|
||||
|
||||
const diagnostics = client.diagnostics.get(file) ?? []
|
||||
expect(diagnostics).toHaveLength(1)
|
||||
expect(diagnostics[0]?.message).toBe("push diagnostic")
|
||||
|
||||
const count = await client.connection.sendRequest("test/get-diagnostic-request-count", {})
|
||||
expect(count).toBe(0)
|
||||
|
||||
await client.shutdown()
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("document mode accepts matching push diagnostics published before waiting", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
await using tmp = await tmpdir()
|
||||
const file = path.join(tmp.path, "client.ts")
|
||||
await Bun.write(file, "const x = 1\n")
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const client = await LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: tmp.path,
|
||||
directory: tmp.path,
|
||||
})
|
||||
|
||||
const version = await client.notify.open({ path: file })
|
||||
await client.connection.sendNotification("test/publish-diagnostics", {
|
||||
uri: pathToFileURL(file).href,
|
||||
version,
|
||||
diagnostics: [
|
||||
{
|
||||
range: {
|
||||
start: { line: 0, character: 0 },
|
||||
end: { line: 0, character: 5 },
|
||||
},
|
||||
message: "push diagnostic",
|
||||
severity: 1,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
for (let i = 0; i < 20 && (client.diagnostics.get(file)?.length ?? 0) === 0; i++) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 25))
|
||||
}
|
||||
|
||||
expect(client.diagnostics.get(file)?.[0]?.message).toBe("push diagnostic")
|
||||
|
||||
const started = Date.now()
|
||||
await client.waitForDiagnostics({ path: file, version, mode: "document" })
|
||||
expect(Date.now() - started).toBeLessThan(1_000)
|
||||
|
||||
await client.shutdown()
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("document mode waits for pull diagnostics", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
await using tmp = await tmpdir()
|
||||
const file = path.join(tmp.path, "client.cs")
|
||||
await Bun.write(file, "class C {}\n")
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const client = await LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: tmp.path,
|
||||
directory: tmp.path,
|
||||
})
|
||||
|
||||
await client.connection.sendRequest("test/configure-pull-diagnostics", {
|
||||
registerOn: "didOpen",
|
||||
registrations: [{ identifier: "DocumentCompilerSemantic" }],
|
||||
documentDiagnosticsByIdentifier: {
|
||||
DocumentCompilerSemantic: [
|
||||
{
|
||||
range: {
|
||||
start: { line: 0, character: 0 },
|
||||
end: { line: 0, character: 5 },
|
||||
},
|
||||
message: "pull diagnostic",
|
||||
severity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const version = await client.notify.open({ path: file })
|
||||
await client.waitForDiagnostics({ path: file, version, mode: "document" })
|
||||
|
||||
const diagnostics = client.diagnostics.get(file) ?? []
|
||||
expect(diagnostics).toHaveLength(1)
|
||||
expect(diagnostics[0]?.message).toBe("pull diagnostic")
|
||||
|
||||
const count = await client.connection.sendRequest("test/get-diagnostic-request-count", {})
|
||||
expect(count).toBeGreaterThan(0)
|
||||
|
||||
await client.shutdown()
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("document mode does not wait for the slowest pull identifier after current-file diagnostics arrive", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
await using tmp = await tmpdir()
|
||||
const file = path.join(tmp.path, "client.cs")
|
||||
await Bun.write(file, "class C {}\n")
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const client = await LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: tmp.path,
|
||||
directory: tmp.path,
|
||||
})
|
||||
|
||||
await client.connection.sendRequest("test/configure-pull-diagnostics", {
|
||||
registrations: [{ identifier: "fast" }, { identifier: "slow" }],
|
||||
documentDiagnosticsByIdentifier: {
|
||||
fast: [
|
||||
{
|
||||
range: {
|
||||
start: { line: 0, character: 0 },
|
||||
end: { line: 0, character: 5 },
|
||||
},
|
||||
message: "fast diagnostic",
|
||||
severity: 1,
|
||||
},
|
||||
],
|
||||
slow: [],
|
||||
},
|
||||
documentDelayMsByIdentifier: {
|
||||
slow: 2_500,
|
||||
},
|
||||
})
|
||||
|
||||
const version = await client.notify.open({ path: file })
|
||||
await client.connection.sendRequest("test/register-configured-pull-diagnostics", {})
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
const started = Date.now()
|
||||
await client.waitForDiagnostics({ path: file, version, mode: "document" })
|
||||
|
||||
expect(Date.now() - started).toBeLessThan(1_000)
|
||||
expect(client.diagnostics.get(file)?.[0]?.message).toBe("fast diagnostic")
|
||||
expect(await client.connection.sendRequest("test/get-diagnostic-request-count", {})).toBeGreaterThan(1)
|
||||
|
||||
await client.shutdown()
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("full mode includes workspace pull diagnostics", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
await using tmp = await tmpdir()
|
||||
const file = path.join(tmp.path, "client.cs")
|
||||
const related = path.join(tmp.path, "other.cs")
|
||||
await Bun.write(file, "class C {}\n")
|
||||
await Bun.write(related, "class D {}\n")
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const client = await LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: tmp.path,
|
||||
directory: tmp.path,
|
||||
})
|
||||
|
||||
await client.connection.sendRequest("test/configure-pull-diagnostics", {
|
||||
registerOn: "didOpen",
|
||||
registrations: [
|
||||
{ identifier: "DocumentCompilerSemantic" },
|
||||
{ identifier: "WorkspaceDocumentsAndProject", workspaceDiagnostics: true },
|
||||
],
|
||||
documentDiagnosticsByIdentifier: {
|
||||
DocumentCompilerSemantic: [
|
||||
{
|
||||
range: {
|
||||
start: { line: 0, character: 0 },
|
||||
end: { line: 0, character: 5 },
|
||||
},
|
||||
message: "current file",
|
||||
severity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
workspaceDiagnosticsByIdentifier: {
|
||||
WorkspaceDocumentsAndProject: [
|
||||
{
|
||||
uri: pathToFileURL(related).href,
|
||||
items: [
|
||||
{
|
||||
range: {
|
||||
start: { line: 0, character: 0 },
|
||||
end: { line: 0, character: 5 },
|
||||
},
|
||||
message: "workspace file",
|
||||
severity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const version = await client.notify.open({ path: file })
|
||||
await client.waitForDiagnostics({ path: file, version, mode: "full" })
|
||||
|
||||
expect(client.diagnostics.get(file)?.[0]?.message).toBe("current file")
|
||||
expect(client.diagnostics.get(related)?.[0]?.message).toBe("workspace file")
|
||||
|
||||
await client.shutdown()
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("full mode treats an empty workspace pull response as handled", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
await using tmp = await tmpdir()
|
||||
const file = path.join(tmp.path, "client.cs")
|
||||
await Bun.write(file, "class C {}\n")
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const client = await LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: tmp.path,
|
||||
directory: tmp.path,
|
||||
})
|
||||
|
||||
await client.connection.sendRequest("test/configure-pull-diagnostics", {
|
||||
registerOn: "didOpen",
|
||||
registrations: [{ identifier: "WorkspaceDocumentsAndProject", workspaceDiagnostics: true }],
|
||||
workspaceDiagnosticsByIdentifier: {
|
||||
WorkspaceDocumentsAndProject: [],
|
||||
},
|
||||
})
|
||||
|
||||
const version = await client.notify.open({ path: file })
|
||||
const started = Date.now()
|
||||
await client.waitForDiagnostics({ path: file, version, mode: "full" })
|
||||
|
||||
expect(Date.now() - started).toBeLessThan(1_000)
|
||||
|
||||
await client.shutdown()
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"name": "@opencode-ai/shared",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
||||
@@ -85,8 +85,8 @@ OpenCode Zen هي بوابة AI تتيح لك الوصول إلى هذه الن
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -96,6 +96,7 @@ OpenCode Zen هي بوابة AI تتيح لك الوصول إلى هذه الن
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
يستخدم [معرّف النموذج](/docs/config/#models) في إعدادات OpenCode الصيغة `opencode/<model-id>`. على سبيل المثال، بالنسبة إلى GPT 5.4، ستستخدم `opencode/gpt-5.4` في إعداداتك.
|
||||
@@ -121,6 +122,7 @@ https://opencode.ai/zen/v1/models
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -170,6 +172,7 @@ https://opencode.ai/zen/v1/models
|
||||
|
||||
- MiniMax M2.5 Free متاح على OpenCode لفترة محدودة. يستخدم الفريق هذه الفترة لجمع الملاحظات وتحسين النموذج.
|
||||
- Ling 2.6 Flash Free متاح على OpenCode لفترة محدودة. يستخدم الفريق هذه الفترة لجمع الملاحظات وتحسين النموذج.
|
||||
- Hy3 Preview Flash Free متاح على OpenCode لفترة محدودة. يستخدم الفريق هذه الفترة لجمع الملاحظات وتحسين النموذج.
|
||||
- Nemotron 3 Super Free متاح على OpenCode لفترة محدودة. يستخدم الفريق هذه الفترة لجمع الملاحظات وتحسين النموذج.
|
||||
- Big Pickle نموذج خفي ومتاح مجانا على OpenCode لفترة محدودة. يستخدم الفريق هذه الفترة لجمع الملاحظات وتحسين النموذج.
|
||||
|
||||
@@ -214,6 +217,7 @@ https://opencode.ai/zen/v1/models
|
||||
- Big Pickle: خلال فترته المجانية، قد تُستخدم البيانات المجمعة لتحسين النموذج.
|
||||
- MiniMax M2.5 Free: خلال فترته المجانية، قد تُستخدم البيانات المجمعة لتحسين النموذج.
|
||||
- Ling 2.6 Flash Free: خلال فترته المجانية، قد تُستخدم البيانات المجمعة لتحسين النموذج.
|
||||
- Hy3 Preview Free: خلال فترته المجانية، قد تُستخدم البيانات المجمعة لتحسين النموذج.
|
||||
- Nemotron 3 Super Free (نقاط نهاية NVIDIA المجانية): يُقدَّم بموجب [شروط خدمة النسخة التجريبية من واجهة NVIDIA API](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). للاستخدام التجريبي فقط، وليس للإنتاج أو البيانات الحساسة. تقوم NVIDIA بتسجيل المطالبات والمخرجات لتحسين نماذجها وخدماتها. لا ترسل بيانات شخصية أو سرية.
|
||||
- OpenAI APIs: يتم الاحتفاظ بالطلبات لمدة 30 يوما وفقا لـ [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: يتم الاحتفاظ بالطلبات لمدة 30 يوما وفقا لـ [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -90,8 +90,8 @@ Našim modelima možete pristupiti i preko sljedećih API endpointa.
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -101,6 +101,7 @@ Našim modelima možete pristupiti i preko sljedećih API endpointa.
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
[model id](/docs/config/#models) u vašoj OpenCode konfiguraciji koristi format
|
||||
@@ -128,6 +129,7 @@ Podržavamo pay-as-you-go model. Ispod su cijene **po 1M tokena**.
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -177,6 +179,7 @@ Besplatni modeli:
|
||||
|
||||
- MiniMax M2.5 Free je dostupan na OpenCode ograničeno vrijeme. Tim koristi ovo vrijeme da prikupi povratne informacije i poboljša model.
|
||||
- Ling 2.6 Flash Free je dostupan na OpenCode ograničeno vrijeme. Tim koristi ovo vrijeme da prikupi povratne informacije i poboljša model.
|
||||
- Hy3 Preview Free je dostupan na OpenCode ograničeno vrijeme. Tim koristi ovo vrijeme da prikupi povratne informacije i poboljša model.
|
||||
- Nemotron 3 Super Free je dostupan na OpenCode ograničeno vrijeme. Tim koristi ovo vrijeme da prikupi povratne informacije i poboljša model.
|
||||
- Big Pickle je stealth model koji je besplatan na OpenCode ograničeno vrijeme. Tim koristi ovo vrijeme da prikupi povratne informacije i poboljša model.
|
||||
|
||||
@@ -226,6 +229,7 @@ i ne koriste vaše podatke za treniranje modela, uz sljedeće izuzetke:
|
||||
- Big Pickle: Tokom besplatnog perioda, prikupljeni podaci mogu se koristiti za poboljšanje modela.
|
||||
- MiniMax M2.5 Free: Tokom besplatnog perioda, prikupljeni podaci mogu se koristiti za poboljšanje modela.
|
||||
- Ling 2.6 Flash Free: Tokom besplatnog perioda, prikupljeni podaci mogu se koristiti za poboljšanje modela.
|
||||
- Hy3 Preview Free: Tokom besplatnog perioda, prikupljeni podaci mogu se koristiti za poboljšanje modela.
|
||||
- Nemotron 3 Super Free (besplatni NVIDIA endpointi): Dostupan je prema [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Samo za probnu upotrebu, nije za produkciju niti osjetljive podatke. NVIDIA bilježi promptove i izlaze radi poboljšanja svojih modela i usluga. Nemojte slati lične ili povjerljive podatke.
|
||||
- OpenAI APIs: Requests are retained for 30 days in accordance with [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: Requests are retained for 30 days in accordance with [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -90,8 +90,8 @@ Du kan også få adgang til vores modeller gennem følgende API-endpoints.
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -101,6 +101,7 @@ Du kan også få adgang til vores modeller gennem følgende API-endpoints.
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
[model id](/docs/config/#models) i din OpenCode-konfiguration
|
||||
@@ -128,6 +129,7 @@ Vi understøtter en pay-as-you-go-model. Nedenfor er priserne **pr. 1M tokens**.
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -177,6 +179,7 @@ De gratis modeller:
|
||||
|
||||
- MiniMax M2.5 Free er tilgængelig på OpenCode i en begrænset periode. Teamet bruger denne tid til at indsamle feedback og forbedre modellen.
|
||||
- Ling 2.6 Flash Free er tilgængelig på OpenCode i en begrænset periode. Teamet bruger denne tid til at indsamle feedback og forbedre modellen.
|
||||
- Hy3 Preview Free er tilgængelig på OpenCode i en begrænset periode. Teamet bruger denne tid til at indsamle feedback og forbedre modellen.
|
||||
- Nemotron 3 Super Free er tilgængelig på OpenCode i en begrænset periode. Teamet bruger denne tid til at indsamle feedback og forbedre modellen.
|
||||
- Big Pickle er en stealth-model, som er gratis på OpenCode i en begrænset periode. Teamet bruger denne tid til at indsamle feedback og forbedre modellen.
|
||||
|
||||
@@ -224,6 +227,7 @@ Alle vores modeller hostes i US. Vores udbydere følger en nul-opbevaringspoliti
|
||||
- Big Pickle: I den gratis periode kan indsamlede data blive brugt til at forbedre modellen.
|
||||
- MiniMax M2.5 Free: I den gratis periode kan indsamlede data blive brugt til at forbedre modellen.
|
||||
- Ling 2.6 Flash Free: I den gratis periode kan indsamlede data blive brugt til at forbedre modellen.
|
||||
- Hy3 Preview Free: I den gratis periode kan indsamlede data blive brugt til at forbedre modellen.
|
||||
- Nemotron 3 Super Free (gratis NVIDIA-endpoints): Leveres under [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Kun til prøvebrug, ikke til produktion eller følsomme data. Prompts og outputs logges af NVIDIA for at forbedre deres modeller og tjenester. Indsend ikke personlige eller fortrolige data.
|
||||
- OpenAI APIs: Anmodninger opbevares i 30 dage i overensstemmelse med [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: Anmodninger opbevares i 30 dage i overensstemmelse med [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -81,8 +81,8 @@ Du kannst auch über die folgenden API-Endpunkte auf unsere Modelle zugreifen.
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -92,6 +92,7 @@ Du kannst auch über die folgenden API-Endpunkte auf unsere Modelle zugreifen.
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
Die [Model-ID](/docs/config/#models) in deiner OpenCode-Konfiguration verwendet das Format `opencode/<model-id>`. Für GPT 5.4 würdest du zum Beispiel `opencode/gpt-5.4` in deiner Konfiguration verwenden.
|
||||
@@ -117,6 +118,7 @@ Wir unterstützen ein Pay-as-you-go-Modell. Unten findest du die Preise **pro 1M
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -166,6 +168,7 @@ Die kostenlosen Modelle:
|
||||
|
||||
- MiniMax M2.5 Free ist für begrenzte Zeit auf OpenCode verfügbar. Das Team nutzt diese Zeit, um Feedback zu sammeln und das Modell zu verbessern.
|
||||
- Ling 2.6 Flash Free ist für begrenzte Zeit auf OpenCode verfügbar. Das Team nutzt diese Zeit, um Feedback zu sammeln und das Modell zu verbessern.
|
||||
- Hy3 Preview Flash Free ist für begrenzte Zeit auf OpenCode verfügbar. Das Team nutzt diese Zeit, um Feedback zu sammeln und das Modell zu verbessern.
|
||||
- Nemotron 3 Super Free ist für begrenzte Zeit auf OpenCode verfügbar. Das Team nutzt diese Zeit, um Feedback zu sammeln und das Modell zu verbessern.
|
||||
- Big Pickle ist ein Stealth-Modell, das für begrenzte Zeit kostenlos auf OpenCode verfügbar ist. Das Team nutzt diese Zeit, um Feedback zu sammeln und das Modell zu verbessern.
|
||||
|
||||
@@ -210,6 +213,7 @@ Alle unsere Modelle werden in den USA gehostet. Unsere Provider folgen einer Zer
|
||||
- Big Pickle: Während des kostenlosen Zeitraums können gesammelte Daten zur Verbesserung des Modells verwendet werden.
|
||||
- MiniMax M2.5 Free: Während des kostenlosen Zeitraums können gesammelte Daten zur Verbesserung des Modells verwendet werden.
|
||||
- Ling 2.6 Flash Free: Während des kostenlosen Zeitraums können gesammelte Daten zur Verbesserung des Modells verwendet werden.
|
||||
- Hy3 Preview Free: Während des kostenlosen Zeitraums können gesammelte Daten zur Verbesserung des Modells verwendet werden.
|
||||
- Nemotron 3 Super Free (kostenlose NVIDIA-Endpunkte): Bereitgestellt gemäß den [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Nur für Testzwecke, nicht für Produktion oder sensible Daten. Eingaben und Ausgaben werden von NVIDIA protokolliert, um seine Modelle und Dienste zu verbessern. Übermitteln Sie keine personenbezogenen oder vertraulichen Daten.
|
||||
- OpenAI APIs: Anfragen werden in Übereinstimmung mit [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data) 30 Tage lang gespeichert.
|
||||
- Anthropic APIs: Anfragen werden in Übereinstimmung mit [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage) 30 Tage lang gespeichert.
|
||||
|
||||
@@ -90,8 +90,8 @@ También puedes acceder a nuestros modelos a través de los siguientes endpoints
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -101,6 +101,7 @@ También puedes acceder a nuestros modelos a través de los siguientes endpoints
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
El [identificador del modelo](/docs/config/#models) en tu configuración de OpenCode
|
||||
@@ -128,6 +129,7 @@ Admitimos un modelo de pago por uso. A continuación se muestran los precios **p
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -177,6 +179,7 @@ Los modelos gratuitos:
|
||||
|
||||
- MiniMax M2.5 Free está disponible en OpenCode por tiempo limitado. El equipo está usando este tiempo para recopilar comentarios y mejorar el modelo.
|
||||
- Ling 2.6 Flash Free está disponible en OpenCode por tiempo limitado. El equipo está usando este tiempo para recopilar comentarios y mejorar el modelo.
|
||||
- Hy3 Preview Flash Free está disponible en OpenCode por tiempo limitado. El equipo está usando este tiempo para recopilar comentarios y mejorar el modelo.
|
||||
- Nemotron 3 Super Free está disponible en OpenCode por tiempo limitado. El equipo está usando este tiempo para recopilar comentarios y mejorar el modelo.
|
||||
- Big Pickle es un modelo stealth que es gratuito en OpenCode por tiempo limitado. El equipo está usando este tiempo para recopilar comentarios y mejorar el modelo.
|
||||
|
||||
@@ -224,6 +227,7 @@ Todos nuestros modelos están alojados en US. Nuestros proveedores siguen una po
|
||||
- Big Pickle: Durante su período gratuito, los datos recopilados pueden usarse para mejorar el modelo.
|
||||
- MiniMax M2.5 Free: Durante su período gratuito, los datos recopilados pueden usarse para mejorar el modelo.
|
||||
- Ling 2.6 Flash Free: Durante su período gratuito, los datos recopilados pueden usarse para mejorar el modelo.
|
||||
- Hy3 Preview Free: Durante su período gratuito, los datos recopilados pueden usarse para mejorar el modelo.
|
||||
- Nemotron 3 Super Free (endpoints gratuitos de NVIDIA): Se ofrece bajo los [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Solo para uso de prueba, no para producción ni datos sensibles. NVIDIA registra los prompts y las salidas para mejorar sus modelos y servicios. No envíes datos personales ni confidenciales.
|
||||
- OpenAI APIs: Las solicitudes se conservan durante 30 días de acuerdo con [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: Las solicitudes se conservan durante 30 días de acuerdo con [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -81,8 +81,8 @@ Vous pouvez également accéder à nos modèles via les points de terminaison AP
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -92,6 +92,7 @@ Vous pouvez également accéder à nos modèles via les points de terminaison AP
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
Le [model id](/docs/config/#models) dans votre configuration OpenCode utilise le format `opencode/<model-id>`. Par exemple, pour GPT 5.4, vous utiliseriez `opencode/gpt-5.4` dans votre configuration.
|
||||
@@ -117,6 +118,7 @@ Nous prenons en charge un modèle de paiement à l'utilisation. Vous trouverez c
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -166,6 +168,7 @@ Les modèles gratuits :
|
||||
|
||||
- MiniMax M2.5 Free est disponible sur OpenCode pour une durée limitée. L'équipe utilise cette période pour recueillir des retours et améliorer le modèle.
|
||||
- Ling 2.6 Flash Free est disponible sur OpenCode pour une durée limitée. L'équipe utilise cette période pour recueillir des retours et améliorer le modèle.
|
||||
- Hy3 Preview Free est disponible sur OpenCode pour une durée limitée. L'équipe utilise cette période pour recueillir des retours et améliorer le modèle.
|
||||
- Nemotron 3 Super Free est disponible sur OpenCode pour une durée limitée. L'équipe utilise cette période pour recueillir des retours et améliorer le modèle.
|
||||
- Big Pickle est un modèle stealth gratuit sur OpenCode pour une durée limitée. L'équipe utilise cette période pour recueillir des retours et améliorer le modèle.
|
||||
|
||||
@@ -210,6 +213,7 @@ Tous nos modèles sont hébergés aux US. Nos fournisseurs suivent une politique
|
||||
- Big Pickle : Pendant sa période gratuite, les données collectées peuvent être utilisées pour améliorer le modèle.
|
||||
- MiniMax M2.5 Free : Pendant sa période gratuite, les données collectées peuvent être utilisées pour améliorer le modèle.
|
||||
- Ling 2.6 Flash Free : Pendant sa période gratuite, les données collectées peuvent être utilisées pour améliorer le modèle.
|
||||
- Hy3 Preview Free : Pendant sa période gratuite, les données collectées peuvent être utilisées pour améliorer le modèle.
|
||||
- Nemotron 3 Super Free (endpoints NVIDIA gratuits) : Fourni dans le cadre des [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Réservé à un usage d'essai, pas à la production ni aux données sensibles. Les prompts et les sorties sont journalisés par NVIDIA pour améliorer ses modèles et services. N'envoyez pas de données personnelles ou confidentielles.
|
||||
- OpenAI APIs : Les requêtes sont conservées pendant 30 jours conformément à [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs : Les requêtes sont conservées pendant 30 jours conformément à [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -90,8 +90,8 @@ Puoi anche accedere ai nostri modelli tramite i seguenti endpoint API.
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -101,6 +101,7 @@ Puoi anche accedere ai nostri modelli tramite i seguenti endpoint API.
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
Il [model id](/docs/config/#models) nella config di OpenCode
|
||||
@@ -128,6 +129,7 @@ Supportiamo un modello pay-as-you-go. Qui sotto trovi i prezzi **per 1M token**.
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -177,6 +179,7 @@ I modelli gratuiti:
|
||||
|
||||
- MiniMax M2.5 Free è disponibile su OpenCode per un periodo limitato. Il team usa questo periodo per raccogliere feedback e migliorare il modello.
|
||||
- Ling 2.6 Flash Free è disponibile su OpenCode per un periodo limitato. Il team usa questo periodo per raccogliere feedback e migliorare il modello.
|
||||
- Hy3 Preview Flash Free è disponibile su OpenCode per un periodo limitato. Il team usa questo periodo per raccogliere feedback e migliorare il modello.
|
||||
- Nemotron 3 Super Free è disponibile su OpenCode per un periodo limitato. Il team usa questo periodo per raccogliere feedback e migliorare il modello.
|
||||
- Big Pickle è un modello stealth che è gratuito su OpenCode per un periodo limitato. Il team usa questo periodo per raccogliere feedback e migliorare il modello.
|
||||
|
||||
@@ -224,6 +227,7 @@ Tutti i nostri modelli sono ospitati negli US. I nostri provider seguono una pol
|
||||
- Big Pickle: durante il periodo gratuito, i dati raccolti possono essere usati per migliorare il modello.
|
||||
- MiniMax M2.5 Free: durante il periodo gratuito, i dati raccolti possono essere usati per migliorare il modello.
|
||||
- Ling 2.6 Flash Free: durante il periodo gratuito, i dati raccolti possono essere usati per migliorare il modello.
|
||||
- Hy3 Preview Free: durante il periodo gratuito, i dati raccolti possono essere usati per migliorare il modello.
|
||||
- Nemotron 3 Super Free (endpoint NVIDIA gratuiti): fornito secondo i [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Solo per uso di prova, non per produzione o dati sensibili. NVIDIA registra prompt e output per migliorare i propri modelli e servizi. Non inviare dati personali o riservati.
|
||||
- OpenAI APIs: le richieste vengono conservate per 30 giorni in conformità con [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: le richieste vengono conservate per 30 giorni in conformità con [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -81,8 +81,8 @@ OpenCode Zen は、OpenCode のほかのプロバイダーと同じように動
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -92,6 +92,7 @@ OpenCode Zen は、OpenCode のほかのプロバイダーと同じように動
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
OpenCode 設定で使う [model id](/docs/config/#models) は `opencode/<model-id>` 形式です。たとえば、GPT 5.4 では設定に `opencode/gpt-5.4` を使用します。
|
||||
@@ -117,6 +118,7 @@ https://opencode.ai/zen/v1/models
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -166,6 +168,7 @@ https://opencode.ai/zen/v1/models
|
||||
|
||||
- MiniMax M2.5 Free は期間限定で OpenCode で利用できます。チームはこの期間中にフィードバックを集め、モデルを改善しています。
|
||||
- Ling 2.6 Flash Free は期間限定で OpenCode で利用できます。チームはこの期間中にフィードバックを集め、モデルを改善しています。
|
||||
- Hy3 Preview Flash Free は期間限定で OpenCode で利用できます。チームはこの期間中にフィードバックを集め、モデルを改善しています。
|
||||
- Nemotron 3 Super Free は期間限定で OpenCode で利用できます。チームはこの期間中にフィードバックを集め、モデルを改善しています。
|
||||
- Big Pickle はステルスモデルで、期間限定で OpenCode で無料提供されています。チームはこの期間中にフィードバックを集め、モデルを改善しています。
|
||||
|
||||
@@ -210,6 +213,7 @@ https://opencode.ai/zen/v1/models
|
||||
- Big Pickle: 無料提供期間中、収集されたデータがモデル改善に使われる場合があります。
|
||||
- MiniMax M2.5 Free: 無料提供期間中、収集されたデータがモデル改善に使われる場合があります。
|
||||
- Ling 2.6 Flash Free: 無料提供期間中、収集されたデータがモデル改善に使われる場合があります。
|
||||
- Hy3 Preview Free: 無料提供期間中、収集されたデータがモデル改善に使われる場合があります。
|
||||
- Nemotron 3 Super Free(NVIDIA の無料エンドポイント): [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf) に基づいて提供されます。試用専用であり、本番環境や機密性の高いデータには使用しないでください。プロンプトと出力は、NVIDIA が自社のモデルとサービスを改善するために記録します。個人情報や機密データは送信しないでください。
|
||||
- OpenAI APIs: リクエストは [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data) に従って 30 日間保持されます。
|
||||
- Anthropic APIs: リクエストは [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage) に従って 30 日間保持されます。
|
||||
|
||||
@@ -81,8 +81,8 @@ OpenCode Zen은 OpenCode의 다른 provider와 똑같이 작동합니다.
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -92,6 +92,7 @@ OpenCode Zen은 OpenCode의 다른 provider와 똑같이 작동합니다.
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
OpenCode config에서 사용하는 [모델 ID](/docs/config/#models)는 `opencode/<model-id>` 형식입니다. 예를 들어 GPT 5.4를 사용하려면 config에서 `opencode/gpt-5.4`를 사용하면 됩니다.
|
||||
@@ -117,6 +118,7 @@ https://opencode.ai/zen/v1/models
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -166,6 +168,7 @@ https://opencode.ai/zen/v1/models
|
||||
|
||||
- MiniMax M2.5 Free는 한정된 기간 동안 OpenCode에서 제공됩니다. 팀은 이 기간에 피드백을 수집하고 모델을 개선합니다.
|
||||
- Ling 2.6 Flash Free는 한정된 기간 동안 OpenCode에서 제공됩니다. 팀은 이 기간에 피드백을 수집하고 모델을 개선합니다.
|
||||
- Hy3 Preview Flash Free는 한정된 기간 동안 OpenCode에서 제공됩니다. 팀은 이 기간에 피드백을 수집하고 모델을 개선합니다.
|
||||
- Nemotron 3 Super Free는 한정된 기간 동안 OpenCode에서 제공됩니다. 팀은 이 기간에 피드백을 수집하고 모델을 개선합니다.
|
||||
- Big Pickle은 한정된 기간 동안 OpenCode에서 무료로 제공되는 stealth model입니다. 팀은 이 기간에 피드백을 수집하고 모델을 개선합니다.
|
||||
|
||||
@@ -210,6 +213,7 @@ https://opencode.ai/zen/v1/models
|
||||
- Big Pickle: 무료 제공 기간에는 수집된 데이터가 모델 개선에 사용될 수 있습니다.
|
||||
- MiniMax M2.5 Free: 무료 제공 기간에는 수집된 데이터가 모델 개선에 사용될 수 있습니다.
|
||||
- Ling 2.6 Flash Free: 무료 제공 기간에는 수집된 데이터가 모델 개선에 사용될 수 있습니다.
|
||||
- Hy3 Preview Free: 무료 제공 기간에는 수집된 데이터가 모델 개선에 사용될 수 있습니다.
|
||||
- Nemotron 3 Super Free(NVIDIA 무료 엔드포인트): [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf)에 따라 제공됩니다. 평가판 전용이며 프로덕션 환경이나 민감한 데이터에는 사용할 수 없습니다. NVIDIA는 자사 모델과 서비스를 개선하기 위해 프롬프트와 출력을 기록합니다. 개인 정보나 기밀 데이터는 제출하지 마세요.
|
||||
- OpenAI APIs: 요청은 [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data)에 따라 30일 동안 보관됩니다.
|
||||
- Anthropic APIs: 요청은 [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage)에 따라 30일 동안 보관됩니다.
|
||||
|
||||
@@ -90,8 +90,8 @@ Du kan også få tilgang til modellene våre gjennom følgende API-endepunkter.
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -101,6 +101,7 @@ Du kan også få tilgang til modellene våre gjennom følgende API-endepunkter.
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
[modell-id](/docs/config/#models) i OpenCode-konfigurasjonen din
|
||||
@@ -128,6 +129,7 @@ Vi støtter en pay-as-you-go-modell. Nedenfor er prisene **per 1M tokens**.
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -177,6 +179,7 @@ Gratis-modellene:
|
||||
|
||||
- MiniMax M2.5 Free er tilgjengelig på OpenCode i en begrenset periode. Teamet bruker denne tiden til å samle inn tilbakemeldinger og forbedre modellen.
|
||||
- Ling 2.6 Flash Free er tilgjengelig på OpenCode i en begrenset periode. Teamet bruker denne tiden til å samle inn tilbakemeldinger og forbedre modellen.
|
||||
- Hy3 Preview Free er tilgjengelig på OpenCode i en begrenset periode. Teamet bruker denne tiden til å samle inn tilbakemeldinger og forbedre modellen.
|
||||
- Nemotron 3 Super Free er tilgjengelig på OpenCode i en begrenset periode. Teamet bruker denne tiden til å samle inn tilbakemeldinger og forbedre modellen.
|
||||
- Big Pickle er en stealth-modell som er gratis på OpenCode i en begrenset periode. Teamet bruker denne tiden til å samle inn tilbakemeldinger og forbedre modellen.
|
||||
|
||||
@@ -224,6 +227,7 @@ Alle modellene våre hostes i US. Leverandørene våre følger en policy for zer
|
||||
- Big Pickle: I gratisperioden kan innsamlede data brukes til å forbedre modellen.
|
||||
- MiniMax M2.5 Free: I gratisperioden kan innsamlede data brukes til å forbedre modellen.
|
||||
- Ling 2.6 Flash Free: I gratisperioden kan innsamlede data brukes til å forbedre modellen.
|
||||
- Hy3 Preview Free: I gratisperioden kan innsamlede data brukes til å forbedre modellen.
|
||||
- Nemotron 3 Super Free (gratis NVIDIA-endepunkter): Leveres under [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Kun for prøvebruk, ikke for produksjon eller sensitive data. Prompter og svar logges av NVIDIA for å forbedre modellene og tjenestene deres. Ikke send inn personopplysninger eller konfidensielle data.
|
||||
- OpenAI APIs: Forespørsler lagres i 30 dager i samsvar med [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: Forespørsler lagres i 30 dager i samsvar med [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -90,8 +90,8 @@ Możesz też uzyskać dostęp do naszych modeli przez poniższe endpointy API.
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -101,6 +101,7 @@ Możesz też uzyskać dostęp do naszych modeli przez poniższe endpointy API.
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
[ID modelu](/docs/config/#models) w Twojej konfiguracji OpenCode używa formatu
|
||||
@@ -128,6 +129,7 @@ Obsługujemy model pay-as-you-go. Poniżej znajdują się ceny **za 1M tokenów*
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -178,6 +180,7 @@ Darmowe modele:
|
||||
|
||||
- MiniMax M2.5 Free jest dostępny w OpenCode przez ograniczony czas. Zespół wykorzystuje ten czas do zbierania opinii i ulepszania modelu.
|
||||
- Ling 2.6 Flash Free jest dostępny w OpenCode przez ograniczony czas. Zespół wykorzystuje ten czas do zbierania opinii i ulepszania modelu.
|
||||
- Hy3 Preview Flash Free jest dostępny w OpenCode przez ograniczony czas. Zespół wykorzystuje ten czas do zbierania opinii i ulepszania modelu.
|
||||
- Nemotron 3 Super Free jest dostępny w OpenCode przez ograniczony czas. Zespół wykorzystuje ten czas do zbierania opinii i ulepszania modelu.
|
||||
- Big Pickle to stealth model, który jest darmowy w OpenCode przez ograniczony czas. Zespół wykorzystuje ten czas do zbierania opinii i ulepszania modelu.
|
||||
|
||||
@@ -225,6 +228,7 @@ Wszystkie nasze modele są hostowane w US. Nasi dostawcy stosują politykę zero
|
||||
- Big Pickle: W czasie darmowego okresu zebrane dane mogą być wykorzystywane do ulepszania modelu.
|
||||
- MiniMax M2.5 Free: W czasie darmowego okresu zebrane dane mogą być wykorzystywane do ulepszania modelu.
|
||||
- Ling 2.6 Flash Free: W czasie darmowego okresu zebrane dane mogą być wykorzystywane do ulepszania modelu.
|
||||
- Hy3 Preview Free: W czasie darmowego okresu zebrane dane mogą być wykorzystywane do ulepszania modelu.
|
||||
- Nemotron 3 Super Free (darmowe endpointy NVIDIA): Udostępniany zgodnie z [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Tylko do użytku próbnego, nie do produkcji ani danych wrażliwych. NVIDIA rejestruje prompty i odpowiedzi, aby ulepszać swoje modele i usługi. Nie przesyłaj danych osobowych ani poufnych.
|
||||
- OpenAI APIs: Żądania są przechowywane przez 30 dni zgodnie z [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: Żądania są przechowywane przez 30 dni zgodnie z [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -81,8 +81,8 @@ Você também pode acessar nossos modelos pelos seguintes endpoints de API.
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -92,6 +92,7 @@ Você também pode acessar nossos modelos pelos seguintes endpoints de API.
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
O [model id](/docs/config/#models) na sua configuração do OpenCode usa o formato `opencode/<model-id>`. Por exemplo, para GPT 5.4, você usaria `opencode/gpt-5.4` na sua configuração.
|
||||
@@ -117,6 +118,7 @@ Oferecemos um modelo pay-as-you-go. Abaixo estão os preços **por 1M tokens**.
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -166,6 +168,7 @@ Os modelos gratuitos:
|
||||
|
||||
- MiniMax M2.5 Free está disponível no OpenCode por tempo limitado. A equipe está usando esse período para coletar feedback e melhorar o modelo.
|
||||
- Ling 2.6 Flash Free está disponível no OpenCode por tempo limitado. A equipe está usando esse período para coletar feedback e melhorar o modelo.
|
||||
- Hy3 Preview Free está disponível no OpenCode por tempo limitado. A equipe está usando esse período para coletar feedback e melhorar o modelo.
|
||||
- Nemotron 3 Super Free está disponível no OpenCode por tempo limitado. A equipe está usando esse período para coletar feedback e melhorar o modelo.
|
||||
- Big Pickle é um modelo stealth que está gratuito no OpenCode por tempo limitado. A equipe está usando esse período para coletar feedback e melhorar o modelo.
|
||||
|
||||
@@ -210,6 +213,7 @@ Todos os nossos modelos são hospedados nos US. Nossos provedores seguem uma pol
|
||||
- Big Pickle: Durante seu período gratuito, os dados coletados podem ser usados para melhorar o modelo.
|
||||
- MiniMax M2.5 Free: Durante seu período gratuito, os dados coletados podem ser usados para melhorar o modelo.
|
||||
- Ling 2.6 Flash Free: Durante seu período gratuito, os dados coletados podem ser usados para melhorar o modelo.
|
||||
- Hy3 Preview Free: Durante seu período gratuito, os dados coletados podem ser usados para melhorar o modelo.
|
||||
- Nemotron 3 Super Free (endpoints gratuitos da NVIDIA): Fornecido sob os [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Apenas para uso de avaliação, não para produção nem dados sensíveis. A NVIDIA registra prompts e saídas para melhorar seus modelos e serviços. Não envie dados pessoais ou confidenciais.
|
||||
- OpenAI APIs: As solicitações são retidas por 30 dias de acordo com [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: As solicitações são retidas por 30 dias de acordo com [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -90,8 +90,8 @@ OpenCode Zen работает как любой другой провайдер
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -101,6 +101,7 @@ OpenCode Zen работает как любой другой провайдер
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
[идентификатор модели](/docs/config/#models) в вашей конфигурации OpenCode
|
||||
@@ -128,6 +129,7 @@ https://opencode.ai/zen/v1/models
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -177,6 +179,7 @@ https://opencode.ai/zen/v1/models
|
||||
|
||||
- MiniMax M2.5 Free доступна в OpenCode ограниченное время. Команда использует это время, чтобы собирать отзывы и улучшать модель.
|
||||
- Ling 2.6 Flash Free доступна в OpenCode ограниченное время. Команда использует это время, чтобы собирать отзывы и улучшать модель.
|
||||
- Hy3 Preview Flash Free доступна в OpenCode ограниченное время. Команда использует это время, чтобы собирать отзывы и улучшать модель.
|
||||
- Nemotron 3 Super Free доступна в OpenCode ограниченное время. Команда использует это время, чтобы собирать отзывы и улучшать модель.
|
||||
- Big Pickle — это скрытая модель, которая доступна бесплатно в OpenCode ограниченное время. Команда использует это время, чтобы собирать отзывы и улучшать модель.
|
||||
|
||||
@@ -224,6 +227,7 @@ https://opencode.ai/zen/v1/models
|
||||
- Big Pickle: во время бесплатного периода собранные данные могут использоваться для улучшения модели.
|
||||
- MiniMax M2.5 Free: во время бесплатного периода собранные данные могут использоваться для улучшения модели.
|
||||
- Ling 2.6 Flash Free: во время бесплатного периода собранные данные могут использоваться для улучшения модели.
|
||||
- Hy3 Preview Free: во время бесплатного периода собранные данные могут использоваться для улучшения модели.
|
||||
- Nemotron 3 Super Free (бесплатные эндпоинты NVIDIA): предоставляется в соответствии с [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Только для пробного использования, не для продакшена и не для чувствительных данных. NVIDIA логирует запросы и ответы, чтобы улучшать свои модели и сервисы. Не отправляйте персональные или конфиденциальные данные.
|
||||
- OpenAI APIs: запросы хранятся 30 дней в соответствии с [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: запросы хранятся 30 дней в соответствии с [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -83,8 +83,8 @@ OpenCode Zen ทำงานเหมือน provider อื่น ๆ ใน
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -94,6 +94,7 @@ OpenCode Zen ทำงานเหมือน provider อื่น ๆ ใน
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
[model id](/docs/config/#models) ใน OpenCode config ของคุณใช้รูปแบบ `opencode/<model-id>` ตัวอย่างเช่น สำหรับ GPT 5.4 คุณจะใช้ `opencode/gpt-5.4` ใน config ของคุณ
|
||||
@@ -119,6 +120,7 @@ https://opencode.ai/zen/v1/models
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -168,6 +170,7 @@ https://opencode.ai/zen/v1/models
|
||||
|
||||
- MiniMax M2.5 Free เปิดให้ใช้บน OpenCode ในช่วงเวลาจำกัด ทีมกำลังใช้ช่วงเวลานี้เพื่อเก็บ feedback และปรับปรุงโมเดล
|
||||
- Ling 2.6 Flash Free เปิดให้ใช้บน OpenCode ในช่วงเวลาจำกัด ทีมกำลังใช้ช่วงเวลานี้เพื่อเก็บ feedback และปรับปรุงโมเดล
|
||||
- Hy3 Preview Free เปิดให้ใช้บน OpenCode ในช่วงเวลาจำกัด ทีมกำลังใช้ช่วงเวลานี้เพื่อเก็บ feedback และปรับปรุงโมเดล
|
||||
- Nemotron 3 Super Free เปิดให้ใช้บน OpenCode ในช่วงเวลาจำกัด ทีมกำลังใช้ช่วงเวลานี้เพื่อเก็บ feedback และปรับปรุงโมเดล
|
||||
- Big Pickle เป็น stealth model ที่ใช้งานฟรีบน OpenCode ในช่วงเวลาจำกัด ทีมกำลังใช้ช่วงเวลานี้เพื่อเก็บ feedback และปรับปรุงโมเดล
|
||||
|
||||
@@ -212,6 +215,7 @@ https://opencode.ai/zen/v1/models
|
||||
- Big Pickle: ระหว่างช่วงที่เปิดให้ใช้ฟรี ข้อมูลที่เก็บรวบรวมอาจถูกนำไปใช้เพื่อปรับปรุงโมเดล
|
||||
- MiniMax M2.5 Free: ระหว่างช่วงที่เปิดให้ใช้ฟรี ข้อมูลที่เก็บรวบรวมอาจถูกนำไปใช้เพื่อปรับปรุงโมเดล
|
||||
- Ling 2.6 Flash Free: ระหว่างช่วงที่เปิดให้ใช้ฟรี ข้อมูลที่เก็บรวบรวมอาจถูกนำไปใช้เพื่อปรับปรุงโมเดล
|
||||
- Hy3 Preview Free: ระหว่างช่วงที่เปิดให้ใช้ฟรี ข้อมูลที่เก็บรวบรวมอาจถูกนำไปใช้เพื่อปรับปรุงโมเดล
|
||||
- Nemotron 3 Super Free (endpoint ฟรีของ NVIDIA): ให้บริการภายใต้ [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf) ใช้สำหรับการทดลองเท่านั้น ไม่เหมาะสำหรับ production หรือข้อมูลที่อ่อนไหว NVIDIA จะบันทึก prompt และ output เพื่อนำไปปรับปรุงโมเดลและบริการของตน โปรดอย่าส่งข้อมูลส่วนบุคคลหรือข้อมูลลับ.
|
||||
- OpenAI APIs: คำขอจะถูกเก็บไว้เป็นเวลา 30 วันตาม [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: คำขอจะถูกเก็บไว้เป็นเวลา 30 วันตาม [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -81,8 +81,8 @@ Modellerimize aşağıdaki API uç noktaları aracılığıyla da erişebilirsin
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -92,6 +92,7 @@ Modellerimize aşağıdaki API uç noktaları aracılığıyla da erişebilirsin
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
OpenCode yapılandırmanızdaki [model id](/docs/config/#models) `opencode/<model-id>` biçimini kullanır. Örneğin, GPT 5.4 için yapılandırmanızda `opencode/gpt-5.4` kullanırsınız.
|
||||
@@ -117,6 +118,7 @@ Kullandıkça öde modelini destekliyoruz. Aşağıda **1M token başına** fiya
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -166,6 +168,7 @@ Kredi kartı ücretleri maliyet üzerinden yansıtılır (%4.4 + işlem başına
|
||||
|
||||
- MiniMax M2.5 Free, sınırlı bir süre için OpenCode'da ücretsizdir. Ekip bu süreyi geri bildirim toplamak ve modeli iyileştirmek için kullanıyor.
|
||||
- Ling 2.6 Flash Free, sınırlı bir süre için OpenCode'da ücretsizdir. Ekip bu süreyi geri bildirim toplamak ve modeli iyileştirmek için kullanıyor.
|
||||
- Hy3 Preview Free, sınırlı bir süre için OpenCode'da ücretsizdir. Ekip bu süreyi geri bildirim toplamak ve modeli iyileştirmek için kullanıyor.
|
||||
- Nemotron 3 Super Free, sınırlı bir süre için OpenCode'da ücretsizdir. Ekip bu süreyi geri bildirim toplamak ve modeli iyileştirmek için kullanıyor.
|
||||
- Big Pickle, sınırlı bir süre için OpenCode'da ücretsiz olan gizli bir modeldir. Ekip bu süreyi geri bildirim toplamak ve modeli iyileştirmek için kullanıyor.
|
||||
|
||||
@@ -210,6 +213,7 @@ Tüm modellerimiz US'de barındırılıyor. Sağlayıcılarımız zero-retention
|
||||
- Big Pickle: Ücretsiz döneminde toplanan veriler modeli iyileştirmek için kullanılabilir.
|
||||
- MiniMax M2.5 Free: Ücretsiz döneminde toplanan veriler modeli iyileştirmek için kullanılabilir.
|
||||
- Ling 2.6 Flash Free: Ücretsiz döneminde toplanan veriler modeli iyileştirmek için kullanılabilir.
|
||||
- Hy3 Preview Free: Ücretsiz döneminde toplanan veriler modeli iyileştirmek için kullanılabilir.
|
||||
- Nemotron 3 Super Free (ücretsiz NVIDIA uç noktaları): [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf) kapsamında sunulur. Yalnızca deneme amaçlıdır; üretim veya hassas veriler için uygun değildir. NVIDIA, modellerini ve hizmetlerini geliştirmek için promptları ve çıktıları kaydeder. Kişisel veya gizli veri göndermeyin.
|
||||
- OpenAI APIs: İstekler [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data) uyarınca 30 gün boyunca saklanır.
|
||||
- Anthropic APIs: İstekler [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage) uyarınca 30 gün boyunca saklanır.
|
||||
|
||||
@@ -90,8 +90,8 @@ You can also access our models through the following API endpoints.
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -101,6 +101,7 @@ You can also access our models through the following API endpoints.
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
The [model id](/docs/config/#models) in your OpenCode config
|
||||
@@ -128,6 +129,7 @@ We support a pay-as-you-go model. Below are the prices **per 1M tokens**.
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -177,6 +179,7 @@ The free models:
|
||||
|
||||
- MiniMax M2.5 Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model.
|
||||
- Ling 2.6 Flash Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model.
|
||||
- Hy3 Preview Flash Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model.
|
||||
- Nemotron 3 Super Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model.
|
||||
- Big Pickle is a stealth model that's free on OpenCode for a limited time. The team is using this time to collect feedback and improve the model.
|
||||
|
||||
@@ -224,6 +227,7 @@ All our models are hosted in the US. Our providers follow a zero-retention polic
|
||||
- Big Pickle: During its free period, collected data may be used to improve the model.
|
||||
- MiniMax M2.5 Free: During its free period, collected data may be used to improve the model.
|
||||
- Ling 2.6 Flash Free: During its free period, collected data may be used to improve the model.
|
||||
- Hy3 Preview Free: During its free period, collected data may be used to improve the model.
|
||||
- Nemotron 3 Super Free (NVIDIA free endpoints): Provided under the [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf). Trial use only — not for production or sensitive data. Prompts and outputs are logged by NVIDIA to improve its models and services. Do not submit personal or confidential data.
|
||||
- OpenAI APIs: Requests are retained for 30 days in accordance with [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
|
||||
- Anthropic APIs: Requests are retained for 30 days in accordance with [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
|
||||
|
||||
@@ -81,8 +81,8 @@ OpenCode Zen 的工作方式与 OpenCode 中的任何其他提供商相同。
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -92,6 +92,7 @@ OpenCode Zen 的工作方式与 OpenCode 中的任何其他提供商相同。
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
在你的 OpenCode 配置中,[模型 ID](/docs/config/#models) 使用 `opencode/<model-id>` 格式。例如,对于 GPT 5.4,你需要在配置中使用 `opencode/gpt-5.4`。
|
||||
@@ -117,6 +118,7 @@ https://opencode.ai/zen/v1/models
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -166,6 +168,7 @@ https://opencode.ai/zen/v1/models
|
||||
|
||||
- MiniMax M2.5 Free 目前在 OpenCode 上限时免费提供。团队正在利用这段时间收集反馈并改进模型。
|
||||
- Ling 2.6 Flash Free 目前在 OpenCode 上限时免费提供。团队正在利用这段时间收集反馈并改进模型。
|
||||
- Hy3 Preview Flash Free 目前在 OpenCode 上限时免费提供。团队正在利用这段时间收集反馈并改进模型。
|
||||
- Nemotron 3 Super Free 目前在 OpenCode 上限时免费提供。团队正在利用这段时间收集反馈并改进模型。
|
||||
- Big Pickle 是一个隐身模型,目前在 OpenCode 上限时免费提供。团队正在利用这段时间收集反馈并改进模型。
|
||||
|
||||
@@ -210,6 +213,7 @@ https://opencode.ai/zen/v1/models
|
||||
- Big Pickle:在免费期间,收集的数据可能会被用于改进模型。
|
||||
- MiniMax M2.5 Free:在免费期间,收集的数据可能会被用于改进模型。
|
||||
- Ling 2.6 Flash Free:在免费期间,收集的数据可能会被用于改进模型。
|
||||
- Hy3 Preview Free:在免费期间,收集的数据可能会被用于改进模型。
|
||||
- Nemotron 3 Super Free(NVIDIA 免费端点):根据 [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf) 提供。仅供试用,不适用于生产环境或敏感数据。NVIDIA 会记录提示词和输出内容,以改进其模型和服务。请勿提交个人或机密数据。
|
||||
- OpenAI APIs:请求会根据 [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data) 保留 30 天。
|
||||
- Anthropic APIs:请求会根据 [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage) 保留 30 天。
|
||||
|
||||
@@ -85,8 +85,8 @@ OpenCode Zen 的運作方式和 OpenCode 中的其他供應商一樣。
|
||||
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
|
||||
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/alibaba` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Qwen3.5 Plus | qwen3.5-plus | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -96,6 +96,7 @@ OpenCode Zen 的運作方式和 OpenCode 中的其他供應商一樣。
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Ling 2.6 Flash | ling-2.6-flash | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 Preview Free | hy3-preview-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Nemotron 3 Super Free | nemotron-3-super-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
|
||||
OpenCode 設定中的 [模型 ID](/docs/config/#models) 會使用 `opencode/<model-id>`
|
||||
@@ -122,6 +123,7 @@ https://opencode.ai/zen/v1/models
|
||||
| Big Pickle | Free | Free | Free | - |
|
||||
| MiniMax M2.5 Free | Free | Free | Free | - |
|
||||
| Ling 2.6 Flash Free | Free | Free | Free | - |
|
||||
| Hy3 Preview Free | Free | Free | Free | - |
|
||||
| Nemotron 3 Super Free | Free | Free | Free | - |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 |
|
||||
@@ -172,6 +174,7 @@ https://opencode.ai/zen/v1/models
|
||||
|
||||
- MiniMax M2.5 Free 在 OpenCode 上限時提供。團隊正在利用這段時間收集回饋並改進模型。
|
||||
- Ling 2.6 Flash Free 在 OpenCode 上限時提供。團隊正在利用這段時間收集回饋並改進模型。
|
||||
- Hy3 Preview Free 在 OpenCode 上限時提供。團隊正在利用這段時間收集回饋並改進模型。
|
||||
- Nemotron 3 Super Free 在 OpenCode 上限時提供。團隊正在利用這段時間收集回饋並改進模型。
|
||||
- Big Pickle 是一個隱身模型,在 OpenCode 上限時免費提供。團隊正在利用這段時間收集回饋並改進模型。
|
||||
|
||||
@@ -217,6 +220,7 @@ https://opencode.ai/zen/v1/models
|
||||
- Big Pickle: 在免費期間,收集到的資料可能會用於改進模型。
|
||||
- MiniMax M2.5 Free: 在免費期間,收集到的資料可能會用於改進模型。
|
||||
- Ling 2.6 Flash Free: 在免費期間,收集到的資料可能會用於改進模型。
|
||||
- Hy3 Preview Free: 在免費期間,收集到的資料可能會用於改進模型。
|
||||
- Nemotron 3 Super Free(NVIDIA 免費端點):依據 [NVIDIA API Trial Terms of Service](https://assets.ngc.nvidia.com/products/api-catalog/legal/NVIDIA%20API%20Trial%20Terms%20of%20Service.pdf) 提供。僅供試用,不適用於正式環境或敏感資料。NVIDIA 會記錄提示詞與輸出內容,以改進其模型與服務。請勿提交個人或機密資料。
|
||||
- OpenAI APIs: 請求會依據 [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data) 保留 30 天。
|
||||
- Anthropic APIs: 請求會依據 [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage) 保留 30 天。
|
||||
|
||||
+3
-3
@@ -61,7 +61,7 @@ async function typecheck() {
|
||||
console.log(" Running typecheck...")
|
||||
|
||||
try {
|
||||
await $`bun typecheck`.cwd("packages/opencode")
|
||||
await $`bun typecheck`
|
||||
return true
|
||||
} catch (err) {
|
||||
console.log(`Typecheck failed: ${err}`)
|
||||
@@ -113,7 +113,7 @@ async function fix(pr: PR, files: string[], prs: PR[], applied: number[], idx: n
|
||||
"If bun.lock is conflicted, do not hand-merge it. Delete bun.lock and run bun install after the code conflicts are resolved.",
|
||||
"If a PR already deleted a file/directory, do not re-add it, instead apply changes in the new semantic location.",
|
||||
"If a PR already changed an import, keep that change.",
|
||||
"After resolving the conflicts, run `bun typecheck` in `packages/opencode`.",
|
||||
"After resolving the conflicts, run `bun typecheck` at the repo root.",
|
||||
"If typecheck fails, you may also update any files reported by typecheck.",
|
||||
"Keep any non-conflict edits narrowly scoped to restoring a valid merged state for the current PR batch.",
|
||||
"Fix any merge-caused typecheck errors before finishing.",
|
||||
@@ -149,7 +149,7 @@ async function smoke(prs: PR[], applied: number[]) {
|
||||
const prompt = [
|
||||
"The beta merge batch is complete.",
|
||||
`Merged PRs on HEAD:\n${done}`,
|
||||
"Run `bun typecheck` in `packages/opencode`.",
|
||||
"Run `bun typecheck` at the repo root.",
|
||||
"Run `./script/build.ts --single` in `packages/opencode`.",
|
||||
"Fix any merge-caused issues until both commands pass.",
|
||||
"Do not create a commit.",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.14.20",
|
||||
"version": "1.14.21",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user