Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
c2e019b
sync release versions for v1.17.18
Jul 9, 2026
9c6bb4f
fix(app): show tree while opening files (#36018)
Hona Jul 9, 2026
b94e55c
fix(app): align context tokens with usage (#36015)
Hona Jul 9, 2026
13b9e55
fix(ui): sometimes outlines get clipped (#33846)
opencode-agent[bot] Jul 9, 2026
251177d
fix(tui): forward environment to worker (#35925)
opencode-agent[bot] Jul 9, 2026
85ef35d
fix(data): oom crash in stats sync
adamdotdevin Jul 10, 2026
d09d561
chore: generate
opencode-agent[bot] Jul 10, 2026
ea982c3
fix(app): wrap session creation state updates in startTransition (#36…
Brendonovich Jul 10, 2026
01131c7
fix(app): focus input on new session (#36077)
opencode-agent[bot] Jul 10, 2026
c769380
feat(app): visual improvements (#36073)
arvsrn Jul 10, 2026
772e976
fix(app): timeline width jumps around when review panel is open/close…
arvsrn Jul 10, 2026
d0ba538
zen: track openai cache_write_tokens
fwang Jul 10, 2026
8a03fc2
feat(app): extract prompt state and add composer model selection (#36…
Brendonovich Jul 10, 2026
60c8833
feat(stats): update model comparison page
adamdotdevin Jul 10, 2026
08a2a62
fix(data): cut athena stats sync cost
adamdotdevin Jul 10, 2026
7dbfd0b
chore: generate
opencode-agent[bot] Jul 10, 2026
f4faacb
feat(stats): unify comparison cards
adamdotdevin Jul 10, 2026
0340a4f
fix(stats): restore native trackpad scrolling
adamdotdevin Jul 10, 2026
a84151e
fix(stats): restore sticky comparison header
adamdotdevin Jul 10, 2026
1c7f65f
fix(stats): stop comparison overscroll
adamdotdevin Jul 10, 2026
4a1982f
feat(stats): add model comparison radar
adamdotdevin Jul 10, 2026
233f069
feat(stats): add comparison seo pages
adamdotdevin Jul 10, 2026
5df822b
fix(stats): polish comparison details
adamdotdevin Jul 10, 2026
9976269
docs: publish GPT 5.6 Sol, Terra, and Luna model availability and pri…
fwang Jul 10, 2026
2db96c9
fix(core): disable unused fff content caches (#36453)
opencode-agent[bot] Jul 11, 2026
34e5809
feat(tui): show idle session directory (#36457)
opencode-agent[bot] Jul 11, 2026
a244d82
test(opencode): refresh models.dev fixture (#36541)
rekram1-node Jul 12, 2026
184da0e
test(opencode): refresh stale model references (#36546)
rekram1-node Jul 12, 2026
d7c0db8
fix(openai): use codex context limits for gpt-5.6 (#36248)
nabilfreeman Jul 12, 2026
4dcfd91
chore: generate
opencode-agent[bot] Jul 12, 2026
6f8e1dd
fix(provider): derive variants from reasoning options (#36543)
rekram1-node Jul 12, 2026
cf75036
chore: generate
opencode-agent[bot] Jul 12, 2026
8036440
ci: remove starptech from core triage assignees (#36618)
opencode-agent[bot] Jul 13, 2026
8168f0f
fix(provider): route gateway variants by api id (#36614)
rekram1-node Jul 13, 2026
f476847
fix(opencode): filter unsupported GPT-5.6 OAuth alias (#36621)
rekram1-node Jul 13, 2026
a8062ea
fix(provider): derive variants from reasoning metadata (#36624)
rekram1-node Jul 13, 2026
dd02cea
feat(desktop): fix clipped labels and branch tooltip (#35724)
usrnk1 Jul 13, 2026
140e796
feat(ui): add accent badge variant
usrnk1 Jul 8, 2026
ac5e249
feat(app): add interface transition setting
usrnk1 Jul 8, 2026
97f502e
feat(app): middle click to open new tab (#36215)
arvsrn Jul 13, 2026
9bbf791
chore: generate
opencode-agent[bot] Jul 13, 2026
17cd4a8
feat(app): align edit project modal with v2 style (#36213)
arvsrn Jul 13, 2026
d595c7e
feat(app): review panel updates (#36240)
arvsrn Jul 13, 2026
49028b6
chore: generate
opencode-agent[bot] Jul 13, 2026
446510a
tui: surface a dismissible new-interface notice and tighten settings …
usrnk1 Jul 13, 2026
8ce5038
Merge branch 'dev' of https://github.com/anomalyco/opencode into inte…
usrnk1 Jul 13, 2026
2b29854
chore: generate
opencode-agent[bot] Jul 13, 2026
b4e49d5
feat(app): redesign attachment cards (#35945)
arvsrn Jul 13, 2026
51b9c72
fix(app): remove interface transition changes accidentally merged int…
usrnk1 Jul 13, 2026
b3a012c
fix(cli): switch org after console logout (#36276)
opencode-agent[bot] Jul 13, 2026
99668cf
fix(provider): tweak reasoning option driven max budget variants (#36…
rekram1-node Jul 13, 2026
5401eba
chore: bump gitlab-ai-provider to 6.11.0 (#36668)
vglafirov Jul 13, 2026
7ec3d67
chore: update nix node_modules hashes
opencode-agent[bot] Jul 13, 2026
67aa9ce
fix(opencode): support Luna Responses Lite over OAuth (#36685)
rekram1-node Jul 13, 2026
49d997a
fix(xai): default store to false for Responses (#36629)
geraint0923 Jul 13, 2026
92cc8f5
fix(stats): correct breadcrumb behavior
adamdotdevin Jul 13, 2026
c2f93ae
chore: generate
opencode-agent[bot] Jul 13, 2026
e434ce0
fix(provider): support OpenAI pro reasoning mode (#36694)
rekram1-node Jul 13, 2026
dc927a7
release: v1.17.19
Jul 13, 2026
b3bd21d
chore(termux): seed release automation
github-actions[bot] Jul 13, 2026
a991590
Termux v1.17.19 (#24)
unemployabot[bot] Jul 13, 2026
6490a7d
checkpoint: prepare release-1.17.19 for termux-target
github-actions[bot] Jul 13, 2026
ad982af
fix(termux): resolve 1.17.19 checkpoint conflicts
wallentx Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .opencode/tool/github-triage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { tool } from "@opencode-ai/plugin"
const TEAM = {
tui: ["kommander", "simonklee"],
desktop_web: ["Hona", "Brendonovich"],
core: ["jlongster", "rekram1-node", "nexxeln", "kitlangton", "starptech"],
core: ["jlongster", "rekram1-node", "nexxeln", "kitlangton"],
inference: ["fwang", "MrMushrooooom", "starptech"],
windows: ["Hona"],
} as const
Expand Down
2 changes: 1 addition & 1 deletion .termux-release-automation
Submodule .termux-release-automation updated from 7960e0 to a99159
10 changes: 10 additions & 0 deletions artifacts/glm52-rise-video/sst-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */

/// <reference path="../../sst-env.d.ts" />

import "sst"
export {}
82 changes: 47 additions & 35 deletions bun.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions infra/lake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ const athenaWorkgroup = new aws.athena.Workgroup("LakeAthenaWorkgroup", {
configuration: {
enforceWorkgroupConfiguration: true,
publishCloudwatchMetricsEnabled: true,
// Athena bills $5/TB scanned; kill any query that would scan more than 2 TB
// so a regression cannot silently burn money. Stats sync full passes scan
// ~250 GB as of 2026-07.
bytesScannedCutoffPerQuery: 2 * 1024 ** 4,
resultConfiguration: {
outputLocation: $interpolate`s3://${athenaResultsBucket.bucket}/`,
},
Expand Down
4 changes: 3 additions & 1 deletion infra/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,9 @@ export const statSync = new sst.aws.Service("StatsSyncService", {
cluster: lakeCluster,
architecture: "arm64",
cpu: "0.25 vCPU",
memory: "0.5 GB",
// 0.5 GB caused an OOM crash loop: every restart immediately re-ran the 4 Athena
// stats queries (~$5/pass) every ~5 minutes instead of hourly.
memory: "2 GB",
image: {
context: ".",
dockerfile: "packages/stats/server/Dockerfile",
Expand Down
8 changes: 4 additions & 4 deletions nix/hashes.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-JTtn+wXTXg+yklvIMDLcGFaYhTU6ZrCgKT9JTNEQ3gA=",
"aarch64-linux": "sha256-gXU6zyhvAZrZirkL/PlHdkHtEof/7PVSPCaE34Jnd4U=",
"aarch64-darwin": "sha256-Q0oTG3uzOlD/X2kJingLle529lKFoTpyCW2rHXOZ6iE=",
"x86_64-darwin": "sha256-LINvKHxPibTlJeNzfACQx0x+Yj5oROT6Du3I5AtqqXk="
"x86_64-linux": "sha256-QlwoEmN1jiw9TqQgOutPOv4mEMCNUR0Mm3ivYIWlc1o=",
"aarch64-linux": "sha256-KS8unXRXev29zbPskvp2ddtfAKXJN8VfInXnmPqypBI=",
"aarch64-darwin": "sha256-T3tgbuodPitCx2U5JJIQFLkRguAFeiMM7flTsF2H5Iw=",
"x86_64-darwin": "sha256-aITMNkQpFYAChDz5jZCchFkSylsDhJJQoyPu9k23Hl8="
}
}
150 changes: 150 additions & 0 deletions packages/app/e2e/regression/file-browser-sidebar-tab-switch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { base64Encode } from "@opencode-ai/core/util/encode"
import { expect, test, type Page } from "@playwright/test"
import { mockOpenCodeServer } from "../utils/mock-server"
import { expectSessionTitle } from "../utils/waits"

const directory = "C:/OpenCode/FileBrowserSidebar"
const projectID = "proj_file_browser_sidebar"
const sessionID = "ses_file_browser_sidebar"
const title = "File browser sidebar"
const server = `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`
const files = Array.from({ length: 80 }, (_, index) => `file-${String(index).padStart(2, "0")}.ts`)
// Marks the file-browser sidebar DOM node so a remount (fresh node) is detectable.
const PROBE = "original"

test.use({ viewport: { width: 1440, height: 900 } })

// The file-browser sidebar must stay mounted across preview/pinned file-tab
// switches. Remounting resets scroll and filter state.
test("keeps the file-browser sidebar mounted when switching file tabs", async ({ page }) => {
await setup(page)

await page.goto(`/server/${base64Encode(server)}/session/${sessionID}`)
await expectSessionTitle(page, title)

const panel = page.locator("#review-panel")
await panel.getByRole("button", { name: "Open file" }).click()
await expect(panel.getByRole("tab", { name: "Open file" })).toHaveAttribute("data-selected", "")

const sidebar = panel.locator('[data-component="session-review-v2-sidebar-root"]')
await expect(sidebar).toBeVisible()
await expect(panel.getByRole("button", { name: "file-00.ts" })).toBeVisible()

await panel.getByRole("button", { name: "file-00.ts" }).click()
await expect(panel.getByRole("tab", { name: "file-00.ts" })).toHaveAttribute("data-selected", "")
await expect(panel.getByText("contents:file-00.ts", { exact: true })).toBeVisible()

const viewport = panel.locator('[data-slot="session-review-v2-sidebar-tree"] .scroll-view__viewport')
await viewport.hover()
await page.mouse.wheel(0, 100_000)
await expect
.poll(() => viewport.evaluate((element) => element.scrollHeight - element.clientHeight - element.scrollTop))
.toBeLessThanOrEqual(1)
const scrolled = await viewport.evaluate((element) => element.scrollTop)
expect(scrolled).toBeGreaterThan(0)
await writeProbe(page)

await panel.getByRole("button", { name: "file-79.ts" }).click()
await expect(panel.getByRole("tab", { name: "file-79.ts" })).toHaveAttribute("data-selected", "")
await expect(panel.getByText("contents:file-79.ts", { exact: true })).toBeVisible()
expect(await readProbe(page)).toBe(PROBE)
await expect.poll(() => viewport.evaluate((element) => element.scrollTop)).toBe(scrolled)

await panel.getByRole("button", { name: "file-78.ts" }).dblclick()
await expect(panel.getByRole("tab", { name: "file-78.ts" })).toHaveAttribute("data-selected", "")
await panel.getByRole("button", { name: "file-79.ts" }).click()
await expect(panel.getByRole("tab", { name: "file-79.ts" })).toHaveAttribute("data-selected", "")
await panel.getByRole("tab", { name: "file-78.ts" }).click()
await expect(panel.getByRole("tab", { name: "file-78.ts" })).toHaveAttribute("data-selected", "")
expect(await readProbe(page)).toBe(PROBE)
await expect.poll(() => viewport.evaluate((element) => element.scrollTop)).toBe(scrolled)
})

type Probed = HTMLElement & { __e2eProbe?: string }

async function writeProbe(page: Page) {
await page.locator('#review-panel [data-component="session-review-v2-sidebar-root"]').evaluate((el, probe) => {
;(el as Probed).__e2eProbe = probe
}, PROBE)
}

async function readProbe(page: Page) {
return page
.locator('#review-panel [data-component="session-review-v2-sidebar-root"]')
.evaluate((el) => (el as Probed).__e2eProbe)
}

async function setup(page: Page) {
await mockOpenCodeServer(page, {
directory,
project: {
id: projectID,
worktree: directory,
vcs: "git",
name: "file-browser-sidebar",
time: { created: 1700000000000, updated: 1700000000000 },
sandboxes: [],
},
provider: {
all: [
{
id: "opencode",
name: "OpenCode",
models: { test: { id: "test", name: "Test", limit: { context: 200_000 } } },
},
],
connected: ["opencode"],
default: { providerID: "opencode", modelID: "test" },
},
sessions: [
{
id: sessionID,
slug: sessionID,
projectID,
directory,
title,
version: "dev",
time: { created: 1700000000000, updated: 1700000000000 },
},
],
vcsDiff: [],
fileList: (path) => {
if (path) return []
return files.map((name) => ({
name,
path: name,
absolute: `${directory}/${name}`,
type: "file" as const,
ignored: false,
}))
},
fileContent: (path) => ({ type: "text", content: `contents:${path}` }),
pageMessages: () => ({ items: [] }),
})

await page.addInitScript(
({ directory, server, sessionID }) => {
localStorage.setItem("settings.v3", JSON.stringify({ general: { newLayoutDesigns: true } }))
localStorage.setItem(
"opencode.global.dat:server",
JSON.stringify({
projects: { local: [{ worktree: directory, expanded: true }] },
lastProject: { local: directory },
}),
)
localStorage.setItem(
"opencode.global.dat:layout",
JSON.stringify({ review: { diffStyle: "split", panelOpened: true } }),
)
localStorage.setItem(
"opencode.global.dat:review-panel-v2",
JSON.stringify({ sidebarOpened: true, sidebarWidth: 240, expandMode: "collapse" }),
)
localStorage.setItem(
"opencode.window.browser.dat:tabs",
JSON.stringify([{ type: "session", server, sessionId: sessionID }]),
)
},
{ directory, server, sessionID },
)
}
17 changes: 17 additions & 0 deletions packages/app/e2e/regression/review-open-file.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ test("opens and searches project files inline", async ({ page }) => {
"opencode.global.dat:layout",
JSON.stringify({ review: { diffStyle: "split", panelOpened: true } }),
)
localStorage.setItem(
"opencode.global.dat:review-panel-v2",
JSON.stringify({ sidebarOpened: false, sidebarWidth: 240, expandMode: "collapse" }),
)
localStorage.setItem(
"opencode.window.browser.dat:tabs",
JSON.stringify([{ type: "session", server, sessionId: sessionID }]),
Expand All @@ -86,13 +90,18 @@ test("opens and searches project files inline", async ({ page }) => {
await expectSessionTitle(page, title)

const panel = page.locator("#review-panel")
const sidebar = panel.locator('[data-slot="session-review-v2-sidebar"]')
const sidebarToggle = panel.getByRole("button", { name: "Toggle file tree" })
const contextButton = page.getByRole("button", { name: "View context usage" })
await contextButton.click()
await expect(panel.getByRole("tab", { name: "Context" })).toHaveAttribute("data-selected", "")
await panel.getByRole("button", { name: "Open file" }).click()
await expect(panel.getByRole("tab", { name: "Open file" })).toHaveAttribute("data-selected", "")
await expect(sidebarToggle).toBeDisabled()
await expect(sidebar).toBeVisible()
await contextButton.click()
await expect(panel.getByRole("tab", { name: "Context" })).toHaveAttribute("data-selected", "")
await expect(sidebar).toBeHidden()
await panel.getByRole("button", { name: "Open file" }).click()
const filter = panel.getByRole("combobox", { name: "Filter files" })
await expect(filter).toBeFocused()
Expand All @@ -101,10 +110,13 @@ test("opens and searches project files inline", async ({ page }) => {

await panel.getByRole("button", { name: "README.md" }).click()
await expect(panel.getByRole("tab", { name: "README.md" })).toHaveAttribute("data-selected", "")
await expect(sidebarToggle).toBeEnabled()
await expect(panel.getByText("contents:README.md", { exact: true })).toBeVisible()
await expect(sidebar).toHaveCount(0)

await panel.getByRole("button", { name: "Open file" }).click()
await expect(panel.getByRole("tab", { name: "README.md" })).toHaveCount(0)
await expect(sidebar).toBeVisible()
await filter.fill("nested")
const result = panel.getByRole("option", { name: /nested\.ts/ })
await expect(result).toBeVisible()
Expand All @@ -113,12 +125,17 @@ test("opens and searches project files inline", async ({ page }) => {
await expect(filter).toHaveAttribute("aria-activedescendant", resultID!)
await filter.press("Enter")
await expect(panel.getByRole("tab", { name: "nested.ts" })).toHaveAttribute("data-selected", "")
await expect(sidebarToggle).toBeEnabled()
await expect(panel.getByText("contents:src/nested.ts", { exact: true })).toBeVisible()
expect(searches).toContainEqual({ query: "nested", dirs: "false", limit: 200 })

await panel.getByRole("button", { name: "Open file" }).click()
await expect(panel.getByRole("tab", { name: "nested.ts" })).toHaveCount(1)
await expect(panel.getByRole("tab", { name: "Open file" })).toHaveAttribute("data-selected", "")
await expect(sidebarToggle).toBeDisabled()
await panel.getByRole("tab", { name: /Review/ }).click()
await expect(sidebarToggle).toBeEnabled()
await panel.getByRole("tab", { name: "Open file" }).click()
await page.keyboard.press("Control+w")
await expect(panel.getByRole("tab", { name: "Open file" })).toHaveCount(0)
await expect(panel.getByRole("tab", { name: "nested.ts" })).toHaveAttribute("data-selected", "")
Expand Down
4 changes: 2 additions & 2 deletions packages/app/e2e/regression/review-tab-switch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ test("keeps the v2 review pane mounted when switching session tabs in a workspac
await expectSessionTitle(page, titleA)

await page.getByRole("button", { name: "Toggle review" }).click()
const reviewTab = page.getByRole("tab", { name: /Review/ })
const reviewTabPanel = page.getByRole("tabpanel", { name: /Review/ })
const reviewTab = page.locator("#session-side-panel-review-tab")
const reviewTabPanel = page.locator("#session-side-panel-review-tabpanel")
await expect(reviewTab).toHaveAttribute("aria-controls", "session-side-panel-review-tabpanel")
await expect(reviewTabPanel).toHaveAttribute("id", "session-side-panel-review-tabpanel")
const review = page.locator('#review-panel [data-component="session-review-v2"]')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ test("keeps the review tree and terminal sized when both panels are open", async
await expectTree(page, 8, "git-0.ts")

await selectMode(page, "Git changes", "Branch changes")
await expect(page.getByRole("tab", { name: "Review 2740" })).toBeVisible()
await expect(page.locator("#session-side-panel-review-tab")).toHaveText("Files Changed 2740")
await page.keyboard.press("Control+Backquote")
await expect(page.locator("#terminal-panel")).toBeVisible()
await expectTree(page, 2_773, "action.yml")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ test.describe("session timeline projection", () => {
parentID: "msg_2000_diff_next_user",
created: 1700000011000,
})
await setupTimeline(page, { messages: [user, assistantMessage(), nextUser, nextAssistant] })
await setupTimeline(page, {
messages: [user, assistantMessage(), nextUser, nextAssistant],
settings: { newLayoutDesigns: false },
})
const scroller = page.locator(".scroll-view__viewport", { has: page.locator("[data-timeline-row]") })
await scroller.evaluate((element) => (element.scrollTop = 0))

Expand Down
Loading