mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-10 19:49:48 -04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97c49512ac | |||
| b9f3b382fc | |||
| 550d1ffd24 | |||
| 941e71dbbb | |||
| d90532a595 |
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
@@ -96,7 +96,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"bin": {
|
||||
"lildax": "./bin/lildax.cjs",
|
||||
},
|
||||
@@ -144,7 +144,7 @@
|
||||
},
|
||||
"packages/codemode": {
|
||||
"name": "@opencode-ai/codemode",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"acorn": "8.15.0",
|
||||
"effect": "catalog:",
|
||||
@@ -158,7 +158,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -194,7 +194,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -221,7 +221,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "3.0.82",
|
||||
"@ai-sdk/openai": "3.0.48",
|
||||
@@ -243,7 +243,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -267,7 +267,7 @@
|
||||
},
|
||||
"packages/console/support": {
|
||||
"name": "@opencode-ai/console-support",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
@@ -287,7 +287,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@opencode-ai/core",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -381,7 +381,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"drizzle-orm": "catalog:",
|
||||
@@ -435,7 +435,7 @@
|
||||
},
|
||||
"packages/effect-drizzle-sqlite": {
|
||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -449,7 +449,7 @@
|
||||
},
|
||||
"packages/effect-sqlite-node": {
|
||||
"name": "@opencode-ai/effect-sqlite-node",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"effect": "catalog:",
|
||||
},
|
||||
@@ -461,7 +461,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@hono/standard-validator": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
@@ -493,7 +493,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -509,7 +509,7 @@
|
||||
},
|
||||
"packages/http-recorder": {
|
||||
"name": "@opencode-ai/http-recorder",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "4.0.0-beta.83",
|
||||
"@effect/platform-node-shared": "4.0.0-beta.83",
|
||||
@@ -540,7 +540,7 @@
|
||||
},
|
||||
"packages/llm": {
|
||||
"name": "@opencode-ai/llm",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@smithy/eventstream-codec": "4.2.14",
|
||||
@@ -559,7 +559,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -690,7 +690,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "3.0.8",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -766,7 +766,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"cross-spawn": "catalog:",
|
||||
},
|
||||
@@ -781,7 +781,7 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@opencode-ai/server",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/protocol": "workspace:*",
|
||||
@@ -796,7 +796,7 @@
|
||||
},
|
||||
"packages/session-ui": {
|
||||
"name": "@opencode-ai/session-ui",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/client": "file:../app/vendor/opencode-ai-client-1.17.13-v2.tgz",
|
||||
@@ -836,7 +836,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -849,7 +849,7 @@
|
||||
},
|
||||
"packages/stats/app": {
|
||||
"name": "@opencode-ai/stats-app",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -883,7 +883,7 @@
|
||||
},
|
||||
"packages/stats/core": {
|
||||
"name": "@opencode-ai/stats-core",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-athena": "3.933.0",
|
||||
"@planetscale/database": "1.19.0",
|
||||
@@ -902,7 +902,7 @@
|
||||
},
|
||||
"packages/stats/server": {
|
||||
"name": "@opencode-ai/stats-server",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-firehose": "3.933.0",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -944,7 +944,7 @@
|
||||
},
|
||||
"packages/tui": {
|
||||
"name": "@opencode-ai/tui",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
@@ -971,7 +971,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/diffs": "catalog:",
|
||||
@@ -1022,7 +1022,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -103,6 +103,7 @@ describe("normalizeProviderList", () => {
|
||||
)
|
||||
|
||||
expect(result.connected).toEqual(["openai"])
|
||||
expect(result.defaultModel).toEqual({ providerID: "openai", modelID: "gpt-5" })
|
||||
expect(result.default).toEqual({ openai: "gpt-5" })
|
||||
expect(result.all.get("openai")?.models["gpt-old"]).toBeUndefined()
|
||||
expect(result.all.get("openai")?.models["gpt-5"]).toMatchObject({
|
||||
@@ -113,6 +114,10 @@ describe("normalizeProviderList", () => {
|
||||
variants: { high: {} },
|
||||
})
|
||||
})
|
||||
|
||||
test("preserves an empty current default", () => {
|
||||
expect(normalizeProviderList([] as ProviderListOutput["data"], [], null).defaultModel).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe("directoryKey", () => {
|
||||
|
||||
@@ -139,6 +139,7 @@ export function normalizeProviderList(
|
||||
return {
|
||||
all,
|
||||
connected: providers.map((provider) => provider.id),
|
||||
defaultModel: defaultModel ? { providerID: defaultModel.providerID, modelID: defaultModel.id } : null,
|
||||
default: Object.fromEntries(
|
||||
providers.flatMap((provider) => {
|
||||
const model =
|
||||
|
||||
@@ -6,6 +6,7 @@ import { createStore } from "solid-js/store"
|
||||
import { useModels } from "@/context/models"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { useProviders } from "@/hooks/use-providers"
|
||||
import { resolveDefaultModel } from "@/hooks/provider-catalog"
|
||||
import { Persist, persisted } from "@/utils/persist"
|
||||
import { hasCustomAgent, resolveAgent } from "./local-agent"
|
||||
import { cycleModelVariant, getConfiguredAgentVariant, resolveModelVariant } from "./model-variant"
|
||||
@@ -149,10 +150,8 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
})
|
||||
|
||||
const configuredModel = () => {
|
||||
const configured = sync().data.config.model
|
||||
if (!configured) return
|
||||
const [providerID, modelID] = configured.split("/")
|
||||
const model = { providerID, modelID }
|
||||
const model = resolveDefaultModel(providers.defaultModel(), sync().data.config.model)
|
||||
if (!model) return
|
||||
if (validModel(model)) return model
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect, test } from "bun:test"
|
||||
import type { NormalizedProviderListResponse } from "@opencode-ai/session-ui/context"
|
||||
import { selectProviderCatalog } from "./provider-catalog"
|
||||
import { resolveDefaultModel, selectProviderCatalog } from "./provider-catalog"
|
||||
|
||||
const catalog = (id: string): NormalizedProviderListResponse => ({
|
||||
all: new Map([[id, { id, name: id, source: "api", env: [], options: {}, models: {} }]]),
|
||||
@@ -57,3 +57,21 @@ test("falls back to the global catalog for route consumers", () => {
|
||||
}),
|
||||
).toBe(global)
|
||||
})
|
||||
|
||||
test("uses the current server default model", () => {
|
||||
expect(resolveDefaultModel({ providerID: "openai", modelID: "gpt-5" }, "anthropic/claude")).toEqual({
|
||||
providerID: "openai",
|
||||
modelID: "gpt-5",
|
||||
})
|
||||
})
|
||||
|
||||
test("does not use legacy config when the current server has no default", () => {
|
||||
expect(resolveDefaultModel(null, "anthropic/claude")).toBeUndefined()
|
||||
})
|
||||
|
||||
test("uses config for legacy servers", () => {
|
||||
expect(resolveDefaultModel(undefined, "anthropic/claude")).toEqual({
|
||||
providerID: "anthropic",
|
||||
modelID: "claude",
|
||||
})
|
||||
})
|
||||
|
||||
@@ -25,3 +25,13 @@ export function selectProviderCatalog(input: ProviderCatalogInput) {
|
||||
if (input.explicit) return emptyProviderCatalog
|
||||
return input.global
|
||||
}
|
||||
|
||||
export function resolveDefaultModel(
|
||||
current: NormalizedProviderListResponse["defaultModel"],
|
||||
legacy: string | undefined,
|
||||
) {
|
||||
if (current !== undefined) return current ?? undefined
|
||||
if (!legacy) return undefined
|
||||
const [providerID, modelID] = legacy.split("/")
|
||||
return { providerID, modelID }
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ export function useProviders(directory: Accessor<string | undefined>) {
|
||||
return {
|
||||
all: () => providers().all,
|
||||
default: () => providers().default,
|
||||
defaultModel: () => providers().defaultModel,
|
||||
popular: () =>
|
||||
pipe(
|
||||
providers().all,
|
||||
|
||||
@@ -583,7 +583,7 @@ export const dict = {
|
||||
"dialog.usageExceeded.dontShowAgain": "不再显示",
|
||||
|
||||
"context.breakdown.title": "上下文细分",
|
||||
"context.breakdown.note": "输入词元的大致细分。“其他”包含工具定义和开销。",
|
||||
"context.breakdown.note": "输入 token 的大致细分。“其他”包含工具定义和开销。",
|
||||
"context.breakdown.system": "系统",
|
||||
"context.breakdown.user": "用户",
|
||||
"context.breakdown.assistant": "助手",
|
||||
@@ -598,18 +598,18 @@ export const dict = {
|
||||
"context.stats.provider": "提供商",
|
||||
"context.stats.model": "模型",
|
||||
"context.stats.limit": "上下文限制",
|
||||
"context.stats.totalTokens": "词元总数",
|
||||
"context.stats.totalTokens": "总 token",
|
||||
"context.stats.usage": "使用率",
|
||||
"context.stats.inputTokens": "输入词元",
|
||||
"context.stats.outputTokens": "输出词元",
|
||||
"context.stats.reasoningTokens": "推理词元",
|
||||
"context.stats.cacheTokens": "缓存词元(读/写)",
|
||||
"context.stats.inputTokens": "输入 token",
|
||||
"context.stats.outputTokens": "输出 token",
|
||||
"context.stats.reasoningTokens": "推理 token",
|
||||
"context.stats.cacheTokens": "缓存 token(读/写)",
|
||||
"context.stats.userMessages": "用户消息",
|
||||
"context.stats.assistantMessages": "助手消息",
|
||||
"context.stats.totalCost": "总成本",
|
||||
"context.stats.sessionCreated": "创建时间",
|
||||
"context.stats.lastActivity": "最后活动",
|
||||
"context.usage.tokens": "词元数",
|
||||
"context.usage.tokens": "Token",
|
||||
"context.usage.usage": "使用率",
|
||||
"context.usage.cost": "成本",
|
||||
"context.usage.clickToView": "点击查看上下文",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { usePrompt } from "@/context/prompt"
|
||||
import { useSDK } from "@/context/sdk"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { useProviders } from "@/hooks/use-providers"
|
||||
import { resolveDefaultModel } from "@/hooks/provider-catalog"
|
||||
|
||||
export function createPromptModelSelection(input: { agent: () => { model?: ModelKey; variant?: string } | undefined }) {
|
||||
const sdk = useSDK()
|
||||
@@ -21,10 +22,8 @@ export function createPromptModelSelection(input: { agent: () => { model?: Model
|
||||
}
|
||||
|
||||
const configured = () => {
|
||||
const value = sync().data.config.model
|
||||
if (!value) return
|
||||
const [providerID, modelID] = value.split("/")
|
||||
const model = { providerID, modelID }
|
||||
const model = resolveDefaultModel(providers.defaultModel(), sync().data.config.model)
|
||||
if (!model) return
|
||||
if (valid(model)) return model
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/codemode",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"description": "Effect-native confined code execution over schema-described tools",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-support",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"name": "@opencode-ai/core",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"private": true,
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://opencode.ai",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"name": "@opencode-ai/effect-sqlite-node",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"name": "@opencode-ai/http-recorder",
|
||||
"description": "Record and replay Effect HTTP client traffic with deterministic cassettes",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"name": "@opencode-ai/llm",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
You are OpenCode, a coding agent that helps users with software engineering tasks. You are powered by Muse Spark, a large language model trained by Meta MSL.
|
||||
You are OpenCode, a coding agent that helps users with software engineering tasks. You are powered by {{MODEL_NAME}}, a large language model trained by Meta MSL.
|
||||
|
||||
Use the instructions below and the tools available to assist the user.
|
||||
|
||||
@@ -61,5 +61,5 @@ Use the instructions below and the tools available to assist the user.
|
||||
- NEVER use comments as a place for long-winded chain-of-thought. Long thinking texts must be generated as private reasoning. Comments in code must be appropriately concise.
|
||||
|
||||
# User Help & Feedback
|
||||
- Users can give feedback or report issues at https://github.com/anomalyco/opencode and mention that they are using Meta Muse Spark.
|
||||
- Users can give feedback or report issues at https://github.com/anomalyco/opencode and mention that they are using Meta {{MODEL_NAME}}.
|
||||
- When users ask directly about OpenCode (eg. "can OpenCode do...", "are you able to do...") or its features (eg. implement a hook, write a slash command, or install an MCP server), use the WebFetch tool to gather information to answer the question from the OpenCode docs at https://opencode.ai/docs.
|
||||
|
||||
@@ -25,7 +25,10 @@ import { MCP } from "@/mcp"
|
||||
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
|
||||
|
||||
export function provider(model: Provider.Model) {
|
||||
if (model.api.id.includes("muse-spark")) return [PROMPT_META]
|
||||
if (model.api.id.includes("muse")) {
|
||||
const name = model.api.id.includes("muse-glimmer") ? "Muse Glimmer" : "Muse Spark"
|
||||
return [PROMPT_META.replaceAll("{{MODEL_NAME}}", name)]
|
||||
}
|
||||
if (model.api.id.includes("gpt-4") || model.api.id.includes("o1") || model.api.id.includes("o3"))
|
||||
return [PROMPT_BEAST]
|
||||
if (model.api.id.includes("gpt")) {
|
||||
|
||||
@@ -85,9 +85,21 @@ const it = testEffect(
|
||||
|
||||
describe("session.system", () => {
|
||||
test("selects the Meta prompt for Muse Spark model IDs", () => {
|
||||
expect(SystemPrompt.provider({ api: { id: "meta/muse-spark-preview" } } as Provider.Model)[0]).toContain(
|
||||
"Meta Muse Spark",
|
||||
)
|
||||
for (const id of ["meta/muse-spark-preview", "muse-spark-1.1", "muse-spark-1.2"]) {
|
||||
const prompt = SystemPrompt.provider({ api: { id } } as Provider.Model)[0]
|
||||
expect(prompt).toContain("powered by Muse Spark,")
|
||||
expect(prompt).toContain("using Meta Muse Spark.")
|
||||
expect(prompt).not.toContain("{{MODEL_NAME}}")
|
||||
}
|
||||
})
|
||||
|
||||
test("selects the Meta prompt for Muse Glimmer model IDs", () => {
|
||||
for (const id of ["meta/muse-glimmer", "meta/muse-glimmer-30b", "muse-glimmer-30b"]) {
|
||||
const prompt = SystemPrompt.provider({ api: { id } } as Provider.Model)[0]
|
||||
expect(prompt).toContain("powered by Muse Glimmer,")
|
||||
expect(prompt).toContain("using Meta Muse Glimmer.")
|
||||
expect(prompt).not.toContain("{{MODEL_NAME}}")
|
||||
}
|
||||
})
|
||||
|
||||
it.effect("skills output is sorted by name and stable across calls", () =>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -517,7 +517,15 @@ export type TuiSlots = {
|
||||
}
|
||||
|
||||
export type TuiEventBus = {
|
||||
on: <Type extends Event["type"]>(type: Type, handler: (event: Extract<Event, { type: Type }>) => void) => () => void
|
||||
on: <Type extends Event["type"]>(
|
||||
type: Type,
|
||||
handler: (event: Extract<Event, { type: Type }>, metadata: TuiEventMetadata) => void,
|
||||
) => () => void
|
||||
}
|
||||
|
||||
export type TuiEventMetadata = {
|
||||
directory: string
|
||||
workspace: string | undefined
|
||||
}
|
||||
|
||||
export type TuiDispose = () => void | Promise<void>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/server",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/session-ui",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -5,6 +5,10 @@ import { PreloadMultiFileDiffResult } from "@pierre/diffs/ssr"
|
||||
|
||||
export type NormalizedProviderListResponse = {
|
||||
all: Map<string, Provider>
|
||||
defaultModel?: {
|
||||
providerID: string
|
||||
modelID: string
|
||||
} | null
|
||||
default: {
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/stats-app",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/stats-core",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/stats-server",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/tui",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import type { Event } from "@opencode-ai/sdk/v2"
|
||||
import type { TuiAttentionSoundName, TuiPlugin, TuiPluginApi } from "@opencode-ai/plugin/tui"
|
||||
import type { TuiAttentionSoundName, TuiEventMetadata, TuiPlugin, TuiPluginApi } from "@opencode-ai/plugin/tui"
|
||||
import type { BuiltinTuiPlugin } from "../builtins"
|
||||
|
||||
const id = "internal:notifications"
|
||||
|
||||
type SessionError = Extract<Event, { type: "session.error" }>["properties"]["error"]
|
||||
|
||||
function matchesSession(api: TuiPluginApi, sessionID: string, metadata: TuiEventMetadata) {
|
||||
const session = api.state.session.get(sessionID)
|
||||
return session?.directory === metadata.directory && session.workspaceID === metadata.workspace
|
||||
}
|
||||
|
||||
function notify(api: TuiPluginApi, sessionID: string | undefined, message: string, sound: TuiAttentionSoundName) {
|
||||
const session = sessionID ? api.state.session.get(sessionID) : undefined
|
||||
if (!session) return
|
||||
const isSubagent = session?.parentID !== undefined
|
||||
void api.attention.notify({
|
||||
title: session?.title,
|
||||
@@ -32,32 +38,41 @@ const tui: TuiPlugin = async (api) => {
|
||||
const questions = new Set<string>()
|
||||
const permissions = new Set<string>()
|
||||
|
||||
api.event.on("question.asked", (event) => {
|
||||
api.event.on("question.asked", (event, metadata) => {
|
||||
if (!matchesSession(api, event.properties.sessionID, metadata)) return
|
||||
if (!api.state.session.question(event.properties.sessionID).some((item) => item.id === event.properties.id)) return
|
||||
if (questions.has(event.properties.id)) return
|
||||
questions.add(event.properties.id)
|
||||
notify(api, event.properties.sessionID, "Question needs input", "question")
|
||||
})
|
||||
|
||||
api.event.on("question.replied", (event) => {
|
||||
api.event.on("question.replied", (event, metadata) => {
|
||||
if (!matchesSession(api, event.properties.sessionID, metadata)) return
|
||||
questions.delete(event.properties.requestID)
|
||||
})
|
||||
|
||||
api.event.on("question.rejected", (event) => {
|
||||
api.event.on("question.rejected", (event, metadata) => {
|
||||
if (!matchesSession(api, event.properties.sessionID, metadata)) return
|
||||
questions.delete(event.properties.requestID)
|
||||
})
|
||||
|
||||
api.event.on("permission.asked", (event) => {
|
||||
api.event.on("permission.asked", (event, metadata) => {
|
||||
if (!matchesSession(api, event.properties.sessionID, metadata)) return
|
||||
if (!api.state.session.permission(event.properties.sessionID).some((item) => item.id === event.properties.id))
|
||||
return
|
||||
if (permissions.has(event.properties.id)) return
|
||||
permissions.add(event.properties.id)
|
||||
notify(api, event.properties.sessionID, "Permission needs input", "permission")
|
||||
})
|
||||
|
||||
api.event.on("permission.replied", (event) => {
|
||||
api.event.on("permission.replied", (event, metadata) => {
|
||||
if (!matchesSession(api, event.properties.sessionID, metadata)) return
|
||||
permissions.delete(event.properties.requestID)
|
||||
})
|
||||
|
||||
api.event.on("session.status", (event) => {
|
||||
api.event.on("session.status", (event, metadata) => {
|
||||
const sessionID = event.properties.sessionID
|
||||
if (!matchesSession(api, sessionID, metadata)) return
|
||||
if (event.properties.status.type === "busy" || event.properties.status.type === "retry") {
|
||||
active.add(sessionID)
|
||||
errored.delete(sessionID)
|
||||
@@ -77,9 +92,10 @@ const tui: TuiPlugin = async (api) => {
|
||||
notify(api, sessionID, "Session done", session?.parentID ? "subagent_done" : "done")
|
||||
})
|
||||
|
||||
api.event.on("session.error", (event) => {
|
||||
api.event.on("session.error", (event, metadata) => {
|
||||
const sessionID = event.properties.sessionID
|
||||
if (!sessionID) return
|
||||
if (!matchesSession(api, sessionID, metadata)) return
|
||||
if (!active.has(sessionID)) return
|
||||
errored.add(sessionID)
|
||||
notify(api, sessionID, sessionErrorMessage(event.properties.error), "error")
|
||||
|
||||
@@ -4,9 +4,14 @@ import type { Event, PermissionRequest, QuestionRequest, Session } from "@openco
|
||||
import type { TuiAttentionNotifyInput } from "@opencode-ai/plugin/tui"
|
||||
import { createTuiPluginApi } from "../../../fixture/tui-plugin"
|
||||
|
||||
async function setup() {
|
||||
async function setup(input: { auto?: boolean } = {}) {
|
||||
const notifications: TuiAttentionNotifyInput[] = []
|
||||
const handlers = new Map<Event["type"], ((event: Event) => void)[]>()
|
||||
const handlers = new Map<
|
||||
Event["type"],
|
||||
((event: Event, metadata: { directory: string; workspace: string | undefined }) => void)[]
|
||||
>()
|
||||
const permissions: Record<string, PermissionRequest[]> = {}
|
||||
const questions: Record<string, QuestionRequest[]> = {}
|
||||
const session = (id: string, title: string, parentID?: string): Session => ({
|
||||
id,
|
||||
title,
|
||||
@@ -33,9 +38,18 @@ async function setup() {
|
||||
},
|
||||
},
|
||||
event: {
|
||||
on: <Type extends Event["type"]>(type: Type, handler: (event: Extract<Event, { type: Type }>) => void) => {
|
||||
on: <Type extends Event["type"]>(
|
||||
type: Type,
|
||||
handler: (
|
||||
event: Extract<Event, { type: Type }>,
|
||||
metadata: { directory: string; workspace: string | undefined },
|
||||
) => void,
|
||||
) => {
|
||||
const list = handlers.get(type) ?? []
|
||||
const wrapped = handler as (event: Event) => void
|
||||
const wrapped = handler as (
|
||||
event: Event,
|
||||
metadata: { directory: string; workspace: string | undefined },
|
||||
) => void
|
||||
list.push(wrapped)
|
||||
handlers.set(type, list)
|
||||
return () => {
|
||||
@@ -49,6 +63,8 @@ async function setup() {
|
||||
state: {
|
||||
session: {
|
||||
get: (sessionID: string) => sessions[sessionID],
|
||||
permission: (sessionID: string) => permissions[sessionID] ?? [],
|
||||
question: (sessionID: string) => questions[sessionID] ?? [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -58,8 +74,30 @@ async function setup() {
|
||||
|
||||
return {
|
||||
notifications,
|
||||
emit(event: Event) {
|
||||
for (const handler of handlers.get(event.type) ?? []) handler(event)
|
||||
emit(event: Event, metadata = { directory: "/workspace", workspace: undefined as string | undefined }) {
|
||||
if (event.type === "permission.asked" && !input.auto) {
|
||||
permissions[event.properties.sessionID] = [
|
||||
...(permissions[event.properties.sessionID] ?? []).filter((item) => item.id !== event.properties.id),
|
||||
event.properties,
|
||||
]
|
||||
}
|
||||
if (event.type === "permission.replied") {
|
||||
permissions[event.properties.sessionID] = (permissions[event.properties.sessionID] ?? []).filter(
|
||||
(item) => item.id !== event.properties.requestID,
|
||||
)
|
||||
}
|
||||
if (event.type === "question.asked") {
|
||||
questions[event.properties.sessionID] = [
|
||||
...(questions[event.properties.sessionID] ?? []).filter((item) => item.id !== event.properties.id),
|
||||
event.properties,
|
||||
]
|
||||
}
|
||||
if (event.type === "question.replied" || event.type === "question.rejected") {
|
||||
questions[event.properties.sessionID] = (questions[event.properties.sessionID] ?? []).filter(
|
||||
(item) => item.id !== event.properties.requestID,
|
||||
)
|
||||
}
|
||||
for (const handler of handlers.get(event.type) ?? []) handler(event, metadata)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -136,6 +174,52 @@ describe("internal notifications TUI plugin", () => {
|
||||
])
|
||||
})
|
||||
|
||||
test("suppresses auto-approved permission requests", async () => {
|
||||
const harness = await setup({ auto: true })
|
||||
|
||||
harness.emit({ id: "event-1", type: "permission.asked", properties: permission("permission-1") })
|
||||
|
||||
expect(harness.notifications).toEqual([])
|
||||
})
|
||||
|
||||
test("ignores events outside the session location", async () => {
|
||||
const harness = await setup()
|
||||
const foreign = { directory: "/other", workspace: undefined }
|
||||
|
||||
harness.emit({ id: "event-1", type: "question.asked", properties: question("question-1") }, foreign)
|
||||
harness.emit({ id: "event-2", type: "permission.asked", properties: permission("permission-1") }, foreign)
|
||||
harness.emit(
|
||||
{ id: "event-2b", type: "permission.asked", properties: permission("permission-2") },
|
||||
{ directory: "/workspace", workspace: "other-workspace" },
|
||||
)
|
||||
harness.emit(
|
||||
{
|
||||
id: "event-3",
|
||||
type: "session.status",
|
||||
properties: { sessionID: "session", status: { type: "busy" } },
|
||||
},
|
||||
foreign,
|
||||
)
|
||||
harness.emit(
|
||||
{
|
||||
id: "event-4",
|
||||
type: "session.error",
|
||||
properties: { sessionID: "session", error: { name: "UnknownError", data: { message: "boom" } } },
|
||||
},
|
||||
foreign,
|
||||
)
|
||||
harness.emit(
|
||||
{
|
||||
id: "event-5",
|
||||
type: "session.status",
|
||||
properties: { sessionID: "session", status: { type: "idle" } },
|
||||
},
|
||||
foreign,
|
||||
)
|
||||
|
||||
expect(harness.notifications).toEqual([])
|
||||
})
|
||||
|
||||
test("notifies when an active session becomes idle and suppresses no-op idle", async () => {
|
||||
const harness = await setup()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.18.15",
|
||||
"version": "1.18.16",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user