Compare commits

..

1 Commits

Author SHA1 Message Date
Brendan Allan 73896b78fc fix(app): route review diffs through workspace 2026-08-10 16:46:13 +08:00
9 changed files with 42 additions and 67 deletions
+7 -7
View File
@@ -583,7 +583,7 @@ export const dict = {
"dialog.usageExceeded.dontShowAgain": "不再显示",
"context.breakdown.title": "上下文细分",
"context.breakdown.note": "输入 token 的大致细分。“其他”包含工具定义和开销。",
"context.breakdown.note": "输入词元的大致细分。“其他”包含工具定义和开销。",
"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": "总 token",
"context.stats.totalTokens": "词元总数",
"context.stats.usage": "使用率",
"context.stats.inputTokens": "输入 token",
"context.stats.outputTokens": "输出 token",
"context.stats.reasoningTokens": "推理 token",
"context.stats.cacheTokens": "缓存 token(读/写)",
"context.stats.inputTokens": "输入词元",
"context.stats.outputTokens": "输出词元",
"context.stats.reasoningTokens": "推理词元",
"context.stats.cacheTokens": "缓存词元(读/写)",
"context.stats.userMessages": "用户消息",
"context.stats.assistantMessages": "助手消息",
"context.stats.totalCost": "总成本",
"context.stats.sessionCreated": "创建时间",
"context.stats.lastActivity": "最后活动",
"context.usage.tokens": "Token",
"context.usage.tokens": "词元数",
"context.usage.usage": "使用率",
"context.usage.cost": "成本",
"context.usage.clickToView": "点击查看上下文",
+13 -3
View File
@@ -676,9 +676,16 @@ export default function Page() {
const mode = reviewMode()
if (mode === "git" || mode === "branch") return mode
})
const vcsWorkspace = createMemo(() => info()?.workspaceID)
const vcsKey = createMemo(
() =>
["session-vcs", sdk().directory, sync().data.vcs?.branch ?? "", sync().data.vcs?.default_branch ?? ""] as const,
[
"session-vcs",
sdk().directory,
vcsWorkspace() ?? "",
sync().data.vcs?.branch ?? "",
sync().data.vcs?.default_branch ?? "",
] as const,
)
const vcsQuery = createQuery(() => {
const mode = vcsMode()
@@ -690,7 +697,10 @@ export default function Page() {
queryFn: mode
? () =>
sdk()
.api.vcs.diff({ location: { directory: sdk().directory }, mode: mode === "git" ? "working" : mode })
.api.vcs.diff({
location: { directory: sdk().directory, workspace: vcsWorkspace() },
mode: mode === "git" ? "working" : mode,
})
.then((result) => result.data)
.catch((error) => {
console.debug("[session-review] failed to load vcs diff", { mode, error })
@@ -739,7 +749,7 @@ export default function Page() {
queryFn: () =>
sdk()
.api.vcs.diff({
location: { directory: scope },
location: { directory: scope, workspace: vcsWorkspace() },
mode: mode === "git" ? "working" : mode,
context,
})
@@ -184,6 +184,20 @@ describe("createCompatibleApi", () => {
expect(url.searchParams.get("limit")).toBe("20")
})
test("routes V1 diffs through the requested workspace", async () => {
const { api, requests } = setup("v1")
await api.vcs.diff({
location: { directory: "/repo", workspace: "workspace-1" },
mode: "working",
})
const url = new URL(requests[0]!.url)
expect(url.pathname).toBe("/vcs/diff")
expect(url.searchParams.get("directory")).toBe("/repo")
expect(url.searchParams.get("workspace")).toBe("workspace-1")
expect(url.searchParams.get("mode")).toBe("git")
})
test("routes V1 permission replies through the requested directory", async () => {
const { api, requests } = setup("v1")
await api.permission.reply({
+1
View File
@@ -344,6 +344,7 @@ function createV1Api(input: CompatibleInput): CompatibleApi {
const result = await legacy(value.location).vcs.diff({
mode: value.mode === "working" ? "git" : value.mode,
context: value.context,
workspace: value.location?.workspace,
})
return located(
(result.data ?? []).map((file) => ({
@@ -5,9 +5,6 @@ export default {
id: "20260410174513_workspace-name",
up(tx) {
return Effect.gen(function* () {
const columns = yield* tx.all<{ name: string }>(`PRAGMA table_info(\`workspace\`)`)
const name = columns.some((column) => column.name === "name") ? "`name`" : "''"
yield* tx.run(`PRAGMA foreign_keys=OFF;`)
yield* tx.run(`
CREATE TABLE \`__new_workspace\` (
@@ -22,7 +19,7 @@ export default {
);
`)
yield* tx.run(
`INSERT INTO \`__new_workspace\`(\`id\`, \`type\`, \`branch\`, \`name\`, \`directory\`, \`extra\`, \`project_id\`) SELECT \`id\`, \`type\`, \`branch\`, ${name}, \`directory\`, \`extra\`, \`project_id\` FROM \`workspace\`;`,
`INSERT INTO \`__new_workspace\`(\`id\`, \`type\`, \`branch\`, \`name\`, \`directory\`, \`extra\`, \`project_id\`) SELECT \`id\`, \`type\`, \`branch\`, \`name\`, \`directory\`, \`extra\`, \`project_id\` FROM \`workspace\`;`,
)
yield* tx.run(`DROP TABLE \`workspace\`;`)
yield* tx.run(`ALTER TABLE \`__new_workspace\` RENAME TO \`workspace\`;`)
@@ -8,7 +8,6 @@ import { Effect, Layer } from "effect"
import { eq, inArray, sql } from "drizzle-orm"
import { DatabaseMigration } from "@opencode-ai/core/database/migration"
import { migrations } from "@opencode-ai/core/database/migration.gen"
import workspaceNameMigration from "@opencode-ai/core/database/migration/20260410174513_workspace-name"
import sessionUsageMigration from "@opencode-ai/core/database/migration/20260510033149_session_usage"
import normalizeStoragePathsMigration from "@opencode-ai/core/database/migration/20260601010001_normalize_storage_paths"
import sessionMessageProjectionOrderMigration from "@opencode-ai/core/database/migration/20260603040000_session_message_projection_order"
@@ -320,37 +319,6 @@ describe("DatabaseMigration", () => {
)
})
test("defaults missing workspace names during legacy workspace rebuild", async () => {
await run(
Effect.gen(function* () {
const db = yield* makeDb
yield* db.run(sql`
CREATE TABLE workspace (
id text PRIMARY KEY,
type text NOT NULL,
branch text,
directory text,
extra text,
project_id text NOT NULL
)
`)
yield* db.run(sql`
INSERT INTO workspace (id, type, branch, directory, extra, project_id)
VALUES ('wrk_legacy', 'remote', 'main', '/repo', '{}', 'proj_legacy')
`)
yield* DatabaseMigration.applyOnly(db, [workspaceNameMigration])
expect(yield* db.get(sql`SELECT id, name, directory, extra FROM workspace`)).toEqual({
id: "wrk_legacy",
name: "",
directory: "/repo",
extra: "{}",
})
}),
)
})
test("resets incompatible projected Session messages before adding sequence order", async () => {
await run(
Effect.gen(function* () {
@@ -1,4 +1,4 @@
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.
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.
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 {{MODEL_NAME}}.
- Users can give feedback or report issues at https://github.com/anomalyco/opencode and mention that they are using Meta Muse Spark.
- 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.
+1 -4
View File
@@ -25,10 +25,7 @@ import { MCP } from "@/mcp"
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
export function provider(model: Provider.Model) {
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("muse-spark")) return [PROMPT_META]
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")) {
+3 -15
View File
@@ -85,21 +85,9 @@ const it = testEffect(
describe("session.system", () => {
test("selects the Meta prompt for Muse Spark model IDs", () => {
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}}")
}
expect(SystemPrompt.provider({ api: { id: "meta/muse-spark-preview" } } as Provider.Model)[0]).toContain(
"Meta Muse Spark",
)
})
it.effect("skills output is sorted by name and stable across calls", () =>