Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e4c5eb9cd | ||
|
|
f9248e8faa | ||
|
|
e1ed51b7fc | ||
|
|
ade6287241 | ||
|
|
4a72af3ed7 | ||
|
|
3fd1bddca1 | ||
|
|
0d57ecd879 | ||
|
|
f1dba7a9b8 | ||
|
|
fba26d1f9c | ||
|
|
44c0ec7847 | ||
|
|
d4db264a1a | ||
|
|
5c0d9ed030 | ||
|
|
43234b8b0c | ||
|
|
6fdb256ac2 | ||
|
|
861fe245c8 | ||
|
|
12c6c0925f | ||
|
|
240201b139 | ||
|
|
dcbe29c7c6 | ||
|
|
bd14ab0174 | ||
|
|
f59b41f1e0 | ||
|
|
7ab318a7a5 |
@@ -1,44 +0,0 @@
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||
import { JSX } from "solid-js"
|
||||
|
||||
export type DialogGoUpsellProps = {
|
||||
title: string
|
||||
description: JSX.Element
|
||||
link?: string
|
||||
actionLabel: string
|
||||
onClose?: (dontShowAgain?: boolean) => void
|
||||
}
|
||||
|
||||
export function DialogUsageExceeded(props: DialogGoUpsellProps) {
|
||||
const dialog = useDialog()
|
||||
const platform = usePlatform()
|
||||
|
||||
const runAction = () => {
|
||||
if (props.link) platform.openLink(props.link)
|
||||
props.onClose?.()
|
||||
dialog.close()
|
||||
}
|
||||
|
||||
const dismiss = () => {
|
||||
props.onClose?.(true)
|
||||
dialog.close()
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog title={props.title} description={props.description} fit>
|
||||
<div class="flex flex-col gap-4 pl-6 pr-2.5 pb-3">
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button variant="ghost" size="large" onClick={dismiss}>
|
||||
Don't show again
|
||||
</Button>
|
||||
<Button variant="primary" size="large" onClick={runAction}>
|
||||
{props.actionLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -64,7 +64,6 @@ import { Persist, persisted } from "@/utils/persist"
|
||||
import { extractPromptFromParts } from "@/utils/prompt"
|
||||
import { same } from "@/utils/same"
|
||||
import { formatServerError } from "@/utils/server-errors"
|
||||
import { useUsageExceededDialogs } from "./session/usage-exceeded-dialogs"
|
||||
|
||||
const emptyUserMessages: UserMessage[] = []
|
||||
type FollowupItem = FollowupDraft & { id: string }
|
||||
@@ -1646,8 +1645,6 @@ export default function Page() {
|
||||
if (fillFrame !== undefined) cancelAnimationFrame(fillFrame)
|
||||
})
|
||||
|
||||
useUsageExceededDialogs()
|
||||
|
||||
return (
|
||||
<div class="relative bg-background-base size-full overflow-hidden flex flex-col">
|
||||
{sessionSync() ?? ""}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
import { useSDK } from "@/context/sdk"
|
||||
import { Persist, persisted } from "@/utils/persist"
|
||||
import { SessionStatus } from "@opencode-ai/sdk/v2"
|
||||
import { onCleanup } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useSessionLayout } from "./session-layout"
|
||||
import { useDialog } from "@opencode-ai/ui/context"
|
||||
import { DialogUsageExceeded } from "@/components/dialog-usage-exceeded"
|
||||
import { useI18n } from "@opencode-ai/ui/context"
|
||||
|
||||
const GO_UPSELL_FREE_TIER_LAST_SEEN_AT = "go_upsell_last_seen_at"
|
||||
const GO_UPSELL_FREE_TIER_DONT_SHOW = "go_upsell_dont_show"
|
||||
const GO_UPSELL_ACCOUNT_RATE_LIMIT_LAST_SEEN_AT = "go_upsell_account_rate_limit_last_seen_at"
|
||||
const GO_UPSELL_ACCOUNT_RATE_LIMIT_DONT_SHOW = "go_upsell_account_rate_limit_dont_show"
|
||||
const GO_UPSELL_WINDOW = 86_400_000 // 24 hrs
|
||||
const GO_UPSELL_PROVIDERS = new Set(["opencode", "opencode-go"])
|
||||
|
||||
function goUpsellKeys(status: SessionStatus) {
|
||||
if (status.type !== "retry" || !status.action) return
|
||||
const { action } = status
|
||||
if (!GO_UPSELL_PROVIDERS.has(action.provider)) return
|
||||
if (action.reason === "free_tier_limit") {
|
||||
return {
|
||||
lastSeenAt: GO_UPSELL_FREE_TIER_LAST_SEEN_AT,
|
||||
dontShow: GO_UPSELL_FREE_TIER_DONT_SHOW,
|
||||
} as const
|
||||
}
|
||||
if (action.reason === "account_rate_limit") {
|
||||
return {
|
||||
lastSeenAt: GO_UPSELL_ACCOUNT_RATE_LIMIT_LAST_SEEN_AT,
|
||||
dontShow: GO_UPSELL_ACCOUNT_RATE_LIMIT_DONT_SHOW,
|
||||
} as const
|
||||
}
|
||||
}
|
||||
|
||||
export function useUsageExceededDialogs() {
|
||||
const sdk = useSDK()
|
||||
const dialog = useDialog()
|
||||
const { params } = useSessionLayout()
|
||||
const { t, locale } = useI18n()
|
||||
const isEnglish = () => locale() === "en"
|
||||
|
||||
const [goUpsellState, setGoUpsellState] = persisted(
|
||||
Persist.global("go-upsell"),
|
||||
createStore({
|
||||
[GO_UPSELL_FREE_TIER_LAST_SEEN_AT]: null as null | number,
|
||||
[GO_UPSELL_FREE_TIER_DONT_SHOW]: null as null | number,
|
||||
[GO_UPSELL_ACCOUNT_RATE_LIMIT_LAST_SEEN_AT]: null as null | number,
|
||||
[GO_UPSELL_ACCOUNT_RATE_LIMIT_DONT_SHOW]: null as null | number,
|
||||
}),
|
||||
)
|
||||
|
||||
onCleanup(
|
||||
sdk.event.on("session.status", (evt) => {
|
||||
if (evt.properties.sessionID !== params.id) return
|
||||
if (evt.properties.status.type !== "retry") return
|
||||
const { action } = evt.properties.status
|
||||
if (!action) return
|
||||
if (dialog.active) return
|
||||
|
||||
const keys = goUpsellKeys(evt.properties.status)
|
||||
if (!keys) return
|
||||
|
||||
const seen = goUpsellState[keys.lastSeenAt]
|
||||
if (seen && Date.now() - seen < GO_UPSELL_WINDOW) return
|
||||
if (goUpsellState[keys.dontShow]) return
|
||||
|
||||
if (action.reason === "free_tier_limit") {
|
||||
dialog.show(() => (
|
||||
<DialogUsageExceeded
|
||||
title={isEnglish() ? action.title : t("dialog.usageExceeded.freeTier.title")}
|
||||
description={isEnglish() ? action.message : t("dialog.usageExceeded.freeTier.description")}
|
||||
actionLabel={isEnglish() ? action.label : t("dialog.usageExceeded.freeTier.actionLabel")}
|
||||
link={action.link}
|
||||
onClose={(dontShowAgain) => {
|
||||
setGoUpsellState(keys.lastSeenAt, Date.now())
|
||||
if (dontShowAgain) setGoUpsellState(keys.dontShow, Date.now())
|
||||
else {
|
||||
void import("../../components/dialog-connect-provider").then((x) =>
|
||||
dialog.show(() => <x.DialogConnectProvider provider="opencode-go" />),
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))
|
||||
} else if (action.reason === "account_rate_limit") {
|
||||
dialog.show(() => (
|
||||
<DialogUsageExceeded
|
||||
title={isEnglish() ? action.title : t("dialog.usageExceeded.accountRateLimit.title")}
|
||||
description={isEnglish() ? action.message : t("dialog.usageExceeded.accountRateLimit.description")}
|
||||
actionLabel={isEnglish() ? action.label : t("dialog.usageExceeded.accountRateLimit.actionLabel")}
|
||||
link={action.link}
|
||||
onClose={(dontShowAgain) => {
|
||||
setGoUpsellState(keys.lastSeenAt, Date.now())
|
||||
if (dontShowAgain) setGoUpsellState(keys.dontShow, Date.now())
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -9,8 +9,6 @@ const rendererRoot = join(root, "../renderer")
|
||||
const rendererProtocol = "oc"
|
||||
const rendererHost = "renderer"
|
||||
const clipboardWritePermission = "clipboard-sanitized-write"
|
||||
const notificationPermission = "notifications"
|
||||
const rendererPermissions = new Set([clipboardWritePermission, notificationPermission])
|
||||
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
@@ -111,7 +109,7 @@ export function createMainWindow() {
|
||||
},
|
||||
})
|
||||
|
||||
allowRendererPermissions(win)
|
||||
allowClipboardWrite(win)
|
||||
|
||||
win.webContents.session.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||
const { requestHeaders } = details
|
||||
@@ -164,7 +162,7 @@ export function createLoadingWindow() {
|
||||
},
|
||||
})
|
||||
|
||||
allowRendererPermissions(win)
|
||||
allowClipboardWrite(win)
|
||||
|
||||
loadWindow(win, "loading.html")
|
||||
|
||||
@@ -201,16 +199,16 @@ function loadWindow(win: BrowserWindow, html: string) {
|
||||
void win.loadURL(`${rendererProtocol}://${rendererHost}/${html}`)
|
||||
}
|
||||
|
||||
function allowRendererPermissions(win: BrowserWindow) {
|
||||
function allowClipboardWrite(win: BrowserWindow) {
|
||||
win.webContents.session.setPermissionRequestHandler((webContents, permission, callback, details) => {
|
||||
callback(
|
||||
rendererPermissions.has(permission) &&
|
||||
permission === clipboardWritePermission &&
|
||||
isTrustedRendererUrl(details.requestingUrl) &&
|
||||
webContents.id === win.webContents.id,
|
||||
)
|
||||
})
|
||||
win.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
|
||||
if (!rendererPermissions.has(permission)) return false
|
||||
if (permission !== clipboardWritePermission) return false
|
||||
if (webContents && webContents.id !== win.webContents.id) return false
|
||||
return isTrustedRendererUrl(details.requestingUrl) || isTrustedRendererUrl(requestingOrigin)
|
||||
})
|
||||
|
||||
@@ -82,28 +82,6 @@ const STRUCTURED_OUTPUT_SYSTEM_PROMPT = `IMPORTANT: The user has requested struc
|
||||
|
||||
const log = Log.create({ service: "session.prompt" })
|
||||
const elog = EffectLogger.create({ service: "session.prompt" })
|
||||
const COMPACTION_WITHOUT_TAIL = "none"
|
||||
|
||||
function latestAutoCompactionBoundary(messages: MessageV2.WithParts[]) {
|
||||
const users = new Map(
|
||||
messages.flatMap((message) => {
|
||||
if (message.info.role !== "user") return []
|
||||
const part = message.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction" && item.auto)
|
||||
return part ? [[message.info.id, part] as const] : []
|
||||
}),
|
||||
)
|
||||
const latest = messages
|
||||
.flatMap((message) => {
|
||||
if (message.info.role !== "assistant") return []
|
||||
if (!message.info.summary || !message.info.finish || message.info.error) return []
|
||||
if (!users.has(message.info.parentID)) return []
|
||||
return [message.info]
|
||||
})
|
||||
.sort((a, b) => a.id.localeCompare(b.id))
|
||||
.at(-1)
|
||||
if (!latest) return
|
||||
return users.get(latest.parentID)?.tail_start_id ?? COMPACTION_WITHOUT_TAIL
|
||||
}
|
||||
|
||||
type ReferencePromptMetadata = {
|
||||
name: string
|
||||
@@ -1668,66 +1646,8 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
const slog = elog.with({ sessionID })
|
||||
let structured: unknown
|
||||
let step = 0
|
||||
let pendingAutoCompactionBoundary = latestAutoCompactionBoundary(yield* MessageV2.filterCompactedEffect(sessionID))
|
||||
let awaitingAutoCompactionProgress = false
|
||||
const session = yield* sessions.get(sessionID).pipe(Effect.orDie)
|
||||
|
||||
const requestAutoCompaction = Effect.fn("SessionPrompt.requestAutoCompaction")(function* (input: {
|
||||
lastUser: MessageV2.User
|
||||
model: Provider.Model
|
||||
messages: MessageV2.WithParts[]
|
||||
overflow?: boolean
|
||||
assistant?: MessageV2.Assistant
|
||||
}) {
|
||||
const boundary = latestAutoCompactionBoundary(input.messages)
|
||||
// A completed auto-compaction only counts as progress when it advances
|
||||
// the retained tail boundary. Otherwise another auto-trigger would loop.
|
||||
if (!awaitingAutoCompactionProgress || boundary !== pendingAutoCompactionBoundary) {
|
||||
pendingAutoCompactionBoundary = boundary
|
||||
awaitingAutoCompactionProgress = true
|
||||
yield* compaction.create({
|
||||
sessionID,
|
||||
agent: input.lastUser.agent,
|
||||
model: input.lastUser.model,
|
||||
auto: true,
|
||||
overflow: input.overflow,
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
const error = new MessageV2.ContextOverflowError({
|
||||
message:
|
||||
"Automatic compaction could not reduce this session enough to continue. Please start a new session.",
|
||||
}).toObject()
|
||||
yield* slog.info("auto compaction blocked", { boundary })
|
||||
if (input.assistant) {
|
||||
input.assistant.error = error
|
||||
input.assistant.finish = "error"
|
||||
input.assistant.time.completed ??= Date.now()
|
||||
yield* sessions.updateMessage(input.assistant)
|
||||
} else {
|
||||
yield* sessions.updateMessage({
|
||||
id: MessageID.ascending(),
|
||||
parentID: input.lastUser.id,
|
||||
role: "assistant",
|
||||
mode: input.lastUser.agent,
|
||||
agent: input.lastUser.agent,
|
||||
variant: input.lastUser.model.variant,
|
||||
path: { cwd: ctx.directory, root: ctx.worktree },
|
||||
cost: 0,
|
||||
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
modelID: input.model.id,
|
||||
providerID: input.model.providerID,
|
||||
time: { created: Date.now(), completed: Date.now() },
|
||||
sessionID,
|
||||
finish: "error",
|
||||
error,
|
||||
})
|
||||
}
|
||||
yield* bus.publish(Session.Event.Error, { sessionID, error })
|
||||
return false
|
||||
})
|
||||
|
||||
while (true) {
|
||||
yield* status.set(sessionID, { type: "busy" })
|
||||
yield* slog.info("loop", { step })
|
||||
@@ -1792,8 +1712,8 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
lastFinished.summary !== true &&
|
||||
(yield* compaction.isOverflow({ tokens: lastFinished.tokens, model }))
|
||||
) {
|
||||
if (yield* requestAutoCompaction({ lastUser, model, messages: msgs })) continue
|
||||
break
|
||||
yield* compaction.create({ sessionID, agent: lastUser.agent, model: lastUser.model, auto: true })
|
||||
continue
|
||||
}
|
||||
|
||||
const agent = yield* agents.get(lastUser.agent)
|
||||
@@ -1932,14 +1852,13 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
|
||||
if (result === "stop") return "break" as const
|
||||
if (result === "compact") {
|
||||
const created = yield* requestAutoCompaction({
|
||||
lastUser,
|
||||
model,
|
||||
messages: msgs,
|
||||
assistant: handle.message,
|
||||
yield* compaction.create({
|
||||
sessionID,
|
||||
agent: lastUser.agent,
|
||||
model: lastUser.model,
|
||||
auto: true,
|
||||
overflow: !handle.message.finish,
|
||||
})
|
||||
if (!created) return "break" as const
|
||||
}
|
||||
return "continue" as const
|
||||
}).pipe(
|
||||
|
||||
@@ -163,37 +163,7 @@ const blockingProcessor = Layer.succeed(
|
||||
}),
|
||||
)
|
||||
|
||||
const compactLoopProcessor = Layer.effect(
|
||||
SessionProcessor.Service,
|
||||
Effect.gen(function* () {
|
||||
const sessions = yield* Session.Service
|
||||
return SessionProcessor.Service.of({
|
||||
create: (input) =>
|
||||
Effect.succeed({
|
||||
message: input.assistantMessage,
|
||||
updateToolCall: () => Effect.succeed(undefined),
|
||||
completeToolCall: () => Effect.void,
|
||||
process: () =>
|
||||
Effect.gen(function* () {
|
||||
if (!input.assistantMessage.summary) return "compact" as const
|
||||
input.assistantMessage.finish = "stop"
|
||||
input.assistantMessage.time.completed = Date.now()
|
||||
yield* sessions.updateMessage(input.assistantMessage)
|
||||
yield* sessions.updatePart({
|
||||
id: PartID.ascending(),
|
||||
messageID: input.assistantMessage.id,
|
||||
sessionID: input.assistantMessage.sessionID,
|
||||
type: "text",
|
||||
text: "summary",
|
||||
})
|
||||
return "continue" as const
|
||||
}),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
function makeHttp(input?: { processor?: "blocking" | "compact-loop" }) {
|
||||
function makeHttp(input?: { processor?: "blocking" }) {
|
||||
const deps = Layer.mergeAll(
|
||||
Session.defaultLayer,
|
||||
Snapshot.defaultLayer,
|
||||
@@ -229,21 +199,15 @@ function makeHttp(input?: { processor?: "blocking" | "compact-loop" }) {
|
||||
Layer.provideMerge(deps),
|
||||
)
|
||||
const trunc = Truncate.layer.pipe(Layer.provideMerge(deps))
|
||||
const proc = (() => {
|
||||
switch (input?.processor) {
|
||||
case "blocking":
|
||||
return blockingProcessor
|
||||
case "compact-loop":
|
||||
return compactLoopProcessor.pipe(Layer.provideMerge(deps))
|
||||
default:
|
||||
return SessionProcessor.layer.pipe(
|
||||
const proc =
|
||||
input?.processor === "blocking"
|
||||
? blockingProcessor
|
||||
: SessionProcessor.layer.pipe(
|
||||
Layer.provide(summary),
|
||||
Layer.provide(Image.defaultLayer),
|
||||
Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true })),
|
||||
Layer.provideMerge(deps),
|
||||
)
|
||||
}
|
||||
})()
|
||||
const compact = SessionCompaction.layer.pipe(
|
||||
Layer.provide(RuntimeFlags.layer({ experimentalEventSystem: true })),
|
||||
Layer.provideMerge(proc),
|
||||
@@ -271,7 +235,6 @@ function makeHttp(input?: { processor?: "blocking" | "compact-loop" }) {
|
||||
|
||||
const it = testEffect(makeHttp())
|
||||
const race = testEffect(makeHttp({ processor: "blocking" }))
|
||||
const compactLoop = testEffect(makeHttp({ processor: "compact-loop" }))
|
||||
const unix = process.platform !== "win32" ? it.instance : it.instance.skip
|
||||
|
||||
// Config that registers a custom "test" provider with a "test-model" model
|
||||
@@ -514,34 +477,6 @@ it.instance(
|
||||
{ git: true },
|
||||
)
|
||||
|
||||
compactLoop.instance(
|
||||
"loop stops when auto-compaction does not make progress",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
yield* useServerConfig(providerCfg)
|
||||
const prompt = yield* SessionPrompt.Service
|
||||
const sessions = yield* Session.Service
|
||||
const chat = yield* sessions.create({ title: "Pinned" })
|
||||
yield* prompt.prompt({
|
||||
sessionID: chat.id,
|
||||
agent: "build",
|
||||
noReply: true,
|
||||
parts: [{ type: "text", text: "keep going until compaction fails" }],
|
||||
})
|
||||
|
||||
const result = yield* prompt.loop({ sessionID: chat.id })
|
||||
expect(result.info.role).toBe("assistant")
|
||||
if (result.info.role === "assistant") {
|
||||
expect(result.info.finish).toBe("error")
|
||||
expect(result.info.error?.name).toBe("ContextOverflowError")
|
||||
}
|
||||
|
||||
const messages = yield* sessions.messages({ sessionID: chat.id })
|
||||
expect(messages.flatMap((message) => message.parts).filter((part) => part.type === "compaction")).toHaveLength(2)
|
||||
}),
|
||||
{ git: true },
|
||||
)
|
||||
|
||||
it.instance(
|
||||
"prompt emits v2 prompted and synthetic events",
|
||||
() =>
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
runWithOwner,
|
||||
useContext,
|
||||
type JSX,
|
||||
startTransition,
|
||||
} from "solid-js"
|
||||
import { Dialog as Kobalte } from "@kobalte/core/dialog"
|
||||
import { makeEventListener } from "@solid-primitives/event-listener"
|
||||
@@ -155,7 +154,7 @@ export function useDialog() {
|
||||
},
|
||||
show(element: DialogElement, onClose?: () => void) {
|
||||
const base = ctx.active?.owner ?? owner
|
||||
return startTransition(() => ctx.show(element, base, onClose))
|
||||
ctx.show(element, base, onClose)
|
||||
},
|
||||
close() {
|
||||
ctx.close()
|
||||
|
||||
@@ -46,15 +46,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "تم تجاوز حد الاستخدام المجاني",
|
||||
"ui.sessionTurn.error.addCredits": "إضافة رصيد",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "تم الوصول إلى الحد المجاني",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"اشترك في OpenCode Go للحصول على وصول موثوق إلى أفضل النماذج مفتوحة المصدر، ابتداءً من $5/شهر.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "اشترك",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "تم الوصول إلى حد Go",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"تم الوصول إلى حد الاستخدام. لمتابعة استخدام هذا النموذج الآن، قم بتفعيل الاستخدام من رصيدك المتاح",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "فتح الإعدادات",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "تفويض العمل",
|
||||
"ui.sessionTurn.status.planning": "تخطيط الخطوات التالية",
|
||||
"ui.sessionTurn.status.gatheringContext": "استكشاف",
|
||||
|
||||
@@ -46,15 +46,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Limite de uso gratuito excedido",
|
||||
"ui.sessionTurn.error.addCredits": "Adicionar créditos",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Limite gratuito atingido",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"Assine o OpenCode Go para ter acesso confiável aos melhores modelos open-source, a partir de $5/mês.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "Assinar",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Limite do Go atingido",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Limite de uso atingido. Para continuar usando este modelo agora, ative o uso a partir do seu saldo disponível",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Abrir configurações",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Delegando trabalho",
|
||||
"ui.sessionTurn.status.planning": "Planejando próximos passos",
|
||||
"ui.sessionTurn.status.gatheringContext": "Explorando",
|
||||
|
||||
@@ -50,15 +50,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Besplatna upotreba premašena",
|
||||
"ui.sessionTurn.error.addCredits": "Dodaj kredite",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Dostignut besplatan limit",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"Pretplatite se na OpenCode Go za pouzdan pristup najboljim open-source modelima, počevši od $5/mjesec.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "Pretplati se",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Dostignut Go limit",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Dostignut je limit korištenja. Da nastavite koristiti ovaj model sada, omogućite korištenje iz vašeg dostupnog stanja",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Otvori postavke",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Delegiranje posla",
|
||||
"ui.sessionTurn.status.planning": "Planiranje sljedećih koraka",
|
||||
"ui.sessionTurn.status.gatheringContext": "Istraživanje",
|
||||
|
||||
@@ -45,15 +45,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Gratis forbrug overskredet",
|
||||
"ui.sessionTurn.error.addCredits": "Tilføj kreditter",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Gratis grænse nået",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"Abonnér på OpenCode Go for pålidelig adgang til de bedste open source-modeller, fra $5/måned.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "Abonnér",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Go-grænse nået",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Forbrugsgrænse nået. For at fortsætte med at bruge denne model nu, aktivér forbrug fra din tilgængelige saldo",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Åbn indstillinger",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Delegerer arbejde",
|
||||
"ui.sessionTurn.status.planning": "Planlægger næste trin",
|
||||
"ui.sessionTurn.status.gatheringContext": "Udforsker",
|
||||
|
||||
@@ -51,15 +51,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Kostenloses Nutzungslimit überschritten",
|
||||
"ui.sessionTurn.error.addCredits": "Guthaben aufladen",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Kostenloses Limit erreicht",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"Abonniere OpenCode Go für zuverlässigen Zugriff auf die besten Open-Source-Modelle, ab $5/Monat.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "Abonnieren",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Go-Limit erreicht",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Nutzungslimit erreicht. Um dieses Modell jetzt weiter zu nutzen, aktiviere die Nutzung über dein verfügbares Guthaben",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Einstellungen öffnen",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Arbeit delegieren",
|
||||
"ui.sessionTurn.status.planning": "Nächste Schritte planen",
|
||||
"ui.sessionTurn.status.gatheringContext": "Erkunden",
|
||||
|
||||
@@ -53,15 +53,6 @@ export const dict: Record<string, string> = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Free usage exceeded",
|
||||
"ui.sessionTurn.error.addCredits": "Add credits",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Free limit reached",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"Subscribe to OpenCode Go for reliable access to the best open-source models, starting at $5/month.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "Subscribe",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Go limit reached",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Usage limit reached. To continue using this model now, enable usage from your available balance",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Open settings",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Delegating work",
|
||||
"ui.sessionTurn.status.planning": "Planning next steps",
|
||||
"ui.sessionTurn.status.gatheringContext": "Exploring",
|
||||
|
||||
@@ -46,15 +46,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Límite de uso gratuito excedido",
|
||||
"ui.sessionTurn.error.addCredits": "Añadir créditos",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Límite gratuito alcanzado",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"Suscríbete a OpenCode Go para acceso fiable a los mejores modelos de código abierto, desde $5/mes.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "Suscribirse",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Límite de Go alcanzado",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Límite de uso alcanzado. Para seguir usando este modelo ahora, habilita el uso desde tu saldo disponible",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Abrir configuración",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Delegando trabajo",
|
||||
"ui.sessionTurn.status.planning": "Planificando siguientes pasos",
|
||||
"ui.sessionTurn.status.gatheringContext": "Explorando",
|
||||
|
||||
@@ -46,15 +46,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Limite d'utilisation gratuite dépassée",
|
||||
"ui.sessionTurn.error.addCredits": "Ajouter des crédits",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Limite gratuite atteinte",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"Abonnez-vous à OpenCode Go pour un accès fiable aux meilleurs modèles open source, à partir de $5/mois.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "S'abonner",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Limite Go atteinte",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Limite d'utilisation atteinte. Pour continuer à utiliser ce modèle maintenant, activez l'utilisation depuis votre solde disponible",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Ouvrir les paramètres",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Délégation du travail",
|
||||
"ui.sessionTurn.status.planning": "Planification des prochaines étapes",
|
||||
"ui.sessionTurn.status.gatheringContext": "Exploration",
|
||||
|
||||
@@ -45,15 +45,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "無料使用制限に達しました",
|
||||
"ui.sessionTurn.error.addCredits": "クレジットを追加",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "無料制限に達しました",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"OpenCode Go にサブスクライブして、最高のオープンソースモデルに安定してアクセスできます。月額 $5 から。",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "サブスクライブ",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Go の制限に達しました",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"使用制限に達しました。今すぐこのモデルを使い続けるには、利用可能な残高からの使用を有効にしてください",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "設定を開く",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "作業を委任中",
|
||||
"ui.sessionTurn.status.planning": "次のステップを計画中",
|
||||
"ui.sessionTurn.status.gatheringContext": "探索中",
|
||||
|
||||
@@ -46,15 +46,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "무료 사용량 초과",
|
||||
"ui.sessionTurn.error.addCredits": "크레딧 추가",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "무료 한도에 도달했습니다",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"OpenCode Go를 구독하여 최고의 오픈 소스 모델에 안정적으로 액세스하세요. 월 $5부터 시작합니다.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "구독",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Go 한도에 도달했습니다",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"사용량 한도에 도달했습니다. 지금 이 모델을 계속 사용하려면 사용 가능한 잔액에서 사용을 활성화하세요",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "설정 열기",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "작업 위임 중",
|
||||
"ui.sessionTurn.status.planning": "다음 단계 계획 중",
|
||||
"ui.sessionTurn.status.gatheringContext": "탐색 중",
|
||||
|
||||
@@ -49,15 +49,6 @@ export const dict: Record<Keys, string> = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Gratis bruk overskredet",
|
||||
"ui.sessionTurn.error.addCredits": "Legg til kreditt",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Gratis grense nådd",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"Abonner på OpenCode Go for pålitelig tilgang til de beste åpen kildekode-modellene, fra $5/måned.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "Abonner",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Go-grense nådd",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Bruksgrense nådd. For å fortsette å bruke denne modellen nå, aktiver bruk fra din tilgjengelige saldo",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Åpne innstillinger",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Delegerer arbeid",
|
||||
"ui.sessionTurn.status.planning": "Planlegger neste trinn",
|
||||
"ui.sessionTurn.status.gatheringContext": "Utforsker",
|
||||
|
||||
@@ -45,15 +45,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Przekroczono limit darmowego użytkowania",
|
||||
"ui.sessionTurn.error.addCredits": "Dodaj kredyty",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Osiągnięto limit darmowy",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"Subskrybuj OpenCode Go, aby uzyskać niezawodny dostęp do najlepszych modeli open source, od $5/miesiąc.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "Subskrybuj",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Osiągnięto limit Go",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Osiągnięto limit użycia. Aby kontynuować korzystanie z tego modelu teraz, włącz użycie z dostępnego salda",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Otwórz ustawienia",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Delegowanie pracy",
|
||||
"ui.sessionTurn.status.planning": "Planowanie kolejnych kroków",
|
||||
"ui.sessionTurn.status.gatheringContext": "Eksplorowanie",
|
||||
|
||||
@@ -45,15 +45,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Лимит бесплатного использования превышен",
|
||||
"ui.sessionTurn.error.addCredits": "Добавить кредиты",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Достигнут бесплатный лимит",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"Подпишитесь на OpenCode Go для надёжного доступа к лучшим моделям с открытым исходным кодом, от $5/месяц.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "Подписаться",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Достигнут лимит Go",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Достигнут лимит использования. Чтобы продолжить использовать эту модель сейчас, включите использование из доступного баланса",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Открыть настройки",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Делегирование работы",
|
||||
"ui.sessionTurn.status.planning": "Планирование следующих шагов",
|
||||
"ui.sessionTurn.status.gatheringContext": "Исследование",
|
||||
|
||||
@@ -47,15 +47,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "เกินขีดจำกัดการใช้งานฟรี",
|
||||
"ui.sessionTurn.error.addCredits": "เพิ่มเครดิต",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "ถึงขีดจำกัดฟรีแล้ว",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"สมัครสมาชิก OpenCode Go เพื่อการเข้าถึงโมเดลโอเพนซอร์สที่ดีที่สุดอย่างเชื่อถือได้ เริ่มต้นที่ $5/เดือน",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "สมัครสมาชิก",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "ถึงขีดจำกัดของ Go แล้ว",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"ถึงขีดจำกัดการใช้งานแล้ว หากต้องการใช้โมเดลนี้ต่อในตอนนี้ ให้เปิดใช้งานจากยอดคงเหลือที่มี",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "เปิดการตั้งค่า",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "มอบหมายงาน",
|
||||
"ui.sessionTurn.status.planning": "วางแผนขั้นตอนถัดไป",
|
||||
"ui.sessionTurn.status.gatheringContext": "กำลังสำรวจ",
|
||||
|
||||
@@ -52,15 +52,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "Ücretsiz kullanım aşıldı",
|
||||
"ui.sessionTurn.error.addCredits": "Kredi ekle",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "Ücretsiz sınıra ulaşıldı",
|
||||
"dialog.usageExceeded.freeTier.description":
|
||||
"En iyi açık kaynak modellere güvenilir erişim için OpenCode Go'ya abone olun. Aylık $5'tan başlar.",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "Abone ol",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Go sınırına ulaşıldı",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"Kullanım sınırına ulaşıldı. Bu modeli şimdi kullanmaya devam etmek için mevcut bakiyenizden kullanımı etkinleştirin",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "Ayarları aç",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "Görev devrediliyor",
|
||||
"ui.sessionTurn.status.planning": "Sonraki adımlar planlanıyor",
|
||||
"ui.sessionTurn.status.gatheringContext": "Keşfediliyor",
|
||||
|
||||
@@ -50,14 +50,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "免费使用额度已用完",
|
||||
"ui.sessionTurn.error.addCredits": "添加积分",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "免费额度已用完",
|
||||
"dialog.usageExceeded.freeTier.description": "订阅 OpenCode Go,可靠地使用最佳开源模型,每月 $5 起。",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "订阅",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "Go 额度已用完",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"使用额度已达上限。如需现在继续使用此模型,请从可用余额中启用使用",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "打开设置",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "正在委派工作",
|
||||
"ui.sessionTurn.status.planning": "正在规划下一步",
|
||||
"ui.sessionTurn.status.gatheringContext": "正在探索",
|
||||
|
||||
@@ -50,14 +50,6 @@ export const dict = {
|
||||
"ui.sessionTurn.error.freeUsageExceeded": "免費使用額度已用完",
|
||||
"ui.sessionTurn.error.addCredits": "新增點數",
|
||||
|
||||
"dialog.usageExceeded.freeTier.title": "已達免費額度上限",
|
||||
"dialog.usageExceeded.freeTier.description": "訂閱 OpenCode Go,可靠地使用最佳開源模型,每月 $5 起。",
|
||||
"dialog.usageExceeded.freeTier.actionLabel": "訂閱",
|
||||
"dialog.usageExceeded.accountRateLimit.title": "已達 Go 額度上限",
|
||||
"dialog.usageExceeded.accountRateLimit.description":
|
||||
"已達使用額度上限。若要現在繼續使用此模型,請從可用餘額中啟用使用",
|
||||
"dialog.usageExceeded.accountRateLimit.actionLabel": "開啟設定",
|
||||
|
||||
"ui.sessionTurn.status.delegating": "正在委派工作",
|
||||
"ui.sessionTurn.status.planning": "正在規劃下一步",
|
||||
"ui.sessionTurn.status.gatheringContext": "正在探索",
|
||||
|
||||
Reference in New Issue
Block a user