Compare commits

...

553 commits

Author SHA1 Message Date
Luke Parker
f5c3d352a1
fix(app): require query functions for sync queries (#25939)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-05-06 10:09:32 +10:00
Luke Parker
e117397d0f
fix(server): restore web terminal CSP allowances (#25937) 2026-05-06 09:32:51 +10:00
opencode-agent[bot]
1fbc13a1b4 chore: generate 2026-05-05 23:08:37 +00:00
Aiden Cline
6409aceb1a
fix: sanitize surrogates (#25934) 2026-05-05 18:07:23 -05:00
opencode-agent[bot]
837cc92586 chore: generate 2026-05-05 21:56:18 +00:00
Nathan Nguyen
ca77b8f8e9
fix(cf-ai-gateway): route provider options through openaiCompatible key (#24432) (#25573) 2026-05-05 16:55:08 -05:00
opencode-agent[bot]
25547e9337 chore: generate 2026-05-05 20:41:12 +00:00
James Long
12f3d1f505
fix(core): use current workspace with /new; fix warping into local project (#25894) 2026-05-05 16:39:37 -04:00
James Long
8e182c7782
fix(core): better state handling of editor context (#25911) 2026-05-05 15:53:05 -04:00
OpeOginni
8a797ed9a1
fix(TUI): update agent create target path from "/agent" to "/agents" (#14427) 2026-05-05 14:51:40 -05:00
Aiden Cline
25ecf0af6b
fix: retry server_is_overloaded errors (#25888)
Some checks are pending
containers / build (push) Waiting to run
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / build-cli (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-05-05 10:39:25 -05:00
Aiden Cline
576480b5dc
fix: ensure mistral medium 3.5 has variants properly setup (#25887) 2026-05-05 10:34:20 -05:00
opencode-agent[bot]
fdb4b7c4a5 chore: update nix node_modules hashes 2026-05-05 14:27:06 +00:00
Victor Navarro
726ae6f541
chore: configure alerting and monitoring (#25857) 2026-05-05 16:08:28 +02:00
opencode
773078e81f sync release versions for v1.14.39 2026-05-05 10:57:52 +00:00
Shoubhit Dash
811954880e
fix(compaction): order compaction summary before retained tail (#25851) 2026-05-05 16:12:37 +05:30
Luke Parker
465c83cf82
fix(desktop): respect proxy environment (#25846) 2026-05-05 20:34:28 +10:00
Brendan Allan
bb9b81aa37
fix(desktop): add error handling to store-get IPC handler (#25850) 2026-05-05 18:24:21 +08:00
opencode
a20446fcc8 sync release versions for v1.14.38 2026-05-05 09:48:25 +00:00
opencode-agent[bot]
292c2aa458 chore: update nix node_modules hashes 2026-05-05 09:06:37 +00:00
Brendan Allan
52bb088753
fix(server): allow all connect-src origins in CSP for embedded UI (#25838) 2026-05-05 17:06:30 +08:00
Luke Parker
b8f8f5d3a8
fix(desktop): trust system certificates (#25837) 2026-05-05 18:47:38 +10:00
opencode
8df3ef10fc sync release versions for v1.14.37
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / build-cli (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
containers / build (push) Waiting to run
2026-05-05 07:26:49 +00:00
opencode-agent[bot]
301ab36159 chore: update nix node_modules hashes 2026-05-05 06:06:32 +00:00
Brendan Allan
03544a26cd
fix(desktop): update main process (#25825) 2026-05-05 13:44:53 +08:00
Brendan Allan
b4147c8d08
refactor(desktop): consolidate desktop-electron into desktop package (#25822) 2026-05-05 13:43:36 +08:00
opencode-agent[bot]
6f7d63e9ce chore: generate 2026-05-05 04:27:38 +00:00
Luke Parker
07f1c8c0ac
fix(desktop): stabilize Windows titlebar zoom (#25813) 2026-05-05 04:26:35 +00:00
opencode-agent[bot]
2d0a757eb2 chore: generate 2026-05-05 02:37:07 +00:00
Kit Langton
75d141b574
fix(session): cancel subtask child sessions (#25798) 2026-05-04 22:36:06 -04:00
Dax
39c88f9afb
Improve v2 session message rendering (#25634) 2026-05-05 02:35:21 +00:00
Dax Raad
0df2bb0f3b docs: restore v2 todo 2026-05-04 22:22:39 -04:00
Brendan Allan
f6a3615f59
fix(console): remove Cloudflare cache config from download fetch (#25804)
Some checks are pending
deploy / deploy (push) Waiting to run
2026-05-05 10:15:00 +08:00
James Long
edd480f56b
fix(tui): fix type error for calling workspace.warp (#25801) 2026-05-04 22:06:33 -04:00
Luke Parker
2740d398fa
devex: Enable Electron MCP servers with DevTools debug port (#25795) 2026-05-05 11:37:18 +10:00
opencode-agent[bot]
f33b17e8ac chore: generate 2026-05-05 01:29:49 +00:00
James Long
22a4a9df8b
feat(core): session warping (#25768) 2026-05-04 21:28:38 -04:00
Brendan Allan
84afd2bef8
update: normalize download asset names to match new naming convention (#25796) 2026-05-05 09:19:13 +08:00
Luke Parker
ca2411d332
Run UI unit tests in CI (#25792) 2026-05-05 11:05:53 +10:00
opencode
6b852774e1 sync release versions for v1.14.35 2026-05-05 01:01:47 +00:00
opencode-agent[bot]
f14784d531 chore: generate 2026-05-05 00:35:18 +00:00
Luke Parker
6a5e329427
fix(vcs): preserve batched patch boundaries (#25787) 2026-05-05 00:34:06 +00:00
opencode
4b65b1e053 sync release versions for v1.14.34
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-05-04 23:26:02 +00:00
Aiden Cline
d431a0e4b4
fix: ensure effect server middleware properly parses errors (#25717) 2026-05-04 18:29:00 -04:00
Frank
5720883d5d sync
Some checks are pending
deploy / deploy (push) Waiting to run
2026-05-04 15:51:29 -04:00
Kit Langton
007b57f078
test(agent): skip InstanceBootstrap in plugin-agent regression test (#25737) 2026-05-04 17:11:33 +00:00
Kit Langton
fb07c2070c
fix(server): provide fresh ConfigProvider per HttpApi listener (#25726) 2026-05-04 13:06:29 -04:00
Kit Langton
25dc6f09bc
fix(worktree): fork workspace worktree boot (#25723) 2026-05-04 12:01:13 -04:00
Colby Gilbert
b70e2700ef
chore(docs): rename firmware provider to frogbot (#25453)
Some checks failed
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
docs-locale-sync / sync-locales (push) Has been cancelled
2026-05-04 10:27:03 -05:00
Frank
1aed6b1d8b sync
Some checks are pending
deploy / deploy (push) Waiting to run
2026-05-04 11:16:28 -04:00
Aiden Cline
c1f607d206
fix: ensure anthropic sdk properly resolves when using azure (#25721) 2026-05-04 09:58:21 -05:00
opencode-agent[bot]
2c819f290f chore: generate 2026-05-04 13:55:28 +00:00
Kit Langton
6e9f10ad3f
test(server): regression reproducers for #25698 (#25714) 2026-05-04 09:54:19 -04:00
OpeOginni
1251a870cb
fix(opencode): strip transfer-encoding in UI proxy and allow public manifest assets (#25698)
Co-authored-by: Kit Langton <kit.langton@gmail.com>
2026-05-04 09:43:03 -04:00
opencode-agent[bot]
67047fa766 chore: generate 2026-05-04 13:26:08 +00:00
Luke Parker
a366128a93
fix(app): prevent terminal recovery loops (#25710) 2026-05-04 23:24:57 +10:00
opencode-agent[bot]
9f708e748a chore: generate
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-05-04 02:57:18 +00:00
Kit Langton
7bc26dafae
feat(server): pty websocket auth tickets (#25660) 2026-05-03 22:56:14 -04:00
Utkub24
ce89bcb8e2
fix: allow Codex Spark with Codex OAuth (#25640)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-05-03 17:58:16 -05:00
Kit Langton
c2b1974ddd
Effectify plugin agent regression test (#25646) 2026-05-03 22:07:10 +00:00
Kit Langton
ca6150d6f0
fix(app): preserve auth token credentials (#25636) 2026-05-03 21:13:42 +00:00
Kit Langton
825ab2e38d
refactor(cli): effectify provider commands (#25633) 2026-05-03 16:41:10 -04:00
opencode-agent[bot]
755cd561ec chore: generate 2026-05-03 19:45:26 +00:00
Kit Langton
6312c55d55
fix(server): serve embedded UI from bunfs (#25632) 2026-05-03 19:44:23 +00:00
opencode-agent[bot]
a9dc0fae3d chore: generate 2026-05-03 18:46:50 +00:00
Dax
7749d8e85f
Add v2 session failure events (#25628) 2026-05-03 14:45:48 -04:00
opencode-agent[bot]
28112fbd12 chore: generate 2026-05-03 18:24:37 +00:00
Kit Langton
387220f368
fix(server): support desktop PTY websockets with HttpApi (#25598) 2026-05-03 18:23:29 +00:00
OpeOginni
adb7cb1037
fix(auth): add username option for basic auth in RunCommand (#25600)
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
2026-05-03 22:51:33 +05:30
opencode-agent[bot]
c06af70ab0 chore: generate 2026-05-03 15:44:02 +00:00
Kit Langton
40dc2fa3c1
refactor(cli/providers): flatten — Effect-native handlers end-to-end (#25537) 2026-05-03 15:42:57 +00:00
Kit Langton
df7dd06a0f
refactor(cli/github+run): Stage 4 — drop AppRuntime.runPromise bridges (#25539) 2026-05-03 15:42:05 +00:00
opencode-agent[bot]
57d5c095d8 chore: generate 2026-05-03 15:22:38 +00:00
Kit Langton
13ac849db5
refactor(config+core): drop ConfigPaths.readFile, add AppFileSystem.readFileStringSafe, flatten TuiConfig.loadState (#25602) 2026-05-03 15:21:34 +00:00
Shoubhit Dash
8694c5b68f
fix(auth): respect server username in clients (#25596)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-05-03 19:28:31 +05:30
Shoubhit Dash
0a7d02c87c
feat: group changelog bugfixes (#25597) 2026-05-03 13:48:26 +00:00
Brendan Allan
e77867ef05
ci: only build electron desktop (#19067) 2026-05-03 19:10:15 +05:30
opencode-agent[bot]
fb224d8974 chore: generate 2026-05-03 13:21:15 +00:00
OpeOginni
101566131d
fix(httpapi): add basic auth challenge for browser login
Adds a WWW-Authenticate challenge for unauthorized experimental HttpApi UI fallback responses so browsers open the Basic Auth prompt when a server password is configured.
2026-05-03 13:20:05 +00:00
opencode-agent[bot]
8433e8b433 chore: generate 2026-05-03 13:18:13 +00:00
Kit Langton
379600b5ab
fix(sdk+cli): surface real errors instead of bare {} when server returns empty body (#25592) 2026-05-03 13:17:06 +00:00
Shoubhit Dash
7a503de606
fix(acp): pass server auth to internal client (#25591) 2026-05-03 18:42:24 +05:30
Kit Langton
2ad1eb56d3
feat(server): native HttpApi listener with Bun.serve + WS upgrade (#25547) 2026-05-03 09:09:45 -04:00
opencode-agent[bot]
a43f767abb chore: generate 2026-05-03 13:07:30 +00:00
Kit Langton
0ee3b87289
feat(server): Server.openapi() backed by HttpApi spec, parity-checked against Hono output (#25545) 2026-05-03 09:06:23 -04:00
opencode-agent[bot]
3c9f3c5786 chore: generate 2026-05-03 12:59:40 +00:00
Kit Langton
ca75ac6681
refactor(server): extract Hono-coupled utilities to backend-neutral modules (#25542) 2026-05-03 12:58:34 +00:00
Shoubhit Dash
d1f597b5b5
fix(vcs): avoid unbounded diff memory usage (#25581) 2026-05-03 17:49:46 +05:30
Dax Raad
8299fb3e2b ignore: remove triage-unassigned.ts script
Some checks failed
docs-locale-sync / sync-locales (push) Has been cancelled
containers / build (push) Has been cancelled
storybook / storybook build (push) Has been cancelled
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
This script was used to batch-triage open GitHub issues without assignees.
Removing as the triage workflow has evolved and this batch approach is no longer needed.
2026-05-03 01:59:03 -04:00
Dax Raad
4f7f90133d ci: stop sending daily community recap notifications 2026-05-03 01:54:32 -04:00
Dax Raad
b205e104f6 ci: remove vouch-based contributor filtering workflows
Removes the automated vouch system that filtered issues and PRs from non-vouched users. This simplifies the contribution process by removing the requirement for maintainers to manually vouch contributors before they can participate.
2026-05-03 01:54:32 -04:00
Dax Raad
252e2f98e6 ci: remove automatic labels from GitHub issue templates to allow manual triage 2026-05-03 01:54:32 -04:00
opencode-agent[bot]
e2afdc1202 chore: generate 2026-05-03 05:22:22 +00:00
Dax Raad
a08e4c9651 core: simplify triage workflow to focus on issue ownership
Switch triage agent to gpt-5.4-nano for faster issue assignment. Remove label
management from the triage tool so it only assigns owners based on team
ownership rules. This reduces noise in the issue tracker and ensures issues
get to the right team member immediately without unnecessary labels.

Update team structures to reflect current ownership and add script for
processing unassigned issues.
2026-05-03 01:21:17 -04:00
Dax Raad
7ccab8d272 core: update triage agent to use qwen3.6-plus model for improved response quality 2026-05-03 01:10:14 -04:00
Dax Raad
fc57eb3b8e ci 2026-05-03 01:05:36 -04:00
Dax
9179bafd54
Add debug info command (#25550) 2026-05-03 05:04:52 +00:00
Kit Langton
2df8eda8a3
fix(cli): bridge Instance.current ALS in effectCmd handlers (regression from #25522) (#25546) 2026-05-03 04:24:33 +00:00
Kit Langton
bd32252a7e
refactor(cli/providers): Stage 4 — drop inline AppRuntime.runPromise calls (#25532) 2026-05-02 23:42:40 -04:00
Kit Langton
1717d636a2
refactor(cli/mcp+agent): Stage 4 — drop AppRuntime.runPromise bridges (#25530) 2026-05-02 23:40:59 -04:00
Aiden Cline
8e016b4703
fix: regression w/ auth login where stderr was ignored instead of inherited (#25529) 2026-05-02 22:36:02 -05:00
opencode-agent[bot]
b89d48a2a4 chore: update nix node_modules hashes 2026-05-03 03:25:46 +00:00
Dax
33312bfd1b
fix(session): encode v2 session responses (#25528) 2026-05-03 03:24:46 +00:00
opencode-agent[bot]
3f1ce36418 chore: generate 2026-05-03 03:23:47 +00:00
Kit Langton
0e13279545
refactor(cli): convert agent / providers / mcp to effectCmd (#25525) 2026-05-02 23:22:44 -04:00
Kit Langton
5f03d892c0
fix(httpapi): pagination Link header echoes request host (#25527) 2026-05-02 23:19:33 -04:00
Kit Langton
bdabb102fe
refactor(cli/stats): Stage 4 — fully Effect-native body (#25523) 2026-05-02 23:08:26 -04:00
Kit Langton
a79a6594b0
chore: bump Effect beta (#25524) 2026-05-02 23:08:13 -04:00
opencode-agent[bot]
a3d282a4c2 chore: generate 2026-05-03 03:04:40 +00:00
Kit Langton
db24f89313
refactor(cli): convert mcp list, auth, auth list, logout to effectCmd (#25521) 2026-05-03 03:03:32 +00:00
opencode-agent[bot]
31cb0bfa4f chore: generate 2026-05-03 02:54:20 +00:00
Kit Langton
af9fdf0a1c
refactor(cli): convert github subcommands to effectCmd (#25522) 2026-05-02 22:53:20 -04:00
Youssef Achy
be88cd5cb9
chore(opencode): exclude .map files from CLI binary build (#25500) 2026-05-02 22:52:32 -04:00
Luke Parker
b4cc7d13b6
fix(desktop): limit zoom handler to zoom keys (#25516) 2026-05-03 02:44:52 +00:00
Aiden Cline
0ba013f8de
chore: rm log statement (#25470) 2026-05-02 21:43:48 -05:00
Kit Langton
0956b15c52
refactor(acp): drop async from synchronous ACP.init (#25520) 2026-05-02 22:38:44 -04:00
opencode-agent[bot]
61150f6391 chore: generate 2026-05-03 02:36:41 +00:00
Kit Langton
7409dcc6bd
refactor(cli): convert run command to effectCmd (#25519) 2026-05-02 22:35:20 -04:00
Kit Langton
2829943ad1
refactor(cli): convert debug wait, agent list, acp to effectCmd (#25518) 2026-05-02 22:31:20 -04:00
Kit Langton
c4311dda31
feat(cli): allow effectCmd instance to be a function of args (#25517) 2026-05-03 02:27:41 +00:00
Kit Langton
ad05a46d74
refactor(lifecycle): bootstrap as pure orchestration (#25510) 2026-05-02 22:26:54 -04:00
opencode-agent[bot]
a6cadba814 chore: generate 2026-05-03 02:10:52 +00:00
Dax
a3bc5d35b0
Refactor v2 session events as schemas (#24512) 2026-05-02 22:09:48 -04:00
Kit Langton
1409a0715c
refactor(cli): convert web + account to effectCmd (instance: false) (#25512) 2026-05-02 21:59:35 -04:00
Kit Langton
e98c291866
feat(cli): add instance: false opt-out to effectCmd (#25507) 2026-05-03 01:44:06 +00:00
Kit Langton
e709dc34fb
feat: default HTTP API backend to on for dev/beta channels 2026-05-02 20:43:23 -04:00
opencode-agent[bot]
9293cddb3a chore: generate 2026-05-03 00:43:16 +00:00
Kit Langton
68b3448b09
refactor(cli): drop redundant explicit Effect.ensuring(store.dispose) (#25503) 2026-05-02 20:42:09 -04:00
opencode-agent[bot]
80f2b13a55 chore: generate 2026-05-03 00:40:21 +00:00
Kit Langton
7d91d3b1ed
Normalize instance lifecycle wiring (#25501) 2026-05-02 20:39:20 -04:00
opencode-agent[bot]
a6464062b7 chore: generate 2026-05-03 00:32:24 +00:00
Kit Langton
fd01dc9c89
test(httpapi): add route exerciser 2026-05-02 20:31:21 -04:00
opencode-agent[bot]
d10fb88b66 chore: generate 2026-05-03 00:10:53 +00:00
Luke Parker
6b68b1020e
docs: clarify LSP and formatter opt-in config (#25502) 2026-05-03 00:09:50 +00:00
Kit Langton
85bb9007ba
feat(cli): auto-dispose InstanceContext after effectCmd handlers (#25481) 2026-05-02 19:54:13 -04:00
opencode-agent[bot]
9bef88e3b0 chore: generate 2026-05-02 23:34:40 +00:00
Kit Langton
f98053c34e
fix(instance): run bootstrap from instance store (#25475) 2026-05-02 19:33:38 -04:00
opencode-agent[bot]
36007aecf4 chore: generate 2026-05-02 23:23:53 +00:00
Kit Langton
4de44bbbef
refactor(cli): convert debug subcommands to effectCmd (#25479) 2026-05-02 19:22:51 -04:00
opencode-agent[bot]
9d03d4419e chore: generate 2026-05-02 23:20:15 +00:00
Kit Langton
7ab1c1c74a
refactor(cli): convert debug agent command to effectCmd (#25485) 2026-05-02 19:19:06 -04:00
Luke Parker
3f459819ba
feat: refactor bash tool with shell-aware prompts for bash, pwsh+powershell, and cmd (#20039) 2026-05-03 09:18:48 +10:00
Kit Langton
1986a6e817
refactor(cli): convert session subcommands to effectCmd (#25483)
Some checks are pending
docs-locale-sync / sync-locales (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / build-cli (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-05-02 18:15:28 -04:00
opencode-agent[bot]
dfe1325fca chore: generate 2026-05-02 22:02:14 +00:00
Kit Langton
c1686c6ddc
refactor(cli): convert stats command to effectCmd (#25474) 2026-05-02 18:01:06 -04:00
Kit Langton
79b6ce5db4
refactor(cli): convert import command to effectCmd (#25467) 2026-05-02 21:56:32 +00:00
Kit Langton
0c816eb4b1
refactor(cli): convert plugin command to effectCmd (#25473) 2026-05-02 17:55:13 -04:00
Kit Langton
e318e173d8
refactor(cli): convert export command to effectCmd (#25471) 2026-05-02 17:45:41 -04:00
opencode-agent[bot]
b314781a1a chore: generate 2026-05-02 21:02:46 +00:00
Kit Langton
8396d6b016
refactor(cli): convert pr command to effectCmd (#25465) 2026-05-02 17:01:46 -04:00
opencode
43e20874f4 sync release versions for v1.14.33 2026-05-02 19:53:06 +00:00
opencode-agent[bot]
c444e971b0 chore: generate 2026-05-02 19:27:24 +00:00
HyeokjaeLee
430bde9e9b
fix(instance): restore InstanceBootstrap init parameter for non-Effec… (#25449)
Co-authored-by: Dax Raad <d@ironbay.co>
2026-05-02 15:26:30 -04:00
Kit Langton
05b82a6a30
refactor(cli): drop ModelsDev Promise compat shim (#25460) 2026-05-02 15:11:01 -04:00
Kit Langton
6cd02c05c2
fix(telemetry): emit Tool.execute span for MCP and plugin tools (#25452) 2026-05-02 14:49:56 -04:00
opencode-agent[bot]
b3a7513765 chore: generate 2026-05-02 18:00:11 +00:00
Kit Langton
f8738c9002
feat(models): effectify ModelsDev as Service (#25434) 2026-05-02 13:59:08 -04:00
Aiden Cline
b460db15d7
tweak: allow read tool to accept offset of 0 (#25431) 2026-05-02 11:12:07 -05:00
opencode-agent[bot]
ff4779ca11 chore: generate 2026-05-02 16:09:04 +00:00
Kit Langton
146ff8ad85
feat(cli): add effectCmd wrapper + convert models command (#25429) 2026-05-02 12:08:04 -04:00
OpeOginni
0d0ec7dc46
docs: CLI docs for current commands and flags (#25399) 2026-05-02 11:07:22 -05:00
Jérôme Benoit
1ea6e6cd4b
fix(nix): remove stale packages/shared filter (#24930) 2026-05-02 10:49:51 -05:00
opencode-agent[bot]
96061222d2 chore: generate 2026-05-02 15:45:21 +00:00
Kit Langton
3b9155714d
Delete Instance.dispose and Instance.reload (#25427) 2026-05-02 11:44:16 -04:00
opencode
7371db5cc6 sync release versions for v1.14.32 2026-05-02 15:34:12 +00:00
Kit Langton
b09b7d28b8
refactor(instance-store): consolidate dispose helpers (#25424) 2026-05-02 11:21:40 -04:00
opencode-agent[bot]
31ed4602e1 chore: update nix node_modules hashes 2026-05-02 15:16:12 +00:00
Sebastian
6a76346734
upgrade opentui to 0.2.2 (#25420) 2026-05-02 15:01:53 +00:00
Kit Langton
78b3000031
fix(tui): keep shell-mode prompt editable (#25419) 2026-05-02 10:56:27 -04:00
Kit Langton
4c4860fb24
Replace Instance.disposeAll/load with fixture helper (#25418) 2026-05-02 10:56:15 -04:00
Kit Langton
5242a1c6b4
fix(httpapi): install Instance ALS for adapter Promise bridge (#25417) 2026-05-02 10:49:44 -04:00
Kit Langton
075f876e6f
fix(httpapi): re-land workspace create payload accepts missing extra (#25412)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-05-02 09:35:39 -04:00
opencode-agent[bot]
a849812e9f chore: generate 2026-05-02 13:09:14 +00:00
Kit Langton
d99dde6306
Migrate test inits from Promise to Effect (#25377) 2026-05-02 09:07:59 -04:00
opencode-agent[bot]
becf57ee6a chore: generate
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-05-02 04:03:59 +00:00
Kit Langton
f33aec1139
Convert LoadInput.init to Effect + extract InstanceBootstrap as a Service (#25376) 2026-05-02 00:02:52 -04:00
Kit Langton
1571933096
Drop ALS fallbacks from containsPath and workspace routing (#25374) 2026-05-02 03:06:22 +00:00
Kit Langton
160928a9a9
Extract InstanceStore.provide helper (#25372) 2026-05-01 22:42:03 -04:00
opencode-agent[bot]
d297c29f22 chore: generate 2026-05-02 02:19:48 +00:00
Kit Langton
0b498dd448
fix(httpapi): preserve OpenAPI parameter parity (#25291) 2026-05-01 22:18:52 -04:00
Kit Langton
cec9c6122a
Move instance loading into Effect service (#25277) 2026-05-01 22:18:06 -04:00
Zeke Sikelianos
51e310c9ce
fix(read): prevent unsupported image formats from being sending to provider (#21114)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-05-01 18:14:22 -05:00
Aiden Cline
478156456e
core: fix npm package detection to properly handle cached directories without installed packages (#25354)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-05-01 15:49:14 -05:00
opencode-agent[bot]
6252412d94 chore: generate 2026-05-01 20:03:10 +00:00
Dax Raad
c2609cbf04 core: allow agents to access global tmp directory without permission prompts
Agents can now create temporary files in the global tmp directory without
triggering external_directory permission prompts. This enables agents to
freely use temporary storage for intermediate files during builds and
other operations.
2026-05-01 15:35:45 -04:00
github-actions[bot]
2115df57bf Update VOUCHED list
https://github.com/anomalyco/opencode/issues/25288#issuecomment-4360290197
2026-05-01 16:16:45 +00:00
Aiden Cline
29ec07700c
fix: bedrock reasoning issue (#25303) 2026-05-01 11:15:17 -05:00
Frank
bcae852d28 zen: remove hardcoded safety identifier
Some checks failed
deploy / deploy (push) Has been cancelled
2026-05-01 11:12:28 -04:00
Kit Langton
16ddf5f559
fix(session): use finite archived timestamp schema (#25275)
Some checks failed
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / e2e (linux) (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Has been cancelled
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Has been cancelled
docs-locale-sync / sync-locales (push) Has been cancelled
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Has been cancelled
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Has been cancelled
storybook / storybook build (push) Has been cancelled
nix-hashes / update-hashes (push) Has been cancelled
2026-05-01 11:57:03 +00:00
Kit Langton
8c79c58c4d
refactor: rename workspace adapters (#25272) 2026-05-01 07:36:52 -04:00
luo jiyin
97ed9ba624
fix: correct documentation typos (#25260) 2026-05-01 12:05:06 +02:00
Simon Klee
a6b6395c8a
fix(tui): gate logo subpixel rendering on truecolor support (#25265) 2026-05-01 11:33:44 +02:00
opencode
21f8027ef7 sync release versions for v1.14.31
Some checks are pending
deploy / deploy (push) Waiting to run
2026-05-01 06:13:48 +00:00
Brendan Allan
a5aa72bd7d
fix: update provider store after loading providers in bootstrap (#25236)
Some checks failed
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
containers / build (push) Has been cancelled
2026-05-01 13:39:22 +08:00
Aiden Cline
563177c6ac
fix: fix issue if tool returned image and empty text and it caused api errors (#25241) 2026-05-01 00:13:03 -05:00
opencode-agent[bot]
4eae8ec037 chore: generate 2026-05-01 04:21:51 +00:00
Aiden Cline
08895c396e
docs: fix tui and keybinds documentation (#25233) 2026-04-30 23:20:54 -05:00
opencode-agent[bot]
4e451a4b0f chore: update nix node_modules hashes 2026-05-01 04:07:58 +00:00
Brendan Allan
163290bcf0
desktop: sentry integration (#15300)
Co-authored-by: Jay V <air@live.ca>
2026-05-01 11:56:31 +08:00
Aiden Cline
c68c33d4fe
docs: remove deprecated modes.mdx pages (#25227) 2026-04-30 22:49:32 -05:00
Dax Raad
3615d8e226 core: clarify that temp directory already exists for AI agents
The bash tool description now explicitly states that the temp directory has already been created and exists, preventing agents from unnecessarily trying to create it before use.
2026-04-30 23:48:48 -04:00
Dax
2283979199
Preapprove agent tmp directory access (#25226) 2026-04-30 23:47:15 -04:00
Aiden Cline
33f7f593ee
fix: tui list jank issue (#25219) 2026-04-30 22:45:41 -05:00
opencode-agent[bot]
461e7345b3 chore: update nix node_modules hashes 2026-05-01 03:32:55 +00:00
opencode-agent[bot]
6bd91c68e8 chore: generate 2026-05-01 03:22:36 +00:00
Dax Raad
ff55a40749 core: remove @effect/language-service plugin and optimize hot path type performance
- Removed @effect/language-service from both packages/core and packages/opencode tsconfig files and dependencies

- Wrapped mergeDeep calls in config loading and LLM streaming to avoid expensive remeda conditional merge type instantiations in hot paths

- Narrowed Drizzle migrate() overload signature to avoid expensive variance checks during database initialization

These changes reduce TypeScript type-checking overhead and improve startup and runtime performance for config loading, LLM streaming, and database migrations.
2026-04-30 23:21:05 -04:00
opencode-agent[bot]
8b56d77ea1 chore: generate 2026-05-01 03:02:15 +00:00
Kit Langton
dd3aa96730
test(httpapi): cover more safe GET parity (#25217) 2026-04-30 23:01:11 -04:00
Kit Langton
8b56d1712f
refactor(session): pass project to list (#25215) 2026-04-30 23:00:59 -04:00
Kit Langton
3c24d22d42
fix(httpapi): omit absent optional response fields (#25214) 2026-05-01 02:38:32 +00:00
Kit Langton
4c70ea28d2
fix(tui): scope Zed editor context to containing workspaces (#25211) 2026-04-30 22:33:39 -04:00
Kit Langton
5ba68a28c0
refactor(httpapi): scope async prompt fiber (#25213) 2026-04-30 22:33:02 -04:00
opencode-agent[bot]
bce4def2db chore: generate 2026-05-01 02:26:56 +00:00
Kit Langton
3544ea0244
refactor(httpapi): drop session prompt bridge (#25210) 2026-04-30 22:25:52 -04:00
opencode-agent[bot]
6434918794 chore: generate 2026-05-01 01:46:55 +00:00
Kit Langton
5984d917dc
refactor(session): yield instance context in system prompt (#25207) 2026-04-30 21:45:48 -04:00
Kit Langton
c2a97a7a6c
refactor(file): yield instance context in watcher (#25205) 2026-04-30 21:45:21 -04:00
Kit Langton
a083c88e87
refactor(sync): capture instance context for publish (#25206) 2026-04-30 21:45:02 -04:00
Kit Langton
ce3b0988c4
refactor(project): yield instance context in bootstrap (#25204) 2026-04-30 21:44:52 -04:00
Kit Langton
e8a194a2bb
test(effect): stabilize runner active shell check (#25203) 2026-04-30 21:36:19 -04:00
Kit Langton
8aa8798e07
refactor(session): yield instance context in llm (#25200) 2026-04-30 21:29:28 -04:00
opencode-agent[bot]
6d4629b566 chore: generate 2026-05-01 01:28:37 +00:00
OpeOginni
a9d399699e
fix(desktop): Prevent Model response Interruption when opening settings dialog (#25114) 2026-05-01 01:27:38 +00:00
Kit Langton
bc805b3001
Pass CORS options to HttpApi backend (#25201) 2026-04-30 21:26:32 -04:00
Kit Langton
668d77bb4e
refactor(tool): yield InstanceState context (#25199) 2026-04-30 21:01:06 -04:00
Kit Langton
5c2e06f353
Document HttpApi route patterns (#25188) 2026-04-30 20:48:14 -04:00
Kit Langton
a499fe2b17
refactor(tool/read): yield InstanceState.context instead of reading ALS (#25183) 2026-04-30 20:33:04 -04:00
Kit Langton
451650b584
refactor(httpapi): preserve typed errors in session prompt handlers (#25181) 2026-04-30 20:04:00 -04:00
opencode-agent[bot]
1b76bec0e2 chore: generate 2026-05-01 00:03:55 +00:00
Kit Langton
96f4da1e1d
Serve instance events through HttpApiBuilder (#25182) 2026-04-30 20:02:46 -04:00
opencode-agent[bot]
96a0dd6b04 chore: generate 2026-04-30 23:37:58 +00:00
Kit Langton
2dd1f2d453
Avoid request-time HttpApi layer provisioning (#25179) 2026-04-30 19:36:57 -04:00
opencode-agent[bot]
510f01674a chore: generate 2026-04-30 23:29:58 +00:00
Kit Langton
e3134a2a99
refactor(session): align prompt input types with their schemas (#25178) 2026-04-30 19:28:46 -04:00
opencode-agent[bot]
8805104b8d chore: generate 2026-04-30 23:25:10 +00:00
Kit Langton
fc155e9fc5
Build HttpApi UI route from services (#25177) 2026-04-30 19:24:10 -04:00
opencode-agent[bot]
3aaac0098e chore: generate 2026-04-30 23:06:57 +00:00
Sewer.
a12333310f
fix(provider): split providerOptions key on dot for openai-compatible, openai, and anthropic providers (#25145) 2026-04-30 18:05:56 -05:00
opencode-agent[bot]
247284b9af chore: generate 2026-04-30 22:51:09 +00:00
Kit Langton
e0305e47f3
Protect HttpApi web UI fallback with auth (#25169) 2026-04-30 18:49:54 -04:00
Kit Langton
76a0f0f619
docs(httpapi): update migration spec to current state (#25173) 2026-04-30 18:41:27 -04:00
Aiden Cline
560baae15d
fix: ensure user config takes precendence over plugin hooks for model resolution (#25167) 2026-04-30 17:15:56 -05:00
Kit Langton
5518ecaefe
Fix HttpApi web UI fallback (#25163)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / build-cli (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-30 17:43:18 -04:00
opencode-agent[bot]
924ba97055 chore: generate 2026-04-30 21:04:24 +00:00
Aiden Cline
b80f52f8ad
tweak: adjust codex plugin to use the models hook (#25157) 2026-04-30 16:03:07 -05:00
Kit Langton
feb275d08b
Remove covered workspace websocket todo (#25161) 2026-04-30 20:58:08 +00:00
Kit Langton
fbcbd24063
Add SyncEvent service (#25158) 2026-04-30 16:45:26 -04:00
Kit Langton
3250b814ce
Fix HttpApi raw route authorization (#25154) 2026-04-30 19:55:20 +00:00
Kit Langton
0e9d9282c6
Refactor workspace service boundaries (#25152) 2026-04-30 15:34:37 -04:00
Kit Langton
b315a70773
test: use Effect test helper for agent colors (#25051) 2026-04-30 15:14:25 -04:00
Kit Langton
cedff6fb89
Isolate TUI thread cwd resolution test (#25147) 2026-04-30 15:10:30 -04:00
Kit Langton
87cd9446d8
test: use testEffect for plugin triggers (#25053) 2026-04-30 14:24:53 -04:00
Kit Langton
f4ce240a2e
Use PTY service directly in HTTP routes (#25138) 2026-04-30 14:24:43 -04:00
Kit Langton
320527a3e4
Support multiple Zed selections in TUI context (#25140) 2026-04-30 14:15:50 -04:00
Kit Langton
19271fca2d
Use workspace service in HTTP routes (#25139) 2026-04-30 13:57:25 -04:00
Kit Langton
feeebbe7d4
Preserve workspace context in session HTTP routes (#25136) 2026-04-30 13:53:26 -04:00
Kit Langton
f384675c01
test: use Effect test helper for run-service (#25048) 2026-04-30 17:05:10 +00:00
Kit Langton
ec3ab4a00c
test: use testEffect for retry policy (#25050) 2026-04-30 12:55:33 -04:00
Kit Langton
e4ac936eb9
test: use testEffect for plugin workspace adaptor (#25052) 2026-04-30 12:54:53 -04:00
Kit Langton
79e23b7eb9
test: use testEffect for instance state (#25115) 2026-04-30 12:53:13 -04:00
Kit Langton
92e80b4660
test: use Effect test helper for app runtime logger (#25049) 2026-04-30 12:52:29 -04:00
Kit Langton
ce63ca4d7a
test: use testEffect for system prompt test (#25047) 2026-04-30 12:51:32 -04:00
Kit Langton
fef7981942
test: use Effect runtime in runner deadlock case (#25045) 2026-04-30 12:45:30 -04:00
Aiden Cline
ffe0314c47
fix: ensure disabling OPENCODE_DISABLE_CLAUDE_CODE_SKILLS doesnt disable external skills too (#25123) 2026-04-30 11:15:53 -05:00
opencode-agent[bot]
375444a149 chore: update nix node_modules hashes 2026-04-30 15:48:28 +00:00
Kit Langton
65c15afe9f
test: use testEffect for instruction tests (#25046) 2026-04-30 11:48:13 -04:00
opencode-agent[bot]
8f57a2a462 chore: generate 2026-04-30 15:46:04 +00:00
James Long
53e9cac383
refactor(core): convert control-plane workspace to Effect (#25018) 2026-04-30 11:44:58 -04:00
Sebastian
fe0c182747
upgrade opentui to 0.2.0 (#24810) 2026-04-30 17:33:54 +02:00
opencode-agent[bot]
29b1060c67 chore: generate 2026-04-30 15:08:03 +00:00
Kit Langton
dddfcbf0d8
test: port instance HttpApi path/vcs read coverage to Effect 2026-04-30 11:07:00 -04:00
OpeOginni
62e1335388
fix(opencode): allow oc://renderer origin in cors middleware (#25099)
Some checks are pending
deploy / deploy (push) Waiting to run
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
2026-04-30 12:11:42 +00:00
Brendan Allan
908e28175f
fix: invert *_ready getters to fix server status indicator (#25077) 2026-04-30 15:10:39 +08:00
Brendan Allan
3398fd7719
feat(httpapi): add CORS middleware to instance routes (#25074) 2026-04-30 07:06:17 +00:00
Luke Parker
9bddf7f3ef
fix app crash restoring messages without model (#25062)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-30 14:44:53 +10:00
Dax Raad
8ba374fefa ci: enable sourcemaps for beta releases
Generate linked sourcemaps when building beta releases to help users
debug issues with readable stack traces.
2026-04-30 00:42:22 -04:00
Aiden Cline
3ef0aaf768
tweak: make azure onboarding ux a bit better (#25057) 2026-04-29 23:35:59 -05:00
Tommy D. Rossi
d7701dbfb6
fix(opencode): preserve external_dir and deny parent permissions in task child sessions (#23290) 2026-04-29 22:06:29 -05:00
Kit Langton
c49bf0b402
test: cover ConfigService helper (#25042) 2026-04-30 02:41:59 +00:00
Kit Langton
cee9610d26
refactor: use Effect config for HttpApi authorization (#25035) 2026-04-29 22:22:32 -04:00
Kit Langton
38adc13295
test: cover HttpApi authorization middleware (#25033) 2026-04-29 21:34:52 -04:00
Kit Langton
4fe14abb8c
test: cover HttpApi instance context middleware (#25032) 2026-04-29 21:24:45 -04:00
Kit Langton
9052e8a1ba
test: cover HttpApi workspace routing middleware (#25027) 2026-04-29 21:08:03 -04:00
github-actions[bot]
de78dedceb Update VOUCHED list
https://github.com/anomalyco/opencode/issues/23890#issuecomment-4348703527
2026-04-30 00:58:42 +00:00
Kit Langton
6f508d574e
test: deflake runner cancel test (#25021) 2026-04-29 20:19:52 -04:00
Kit Langton
61dfae31e7
test: cover HttpApi websocket proxy (#25017) 2026-04-29 19:37:50 -04:00
opencode
ac6aa43e3b sync release versions for v1.14.30 2026-04-29 23:33:39 +00:00
Luke Parker
ea89925042
fix: handle invalid mcp urls (#25019) 2026-04-30 09:32:26 +10:00
opencode-agent[bot]
12cbfe5b64 chore: generate 2026-04-29 22:40:26 +00:00
Luke Parker
d7b7be1909
fix(desktop): Path mismatches cause sessions missing + strong ID + existing data fix (#25013) 2026-04-29 22:39:19 +00:00
Aiden Cline
a740d2c667
fix: adjust azure defaults to closer match openai to prevent Item .. of type 'reasoning' was provided without its required following item (#25007) 2026-04-29 16:49:28 -05:00
Aiden Cline
588261076a
fix: make deepseek string check a bit looser (#25012) 2026-04-29 16:47:45 -05:00
Ruben De Smet
639e27c3ce
feat: add Mistral Medium 3.5 with reasoning support (#24996) 2026-04-29 16:26:24 -05:00
opencode-agent[bot]
1124ae17b4 chore: generate
Some checks failed
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / version (push) Waiting to run
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
docs-locale-sync / sync-locales (push) Has been cancelled
2026-04-29 20:52:19 +00:00
Kit Langton
9db5890ce5
Refactor HttpApi workspace routing and proxy boundaries (#25006) 2026-04-29 16:50:54 -04:00
James Long
293877cb7e
fix(core): reconnect editor context for session directory (#24984) 2026-04-29 15:11:44 -04:00
opencode-agent[bot]
c480006554 chore: generate 2026-04-29 18:17:10 +00:00
Aiden Cline
6aa8e894b1
chore: rm broken codesearch tool (#24992) 2026-04-29 13:15:44 -05:00
Aiden Cline
00bb9836a6
tweak: adjust order of system prompt instructions: Global, Project, Skills (#24974) 2026-04-29 10:55:53 -05:00
github-actions[bot]
71f9189607 Update VOUCHED list
https://github.com/anomalyco/opencode/issues/24964#issuecomment-4345349260
2026-04-29 15:54:34 +00:00
opencode-agent[bot]
a3f7ea2555 chore: generate 2026-04-29 13:47:48 +00:00
Kit Langton
d3df8e1180 test(httpapi): clean up SDK parity tests 2026-04-29 09:46:17 -04:00
opencode-agent[bot]
df147b65fd chore: generate 2026-04-29 13:36:05 +00:00
Kit Langton
6015084fa2
Prepare Effect HttpApi backend parity (#24853) 2026-04-29 09:34:50 -04:00
Brendan Allan
65ba1f6c13
fix(download): update beta asset names from electron to desktop (#24908)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-29 14:50:06 +08:00
Brendan Allan
d37e5af57d
Disable Windows update code signature verification (#24905) 2026-04-29 14:40:37 +08:00
spark4862
d71b827d8c
fix(session): remap compaction tail_start_id when forking (#24898)
Co-authored-by: spark4862 <spark4862@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-04-29 00:23:56 -05:00
Aiden Cline
504ca3d3d8
feat: make it easier to toggle on/off paste summary in the tui (#24869)
Some checks are pending
deploy / deploy (push) Waiting to run
docs-locale-sync / sync-locales (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (windows) (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-28 23:55:14 -05:00
Mike
a8c74c04de
docs: add Atomic Chat provider section (#23069) 2026-04-28 23:10:41 -05:00
Brendan Allan
f6b4f54216
refactor(app): convert getProjectAvatarSource to early returns (#24896) 2026-04-29 12:00:13 +08:00
Dax Raad
fc0e3c65b3 ignore 2026-04-28 23:07:46 -04:00
Dax Raad
23b8ed788e ignore 2026-04-28 23:03:48 -04:00
Dax Raad
3bd890f46b ignore: ideas 2026-04-28 23:00:07 -04:00
Luke Parker
9fbeafb63e
fix: clear timeout after promise rejection (#24864) 2026-04-28 23:37:12 +00:00
opencode-agent[bot]
91bd295209 chore: generate 2026-04-28 23:11:59 +00:00
Luke Parker
d4bf70be06
fix(bash): memory leak - release parsed syntax trees (#24861)
Co-authored-by: jiwenshang <jiwenshang@xiaohongshu.com>
2026-04-28 23:10:48 +00:00
opencode-agent[bot]
ae8904c4ff chore: generate 2026-04-28 20:50:56 +00:00
James Long
9209c04370
feat(core): filter sessions by path and add setting to disable (#24849)
Some checks failed
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Has been cancelled
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Has been cancelled
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Has been cancelled
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Has been cancelled
nix-hashes / update-hashes (push) Has been cancelled
2026-04-28 16:49:13 -04:00
Kit Langton
379e7f3f20
test(httpapi): cover sdk effect routes (#24836) 2026-04-28 16:34:06 -04:00
opencode-agent[bot]
366d11e1f8 chore: generate 2026-04-28 20:33:13 +00:00
Kit Langton
58836e75f0
fix(httpapi): wire global and control handlers (#24835) 2026-04-28 16:31:45 -04:00
Aiden Cline
0acac216ae
fix(copilot): ensure available variants sync from api (#24734)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-28 14:58:51 -05:00
opencode
276d162044 sync release versions for v1.14.29 2026-04-28 18:34:48 +00:00
opencode-agent[bot]
1b0ed983c5 chore: generate 2026-04-28 18:25:23 +00:00
Kit Langton
2e8d690ab1
fix(httpapi): finish sdk openapi parity (#24827) 2026-04-28 14:24:10 -04:00
Kit Langton
1ff8d289af
fix(tui): handle Zed selection byte offsets (#24825) 2026-04-28 14:09:39 -04:00
Dax
d54ffbda1c
tui: ignore invalid custom themes to prevent startup crashes (#24645) 2026-04-28 13:58:55 -04:00
Kit Langton
c00058ed7a
fix(httpapi): align request body openapi shape (#24811) 2026-04-28 12:55:37 -04:00
James Long
2c2fc3499b
feat(core): store relative path for sessions (#24704) 2026-04-28 11:51:24 -04:00
Kit Langton
ea3c6c3481
fix(httpapi): document instance query parameters (#24809) 2026-04-28 11:10:00 -04:00
opencode-agent[bot]
9b68b7195a chore: generate 2026-04-28 15:05:37 +00:00
Kit Langton
7739cc53b4
refactor(httpapi): fork server startup by flag (#24799) 2026-04-28 11:02:35 -04:00
David Hill
3fa78a8b01
docs: bump GitHub stars count to 150K (#24792) 2026-04-28 09:24:46 -04:00
Kit Langton
e57d0c2fee
fix(httpapi): document tui bad request responses
Document legacy 400 bad-request responses on TUI Effect HttpApi payload-validation endpoints and cover them with OpenAPI parity tests.
2026-04-28 09:23:54 -04:00
Kit Langton
2a4f2bf527
fix(httpapi): align sync seq validation
Reject negative and fractional sync sequence values in Effect HttpApi schemas so replay/history validation matches the legacy Hono routes.
2026-04-28 09:22:49 -04:00
Brendan Allan
aa07f38b07
fix(app): preserve per-workspace icon override from localStorage (#24738)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
Co-authored-by: Andrew Avsenin <snatvb@ya.ru>
2026-04-28 19:37:40 +08:00
Brendan Allan
9d1f17d836
fix(ui): remove redundant flex overrides in tool components (#24749) 2026-04-28 15:51:18 +08:00
github-actions[bot]
b420952e59 Update VOUCHED list
https://github.com/anomalyco/opencode/issues/24732#issuecomment-4332442679
2026-04-28 04:53:58 +00:00
opencode-agent[bot]
bb9e445257 chore: generate 2026-04-28 04:45:24 +00:00
Aiden Cline
528fb1d404
fix: sanitize tools for moonshot (#24730) 2026-04-27 23:44:21 -05:00
Brendan Allan
c8d9f7aa89
refactor(app): load sync state through TanStack Query (#23792)
Some checks failed
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
containers / build (push) Has been cancelled
2026-04-28 04:24:30 +00:00
opencode-agent[bot]
cd7ec93cdf chore: generate 2026-04-28 02:34:43 +00:00
Kit Langton
796b652d2b
fix(httpapi): preserve mcp oauth error parity (#24706) 2026-04-27 22:33:21 -04:00
Kit Langton
4d74849c1a
fix(tui): keep Zed context polling responsive (#24711) 2026-04-27 22:24:04 -04:00
opencode-agent[bot]
937a7c48a5 chore: update nix node_modules hashes 2026-04-28 02:22:33 +00:00
Kit Langton
704eb00de4
chore: bump effect beta (#24705) 2026-04-27 21:58:11 -04:00
opencode-agent[bot]
bad4599bf9 chore: generate 2026-04-28 01:50:04 +00:00
Kit Langton
892fd85ba7
fix(httpapi): preserve provider oauth authorize parity (#24703) 2026-04-27 21:48:50 -04:00
opencode-agent[bot]
0eaa47d857 chore: generate 2026-04-28 00:54:28 +00:00
Kit Langton
faca24d487
fix(httpapi): align session boolean query parsing (#24693) 2026-04-27 20:53:27 -04:00
Kit Langton
c103202ad5
test(httpapi): cover session json parity (#24682) 2026-04-27 19:48:57 -04:00
Kit Langton
ce78a4265d
fix(session): remove compaction summary dividers (#24677) 2026-04-27 18:15:11 -04:00
Kit Langton
c4a2353ac3
fix(session): omit undefined optional fields (#24676) 2026-04-27 17:50:09 -04:00
Kit Langton
576efed196
fix(httpapi): preserve optional session fields (#24671) 2026-04-27 21:38:28 +00:00
opencode-agent[bot]
dfc0075f90 chore: generate 2026-04-27 20:52:42 +00:00
Kit Langton
acd15dcc8a
test(httpapi): cover full OpenAPI route inventory (#24667) 2026-04-27 16:51:24 -04:00
Kit Langton
139c4fd555
fix(session): harden shell cancellation (#24553) 2026-04-27 20:47:18 +00:00
Cas
e0f3df8252
fix(tui): consume Enter in dialog useKeyboard handlers (#23390) 2026-04-27 15:31:49 -05:00
opencode-agent[bot]
9cd2e3a1c3 chore: generate 2026-04-27 20:31:05 +00:00
Kit Langton
f584f80219
test(httpapi): verify reflected route mounts (#24663) 2026-04-27 16:29:58 -04:00
Kit Langton
45eac589f8
fix(tui): preserve Zed context on terminal focus (#24662) 2026-04-27 16:25:37 -04:00
James Long
fab1768826
feat(core): file context improvements and option to disable (#24661)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / e2e (windows) (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-27 16:10:13 -04:00
Kit Langton
51fc10e407
fix(httpapi): enforce instance route parity (#24660) 2026-04-27 16:07:31 -04:00
opencode-agent[bot]
7a1c8465f5 chore: generate 2026-04-27 19:38:33 +00:00
Kit Langton
5290e9ca7e
fix(tui): stabilize Zed editor context polling (#24656) 2026-04-27 15:37:18 -04:00
Aiden Cline
c361c2953f
fix: ensure toolStreaming is set to off by default when using non anthropic models with anthropic sdk (#24642) 2026-04-27 14:16:00 -05:00
opencode-agent[bot]
ccb7669736 chore: generate 2026-04-27 18:34:44 +00:00
Dax
f25f1485d5
refactor: remove module barrels (#24554) 2026-04-27 14:33:33 -04:00
Kit Langton
55ecb06748
fix(httpapi): accept empty session create body (#24640) 2026-04-27 17:17:11 +00:00
Kit Langton
dc6991e5a8
fix(httpapi): mount workspace bridge routes (#24626) 2026-04-27 12:52:48 -04:00
Aiden Cline
738b3065dc
tweak: make interleaved reasoning_content default to true for openai compat deepseek setups (#24630) 2026-04-27 10:17:38 -05:00
opencode-agent[bot]
26cc537cb1 chore: generate 2026-04-27 14:46:00 +00:00
Seashore Shi
ede354b0e6
docs: fix duplicated word in CLI env var table (#24614)
Co-authored-by: Seashore <ss@SeashoredeMac-mini.local>
2026-04-27 09:44:53 -05:00
Jack
61eabfc60c
update Go DeepSeek flash limits for cache pricing drop (#24592)
Some checks failed
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
deploy / deploy (push) Has been cancelled
docs-locale-sync / sync-locales (push) Has been cancelled
2026-04-27 17:02:27 +08:00
opencode-agent[bot]
2789b770aa chore: generate 2026-04-27 05:40:37 +00:00
Luke Parker
8718b98ee1
fix: pass workspace symbol query to experimental LSP tool (#24576) 2026-04-27 05:39:36 +00:00
opencode-agent[bot]
c8b2f987f9 chore: generate 2026-04-27 05:39:13 +00:00
Frank
52b55b826f Merge branch 'fix/usage-chart' into dev 2026-04-27 01:37:52 -04:00
Frank
e8c20235b8 zen: coupons 2026-04-27 01:36:28 -04:00
opencode-agent[bot]
17701628bd chore: generate 2026-04-27 05:18:33 +00:00
21pounder
0efc6163f1
fix(opencode): agent create generates permissions field with deny ins… (#24482)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-04-27 00:17:08 -05:00
Jack
1e191ba815
update Go DeepSeek request estimates for cache pricing changes (#24575) 2026-04-27 13:06:51 +08:00
Aiden Cline
f19d863689
ignore: split up reasoning transforms (#24574) 2026-04-26 23:57:32 -05:00
Frank
4a1ef327ca sync 2026-04-27 00:52:54 -04:00
Aiden Cline
025a6392ce
fix: default tool call streaming to false for google vertex (#24573) 2026-04-26 23:42:23 -05:00
opencode
e578c442be sync release versions for v1.14.28 2026-04-27 04:23:44 +00:00
opencode-agent[bot]
0cecb1bff2 chore: generate 2026-04-27 04:05:59 +00:00
Frank
5d8971c1ed go: add deepseek icon 2026-04-27 00:04:44 -04:00
Dax
a9b62d67df
Refactor npm config handling (#24565)
Some checks are pending
publish / build-cli (push) Blocked by required conditions
docs-locale-sync / sync-locales (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-27 03:54:59 +00:00
Luke Parker
3525e61906
fix: ignore GitHub Actions changelog contributor (#24567) 2026-04-27 03:47:04 +00:00
github-actions[bot]
059e6c46db Update VOUCHED list
https://github.com/anomalyco/opencode/issues/24563#issuecomment-4323944984
2026-04-27 03:30:38 +00:00
Frank
5cf195e0af go: models endpoint
Some checks are pending
deploy / deploy (push) Waiting to run
2026-04-26 23:02:18 -04:00
opencode
244d1debe4 sync release versions for v1.14.27 2026-04-27 02:09:07 +00:00
opencode-agent[bot]
35734b42fe chore: update nix node_modules hashes 2026-04-27 01:55:51 +00:00
Sebastian
a3128e32c5
upgrade opentui to 0.1.105 (#24555) 2026-04-26 21:39:40 -04:00
Dax
5f8a72bfc4
fix(tui): hide provider checks before onboarding (#24551) 2026-04-27 01:18:26 +00:00
Kit Langton
418a1cf5f3
feat(httpapi): bridge tui routes (#24548) 2026-04-27 01:17:48 +00:00
Dax Raad
60ebd074ac core: refactor Installation service to use a single consolidated result object
Reorganizes the Installation service implementation by grouping info, method, latest, and upgrade methods into a single result object. This improves code locality and makes the service interface more maintainable. Also adds a clarifying comment explaining why the package manager's resolver is used for version lookups (to ensure registries, mirrors, auth, proxies, and dist-tags match upgrade behavior).
2026-04-26 21:05:42 -04:00
Kit Langton
216dd363e8
feat(httpapi): bridge pty routes (#24547) 2026-04-26 21:05:16 -04:00
Luke Parker
141f33d24b
feat: configurable shell selection + desktop settings UI (#20602) 2026-04-27 00:54:55 +00:00
opencode-agent[bot]
c4d8a8183e chore: generate 2026-04-26 23:56:15 +00:00
Kit Langton
58244eb687
feat(httpapi): bridge event stream (#24518) 2026-04-26 19:55:13 -04:00
Dax Raad
e9071b0a80 tui: remove excessive debug logging from workspace creation flow to reduce terminal output noise 2026-04-26 19:33:40 -04:00
OpeOginni
c68907ece2
fix(tui): update toast duration handling to use default value (#23395)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-04-26 17:27:02 -05:00
opencode
af3998c8a6 sync release versions for v1.14.26 2026-04-26 21:01:16 +00:00
opencode-agent[bot]
fad2618757 chore: update nix node_modules hashes 2026-04-26 20:45:18 +00:00
Sebastian
21e01dbe04
upgrade opentui to 0.1.104 (#24531) 2026-04-26 22:31:33 +02:00
Jack
3beadeebff
feat(go): add Go model listing endpoint (#24304)
Some checks are pending
deploy / deploy (push) Waiting to run
docs-locale-sync / sync-locales (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
Co-authored-by: Frank <frank@anoma.ly>
2026-04-26 14:46:49 -04:00
Jermiah Joseph
dcee1c3642
fix(editor): reject lock files with no workspace match for cwd (#24323) 2026-04-26 14:21:29 -04:00
Aiden Cline
00d1a7e090 chore: rm empty file 2026-04-26 12:32:41 -05:00
opencode-agent[bot]
bbb56c2a88 chore: generate 2026-04-26 17:14:24 +00:00
Frank
5186c6964b sync
Some checks are pending
deploy / deploy (push) Waiting to run
2026-04-26 13:11:09 -04:00
Frank
79c66e353f sync 2026-04-26 12:50:03 -04:00
opencode-agent[bot]
41f5e8a861 chore: generate 2026-04-26 16:25:19 +00:00
Kit Langton
c5b67927af
feat(httpapi): bridge remaining session routes (#24510) 2026-04-26 12:24:19 -04:00
opencode-agent[bot]
301ecb185e chore: generate 2026-04-26 16:01:07 +00:00
Kit Langton
151df05eeb
feat(httpapi): bridge session message mutations (#24487) 2026-04-26 12:00:02 -04:00
opencode-agent[bot]
55adcdfd07 chore: generate 2026-04-26 15:51:31 +00:00
Kit Langton
daaa2e5911
feat(httpapi): bridge session lifecycle routes (#24486) 2026-04-26 11:50:26 -04:00
opencode-agent[bot]
daff119fe4 chore: generate 2026-04-26 15:50:09 +00:00
Kit Langton
e0d1ff42c0
feat(httpapi): bridge session read routes (#24485) 2026-04-26 11:49:11 -04:00
opencode-agent[bot]
de413c56ae chore: generate 2026-04-26 15:32:42 +00:00
Kit Langton
da61b0290a
feat(httpapi): bridge sync routes (#24484) 2026-04-26 11:31:46 -04:00
Dax Raad
d03e6cedde ci: update team assignments in github-triage
Update team member assignments in the triage tool:
- Remove thdxr from tui and core teams
- Add simonklee to tui team
- Add kitlangton to core team
2026-04-26 11:18:59 -04:00
Jack
7feb6ab962
fix(docs): correct OpenCode Go DeepSeek endpoints (#24500) 2026-04-26 23:15:55 +08:00
opencode-agent[bot]
854d4b7a53 chore: generate 2026-04-26 15:13:18 +00:00
Kit Langton
aa5999b188
feat(httpapi): bridge workspace mutations (#24483) 2026-04-26 11:12:04 -04:00
opencode-agent[bot]
37c5eab6f8 chore: generate 2026-04-26 14:48:31 +00:00
Kit Langton
6daa2b9aeb
feat(httpapi): bridge experimental session list (#24478) 2026-04-26 10:47:24 -04:00
Ariane Emory
5a5a2e5fa0
fix: correct typo in comment (#24420)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-26 00:26:48 -05:00
opencode-agent[bot]
95c43fc675 chore: update nix node_modules hashes 2026-04-26 05:19:08 +00:00
Aiden Cline
e7053c41f4
fix: bump openrouter sdk version to resolve deepseek reasoning issue (bug was in sdk pkg) (#24435) 2026-04-26 00:05:16 -05:00
Dax Raad
fc6d4b4010 core: Add User-Agent header to identify client version in HTTP requests 2026-04-25 23:51:23 -04:00
Frank
2893588016 sync
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-25 23:23:48 -04:00
Kit Langton
f2d4d816fb
test(provider): avoid plugin dependency install timeout (#24416) 2026-04-26 02:11:25 +00:00
opencode-agent[bot]
097d930668 chore: generate 2026-04-26 01:55:57 +00:00
Kit Langton
7cab6824d1
feat(httpapi): bridge experimental tool routes (#24407) 2026-04-25 21:54:58 -04:00
opencode-agent[bot]
f77277a69e chore: generate 2026-04-25 23:28:25 +00:00
Kit Langton
450128f9be
feat(httpapi): bridge mcp oauth endpoints (#24405) 2026-04-25 19:27:11 -04:00
opencode-agent[bot]
3e35c974a4 chore: generate 2026-04-25 23:17:18 +00:00
Kit Langton
a14c22d4e9
feat(httpapi): bridge mcp control endpoints (#24403) 2026-04-25 19:16:19 -04:00
Kit Langton
58c65874ba
feat(httpapi): bridge project update endpoint (#24398) 2026-04-25 18:55:49 -04:00
opencode-agent[bot]
27b0877714 chore: generate 2026-04-25 22:43:13 +00:00
Kit Langton
5904f599a9
feat(httpapi): bridge project git init endpoint (#24394) 2026-04-25 18:42:02 -04:00
Kit Langton
df9e1d9854
feat(httpapi): bridge config update endpoint (#24387) 2026-04-25 17:52:34 -04:00
opencode-agent[bot]
75a22f82bd chore: generate 2026-04-25 19:36:15 +00:00
Kit Langton
a369130226
feat(httpapi): bridge worktree mutations (#24371) 2026-04-25 15:35:15 -04:00
opencode-agent[bot]
474024f9e6 chore: generate 2026-04-25 19:25:41 +00:00
Kit Langton
b4f4134e81
feat(httpapi): bridge instance dispose endpoint (#24368) 2026-04-25 15:24:07 -04:00
Kit Langton
cd64b67038
feat(tui): show /connect tip when user has no models configured (#24014)
Some checks failed
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
containers / build (push) Has been cancelled
2026-04-25 15:01:41 -04:00
opencode-agent[bot]
9af46df535 chore: generate 2026-04-25 18:56:31 +00:00
Kit Langton
b749866f0b
feat(httpapi): bridge worktree read endpoint (#24366) 2026-04-25 14:55:29 -04:00
opencode-agent[bot]
60fa708f0b chore: update nix node_modules hashes 2026-04-25 18:49:27 +00:00
opencode-agent[bot]
3b74077437 chore: generate 2026-04-25 18:47:06 +00:00
Kit Langton
95d4bb2130
feat(httpapi): bridge experimental read endpoints (#24365) 2026-04-25 14:46:06 -04:00
Dax Raad
f5dce6d960 core: move npm service to core package for shared dependency management 2026-04-25 14:36:15 -04:00
Dax Raad
1e98167b0e core: move cross-spawn-spawner to root and remove unused types
The cross-spawn-spawner module has been moved from src/effect/ to src/
to simplify the core package structure. The src/types.d.ts file which
contained unused type declarations has also been removed. All imports
throughout the codebase have been updated to reflect the new location.

This change reduces the package's internal complexity by flattening the
module hierarchy and removing dead code, making future maintenance easier.
2026-04-25 14:30:16 -04:00
Dax Raad
3eee2f6afa core: move cross-spawn-spawner from opencode to core package
Moved the cross-spawn-spawner module from packages/opencode to packages/core
to enable code sharing across the monorepo. This consolidates the process
spawning infrastructure into the core package so other packages can use
cross-platform child process spawning without duplicating the implementation.

Updated all import statements across the codebase to reference the new
location (@opencode-ai/core/effect/cross-spawn-spawner). Removed the
local copy from the opencode package along with its tests.
2026-04-25 14:23:17 -04:00
opencode-agent[bot]
ff4b60e1f3 chore: generate 2026-04-25 18:14:26 +00:00
Aiden Cline
f91b73b938 ci: fix model name 2026-04-25 14:13:19 -04:00
Kit Langton
05661c60ff
feat(httpapi): bridge file search endpoints (#24356) 2026-04-25 14:12:54 -04:00
Kit Langton
625aca49de
feat(tui): read Zed editor context from state db (#24352) 2026-04-25 18:10:58 +00:00
opencode-agent[bot]
3bc0c36ace chore: generate 2026-04-25 18:01:35 +00:00
Kit Langton
eb0219988b
feat(httpapi): bridge catalog read endpoints (#24353) 2026-04-25 14:00:30 -04:00
Dax Raad
705f792e87 core: move Global module to @opencode-ai/core for centralized path management
Move the Global module from packages/opencode/src/global to packages/core/src/global
to provide a unified location for managing XDG directories and application paths.
This eliminates duplicate path definitions across packages and ensures consistent
access to data, config, cache, state, log, and bin directories throughout the codebase.
2026-04-25 13:52:32 -04:00
Aiden Cline
716cf74190
ci: adjust review flow (#24355) 2026-04-25 13:52:19 -04:00
opencode-agent[bot]
fc8dae2422 chore: update nix node_modules hashes 2026-04-25 17:50:26 +00:00
opencode-agent[bot]
27353df0cc chore: generate 2026-04-25 17:31:57 +00:00
Dax Raad
1a734adb4d core: consolidate shared infrastructure into core package
Moves effect logging, observability, runtime utilities, flags, installation
version info, and process utilities from opencode to core package. This
enables better code sharing across packages and establishes core as the
single source of truth for foundational utilities.

All internal imports updated to use @opencode-ai/core paths for consistency.
2026-04-25 13:30:37 -04:00
Kit Langton
a9740b9133
fix(config): preserve permission order with Effect decode (#24308) 2026-04-25 13:30:12 -04:00
opencode-agent[bot]
62651c7114 chore: update nix node_modules hashes 2026-04-25 15:16:42 +00:00
Dax
1d728fc627
feat: add startup debug command (#24310) 2026-04-25 15:08:19 +00:00
Dax
62ef2a2207
refactor: rename shared package to core (#24309) 2026-04-25 10:59:17 -04:00
Dax
37aa8442dc
refactor: remove lazy cross-spawn runtime (#24305) 2026-04-25 14:46:16 +00:00
opencode-agent[bot]
5b0e828c10 chore: generate 2026-04-25 14:43:27 +00:00
Kit Langton
d5bfaef53d
feat(httpapi): bridge instance read endpoints (#24258) 2026-04-25 10:42:31 -04:00
opencode
bad732c26a sync release versions for v1.14.25 2026-04-25 14:37:01 +00:00
Dax Raad
1b92c95425 core: permission config schema now provides full IntelliSense for all tool permission keys
The permission configuration previously used a generic record type that didn't offer editor completions. Updated the schema to explicitly list all tool permission keys (read, edit, glob, grep, list, bash, task, external_directory, lsp, skill, todowrite, question, webfetch, websearch, codesearch, doom_loop) with proper types, enabling autocomplete when editing permission files.
2026-04-25 09:48:09 -04:00
Dax Raad
d748c71845 ci: centralize opentui dependencies in workspace catalog
Use catalog references for @opentui/core, @opentui/solid, and opentui-spinner
across packages to ensure consistent versions and simplify updates.
2026-04-25 09:41:30 -04:00
opencode-agent[bot]
fc88ed1262 chore: generate 2026-04-25 13:19:42 +00:00
Dax
66f93035b0
fix permission config order (#24222) 2026-04-25 13:18:42 +00:00
Simon Klee
9ff999cc2b
tool/lsp: include request details in permission metadata (#24139) 2026-04-25 14:21:35 +02:00
Kit Langton
4877eccc0d
Fix shell cwd after login startup (#24215)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-25 01:14:52 -04:00
Aiden Cline
f7d527cd28
ci: adjust auto close issue script to use not planned instead of completed (#24253) 2026-04-25 00:47:36 -04:00
opencode-agent[bot]
e29058c346 chore: update nix node_modules hashes 2026-04-25 03:04:44 +00:00
Maddison Hellstrom
49894330d9
fix(build): add prettier to devDependencies (#23255)
Some checks failed
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
docs-locale-sync / sync-locales (push) Has been cancelled
2026-04-24 22:47:05 -04:00
Luke Parker
cdc7d5f2ea
chore: group beta PR logs (#24236) 2026-04-25 10:42:33 +10:00
opencode-agent[bot]
ec201623fb chore: generate 2026-04-25 00:34:02 +00:00
Luke Parker
386091b79a
fix: validate beta before pushing (#24230) 2026-04-25 10:32:41 +10:00
Luke Parker
1e4b7b5451
Add Roslyn support for Razor and C# scripts (#24228) 2026-04-25 10:25:57 +10:00
opencode-agent[bot]
5cd178ba70 chore: generate 2026-04-24 22:05:23 +00:00
Kit Langton
97eb9fdee8
test(httpapi): cover hono bridge middleware (#24216) 2026-04-24 18:03:51 -04:00
Kit Langton
5a04de231e
refactor(ripgrep): migrate result schemas to effect (#24213) 2026-04-24 17:42:52 -04:00
Kyle Altendorf
bb3509b5ff
fix(opencode): clarify git amend condition to require verifying commit landed (#19937)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
2026-04-24 17:42:10 -04:00
Aiden Cline
4a67905266
fix: ensure gpt-5.5 compacts at correct context size when using openai oauth (#24212) 2026-04-24 17:39:06 -04:00
opencode-agent[bot]
c4e33d3168 chore: generate 2026-04-24 21:38:31 +00:00
Aiden Cline
872cdff2ab
ignore: denounce ai spammer 2026-04-24 17:37:28 -04:00
Kit Langton
361d7001d0
Clarify HttpApi migration plan (#24211) 2026-04-24 17:36:49 -04:00
opencode-agent[bot]
7a02eee0f4 chore: generate 2026-04-24 21:13:00 +00:00
Kit Langton
cf45a8d807
refactor(schema): decode effect schemas directly (#24169) 2026-04-24 17:11:52 -04:00
Kit Langton
435becbea0
Refactor HttpApi auth middleware wiring (#24168) 2026-04-24 17:11:07 -04:00
Frank
0405bc74e9 zen: gpt-5.5
Some checks are pending
deploy / deploy (push) Waiting to run
2026-04-24 14:57:23 -04:00
Frank
4dab2a8555 zen: gpt-5.5
Some checks are pending
docs-locale-sync / sync-locales (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-24 14:43:04 -04:00
Frank
3776d85e15 zen: gpt-5.5 2026-04-24 14:39:24 -04:00
Frank
d01ad4c499 zen: gpt-5.5 2026-04-24 14:05:29 -04:00
opencode
28025a0f36 sync release versions for v1.14.24 2026-04-24 15:53:03 +00:00
opencode-agent[bot]
1220f784fe chore: generate 2026-04-24 15:48:08 +00:00
Frank
28f7d31e72 zen: deepseek v4 pro 2026-04-24 11:46:01 -04:00
opencode-agent[bot]
66936b0fff chore: update nix node_modules hashes 2026-04-24 15:45:10 +00:00
Sebastian
3a5507de95
Use OpenTUI theme detection for initial TUI mode, again (#23846) 2026-04-24 17:28:07 +02:00
Aiden Cline
86715fecc4
fix: ensure assistant messages always have reasoning on them for deepseek (#24180) 2026-04-24 11:10:47 -04:00
07akioni
3062d3e070
fix: use existingModel as fallback for interleaved field (#24172) 2026-04-24 10:41:41 -04:00
opencode-agent[bot]
d89bfc32ac chore: generate 2026-04-24 13:20:23 +00:00
Kit Langton
011c23761b
feat(httpapi): bridge mcp status endpoint (#24100) 2026-04-24 09:18:57 -04:00
opencode
a8c8d2dd79 sync release versions for v1.14.23 2026-04-24 13:17:13 +00:00
Kit Langton
9f7ecd65e5
feat(httpapi): bridge file read endpoints (#24098) 2026-04-24 09:12:05 -04:00
Aiden Cline
f8e939d96f
fix: support max for deepseek (#24163) 2026-04-24 08:48:52 -04:00
黑墨水鱼
923af96d26
fix: preserve empty reasoning_content for DeepSeek V4 thinking mode (#24146)
Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-04-24 08:42:57 -04:00
Aiden Cline
a882e958b3
fix: deepseek variants (#24157) 2026-04-24 08:34:48 -04:00
Simon Klee
2cda629c8d
test(prompt): align shell placeholder expectation (#24147) 2026-04-24 13:00:11 +02:00
Brendan Allan
f033d2d8fb
fix(app): conditionally show model variant selector (#24115)
Some checks are pending
deploy / deploy (push) Waiting to run
docs-locale-sync / sync-locales (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
test / unit (linux) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-24 15:20:53 +08:00
Frank
a4bd88ab97 zen: deepseek v4 pro
Some checks are pending
deploy / deploy (push) Waiting to run
2026-04-24 02:09:14 -04:00
Frank
f4616c8268 sync 2026-04-24 02:05:22 -04:00
Brendan Allan
4712f0f3c1
feat(prompt): add shell mode UI with cancel button, custom icon, and example placeholder (#24105) 2026-04-24 14:04:55 +08:00
opencode-agent[bot]
6c1268f3b1 chore: generate 2026-04-24 05:28:43 +00:00
Brendan Allan
2e156b8990
fix(desktop): avoid relaunching without installing updates (#23806) 2026-04-24 13:27:36 +08:00
Brendan Allan
3bfe6a1ef6
ci: add platform-specific bun install flags (#23822) 2026-04-24 04:50:34 +00:00
opencode-agent[bot]
5c5069b622 chore: generate
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:macos-latest platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:macos-latest platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-23 21:44:44 +00:00
rahul
f8c6ddd4cb
feat(truncate): allow configuring tool output truncation limits (#23770)
Co-authored-by: rgs_ramp <rgs@ramp.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-04-23 17:43:33 -04:00
Kit Langton
e50a688ca3
feat(httpapi): bridge workspace read endpoints (#24062) 2026-04-23 17:32:02 -04:00
Aiden Cline
334ab4707c
fix: account for additional openai retry case (#24063) 2026-04-23 17:31:43 -04:00
Aiden Cline
87321942fe
chore: update copilot readme to symlink to an agents md to prevent dumbass agents from touching these files (#24057) 2026-04-23 17:08:46 -04:00
opencode-agent[bot]
a771859362 chore: generate 2026-04-23 20:50:19 +00:00
Kit Langton
31d01d404a
refactor(control-plane): migrate workspace DTO schemas (#24056) 2026-04-23 16:48:56 -04:00
Kit Langton
814e83ffec
docs: update effect schema migration tracker (#24054) 2026-04-23 16:23:45 -04:00
opencode-agent[bot]
4c3e65c877 chore: generate 2026-04-23 20:20:26 +00:00
James Long
98ea5b6e7e
feat(tui): support builtin protocol for handling context from editors (#24034) 2026-04-23 16:19:19 -04:00
opencode-agent[bot]
3f8c659056 chore: generate 2026-04-23 20:10:56 +00:00
Kit Langton
3910a6e527
refactor(tool): migrate tool framework + all 18 built-in tools to Effect Schema (#23244) 2026-04-23 16:09:34 -04:00
opencode-agent[bot]
24892559ae chore: generate 2026-04-23 19:38:54 +00:00
Kit Langton
cd93533b1f
refactor(bus): migrate BusEvent to Effect Schema (#24040) 2026-04-23 15:37:44 -04:00
Kit Langton
0590452456
refactor(schema): use Schema.Int and consolidate PositiveInt/NonNegativeInt (#24029)
Some checks are pending
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:macos-latest platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:macos-latest platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
2026-04-23 13:22:48 -04:00
Kit Langton
93940a1859
refactor(provider): migrate provider domain to Effect Schema (#24027) 2026-04-23 13:17:33 -04:00
Frank
1e439b8226 sync
Some checks are pending
deploy / deploy (push) Waiting to run
2026-04-23 13:13:29 -04:00
Kit Langton
8b2f8355b2
docs(schema): mark sync/index.ts migrated with compat-bridge note (#24024) 2026-04-23 12:54:46 -04:00
opencode-agent[bot]
aed03078f8 chore: generate 2026-04-23 16:44:32 +00:00
Kit Langton
c50d65b4d6
refactor(sync): make session events schema-first (#24019) 2026-04-23 12:43:08 -04:00
opencode-agent[bot]
353532b1c1 chore: generate 2026-04-23 16:00:01 +00:00
Shoubhit Dash
9df7c78ebe
fix(npm): respect npmrc for version lookups (#24016) 2026-04-23 21:28:54 +05:30
opencode
eb7555d3c6 sync release versions for v1.14.22 2026-04-23 15:56:49 +00:00
opencode-agent[bot]
2cd89d64e9 chore: generate 2026-04-23 15:31:16 +00:00
Kit Langton
0517ab4695
refactor(session): migrate session domain to Effect Schema (#24005) 2026-04-23 11:30:02 -04:00
James Long
bbf67d0fff
fix(tui): render all non-synthetic text parts of a user message (#24009) 2026-04-23 11:24:40 -04:00
1430 changed files with 74880 additions and 52149 deletions

View file

@ -1,6 +1,5 @@
name: Bug report name: Bug report
description: Report an issue that should be fixed description: Report an issue that should be fixed
labels: ["bug"]
body: body:
- type: textarea - type: textarea
id: description id: description

View file

@ -1,6 +1,5 @@
name: 🚀 Feature Request name: 🚀 Feature Request
description: Suggest an idea, feature, or enhancement description: Suggest an idea, feature, or enhancement
labels: [discussion]
title: "[FEATURE]:" title: "[FEATURE]:"
body: body:

View file

@ -1,6 +1,5 @@
name: Question name: Question
description: Ask a question description: Ask a question
labels: ["question"]
body: body:
- type: textarea - type: textarea
id: question id: question

View file

@ -11,6 +11,5 @@ MrMushrooooom
nexxeln nexxeln
R44VC0RP R44VC0RP
rekram1-node rekram1-node
RhysSullivan
thdxr thdxr
simonklee simonklee

35
.github/VOUCHED.td vendored
View file

@ -1,35 +0,0 @@
# Vouched contributors for this project.
#
# See https://github.com/mitchellh/vouch for details.
#
# Syntax:
# - One handle per line (without @), sorted alphabetically.
# - Optional platform prefix: platform:username (e.g., github:user).
# - Denounce with minus prefix: -username or -platform:username.
# - Optional details after a space following the handle.
adamdotdevin
-agusbasari29 AI PR slop
ariane-emory
-atharvau AI review spamming literally every PR
-borealbytes
-danieljoshuanazareth
-danieljoshuanazareth
edemaine
-florianleibert
fwang
iamdavidhill
jayair
kitlangton
kommander
-opencode2026
-opencodeengineer bot that spams issues
r44vc0rp
rekram1-node
-ricardo-m-l
-robinmordasiewicz
rubdos
shantur
simonklee
-spider-yamet clawdbot/llm psychosis, spam pinging the team
thdxr
-toastythebot

View file

@ -1,5 +1,10 @@
name: "Setup Bun" name: "Setup Bun"
description: "Setup Bun with caching and install dependencies" description: "Setup Bun with caching and install dependencies"
inputs:
install-flags:
description: "Additional flags to pass to 'bun install'"
required: false
default: ""
runs: runs:
using: "composite" using: "composite"
steps: steps:
@ -46,8 +51,8 @@ runs:
# e.g. ./patches/ for standard-openapi # e.g. ./patches/ for standard-openapi
# https://github.com/oven-sh/bun/issues/28147 # https://github.com/oven-sh/bun/issues/28147
if [ "$RUNNER_OS" = "Windows" ]; then if [ "$RUNNER_OS" = "Windows" ]; then
bun install --linker hoisted bun install --linker hoisted ${{ inputs.install-flags }}
else else
bun install bun install ${{ inputs.install-flags }}
fi fi
shell: bash shell: bash

View file

@ -1,170 +0,0 @@
name: daily-issues-recap
on:
schedule:
# Run at 6 PM EST (23:00 UTC, or 22:00 UTC during daylight saving)
- cron: "0 23 * * *"
workflow_dispatch: # Allow manual trigger for testing
jobs:
daily-recap:
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
issues: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: ./.github/actions/setup-bun
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
- name: Generate daily issues recap
id: recap
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENCODE_PERMISSION: |
{
"bash": {
"*": "deny",
"gh issue*": "allow",
"gh search*": "allow"
},
"webfetch": "deny",
"edit": "deny",
"write": "deny"
}
run: |
# Get today's date range
TODAY=$(date -u +%Y-%m-%d)
opencode run -m opencode/claude-sonnet-4-5 "Generate a daily issues recap for the OpenCode repository.
TODAY'S DATE: ${TODAY}
STEP 1: Gather today's issues
Search for all OPEN issues created today (${TODAY}) using:
gh issue list --repo ${{ github.repository }} --state open --search \"created:${TODAY}\" --json number,title,body,labels,state,comments,createdAt,author --limit 500
IMPORTANT: EXCLUDE all issues authored by Anomaly team members. Filter out issues where the author login matches ANY of these:
adamdotdevin, Brendonovich, fwang, Hona, iamdavidhill, jayair, kitlangton, kommander, MrMushrooooom, R44VC0RP, rekram1-node, thdxr
This recap is specifically for COMMUNITY (external) issues only.
STEP 2: Analyze and categorize
For each issue created today, categorize it:
**Severity Assessment:**
- CRITICAL: Crashes, data loss, security issues, blocks major functionality
- HIGH: Significant bugs affecting many users, important features broken
- MEDIUM: Bugs with workarounds, minor features broken
- LOW: Minor issues, cosmetic, nice-to-haves
**Activity Assessment:**
- Note issues with high comment counts or engagement
- Note issues from repeat reporters (check if author has filed before)
STEP 3: Cross-reference with existing issues
For issues that seem like feature requests or recurring bugs:
- Search for similar older issues to identify patterns
- Note if this is a frequently requested feature
- Identify any issues that are duplicates of long-standing requests
STEP 4: Generate the recap
Create a structured recap with these sections:
===DISCORD_START===
**Daily Issues Recap - ${TODAY}**
**Summary Stats**
- Total issues opened today: [count]
- By category: [bugs/features/questions]
**Critical/High Priority Issues**
[List any CRITICAL or HIGH severity issues with brief descriptions and issue numbers]
**Most Active/Discussed**
[Issues with significant engagement or from active community members]
**Trending Topics**
[Patterns noticed - e.g., 'Multiple reports about X', 'Continued interest in Y feature']
**Duplicates & Related**
[Issues that relate to existing open issues]
===DISCORD_END===
STEP 5: Format for Discord
Format the recap as a Discord-compatible message:
- Use Discord markdown (**, __, etc.)
- BE EXTREMELY CONCISE - this is an EOD summary, not a detailed report
- Use hyperlinked issue numbers with suppressed embeds: [#1234](<https://github.com/${{ github.repository }}/issues/1234>)
- Group related issues on single lines where possible
- Add emoji sparingly for critical items only
- HARD LIMIT: Keep under 1800 characters total
- Skip sections that have nothing notable (e.g., if no critical issues, omit that section)
- Prioritize signal over completeness - only surface what matters
OUTPUT: Output ONLY the content between ===DISCORD_START=== and ===DISCORD_END=== markers. Include the markers so I can extract it." > /tmp/recap_raw.txt
# Extract only the Discord message between markers
sed -n '/===DISCORD_START===/,/===DISCORD_END===/p' /tmp/recap_raw.txt | grep -v '===DISCORD' > /tmp/recap.txt
echo "recap_file=/tmp/recap.txt" >> $GITHUB_OUTPUT
- name: Post to Discord
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_ISSUES_WEBHOOK_URL }}
run: |
if [ -z "$DISCORD_WEBHOOK_URL" ]; then
echo "Warning: DISCORD_ISSUES_WEBHOOK_URL secret not set, skipping Discord post"
cat /tmp/recap.txt
exit 0
fi
# Read the recap
RECAP_RAW=$(cat /tmp/recap.txt)
RECAP_LENGTH=${#RECAP_RAW}
echo "Recap length: ${RECAP_LENGTH} chars"
# Function to post a message to Discord
post_to_discord() {
local msg="$1"
local content=$(echo "$msg" | jq -Rs '.')
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\": ${content}}" \
"$DISCORD_WEBHOOK_URL"
sleep 1
}
# If under limit, send as single message
if [ "$RECAP_LENGTH" -le 1950 ]; then
post_to_discord "$RECAP_RAW"
else
echo "Splitting into multiple messages..."
remaining="$RECAP_RAW"
while [ ${#remaining} -gt 0 ]; do
if [ ${#remaining} -le 1950 ]; then
post_to_discord "$remaining"
break
else
chunk="${remaining:0:1900}"
last_newline=$(echo "$chunk" | grep -bo $'\n' | tail -1 | cut -d: -f1)
if [ -n "$last_newline" ] && [ "$last_newline" -gt 500 ]; then
chunk="${remaining:0:$last_newline}"
remaining="${remaining:$((last_newline+1))}"
else
chunk="${remaining:0:1900}"
remaining="${remaining:1900}"
fi
post_to_discord "$chunk"
fi
done
fi
echo "Posted daily recap to Discord"

View file

@ -1,173 +0,0 @@
name: daily-pr-recap
on:
schedule:
# Run at 5pm EST (22:00 UTC, or 21:00 UTC during daylight saving)
- cron: "0 22 * * *"
workflow_dispatch: # Allow manual trigger for testing
jobs:
pr-recap:
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
pull-requests: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: ./.github/actions/setup-bun
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
- name: Generate daily PR recap
id: recap
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENCODE_PERMISSION: |
{
"bash": {
"*": "deny",
"gh pr*": "allow",
"gh search*": "allow"
},
"webfetch": "deny",
"edit": "deny",
"write": "deny"
}
run: |
TODAY=$(date -u +%Y-%m-%d)
opencode run -m opencode/claude-sonnet-4-5 "Generate a daily PR activity recap for the OpenCode repository.
TODAY'S DATE: ${TODAY}
STEP 1: Gather PR data
Run these commands to gather PR information. ONLY include OPEN PRs created or updated TODAY (${TODAY}):
# Open PRs created today
gh pr list --repo ${{ github.repository }} --state open --search \"created:${TODAY}\" --json number,title,author,labels,createdAt,updatedAt,reviewDecision,isDraft,additions,deletions --limit 100
# Open PRs with activity today (updated today)
gh pr list --repo ${{ github.repository }} --state open --search \"updated:${TODAY}\" --json number,title,author,labels,createdAt,updatedAt,reviewDecision,isDraft,additions,deletions --limit 100
IMPORTANT: EXCLUDE all PRs authored by Anomaly team members. Filter out PRs where the author login matches ANY of these:
adamdotdevin, Brendonovich, fwang, Hona, iamdavidhill, jayair, kitlangton, kommander, MrMushrooooom, R44VC0RP, rekram1-node, thdxr
This recap is specifically for COMMUNITY (external) contributions only.
STEP 2: For high-activity PRs, check comment counts
For promising PRs, run:
gh pr view [NUMBER] --repo ${{ github.repository }} --json comments --jq '[.comments[] | select(.author.login != \"copilot-pull-request-reviewer\" and .author.login != \"github-actions\")] | length'
IMPORTANT: When counting comments/activity, EXCLUDE these bot accounts:
- copilot-pull-request-reviewer
- github-actions
STEP 3: Identify what matters (ONLY from today's PRs)
**Bug Fixes From Today:**
- PRs with 'fix' or 'bug' in title created/updated today
- Small bug fixes (< 100 lines changed) that are easy to review
- Bug fixes from community contributors
**High Activity Today:**
- PRs with significant human comments today (excluding bots listed above)
- PRs with back-and-forth discussion today
**Quick Wins:**
- Small PRs (< 50 lines) that are approved or nearly approved
- PRs that just need a final review
STEP 4: Generate the recap
Create a structured recap:
===DISCORD_START===
**Daily PR Recap - ${TODAY}**
**New PRs Today**
[PRs opened today - group by type: bug fixes, features, etc.]
**Active PRs Today**
[PRs with activity/updates today - significant discussion]
**Quick Wins**
[Small PRs ready to merge]
===DISCORD_END===
STEP 5: Format for Discord
- Use Discord markdown (**, __, etc.)
- BE EXTREMELY CONCISE - surface what we might miss
- Use hyperlinked PR numbers with suppressed embeds: [#1234](<https://github.com/${{ github.repository }}/pull/1234>)
- Include PR author: [#1234](<url>) (@author)
- For bug fixes, add brief description of what it fixes
- Show line count for quick wins: \"(+15/-3 lines)\"
- HARD LIMIT: Keep under 1800 characters total
- Skip empty sections
- Focus on PRs that need human eyes
OUTPUT: Output ONLY the content between ===DISCORD_START=== and ===DISCORD_END=== markers. Include the markers so I can extract it." > /tmp/pr_recap_raw.txt
# Extract only the Discord message between markers
sed -n '/===DISCORD_START===/,/===DISCORD_END===/p' /tmp/pr_recap_raw.txt | grep -v '===DISCORD' > /tmp/pr_recap.txt
echo "recap_file=/tmp/pr_recap.txt" >> $GITHUB_OUTPUT
- name: Post to Discord
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_ISSUES_WEBHOOK_URL }}
run: |
if [ -z "$DISCORD_WEBHOOK_URL" ]; then
echo "Warning: DISCORD_ISSUES_WEBHOOK_URL secret not set, skipping Discord post"
cat /tmp/pr_recap.txt
exit 0
fi
# Read the recap
RECAP_RAW=$(cat /tmp/pr_recap.txt)
RECAP_LENGTH=${#RECAP_RAW}
echo "Recap length: ${RECAP_LENGTH} chars"
# Function to post a message to Discord
post_to_discord() {
local msg="$1"
local content=$(echo "$msg" | jq -Rs '.')
curl -s -H "Content-Type: application/json" \
-X POST \
-d "{\"content\": ${content}}" \
"$DISCORD_WEBHOOK_URL"
sleep 1
}
# If under limit, send as single message
if [ "$RECAP_LENGTH" -le 1950 ]; then
post_to_discord "$RECAP_RAW"
else
echo "Splitting into multiple messages..."
remaining="$RECAP_RAW"
while [ ${#remaining} -gt 0 ]; do
if [ ${#remaining} -le 1950 ]; then
post_to_discord "$remaining"
break
else
chunk="${remaining:0:1900}"
last_newline=$(echo "$chunk" | grep -bo $'\n' | tail -1 | cut -d: -f1)
if [ -n "$last_newline" ] && [ "$last_newline" -gt 500 ]; then
chunk="${remaining:0:$last_newline}"
remaining="${remaining:$((last_newline+1))}"
else
chunk="${remaining:0:1900}"
remaining="${remaining:1900}"
fi
post_to_discord "$chunk"
fi
done
fi
echo "Posted daily PR recap to Discord"

View file

@ -36,3 +36,11 @@ jobs:
PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }} PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }}
PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }} PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }}
STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }} STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }}
HONEYCOMB_API_KEY: ${{ secrets.HONEYCOMB_API_KEY }}
INCIDENT_API_KEY: ${{ secrets.INCIDENT_API_KEY }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ vars.SENTRY_ORG }}
SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }}
SENTRY_RELEASE: web@${{ github.sha }}
VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }}
VITE_SENTRY_RELEASE: web@${{ github.sha }}

View file

@ -88,7 +88,7 @@ jobs:
- name: Build - name: Build
id: build id: build
run: | run: |
./packages/opencode/script/build.ts ./packages/opencode/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }}
env: env:
OPENCODE_VERSION: ${{ needs.version.outputs.version }} OPENCODE_VERSION: ${{ needs.version.outputs.version }}
OPENCODE_RELEASE: ${{ needs.version.outputs.release }} OPENCODE_RELEASE: ${{ needs.version.outputs.release }}
@ -209,182 +209,6 @@ jobs:
packages/opencode/dist/opencode-windows-x64 packages/opencode/dist/opencode-windows-x64
packages/opencode/dist/opencode-windows-x64-baseline packages/opencode/dist/opencode-windows-x64-baseline
build-tauri:
needs:
- build-cli
- version
continue-on-error: false
env:
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }}
AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }}
strategy:
fail-fast: false
matrix:
settings:
- host: macos-latest
target: x86_64-apple-darwin
- host: macos-latest
target: aarch64-apple-darwin
# github-hosted: blacksmith lacks ARM64 MSVC cross-compilation toolchain
- host: windows-2025
target: aarch64-pc-windows-msvc
- host: blacksmith-4vcpu-windows-2025
target: x86_64-pc-windows-msvc
- host: blacksmith-4vcpu-ubuntu-2404
target: x86_64-unknown-linux-gnu
- host: blacksmith-8vcpu-ubuntu-2404-arm
target: aarch64-unknown-linux-gnu
runs-on: ${{ matrix.settings.host }}
steps:
- uses: actions/checkout@v3
with:
fetch-tags: true
- uses: apple-actions/import-codesign-certs@v2
if: ${{ runner.os == 'macOS' }}
with:
keychain: build
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- name: Verify Certificate
if: ${{ runner.os == 'macOS' }}
run: |
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application")
CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV
echo "Certificate imported."
- name: Setup Apple API Key
if: ${{ runner.os == 'macOS' }}
run: |
echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8
- uses: ./.github/actions/setup-bun
- name: Azure login
if: runner.os == 'Windows'
uses: azure/login@v2
with:
client-id: ${{ env.AZURE_CLIENT_ID }}
tenant-id: ${{ env.AZURE_TENANT_ID }}
subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
- uses: actions/setup-node@v4
with:
node-version: "24"
- name: Cache apt packages
if: contains(matrix.settings.host, 'ubuntu')
uses: actions/cache@v4
with:
path: ~/apt-cache
key: ${{ runner.os }}-${{ matrix.settings.target }}-apt-${{ hashFiles('.github/workflows/publish.yml') }}
restore-keys: |
${{ runner.os }}-${{ matrix.settings.target }}-apt-
- name: install dependencies (ubuntu only)
if: contains(matrix.settings.host, 'ubuntu')
run: |
mkdir -p ~/apt-cache && chmod -R a+rw ~/apt-cache
sudo apt-get update
sudo apt-get install -y --no-install-recommends -o dir::cache::archives="$HOME/apt-cache" libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
sudo chmod -R a+rw ~/apt-cache
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.settings.target }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: packages/desktop/src-tauri
shared-key: ${{ matrix.settings.target }}
- name: Prepare
run: |
cd packages/desktop
bun ./scripts/prepare.ts
env:
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
GITHUB_TOKEN: ${{ steps.committer.outputs.token }}
OPENCODE_CLI_ARTIFACT: ${{ (runner.os == 'Windows' && 'opencode-cli-windows') || 'opencode-cli' }}
RUST_TARGET: ${{ matrix.settings.target }}
GH_TOKEN: ${{ github.token }}
GITHUB_RUN_ID: ${{ github.run_id }}
- name: Resolve tauri portable SHA
if: contains(matrix.settings.host, 'ubuntu')
run: echo "TAURI_PORTABLE_SHA=$(git ls-remote https://github.com/tauri-apps/tauri.git refs/heads/feat/truly-portable-appimage | cut -f1)" >> "$GITHUB_ENV"
# Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released
- name: Install tauri-cli from portable appimage branch
uses: taiki-e/cache-cargo-install-action@v3
if: contains(matrix.settings.host, 'ubuntu')
with:
tool: tauri-cli
git: https://github.com/tauri-apps/tauri
# branch: feat/truly-portable-appimage
rev: ${{ env.TAURI_PORTABLE_SHA }}
- name: Show tauri-cli version
if: contains(matrix.settings.host, 'ubuntu')
run: cargo tauri --version
- name: Setup git committer
id: committer
uses: ./.github/actions/setup-git-committer
with:
opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
- name: Build and upload artifacts
uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a
timeout-minutes: 60
with:
projectPath: packages/desktop
uploadWorkflowArtifacts: true
tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }}
args: --target ${{ matrix.settings.target }} --config ${{ (github.ref_name == 'beta' && './src-tauri/tauri.beta.conf.json') || './src-tauri/tauri.prod.conf.json' }} --verbose
updaterJsonPreferNsis: true
releaseId: ${{ needs.version.outputs.release }}
tagName: ${{ needs.version.outputs.tag }}
releaseDraft: true
releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext]
repo: ${{ (github.ref_name == 'beta' && 'opencode-beta') || '' }}
releaseCommitish: ${{ github.sha }}
env:
GITHUB_TOKEN: ${{ steps.committer.outputs.token }}
TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: true
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8
- name: Verify signed Windows desktop artifacts
if: runner.os == 'Windows'
shell: pwsh
run: |
$files = @(
"${{ github.workspace }}\packages\desktop\src-tauri\sidecars\opencode-cli-${{ matrix.settings.target }}.exe"
)
$files += Get-ChildItem "${{ github.workspace }}\packages\desktop\src-tauri\target\${{ matrix.settings.target }}\release\bundle\nsis\*.exe" | Select-Object -ExpandProperty FullName
foreach ($file in $files) {
$sig = Get-AuthenticodeSignature $file
if ($sig.Status -ne "Valid") {
throw "Invalid signature for ${file}: $($sig.Status)"
}
}
build-electron: build-electron:
needs: needs:
- build-cli - build-cli
@ -402,12 +226,14 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
settings: settings:
- host: macos-latest - host: macos-26-intel
target: x86_64-apple-darwin target: x86_64-apple-darwin
platform_flag: --mac --x64 platform_flag: --mac --x64
- host: macos-latest bun_install_flags: --os=darwin --cpu=x64
- host: macos-26
target: aarch64-apple-darwin target: aarch64-apple-darwin
platform_flag: --mac --arm64 platform_flag: --mac --arm64
bun_install_flags: --os=darwin --cpu=arm64
# github-hosted: blacksmith lacks ARM64 MSVC cross-compilation toolchain # github-hosted: blacksmith lacks ARM64 MSVC cross-compilation toolchain
- host: "windows-2025" - host: "windows-2025"
target: aarch64-pc-windows-msvc target: aarch64-pc-windows-msvc
@ -437,6 +263,8 @@ jobs:
run: echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8 run: echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8
- uses: ./.github/actions/setup-bun - uses: ./.github/actions/setup-bun
with:
install-flags: ${{ matrix.settings.bun_install_flags }}
- name: Azure login - name: Azure login
if: runner.os == 'Windows' if: runner.os == 'Windows'
@ -476,7 +304,7 @@ jobs:
- name: Prepare - name: Prepare
run: bun ./scripts/prepare.ts run: bun ./scripts/prepare.ts
working-directory: packages/desktop-electron working-directory: packages/desktop
env: env:
OPENCODE_VERSION: ${{ needs.version.outputs.version }} OPENCODE_VERSION: ${{ needs.version.outputs.version }}
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
@ -487,14 +315,21 @@ jobs:
- name: Build - name: Build
run: bun run build run: bun run build
working-directory: packages/desktop-electron working-directory: packages/desktop
env: env:
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ vars.SENTRY_ORG }}
SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }}
SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }}
VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }}
VITE_SENTRY_ENVIRONMENT: ${{ (github.ref_name == 'beta' && 'beta') || 'production' }}
VITE_SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }}
- name: Package and publish - name: Package and publish
if: needs.version.outputs.release if: needs.version.outputs.release
run: npx electron-builder ${{ matrix.settings.platform_flag }} --publish always --config electron-builder.config.ts run: npx electron-builder ${{ matrix.settings.platform_flag }} --publish always --config electron-builder.config.ts
working-directory: packages/desktop-electron working-directory: packages/desktop
timeout-minutes: 60 timeout-minutes: 60
env: env:
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
@ -508,19 +343,43 @@ jobs:
- name: Package (no publish) - name: Package (no publish)
if: ${{ !needs.version.outputs.release }} if: ${{ !needs.version.outputs.release }}
run: npx electron-builder ${{ matrix.settings.platform_flag }} --publish never --config electron-builder.config.ts run: npx electron-builder ${{ matrix.settings.platform_flag }} --publish never --config electron-builder.config.ts
working-directory: packages/desktop-electron working-directory: packages/desktop
timeout-minutes: 60 timeout-minutes: 60
env: env:
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
- name: Create and upload macOS .app.tar.gz
if: runner.os == 'macOS' && needs.version.outputs.release
working-directory: packages/desktop/dist
env:
GH_TOKEN: ${{ steps.committer.outputs.token }}
run: |
if [[ "${{ matrix.settings.target }}" == "x86_64-apple-darwin" ]]; then
APP_DIR="mac"
OUT_NAME="opencode-desktop-mac-x64.app.tar.gz"
elif [[ "${{ matrix.settings.target }}" == "aarch64-apple-darwin" ]]; then
APP_DIR="mac-arm64"
OUT_NAME="opencode-desktop-mac-arm64.app.tar.gz"
else
echo "Unknown macOS target: ${{ matrix.settings.target }}"
exit 1
fi
APP_PATH=$(find "$APP_DIR" -maxdepth 1 -name "*.app" -type d | head -1)
if [ -z "$APP_PATH" ]; then
echo "No .app bundle found in $APP_DIR"
exit 1
fi
tar -czf "$OUT_NAME" -C "$(dirname "$APP_PATH")" "$(basename "$APP_PATH")"
gh release upload "v${{ needs.version.outputs.version }}" "$OUT_NAME" --clobber --repo "${{ needs.version.outputs.repo }}"
- name: Verify signed Windows Electron artifacts - name: Verify signed Windows Electron artifacts
if: runner.os == 'Windows' if: runner.os == 'Windows'
shell: pwsh shell: pwsh
run: | run: |
$files = @() $files = @()
$files += Get-ChildItem "${{ github.workspace }}\packages\desktop-electron\dist\*.exe" | Select-Object -ExpandProperty FullName $files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*.exe" | Select-Object -ExpandProperty FullName
$files += Get-ChildItem "${{ github.workspace }}\packages\desktop-electron\dist\*unpacked\*.exe" | Select-Object -ExpandProperty FullName $files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*unpacked\*.exe" | Select-Object -ExpandProperty FullName
$files += Get-ChildItem "${{ github.workspace }}\packages\desktop-electron\dist\*unpacked\resources\opencode-cli.exe" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName $files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*unpacked\resources\opencode-cli.exe" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName
foreach ($file in $files | Select-Object -Unique) { foreach ($file in $files | Select-Object -Unique) {
$sig = Get-AuthenticodeSignature $file $sig = Get-AuthenticodeSignature $file
@ -531,21 +390,20 @@ jobs:
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: opencode-electron-${{ matrix.settings.target }} name: opencode-desktop-${{ matrix.settings.target }}
path: packages/desktop-electron/dist/* path: packages/desktop/dist/*
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: needs.version.outputs.release if: needs.version.outputs.release
with: with:
name: latest-yml-${{ matrix.settings.target }} name: latest-yml-${{ matrix.settings.target }}
path: packages/desktop-electron/dist/latest*.yml path: packages/desktop/dist/latest*.yml
publish: publish:
needs: needs:
- version - version
- build-cli - build-cli
- sign-cli-windows - sign-cli-windows
- build-tauri
- build-electron - build-electron
if: always() && !failure() && !cancelled() if: always() && !failure() && !cancelled()
runs-on: blacksmith-4vcpu-ubuntu-2404 runs-on: blacksmith-4vcpu-ubuntu-2404
@ -572,13 +430,6 @@ jobs:
node-version: "24" node-version: "24"
registry-url: "https://registry.npmjs.org" registry-url: "https://registry.npmjs.org"
- name: Setup git committer
id: committer
uses: ./.github/actions/setup-git-committer
with:
opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
with: with:
name: opencode-cli name: opencode-cli
@ -600,6 +451,13 @@ jobs:
pattern: latest-yml-* pattern: latest-yml-*
path: /tmp/latest-yml path: /tmp/latest-yml
- name: Setup git committer
id: committer
uses: ./.github/actions/setup-git-committer
with:
opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
- name: Cache apt packages (AUR) - name: Cache apt packages (AUR)
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
@ -628,3 +486,5 @@ jobs:
GH_REPO: ${{ needs.version.outputs.repo }} GH_REPO: ${{ needs.version.outputs.repo }}
NPM_CONFIG_PROVENANCE: false NPM_CONFIG_PROVENANCE: false
LATEST_YML_DIR: /tmp/latest-yml LATEST_YML_DIR: /tmp/latest-yml
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}

View file

@ -45,13 +45,13 @@ jobs:
- name: Check PR guidelines compliance - name: Check PR guidelines compliance
env: env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENCODE_PERMISSION: '{ "bash": { "*": "deny", "gh*": "allow", "gh pr review*": "deny" } }' OPENCODE_PERMISSION: '{ "bash": { "*": "deny", "gh*": "allow", "gh pr review*": "deny" } }'
PR_TITLE: ${{ steps.pr-details.outputs.title }} PR_TITLE: ${{ steps.pr-details.outputs.title }}
run: | run: |
PR_BODY=$(jq -r .body pr_data.json) PR_BODY=$(jq -r .body pr_data.json)
opencode run -m anthropic/claude-opus-4-5 "A new pull request has been created: '${PR_TITLE}' opencode run -m opencode/gpt-5.5 --variant medium "A new pull request has been created: '${PR_TITLE}'
<pr-number> <pr-number>
${{ steps.pr-number.outputs.number }} ${{ steps.pr-number.outputs.number }}

View file

@ -1,116 +0,0 @@
name: vouch-check-issue
on:
issues:
types: [opened]
permissions:
contents: read
issues: write
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Check if issue author is denounced
uses: actions/github-script@v7
with:
script: |
const author = context.payload.issue.user.login;
const issueNumber = context.payload.issue.number;
// Skip bots
if (author.endsWith('[bot]')) {
core.info(`Skipping bot: ${author}`);
return;
}
// Read the VOUCHED.td file via API (no checkout needed)
let content;
try {
const response = await github.rest.repos.getContent({
owner: context.repo.owner,
repo: context.repo.repo,
path: '.github/VOUCHED.td',
});
content = Buffer.from(response.data.content, 'base64').toString('utf-8');
} catch (error) {
if (error.status === 404) {
core.info('No .github/VOUCHED.td file found, skipping check.');
return;
}
throw error;
}
// Parse the .td file for vouched and denounced users
const vouched = new Set();
const denounced = new Map();
for (const line of content.split('\n')) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) continue;
const isDenounced = trimmed.startsWith('-');
const rest = isDenounced ? trimmed.slice(1).trim() : trimmed;
if (!rest) continue;
const spaceIdx = rest.indexOf(' ');
const handle = spaceIdx === -1 ? rest : rest.slice(0, spaceIdx);
const reason = spaceIdx === -1 ? null : rest.slice(spaceIdx + 1).trim();
// Handle platform:username or bare username
// Only match bare usernames or github: prefix (skip other platforms)
const colonIdx = handle.indexOf(':');
if (colonIdx !== -1) {
const platform = handle.slice(0, colonIdx).toLowerCase();
if (platform !== 'github') continue;
}
const username = colonIdx === -1 ? handle : handle.slice(colonIdx + 1);
if (!username) continue;
if (isDenounced) {
denounced.set(username.toLowerCase(), reason);
continue;
}
vouched.add(username.toLowerCase());
}
// Check if the author is denounced
const reason = denounced.get(author.toLowerCase());
if (reason !== undefined) {
// Author is denounced — close the issue
const body = 'This issue has been automatically closed.';
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body,
});
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
state: 'closed',
state_reason: 'not_planned',
});
core.info(`Closed issue #${issueNumber} from denounced user ${author}`);
return;
}
// Author is positively vouched — add label
if (!vouched.has(author.toLowerCase())) {
core.info(`User ${author} is not denounced or vouched. Allowing issue.`);
return;
}
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
labels: ['Vouched'],
});
core.info(`Added vouched label to issue #${issueNumber} from ${author}`);

View file

@ -1,114 +0,0 @@
name: vouch-check-pr
on:
pull_request_target:
types: [opened]
permissions:
contents: read
issues: write
pull-requests: write
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Check if PR author is denounced
uses: actions/github-script@v7
with:
script: |
const author = context.payload.pull_request.user.login;
const prNumber = context.payload.pull_request.number;
// Skip bots
if (author.endsWith('[bot]')) {
core.info(`Skipping bot: ${author}`);
return;
}
// Read the VOUCHED.td file via API (no checkout needed)
let content;
try {
const response = await github.rest.repos.getContent({
owner: context.repo.owner,
repo: context.repo.repo,
path: '.github/VOUCHED.td',
});
content = Buffer.from(response.data.content, 'base64').toString('utf-8');
} catch (error) {
if (error.status === 404) {
core.info('No .github/VOUCHED.td file found, skipping check.');
return;
}
throw error;
}
// Parse the .td file for vouched and denounced users
const vouched = new Set();
const denounced = new Map();
for (const line of content.split('\n')) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) continue;
const isDenounced = trimmed.startsWith('-');
const rest = isDenounced ? trimmed.slice(1).trim() : trimmed;
if (!rest) continue;
const spaceIdx = rest.indexOf(' ');
const handle = spaceIdx === -1 ? rest : rest.slice(0, spaceIdx);
const reason = spaceIdx === -1 ? null : rest.slice(spaceIdx + 1).trim();
// Handle platform:username or bare username
// Only match bare usernames or github: prefix (skip other platforms)
const colonIdx = handle.indexOf(':');
if (colonIdx !== -1) {
const platform = handle.slice(0, colonIdx).toLowerCase();
if (platform !== 'github') continue;
}
const username = colonIdx === -1 ? handle : handle.slice(colonIdx + 1);
if (!username) continue;
if (isDenounced) {
denounced.set(username.toLowerCase(), reason);
continue;
}
vouched.add(username.toLowerCase());
}
// Check if the author is denounced
const reason = denounced.get(author.toLowerCase());
if (reason !== undefined) {
// Author is denounced — close the PR
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: 'This pull request has been automatically closed.',
});
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
state: 'closed',
});
core.info(`Closed PR #${prNumber} from denounced user ${author}`);
return;
}
// Author is positively vouched — add label
if (!vouched.has(author.toLowerCase())) {
core.info(`User ${author} is not denounced or vouched. Allowing PR.`);
return;
}
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: ['Vouched'],
});
core.info(`Added vouched label to PR #${prNumber} from ${author}`);

View file

@ -1,38 +0,0 @@
name: vouch-manage-by-issue
on:
issue_comment:
types: [created]
concurrency:
group: vouch-manage
cancel-in-progress: false
permissions:
contents: write
issues: write
pull-requests: read
jobs:
manage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
- name: Setup git committer
id: committer
uses: ./.github/actions/setup-git-committer
with:
opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
- uses: mitchellh/vouch/action/manage-by-issue@main
with:
issue-id: ${{ github.event.issue.number }}
comment-id: ${{ github.event.comment.id }}
roles: admin,maintain,write
env:
GITHUB_TOKEN: ${{ steps.committer.outputs.token }}

View file

@ -1,899 +0,0 @@
---
description: Translate content for a specified locale while preserving technical terms
mode: subagent
model: opencode/gpt-5.4
---
You are a professional translator and localization specialist.
Translate the user's content into the requested target locale (language + region, e.g. fr-FR, de-DE).
Requirements:
- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure).
- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks.
- Also preserve every term listed in the Do-Not-Translate glossary below.
- Also apply locale-specific guidance from `.opencode/glossary/<locale>.md` when available (for example, `zh-cn.md`).
- Do not modify fenced code blocks.
- Output ONLY the translation (no commentary).
If the target locale is missing, ask the user to provide it.
If no locale-specific glossary exists, use the global glossary only.
---
# Locale-Specific Glossaries
When a locale glossary exists, use it to:
- Apply preferred wording for recurring UI/docs terms in that locale
- Preserve locale-specific do-not-translate terms and casing decisions
- Prefer natural phrasing over literal translation when the locale file calls it out
- If the repo uses a locale alias slug, apply that file too (for example, `pt-BR` maps to `br.md` in this repo)
Locale guidance does not override code/command preservation rules or the global Do-Not-Translate glossary below.
---
# Do-Not-Translate Terms (OpenCode Docs)
Generated from: `packages/web/src/content/docs/*.mdx` (default English docs)
Generated on: 2026-02-10
Use this as a translation QA checklist / glossary. Preserve listed terms exactly (spelling, casing, punctuation).
General rules (verbatim, even if not listed below):
- Anything inside inline code (single backticks) or fenced code blocks (triple backticks)
- MDX/JS code in docs: `import ... from "..."`, component tags, identifiers
- CLI commands, flags, config keys/values, file paths, URLs/domains, and env vars
## Proper nouns and product names
Additional (not reliably captured via link text):
```text
Astro
Bun
Chocolatey
Cursor
Docker
Git
GitHub Actions
GitLab CI
GNOME Terminal
Homebrew
Mise
Neovim
Node.js
npm
Obsidian
opencode
opencode-ai
Paru
pnpm
ripgrep
Scoop
SST
Starlight
Visual Studio Code
VS Code
VSCodium
Windsurf
Windows Terminal
Yarn
Zellij
Zed
anomalyco
```
Extracted from link labels in the English docs (review and prune as desired):
```text
@openspoon/subtask2
302.AI console
ACP progress report
Agent Client Protocol
Agent Skills
Agentic
AGENTS.md
AI SDK
Alacritty
Anthropic
Anthropic's Data Policies
Atom One
Avante.nvim
Ayu
Azure AI Foundry
Azure portal
Baseten
built-in GITHUB_TOKEN
Bun.$
Catppuccin
Cerebras console
ChatGPT Plus or Pro
Cloudflare dashboard
CodeCompanion.nvim
CodeNomad
Configuring Adapters: Environment Variables
Context7 MCP server
Cortecs console
Deep Infra dashboard
DeepSeek console
Duo Agent Platform
Everforest
Fireworks AI console
Firmware dashboard
Ghostty
GitLab CLI agents docs
GitLab docs
GitLab User Settings > Access Tokens
Granular Rules (Object Syntax)
Grep by Vercel
Groq console
Gruvbox
Helicone
Helicone documentation
Helicone Header Directory
Helicone's Model Directory
Hugging Face Inference Providers
Hugging Face settings
install WSL
IO.NET console
JetBrains IDE
Kanagawa
Kitty
MiniMax API Console
Models.dev
Moonshot AI console
Nebius Token Factory console
Nord
OAuth
Ollama integration docs
OpenAI's Data Policies
OpenChamber
OpenCode
OpenCode config
OpenCode Config
OpenCode TUI with the opencode theme
OpenCode Web - Active Session
OpenCode Web - New Session
OpenCode Web - See Servers
OpenCode Zen
OpenCode-Obsidian
OpenRouter dashboard
OpenWork
OVHcloud panel
Pro+ subscription
SAP BTP Cockpit
Scaleway Console IAM settings
Scaleway Generative APIs
SDK documentation
Sentry MCP server
shell API
Together AI console
Tokyonight
Unified Billing
Venice AI console
Vercel dashboard
WezTerm
Windows Subsystem for Linux (WSL)
WSL
WSL (Windows Subsystem for Linux)
WSL extension
xAI console
Z.AI API console
Zed
ZenMux dashboard
Zod
```
## Acronyms and initialisms
```text
ACP
AGENTS
AI
AI21
ANSI
API
AST
AWS
BTP
CD
CDN
CI
CLI
CMD
CORS
DEBUG
EKS
ERROR
FAQ
GLM
GNOME
GPT
HTML
HTTP
HTTPS
IAM
ID
IDE
INFO
IO
IP
IRSA
JS
JSON
JSONC
K2
LLM
LM
LSP
M2
MCP
MR
NET
NPM
NTLM
OIDC
OS
PAT
PATH
PHP
PR
PTY
README
RFC
RPC
SAP
SDK
SKILL
SSE
SSO
TS
TTY
TUI
UI
URL
US
UX
VCS
VPC
VPN
VS
WARN
WSL
X11
YAML
```
## Code identifiers used in prose (CamelCase, mixedCase)
```text
apiKey
AppleScript
AssistantMessage
baseURL
BurntSushi
ChatGPT
ClangFormat
CodeCompanion
CodeNomad
DeepSeek
DefaultV2
FileContent
FileDiff
FileNode
fineGrained
FormatterStatus
GitHub
GitLab
iTerm2
JavaScript
JetBrains
macOS
mDNS
MiniMax
NeuralNomadsAI
NickvanDyke
NoeFabris
OpenAI
OpenAPI
OpenChamber
OpenCode
OpenRouter
OpenTUI
OpenWork
ownUserPermissions
PowerShell
ProviderAuthAuthorization
ProviderAuthMethod
ProviderInitError
SessionStatus
TabItem
tokenType
ToolIDs
ToolList
TypeScript
typesUrl
UserMessage
VcsInfo
WebView2
WezTerm
xAI
ZenMux
```
## OpenCode CLI commands (as shown in docs)
```text
opencode
opencode [project]
opencode /path/to/project
opencode acp
opencode agent [command]
opencode agent create
opencode agent list
opencode attach [url]
opencode attach http://10.20.30.40:4096
opencode attach http://localhost:4096
opencode auth [command]
opencode auth list
opencode auth login
opencode auth logout
opencode auth ls
opencode export [sessionID]
opencode github [command]
opencode github install
opencode github run
opencode import <file>
opencode import https://opncd.ai/s/abc123
opencode import session.json
opencode mcp [command]
opencode mcp add
opencode mcp auth [name]
opencode mcp auth list
opencode mcp auth ls
opencode mcp auth my-oauth-server
opencode mcp auth sentry
opencode mcp debug <name>
opencode mcp debug my-oauth-server
opencode mcp list
opencode mcp logout [name]
opencode mcp logout my-oauth-server
opencode mcp ls
opencode models --refresh
opencode models [provider]
opencode models anthropic
opencode run [message..]
opencode run Explain the use of context in Go
opencode serve
opencode serve --cors http://localhost:5173 --cors https://app.example.com
opencode serve --hostname 0.0.0.0 --port 4096
opencode serve [--port <number>] [--hostname <string>] [--cors <origin>]
opencode session [command]
opencode session list
opencode session delete <sessionID>
opencode stats
opencode uninstall
opencode upgrade
opencode upgrade [target]
opencode upgrade v0.1.48
opencode web
opencode web --cors https://example.com
opencode web --hostname 0.0.0.0
opencode web --mdns
opencode web --mdns --mdns-domain myproject.local
opencode web --port 4096
opencode web --port 4096 --hostname 0.0.0.0
opencode.server.close()
```
## Slash commands and routes
```text
/agent
/auth/:id
/clear
/command
/config
/config/providers
/connect
/continue
/doc
/editor
/event
/experimental/tool?provider=<p>&model=<m>
/experimental/tool/ids
/export
/file?path=<path>
/file/content?path=<p>
/file/status
/find?pattern=<pat>
/find/file
/find/file?query=<q>
/find/symbol?query=<q>
/formatter
/global/event
/global/health
/help
/init
/instance/dispose
/log
/lsp
/mcp
/mnt/
/mnt/c/
/mnt/d/
/models
/oc
/opencode
/path
/project
/project/current
/provider
/provider/{id}/oauth/authorize
/provider/{id}/oauth/callback
/provider/auth
/q
/quit
/redo
/resume
/session
/session/:id
/session/:id/abort
/session/:id/children
/session/:id/command
/session/:id/diff
/session/:id/fork
/session/:id/init
/session/:id/message
/session/:id/message/:messageID
/session/:id/permissions/:permissionID
/session/:id/prompt_async
/session/:id/revert
/session/:id/share
/session/:id/shell
/session/:id/summarize
/session/:id/todo
/session/:id/unrevert
/session/status
/share
/summarize
/theme
/tui
/tui/append-prompt
/tui/clear-prompt
/tui/control/next
/tui/control/response
/tui/execute-command
/tui/open-help
/tui/open-models
/tui/open-sessions
/tui/open-themes
/tui/show-toast
/tui/submit-prompt
/undo
/Users/username
/Users/username/projects/*
/vcs
```
## CLI flags and short options
```text
--agent
--attach
--command
--continue
--cors
--cwd
--days
--dir
--dry-run
--event
--file
--force
--fork
--format
--help
--hostname
--hostname 0.0.0.0
--keep-config
--keep-data
--log-level
--max-count
--mdns
--mdns-domain
--method
--model
--models
--port
--print-logs
--project
--prompt
--refresh
--session
--share
--title
--token
--tools
--verbose
--version
--wait
-c
-d
-f
-h
-m
-n
-s
-v
```
## Environment variables
```text
AI_API_URL
AI_FLOW_CONTEXT
AI_FLOW_EVENT
AI_FLOW_INPUT
AICORE_DEPLOYMENT_ID
AICORE_RESOURCE_GROUP
AICORE_SERVICE_KEY
ANTHROPIC_API_KEY
AWS_ACCESS_KEY_ID
AWS_BEARER_TOKEN_BEDROCK
AWS_PROFILE
AWS_REGION
AWS_ROLE_ARN
AWS_SECRET_ACCESS_KEY
AWS_WEB_IDENTITY_TOKEN_FILE
AZURE_COGNITIVE_SERVICES_RESOURCE_NAME
AZURE_RESOURCE_NAME
CI_PROJECT_DIR
CI_SERVER_FQDN
CI_WORKLOAD_REF
CLOUDFLARE_ACCOUNT_ID
CLOUDFLARE_API_TOKEN
CLOUDFLARE_GATEWAY_ID
CONTEXT7_API_KEY
GITHUB_TOKEN
GITLAB_AI_GATEWAY_URL
GITLAB_HOST
GITLAB_INSTANCE_URL
GITLAB_OAUTH_CLIENT_ID
GITLAB_TOKEN
GITLAB_TOKEN_OPENCODE
GOOGLE_APPLICATION_CREDENTIALS
GOOGLE_CLOUD_PROJECT
HTTP_PROXY
HTTPS_PROXY
K2_
MY_API_KEY
MY_ENV_VAR
MY_MCP_CLIENT_ID
MY_MCP_CLIENT_SECRET
NO_PROXY
NODE_ENV
NODE_EXTRA_CA_CERTS
NPM_AUTH_TOKEN
OC_ALLOW_WAYLAND
OPENCODE_API_KEY
OPENCODE_AUTH_JSON
OPENCODE_AUTO_SHARE
OPENCODE_CLIENT
OPENCODE_CONFIG
OPENCODE_CONFIG_CONTENT
OPENCODE_CONFIG_DIR
OPENCODE_DISABLE_AUTOCOMPACT
OPENCODE_DISABLE_AUTOUPDATE
OPENCODE_DISABLE_CLAUDE_CODE
OPENCODE_DISABLE_CLAUDE_CODE_PROMPT
OPENCODE_DISABLE_CLAUDE_CODE_SKILLS
OPENCODE_DISABLE_DEFAULT_PLUGINS
OPENCODE_DISABLE_LSP_DOWNLOAD
OPENCODE_DISABLE_MODELS_FETCH
OPENCODE_DISABLE_PRUNE
OPENCODE_DISABLE_TERMINAL_TITLE
OPENCODE_ENABLE_EXA
OPENCODE_ENABLE_EXPERIMENTAL_MODELS
OPENCODE_EXPERIMENTAL
OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS
OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT
OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER
OPENCODE_EXPERIMENTAL_EXA
OPENCODE_EXPERIMENTAL_FILEWATCHER
OPENCODE_EXPERIMENTAL_ICON_DISCOVERY
OPENCODE_EXPERIMENTAL_LSP_TOOL
OPENCODE_EXPERIMENTAL_LSP_TY
OPENCODE_EXPERIMENTAL_MARKDOWN
OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX
OPENCODE_EXPERIMENTAL_OXFMT
OPENCODE_EXPERIMENTAL_PLAN_MODE
OPENCODE_ENABLE_QUESTION_TOOL
OPENCODE_FAKE_VCS
OPENCODE_GIT_BASH_PATH
OPENCODE_MODEL
OPENCODE_MODELS_URL
OPENCODE_PERMISSION
OPENCODE_PORT
OPENCODE_SERVER_PASSWORD
OPENCODE_SERVER_USERNAME
PROJECT_ROOT
RESOURCE_NAME
RUST_LOG
VARIABLE_NAME
VERTEX_LOCATION
XDG_CONFIG_HOME
```
## Package/module identifiers
```text
../../../config.mjs
@astrojs/starlight/components
@opencode-ai/plugin
@opencode-ai/sdk
path
shescape
zod
@
@ai-sdk/anthropic
@ai-sdk/cerebras
@ai-sdk/google
@ai-sdk/openai
@ai-sdk/openai-compatible
@File#L37-42
@modelcontextprotocol/server-everything
@opencode
```
## GitHub owner/repo slugs referenced in docs
```text
24601/opencode-zellij-namer
angristan/opencode-wakatime
anomalyco/opencode
apps/opencode-agent
athal7/opencode-devcontainers
awesome-opencode/awesome-opencode
backnotprop/plannotator
ben-vargas/ai-sdk-provider-opencode-sdk
btriapitsyn/openchamber
BurntSushi/ripgrep
Cluster444/agentic
code-yeongyu/oh-my-opencode
darrenhinde/opencode-agents
different-ai/opencode-scheduler
different-ai/openwork
features/copilot
folke/tokyonight.nvim
franlol/opencode-md-table-formatter
ggml-org/llama.cpp
ghoulr/opencode-websearch-cited.git
H2Shami/opencode-helicone-session
hosenur/portal
jamesmurdza/daytona
jenslys/opencode-gemini-auth
JRedeker/opencode-morph-fast-apply
JRedeker/opencode-shell-strategy
kdcokenny/ocx
kdcokenny/opencode-background-agents
kdcokenny/opencode-notify
kdcokenny/opencode-workspace
kdcokenny/opencode-worktree
login/device
mohak34/opencode-notifier
morhetz/gruvbox
mtymek/opencode-obsidian
NeuralNomadsAI/CodeNomad
nick-vi/opencode-type-inject
NickvanDyke/opencode.nvim
NoeFabris/opencode-antigravity-auth
nordtheme/nord
numman-ali/opencode-openai-codex-auth
olimorris/codecompanion.nvim
panta82/opencode-notificator
rebelot/kanagawa.nvim
remorses/kimaki
sainnhe/everforest
shekohex/opencode-google-antigravity-auth
shekohex/opencode-pty.git
spoons-and-mirrors/subtask2
sudo-tee/opencode.nvim
supermemoryai/opencode-supermemory
Tarquinen/opencode-dynamic-context-pruning
Th3Whit3Wolf/one-nvim
upstash/context7
vtemian/micode
vtemian/octto
yetone/avante.nvim
zenobi-us/opencode-plugin-template
zenobi-us/opencode-skillful
```
## Paths, filenames, globs, and URLs
```text
./.opencode/themes/*.json
./<project-slug>/storage/
./config/#custom-directory
./global/storage/
.agents/skills/*/SKILL.md
.agents/skills/<name>/SKILL.md
.clang-format
.claude
.claude/skills
.claude/skills/*/SKILL.md
.claude/skills/<name>/SKILL.md
.env
.github/workflows/opencode.yml
.gitignore
.gitlab-ci.yml
.ignore
.NET SDK
.npmrc
.ocamlformat
.opencode
.opencode/
.opencode/agents/
.opencode/commands/
.opencode/commands/test.md
.opencode/modes/
.opencode/plans/*.md
.opencode/plugins/
.opencode/skills/<name>/SKILL.md
.opencode/skills/git-release/SKILL.md
.opencode/tools/
.well-known/opencode
{ type: "raw" \| "patch", content: string }
{file:path/to/file}
**/*.js
%USERPROFILE%/intelephense/license.txt
%USERPROFILE%\.cache\opencode
%USERPROFILE%\.config\opencode\opencode.jsonc
%USERPROFILE%\.config\opencode\plugins
%USERPROFILE%\.local\share\opencode
%USERPROFILE%\.local\share\opencode\log
<project-root>/.opencode/themes/*.json
<providerId>/<modelId>
<your-project>/.opencode/plugins/
~
~/...
~/.agents/skills/*/SKILL.md
~/.agents/skills/<name>/SKILL.md
~/.aws/credentials
~/.bashrc
~/.cache/opencode
~/.cache/opencode/node_modules/
~/.claude/CLAUDE.md
~/.claude/skills/
~/.claude/skills/*/SKILL.md
~/.claude/skills/<name>/SKILL.md
~/.config/opencode
~/.config/opencode/AGENTS.md
~/.config/opencode/agents/
~/.config/opencode/commands/
~/.config/opencode/modes/
~/.config/opencode/opencode.json
~/.config/opencode/opencode.jsonc
~/.config/opencode/plugins/
~/.config/opencode/skills/*/SKILL.md
~/.config/opencode/skills/<name>/SKILL.md
~/.config/opencode/themes/*.json
~/.config/opencode/tools/
~/.config/zed/settings.json
~/.local/share
~/.local/share/opencode/
~/.local/share/opencode/auth.json
~/.local/share/opencode/log/
~/.local/share/opencode/mcp-auth.json
~/.local/share/opencode/opencode.jsonc
~/.npmrc
~/.zshrc
~/code/
~/Library/Application Support
~/projects/*
~/projects/personal/
${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts
$HOME/intelephense/license.txt
$HOME/projects/*
$XDG_CONFIG_HOME/opencode/themes/*.json
agent/
agents/
build/
commands/
dist/
http://<wsl-ip>:4096
http://127.0.0.1:8080/callback
http://localhost:<port>
http://localhost:4096
http://localhost:4096/doc
https://app.example.com
https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/
https://opencode.ai/zen/v1/chat/completions
https://opencode.ai/zen/v1/messages
https://opencode.ai/zen/v1/models/gemini-3-flash
https://opencode.ai/zen/v1/models/gemini-3-pro
https://opencode.ai/zen/v1/responses
https://RESOURCE_NAME.openai.azure.com/
laravel/pint
log/
model: "anthropic/claude-sonnet-4-5"
modes/
node_modules/
openai/gpt-4.1
opencode.ai/config.json
opencode/<model-id>
opencode/gpt-5.1-codex
opencode/gpt-5.2-codex
opencode/kimi-k2
openrouter/google/gemini-2.5-flash
opncd.ai/s/<share-id>
packages/*/AGENTS.md
plugins/
project/
provider_id/model_id
provider/model
provider/model-id
rm -rf ~/.cache/opencode
skills/
skills/*/SKILL.md
src/**/*.ts
themes/
tools/
```
## Keybind strings
```text
alt+b
Alt+Ctrl+K
alt+d
alt+f
Cmd+Esc
Cmd+Option+K
Cmd+Shift+Esc
Cmd+Shift+G
Cmd+Shift+P
ctrl+a
ctrl+b
ctrl+d
ctrl+e
Ctrl+Esc
ctrl+f
ctrl+g
ctrl+k
Ctrl+Shift+Esc
Ctrl+Shift+P
ctrl+t
ctrl+u
ctrl+w
ctrl+x
DELETE
Shift+Enter
WIN+R
```
## Model ID strings referenced
```text
{env:OPENCODE_MODEL}
anthropic/claude-3-5-sonnet-20241022
anthropic/claude-haiku-4-20250514
anthropic/claude-haiku-4-5
anthropic/claude-sonnet-4-20250514
anthropic/claude-sonnet-4-5
gitlab/duo-chat-haiku-4-5
lmstudio/google/gemma-3n-e4b
openai/gpt-4.1
openai/gpt-5
opencode/gpt-5.1-codex
opencode/gpt-5.2-codex
opencode/kimi-k2
openrouter/google/gemini-2.5-flash
```

View file

@ -1,7 +1,7 @@
--- ---
mode: primary mode: primary
hidden: true hidden: true
model: opencode/minimax-m2.5 model: opencode/gpt-5.4-nano
color: "#44BA81" color: "#44BA81"
tools: tools:
"*": false "*": false
@ -14,127 +14,30 @@ Use your github-triage tool to triage issues.
This file is the source of truth for ownership/routing rules. This file is the source of truth for ownership/routing rules.
## Labels Assign issues by choosing the team with the strongest overlap. The github-triage tool will assign a random member from that team.
### windows Do not add labels to issues. Only assign an owner.
Use for any issue that mentions Windows (the OS). Be sure they are saying that they are on Windows. When calling github-triage, pass one of these team values: tui, desktop_web, core, inference, windows.
- Use if they mention WSL too ## Teams
#### perf ### TUI
Performance-related issues: Terminal UI issues, including rendering, keybindings, scrolling, terminal compatibility, SSH behavior, crashes in the TUI, and low-level TUI performance.
- Slow performance ### Desktop / Web
- High RAM usage
- High CPU usage
**Only** add if it's likely a RAM or CPU issue. **Do not** add for LLM slowness. Desktop application and browser-based app issues, including `opencode web`, desktop-specific UI behavior, packaging, and web view problems.
#### desktop ### Core
Desktop app issues: Core opencode server and harness issues, including sqlite, snapshots, memory, API behavior, agent context construction, tool execution, provider integrations, model behavior, documentation, and larger architectural features.
- `opencode web` command ### Inference
- The desktop app itself
**Only** add if it's specifically about the Desktop application or `opencode web` view. **Do not** add for terminal, TUI, or general opencode issues. OpenCode Zen, OpenCode Go, and billing issues.
#### nix ### Windows
**Only** add if the issue explicitly mentions nix. Windows-specific issues, including native Windows behavior, WSL interactions, path handling, shell compatibility, and installation or runtime problems that only happen on Windows.
If the issue does not mention nix, do not add nix.
If the issue mentions nix, assign to `rekram1-node`.
#### zen
**Only** add if the issue mentions "zen" or "opencode zen" or "opencode black".
If the issue doesn't have "zen" or "opencode black" in it then don't add zen label
#### core
Use for core server issues in `packages/opencode/`, excluding `packages/opencode/src/cli/cmd/tui/`.
Examples:
- LSP server behavior
- Harness behavior (agent + tools)
- Feature requests for server behavior
- Agent context construction
- API endpoints
- Provider integration issues
- New, broken, or poor-quality models
#### acp
If the issue mentions acp support, assign acp label.
#### docs
Add if the issue requests better documentation or docs updates.
#### opentui
TUI issues potentially caused by our underlying TUI library:
- Keybindings not working
- Scroll speed issues (too fast/slow/laggy)
- Screen flickering
- Crashes with opentui in the log
**Do not** add for general TUI bugs.
When assigning to people here are the following rules:
Desktop / Web:
Use for desktop-labeled issues only.
- adamdotdevin
- iamdavidhill
- Brendonovich
- nexxeln
Zen:
ONLY assign if the issue will have the "zen" label.
- fwang
- MrMushrooooom
TUI (`packages/opencode/src/cli/cmd/tui/...`):
- thdxr for TUI UX/UI product decisions and interaction flow
- kommander for OpenTUI engine issues: rendering artifacts, keybind handling, terminal compatibility, SSH behavior, and low-level perf bottlenecks
- rekram1-node for TUI bugs that are not clearly OpenTUI engine issues
Core (`packages/opencode/...`, excluding TUI subtree):
- thdxr for sqlite/snapshot/memory bugs and larger architectural core features
- jlongster for opencode server + API feature work (tool currently remaps jlongster -> thdxr until assignable)
- rekram1-node for harness issues, provider issues, and other bug-squashing
For core bugs that do not clearly map, either thdxr or rekram1-node is acceptable.
Docs:
- R44VC0RP
Windows:
- Hona (assign any issue that mentions Windows or is likely Windows-specific)
Determinism rules:
- If title + body does not contain "zen", do not add the "zen" label
- If "nix" label is added but title + body does not mention nix/nixos, the tool will drop "nix"
- If title + body mentions nix/nixos, assign to `rekram1-node`
- If "desktop" label is added, the tool will override assignee and randomly pick one Desktop / Web owner
In all other cases, choose the team/section with the most overlap with the issue and assign a member from that team at random.
ACP:
- rekram1-node (assign any acp issues to rekram1-node)

View file

@ -18,9 +18,12 @@ Do not use `git log` or author metadata when deciding attribution.
Rules: Rules:
- Write the final file with sections in this order: - Write the final file with release sections in this order:
`## Core`, `## TUI`, `## Desktop`, `## SDK`, `## Extensions` `## Core`, `## TUI`, `## Desktop`, `## SDK`, `## Extensions`
- Only include sections that have at least one notable entry - Only include sections that have at least one notable entry
- Within each release section, keep bug fixes grouped under `### Bugfixes`
- Keep other notable entries under `### Improvements` when a section has bug fixes too
- Omit empty subsections
- Keep one bullet per commit you keep - Keep one bullet per commit you keep
- Skip commits that are entirely internal, CI, tests, refactors, or otherwise not user-facing - Skip commits that are entirely internal, CI, tests, refactors, or otherwise not user-facing
- Start each bullet with a capital letter - Start each bullet with a capital letter

View file

@ -0,0 +1,14 @@
---
description: translate English to other languages
model: opencode/claude-opus-4-7
---
run git diff and translate changed english doc and UI copy files to other international languages. Translate all languages in parallel to save time.
Requirements:
- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure).
- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks.
- Also preserve every term listed in the Do-Not-Translate glossary below.
- Also apply locale-specific guidance from `.opencode/glossary/<locale>.md` when available (for example, `zh-cn.md`).
- Do not modify fenced code blocks.

View file

@ -1,21 +1,38 @@
--- ---
name: effect name: effect
description: Answer questions about the Effect framework description: Work with Effect v4 / effect-smol TypeScript code in this repo
--- ---
# Effect # Effect
This codebase uses Effect, a framework for writing typescript. This codebase uses Effect for typed, composable TypeScript services, schemas, and workflows.
## How to Answer Effect Questions ## Source Of Truth
1. Clone the Effect repository: `https://github.com/Effect-TS/effect-smol` to Use the current Effect v4 / effect-smol source, not memory or older Effect v2/v3 examples.
`.opencode/references/effect-smol` in this project NOT the skill folder.
2. Use the explore agent to search the codebase for answers about Effect patterns, APIs, and concepts 1. If `.opencode/references/effect-smol` is missing, clone `https://github.com/Effect-TS/effect-smol` there. Do this in the project, not in the skill folder.
3. Provide responses based on the actual Effect source code and documentation 2. Search `.opencode/references/effect-smol` for exact APIs, examples, tests, and naming patterns before answering or implementing Effect-specific code.
3. Also inspect existing repo code for local house style before introducing new patterns.
4. Prefer answers and implementations backed by specific source files or nearby repo examples.
## Guidelines ## Guidelines
- Always use the explore agent with the cloned repository when answering Effect-related questions - Prefer current Effect v4 APIs and project-local patterns over old blog posts, examples, or package-memory guesses.
- Reference specific files and patterns found in the Effect codebase - Use `Effect.gen(function* () { ... })` for multi-step workflows.
- Do not answer from memory - always verify against the source - Use `Effect.fn("Name")` or `Effect.fnUntraced(...)` for named effects when adding reusable service methods or important workflows.
- Prefer Effect `Schema` for API and domain data shapes. Use branded schemas for IDs and `Schema.TaggedErrorClass` for typed domain errors when modeling new error surfaces.
- Keep HTTP handlers thin: decode input, read request context, call services, and map transport errors. Put business rules in services.
- In Effect service code, prefer Effect-aware platform abstractions and dependencies over ad hoc promises where the surrounding code already does so.
- Keep layer composition explicit. Avoid broad hidden provisioning that makes missing dependencies hard to see.
- In tests, prefer the repo's existing Effect test helpers and live tests for filesystem, git, child process, locks, or timing behavior.
- Do not introduce `any`, non-null assertions, unchecked casts, or older Effect APIs just to satisfy types.
- Do not answer from memory. Verify against `.opencode/references/effect-smol` or nearby code first.
## Testing Patterns
- Use `testEffect(...)` from `packages/opencode/test/lib/effect.ts` for tests that exercise Effect services, layers, runtime context, scoped resources, or platform integrations.
- Use `it.live(...)` for filesystem, git repositories, HTTP servers, sockets, child processes, locks, real time, and other live platform behavior.
- Run tests from package directories such as `packages/opencode`; never run package tests from the repo root.
- Prefer explicit test layers over ad hoc managed runtimes. Keep dependency provisioning visible in the test file.
- Use scoped fixtures and finalizers for resources that must be cleaned up, including temporary directories, flags, databases, fibers, servers, and global state.

View file

@ -1,16 +1,14 @@
/// <reference path="../env.d.ts" /> /// <reference path="../env.d.ts" />
import { tool } from "@opencode-ai/plugin" import { tool } from "@opencode-ai/plugin"
const TEAM = { const TEAM = {
desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"], tui: ["kommander", "simonklee"],
zen: ["fwang", "MrMushrooooom"], desktop_web: ["Hona", "Brendonovich"],
tui: ["thdxr", "kommander", "rekram1-node"], core: ["jlongster", "rekram1-node", "nexxeln", "kitlangton"],
core: ["thdxr", "rekram1-node", "jlongster"], inference: ["fwang", "MrMushrooooom"],
docs: ["R44VC0RP"],
windows: ["Hona"], windows: ["Hona"],
} as const } as const
const ASSIGNEES = [...new Set(Object.values(TEAM).flat())]
function pick<T>(items: readonly T[]) { function pick<T>(items: readonly T[]) {
return items[Math.floor(Math.random() * items.length)]! return items[Math.floor(Math.random() * items.length)]!
} }
@ -38,79 +36,25 @@ async function githubFetch(endpoint: string, options: RequestInit = {}) {
} }
export default tool({ export default tool({
description: `Use this tool to assign and/or label a GitHub issue. description: `Use this tool to assign a GitHub issue.
Choose labels and assignee using the current triage policy and ownership rules. Provide the team that should own the issue. This tool picks a random assignee from that team and does not apply labels.`,
Pick the most fitting labels for the issue and assign one owner.
If unsure, choose the team/section with the most overlap with the issue and assign a member from that team at random.`,
args: { args: {
assignee: tool.schema team: tool.schema
.enum(ASSIGNEES as [string, ...string[]]) .enum(Object.keys(TEAM) as [keyof typeof TEAM, ...(keyof typeof TEAM)[]])
.describe("The username of the assignee") .describe("The owning team"),
.default("rekram1-node"),
labels: tool.schema
.array(tool.schema.enum(["nix", "opentui", "perf", "web", "desktop", "zen", "docs", "windows", "core"]))
.describe("The labels(s) to add to the issue")
.default([]),
}, },
async execute(args) { async execute(args) {
const issue = getIssueNumber() const issue = getIssueNumber()
const owner = "anomalyco" const owner = "anomalyco"
const repo = "opencode" const repo = "opencode"
const assignee = pick(TEAM[args.team])
const results: string[] = []
let labels = [...new Set(args.labels.map((x) => (x === "desktop" ? "web" : x)))]
const web = labels.includes("web")
const text = `${process.env.ISSUE_TITLE ?? ""}\n${process.env.ISSUE_BODY ?? ""}`.toLowerCase()
const zen = /\bzen\b/.test(text) || text.includes("opencode black")
const nix = /\bnix(os)?\b/.test(text)
if (labels.includes("nix") && !nix) {
labels = labels.filter((x) => x !== "nix")
results.push("Dropped label: nix (issue does not mention nix)")
}
const assignee = nix ? "rekram1-node" : web ? pick(TEAM.desktop) : args.assignee
if (labels.includes("zen") && !zen) {
throw new Error("Only add the zen label when issue title/body contains 'zen'")
}
if (web && !nix && !(TEAM.desktop as readonly string[]).includes(assignee)) {
throw new Error("Web issues must be assigned to adamdotdevin, iamdavidhill, Brendonovich, or nexxeln")
}
if ((TEAM.zen as readonly string[]).includes(assignee) && !labels.includes("zen")) {
throw new Error("Only zen issues should be assigned to fwang or MrMushrooooom")
}
if (assignee === "Hona" && !labels.includes("windows")) {
throw new Error("Only windows issues should be assigned to Hona")
}
if (assignee === "R44VC0RP" && !labels.includes("docs")) {
throw new Error("Only docs issues should be assigned to R44VC0RP")
}
if (assignee === "kommander" && !labels.includes("opentui")) {
throw new Error("Only opentui issues should be assigned to kommander")
}
await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, { await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, {
method: "POST", method: "POST",
body: JSON.stringify({ assignees: [assignee] }), body: JSON.stringify({ assignees: [assignee] }),
}) })
results.push(`Assigned @${assignee} to issue #${issue}`)
if (labels.length > 0) { return `Assigned @${assignee} from ${args.team} to issue #${issue}`
await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/labels`, {
method: "POST",
body: JSON.stringify({ labels }),
})
results.push(`Added labels: ${labels.join(", ")}`)
}
return results.join("\n")
}, },
}) })

View file

@ -132,7 +132,7 @@ It's very similar to Claude Code in terms of capability. Here are the key differ
- 100% open source - 100% open source
- Not coupled to any provider. Although we recommend the models we provide through [OpenCode Zen](https://opencode.ai/zen), OpenCode can be used with Claude, OpenAI, Google, or even local models. As models evolve, the gaps between them will close and pricing will drop, so being provider-agnostic is important. - Not coupled to any provider. Although we recommend the models we provide through [OpenCode Zen](https://opencode.ai/zen), OpenCode can be used with Claude, OpenAI, Google, or even local models. As models evolve, the gaps between them will close and pricing will drop, so being provider-agnostic is important.
- Out-of-the-box LSP support - Built-in opt-in LSP support
- A focus on TUI. OpenCode is built by neovim users and the creators of [terminal.shop](https://terminal.shop); we are going to push the limits of what's possible in the terminal. - A focus on TUI. OpenCode is built by neovim users and the creators of [terminal.shop](https://terminal.shop); we are going to push the limits of what's possible in the terminal.
- A client/server architecture. This, for example, can allow OpenCode to run on your computer while you drive it remotely from a mobile app, meaning that the TUI frontend is just one of the possible clients. - A client/server architecture. This, for example, can allow OpenCode to run on your computer while you drive it remotely from a mobile app, meaning that the TUI frontend is just one of the possible clients.

596
bun.lock

File diff suppressed because it is too large Load diff

View file

@ -115,6 +115,27 @@ const zenLiteCouponFirstMonth100 = new stripe.Coupon("ZenLiteCouponFirstMonth100
appliesToProducts: [zenLiteProduct.id], appliesToProducts: [zenLiteProduct.id],
duration: "once", duration: "once",
}) })
const zenLiteCouponThreeMonths100 = new stripe.Coupon("ZenLiteCoupon3Months100", {
name: "3 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 3,
})
const zenLiteCouponSixMonths100 = new stripe.Coupon("ZenLiteCoupon6Months100", {
name: "6 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 6,
})
const zenLiteCouponTwelveMonths100 = new stripe.Coupon("ZenLiteCoupon12Months100", {
name: "12 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 12,
})
const zenLitePrice = new stripe.Price("ZenLitePrice", { const zenLitePrice = new stripe.Price("ZenLitePrice", {
product: zenLiteProduct.id, product: zenLiteProduct.id,
currency: "usd", currency: "usd",
@ -131,6 +152,9 @@ const ZEN_LITE_PRICE = new sst.Linkable("ZEN_LITE_PRICE", {
priceInr: 92900, priceInr: 92900,
firstMonth50Coupon: zenLiteCouponFirstMonth50.id, firstMonth50Coupon: zenLiteCouponFirstMonth50.id,
firstMonth100Coupon: zenLiteCouponFirstMonth100.id, firstMonth100Coupon: zenLiteCouponFirstMonth100.id,
threeMonths100Coupon: zenLiteCouponThreeMonths100.id,
sixMonths100Coupon: zenLiteCouponSixMonths100.id,
twelveMonths100Coupon: zenLiteCouponTwelveMonths100.id,
}, },
}) })
@ -197,6 +221,9 @@ const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", { const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
properties: { value: stripeWebhook.secret }, properties: { value: stripeWebhook.secret },
}) })
const INCIDENT_WEBHOOK_SIGNING_SECRET = new sst.Secret("INCIDENT_WEBHOOK_SIGNING_SECRET")
const DISCORD_INCIDENT_WEBHOOK_URL = new sst.Secret("DISCORD_INCIDENT_WEBHOOK_URL")
const gatewayKv = new sst.cloudflare.Kv("GatewayKv") const gatewayKv = new sst.cloudflare.Kv("GatewayKv")
//////////////// ////////////////
@ -227,6 +254,8 @@ new sst.cloudflare.x.SolidStart("Console", {
database, database,
AUTH_API_URL, AUTH_API_URL,
STRIPE_WEBHOOK_SECRET, STRIPE_WEBHOOK_SECRET,
INCIDENT_WEBHOOK_SIGNING_SECRET,
DISCORD_INCIDENT_WEBHOOK_URL,
STRIPE_SECRET_KEY, STRIPE_SECRET_KEY,
EMAILOCTOPUS_API_KEY, EMAILOCTOPUS_API_KEY,
AWS_SES_ACCESS_KEY_ID, AWS_SES_ACCESS_KEY_ID,

320
infra/monitoring.ts Normal file
View file

@ -0,0 +1,320 @@
const displayName = (s: string) =>
s
.split("-")
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
.join(" ")
.replace(/(?<=\d) (?=\d)/g, ".")
const resourceName = (s: string) => displayName(s).replace(/[^a-zA-Z0-9]/g, "")
const varSpec = (label: string, name: string) =>
$jsonStringify({
content: [
{
content: [
{
attrs: {
name,
label,
missing: false,
},
type: "varSpec",
},
],
type: "paragraph",
},
],
type: "doc",
})
const fields = {
model: incident.getAlertAttributeOutput({ name: "Model" }),
product: incident.getAlertAttributeOutput({ name: "Product" }),
}
const alertSource = new incident.AlertSource("HoneycombAlertSource", {
name: $app.stage === "production" ? "Honeycomb" : `Honeycomb (${$app.stage})`,
sourceType: "honeycomb",
template: {
title: {
literal: varSpec("Payload -> Title", "title"),
},
description: {
literal: varSpec("Payload -> Description", "description"),
},
attributes: [
{
alertAttributeId: fields.model.id,
binding: {
value: {
reference: 'expressions["model"]',
},
mergeStrategy: "first_wins",
},
},
{
alertAttributeId: fields.product.id,
binding: {
value: {
reference: 'expressions["product"]',
},
mergeStrategy: "first_wins",
},
},
],
expressions: [
{
label: "Model",
operations: [
{
operationType: "parse",
parse: {
returns: {
array: false,
type: fields.model.type,
},
source: "$['model']",
},
},
],
reference: "model",
rootReference: "payload",
},
{
label: "Product",
operations: [
{
operationType: "parse",
parse: {
returns: {
array: false,
type: fields.product.type,
},
source: "$['product']",
},
},
],
reference: "product",
rootReference: "payload",
},
],
},
})
const webhookRecipient = new honeycomb.WebhookRecipient(`IncidentWebhook`, {
name: $app.stage === "production" ? "Incident.io" : `Incident.io (${$app.stage})`,
url: alertSource.alertEventsUrl,
secret: alertSource.secretToken,
templates: [
{
type: "trigger",
body: $jsonStringify({
title: "{{ .Name }}",
description: "{{ .Description }}",
status: "{{ .Alert.Status }}",
deduplication_key: "{{ .Alert.InstanceID }}",
source_url: "{{ .Result.URL }}",
model: "{{ .Vars.model }}",
product: "{{ .Vars.product }}",
}),
},
],
variables: [
{
name: "model",
},
{
name: "product",
},
],
})
new incident.AlertRoute("HoneycombAlertRoute", {
name: $app.stage === "production" ? "Honeycomb" : `Honeycomb (${$app.stage})`,
enabled: true,
isPrivate: false,
alertSources: [
{
alertSourceId: alertSource.id,
conditionGroups: [
{
conditions: [
{
subject: "alert.title",
operation: "is_set",
paramBindings: [],
},
],
},
],
},
],
conditionGroups: [
{
conditions: [
{
subject: "alert.title",
operation: "is_set",
paramBindings: [],
},
],
},
],
expressions: [],
escalationConfig: {
autoCancelEscalations: true,
escalationTargets: [],
},
incidentConfig: {
autoDeclineEnabled: true,
enabled: true,
conditionGroups: [],
deferTimeSeconds: 0,
groupingKeys: [
{
reference: $interpolate`alert.attributes.${fields.model.id}`,
},
{
reference: $interpolate`alert.attributes.${fields.product.id}`,
},
],
groupingWindowSeconds: 900,
},
incidentTemplate: {
name: {
value: {
literal: varSpec("Alert -> Title", "alert.title"),
},
},
summary: {
value: {
literal: varSpec("Alert -> Description", "alert.description"),
},
},
startInTriage: {
value: {
literal: "true",
},
},
severity: {
mergeStrategy: "first-wins",
},
incidentMode: {
value: {
literal: $app.stage === "production" ? "standard" : "test",
},
},
},
})
type Product = "go" | "zen"
type Trigger = (opts: { model: string; product: Product }) => {
id: string
title: string
description: string
json: honeycomb.GetQuerySpecificationOutputArgs
threshold: { op: ">=" | "<="; value: number }
baseline: 3600 | 86400
}
type Model = { id: string; products: Product[]; triggers: Trigger[] }
const httpErrors: Trigger = ({ model, product }) => ({
id: "increased-http-errors",
title: `Increased HTTP Errors for ${displayName(model)} on ${displayName(product)}`,
description: `Detected increased rate of HTTP errors for ${displayName(model)} on OpenCode ${displayName(product)}`,
json: {
calculations: [
{
op: "COUNT",
name: "TOTAL",
filterCombination: "AND",
filters: [
{ column: "model", op: "=", value: model },
{ column: "isGoTier", op: "=", value: product === "go" ? "true" : "false" },
],
},
{
op: "COUNT",
name: "FAILED",
filterCombination: "AND",
filters: [
{ column: "model", op: "=", value: model },
{ column: "isGoTier", op: "=", value: product === "go" ? "true" : "false" },
{ column: "status", op: ">=", value: "400" },
{ column: "status", op: "!=", value: "401" },
],
},
],
formulas: [{ name: "ERROR", expression: "$FAILED / $TOTAL" }],
timeRange: 900,
},
// Alert when errors surge 50% compared to the previous period
threshold: { op: ">=", value: 50 },
// What previous time period to evaluate against
baseline: 3600,
})
const models: Model[] = [
{ id: "kimi-k2.6", products: ["go", "zen"], triggers: [httpErrors] },
{ id: "kimi-k2.5", products: ["go", "zen"], triggers: [httpErrors] },
{ id: "deepseek-v4-flash", products: ["go", "zen"], triggers: [httpErrors] },
{ id: "deepseek-v4-pro", products: ["go", "zen"], triggers: [httpErrors] },
{ id: "glm-5.1", products: ["go", "zen"], triggers: [httpErrors] },
// { id: "glm-5", products: ["go"], triggers: [httpErrors] },
{ id: "qwen3.6-plus", products: ["go", "zen"], triggers: [httpErrors] },
{ id: "qwen3.5-plus", products: ["go"], triggers: [httpErrors] },
{ id: "minimax-m2.7", products: ["go", "zen"], triggers: [httpErrors] },
// { id: "minimax-m2.5", products: ["go", "zen"], triggers: [httpErrors] },
{ id: "mimo-v2.5-pro", products: ["go"], triggers: [httpErrors] },
// { id: "mimo-v2.5", products: ["go"], triggers: [httpErrors] },
// { id: "mimo-v2-omni", products: ["go"], triggers: [httpErrors] },
// { id: "mimo-v2-pro", products: ["go"], triggers: [httpErrors] },
{ id: "claude-opus-4-7", products: ["zen"], triggers: [httpErrors] },
// { id: "claude-opus-4-6", products: ["zen"], triggers: [httpErrors] },
// { id: "claude-sonnet-4-6", products: ["zen"], triggers: [httpErrors] },
{ id: "gpt-5.5", products: ["zen"], triggers: [httpErrors] },
{ id: "big-pickle", products: ["zen"], triggers: [httpErrors] },
// { id: "minimax-m2.5-free", products: ["zen"], triggers: [httpErrors] },
// { id: "hy3-preview-free", products: ["zen"], triggers: [httpErrors] },
// { id: "nemotron-3-super-free", products: ["zen"], triggers: [httpErrors] },
// { id: "trinity-large-preview-free", products: ["zen"], triggers: [httpErrors] },
// { id: "ling-2.6-flash-free", products: ["zen"], triggers: [httpErrors] },
]
if ($app.stage !== "production") {
models.splice(1)
}
for (const model of models) {
for (const product of model.products) {
for (const trigger of model.triggers) {
const spec = trigger({ model: model.id, product })
new honeycomb.Trigger(resourceName(`${spec.id}-${product}-${model.id}`), {
name: spec.title,
description: spec.description,
queryJson: honeycomb.getQuerySpecificationOutput(spec.json).json,
alertType: "on_change",
// This is the minimum when using % change detection
frequency: 900,
baselineDetails: [{ type: "percentage", offsetMinutes: spec.baseline / 60 }],
thresholds: [{ ...spec.threshold, exceededLimit: 1 }],
recipients: [
{
id: webhookRecipient.id,
notificationDetails: [
{
variables: [
{ name: "model", value: model.id },
{ name: "product", value: product },
],
},
],
},
],
})
}
}
}

View file

@ -1,8 +1,8 @@
{ {
"nodeModules": { "nodeModules": {
"x86_64-linux": "sha256-AgHhYsiygxbsBo3JN4HqHXKAwh8n1qeuSCe2qqxlxW4=", "x86_64-linux": "sha256-Oo27Xkoo5HOzLaRs7FmSobzb1SNyidKIqk1+/BWtcqg=",
"aarch64-linux": "sha256-h2lpWRQ5EDYnjpqZXtUAp1mxKLQxJ4m8MspgSY8Ev78=", "aarch64-linux": "sha256-/d3ukZERWvV7egmc2Rtxg5vroZaXkCs7yVcIjIa4CUE=",
"aarch64-darwin": "sha256-xnd91+WyeAqn06run2ajsekxJvTMiLsnqNPe/rR8VTM=", "aarch64-darwin": "sha256-1CX6n+9Wo2vAuPLekGsdjByReHQBbpKHwuK3L7Pfous=",
"x86_64-darwin": "sha256-rXpz45IOjGEk73xhP9VY86eOj2CZBg2l1vzwzTIOOOQ=" "x86_64-darwin": "sha256-Jqx3LDSoLSy8em7c/455xLEy9Pn4DmoYLHDemA1i+9w="
} }
} }

View file

@ -55,7 +55,6 @@ stdenvNoCC.mkDerivation {
--filter './packages/opencode' \ --filter './packages/opencode' \
--filter './packages/desktop' \ --filter './packages/desktop' \
--filter './packages/app' \ --filter './packages/app' \
--filter './packages/shared' \
--frozen-lockfile \ --frozen-lockfile \
--ignore-scripts \ --ignore-scripts \
--no-progress --no-progress

View file

@ -64,7 +64,7 @@ stdenvNoCC.mkDerivation (finalAttrs: {
[ [
ripgrep ripgrep
] ]
# bun runs sysctl to detect if dunning on rosetta2 # bun runs sysctl to detect if running on rosetta2
++ lib.optional stdenvNoCC.hostPlatform.isDarwin sysctl ++ lib.optional stdenvNoCC.hostPlatform.isDarwin sysctl
) )
} }

View file

@ -7,7 +7,7 @@
"packageManager": "bun@1.3.13", "packageManager": "bun@1.3.13",
"scripts": { "scripts": {
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts", "dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
"dev:desktop": "bun --cwd packages/desktop-electron dev", "dev:desktop": "bun --cwd packages/desktop dev",
"dev:web": "bun --cwd packages/app dev", "dev:web": "bun --cwd packages/app dev",
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev", "dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
"dev:storybook": "bun --cwd packages/storybook storybook", "dev:storybook": "bun --cwd packages/storybook storybook",
@ -27,32 +27,33 @@
"packages/slack" "packages/slack"
], ],
"catalog": { "catalog": {
"@effect/opentelemetry": "4.0.0-beta.48", "@effect/opentelemetry": "4.0.0-beta.57",
"@effect/platform-node": "4.0.0-beta.48", "@effect/platform-node": "4.0.0-beta.57",
"@npmcli/arborist": "9.4.0", "@npmcli/arborist": "9.4.0",
"@types/bun": "1.3.12", "@types/bun": "1.3.12",
"@types/cross-spawn": "6.0.6", "@types/cross-spawn": "6.0.6",
"@octokit/rest": "22.0.0", "@octokit/rest": "22.0.0",
"@hono/zod-validator": "0.4.2", "@hono/zod-validator": "0.4.2",
"@opentui/core": "0.1.99", "@opentui/core": "0.2.2",
"@opentui/solid": "0.1.99", "@opentui/solid": "0.2.2",
"ulid": "3.0.1", "ulid": "3.0.1",
"@kobalte/core": "0.13.11", "@kobalte/core": "0.13.11",
"@types/luxon": "3.7.1", "@types/luxon": "3.7.1",
"@types/node": "22.13.9", "@types/node": "24.12.2",
"@types/semver": "7.7.1", "@types/semver": "7.7.1",
"@tsconfig/node22": "22.0.2", "@tsconfig/node22": "22.0.2",
"@tsconfig/bun": "1.0.9", "@tsconfig/bun": "1.0.9",
"@cloudflare/workers-types": "4.20251008.0", "@cloudflare/workers-types": "4.20251008.0",
"@openauthjs/openauth": "0.0.0-20250322224806", "@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/diffs": "1.1.0-beta.18", "@pierre/diffs": "1.1.0-beta.18",
"opentui-spinner": "0.0.6",
"@solid-primitives/storage": "4.3.3", "@solid-primitives/storage": "4.3.3",
"@tailwindcss/vite": "4.1.11", "@tailwindcss/vite": "4.1.11",
"diff": "8.0.2", "diff": "8.0.2",
"dompurify": "3.3.1", "dompurify": "3.3.1",
"drizzle-kit": "1.0.0-beta.19-d95b7a4", "drizzle-kit": "1.0.0-beta.19-d95b7a4",
"drizzle-orm": "1.0.0-beta.19-d95b7a4", "drizzle-orm": "1.0.0-beta.19-d95b7a4",
"effect": "4.0.0-beta.48", "effect": "4.0.0-beta.59",
"ai": "6.0.168", "ai": "6.0.168",
"cross-spawn": "7.0.6", "cross-spawn": "7.0.6",
"hono": "4.10.7", "hono": "4.10.7",
@ -76,6 +77,8 @@
"@solidjs/meta": "0.29.4", "@solidjs/meta": "0.29.4",
"@solidjs/router": "0.15.4", "@solidjs/router": "0.15.4",
"@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020", "@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020",
"@sentry/solid": "10.36.0",
"@sentry/vite-plugin": "4.6.0",
"solid-js": "1.9.10", "solid-js": "1.9.10",
"vite-plugin-solid": "2.11.10", "vite-plugin-solid": "2.11.10",
"@lydell/node-pty": "1.2.0-beta.10" "@lydell/node-pty": "1.2.0-beta.10"

View file

@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/app", "name": "@opencode-ai/app",
"version": "1.14.21", "version": "1.14.39",
"description": "", "description": "",
"type": "module", "type": "module",
"exports": { "exports": {
@ -27,6 +27,7 @@
"devDependencies": { "devDependencies": {
"@happy-dom/global-registrator": "20.0.11", "@happy-dom/global-registrator": "20.0.11",
"@playwright/test": "catalog:", "@playwright/test": "catalog:",
"@sentry/vite-plugin": "catalog:",
"@tailwindcss/vite": "catalog:", "@tailwindcss/vite": "catalog:",
"@tsconfig/bun": "1.0.9", "@tsconfig/bun": "1.0.9",
"@types/bun": "catalog:", "@types/bun": "catalog:",
@ -40,9 +41,10 @@
}, },
"dependencies": { "dependencies": {
"@kobalte/core": "catalog:", "@kobalte/core": "catalog:",
"@sentry/solid": "catalog:",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@opencode-ai/ui": "workspace:*", "@opencode-ai/ui": "workspace:*",
"@opencode-ai/shared": "workspace:*", "@opencode-ai/core": "workspace:*",
"@shikijs/transformers": "3.9.2", "@shikijs/transformers": "3.9.2",
"@solid-primitives/active-element": "2.1.3", "@solid-primitives/active-element": "2.1.3",
"@solid-primitives/audio": "1.4.2", "@solid-primitives/audio": "1.4.2",

View file

@ -1,4 +1,5 @@
import "@/index.css" import "@/index.css"
import * as Sentry from "@sentry/solid"
import { I18nProvider } from "@opencode-ai/ui/context" import { I18nProvider } from "@opencode-ai/ui/context"
import { DialogProvider } from "@opencode-ai/ui/context/dialog" import { DialogProvider } from "@opencode-ai/ui/context/dialog"
import { FileComponentProvider } from "@opencode-ai/ui/context/file" import { FileComponentProvider } from "@opencode-ai/ui/context/file"
@ -82,7 +83,15 @@ declare global {
} }
function QueryProvider(props: ParentProps) { function QueryProvider(props: ParentProps) {
const client = new QueryClient() const client = new QueryClient({
defaultOptions: {
queries: {
refetchOnReconnect: false,
refetchOnMount: false,
refetchOnWindowFocus: false,
},
},
})
return <QueryClientProvider client={client}>{props.children}</QueryClientProvider> return <QueryClientProvider client={client}>{props.children}</QueryClientProvider>
} }
@ -140,12 +149,19 @@ export function AppBaseProviders(props: ParentProps<{ locale?: Locale }>) {
> >
<LanguageProvider locale={props.locale}> <LanguageProvider locale={props.locale}>
<UiI18nBridge> <UiI18nBridge>
<ErrorBoundary fallback={(error) => <ErrorPage error={error} />}> <ErrorBoundary
<DialogProvider> fallback={(error) => {
<MarkedProvider> Sentry.captureException(error)
<FileComponentProvider component={File}>{props.children}</FileComponentProvider> return <ErrorPage error={error} />
</MarkedProvider> }}
</DialogProvider> >
<QueryProvider>
<DialogProvider>
<MarkedProvider>
<FileComponentProvider component={File}>{props.children}</FileComponentProvider>
</MarkedProvider>
</DialogProvider>
</QueryProvider>
</ErrorBoundary> </ErrorBoundary>
</UiI18nBridge> </UiI18nBridge>
</LanguageProvider> </LanguageProvider>

View file

@ -9,7 +9,7 @@ import { createStore } from "solid-js/store"
import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSDK } from "@/context/global-sdk"
import { useGlobalSync } from "@/context/global-sync" import { useGlobalSync } from "@/context/global-sync"
import { type LocalProject, getAvatarColors } from "@/context/layout" import { type LocalProject, getAvatarColors } from "@/context/layout"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { Avatar } from "@opencode-ai/ui/avatar" import { Avatar } from "@opencode-ai/ui/avatar"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { getProjectAvatarSource } from "@/pages/layout/sidebar-items" import { getProjectAvatarSource } from "@/pages/layout/sidebar-items"

View file

@ -9,7 +9,7 @@ import { List } from "@opencode-ai/ui/list"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { extractPromptFromParts } from "@/utils/prompt" import { extractPromptFromParts } from "@/utils/prompt"
import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client" import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
interface ForkableMessage { interface ForkableMessage {

View file

@ -3,7 +3,7 @@ import { Dialog } from "@opencode-ai/ui/dialog"
import { FileIcon } from "@opencode-ai/ui/file-icon" import { FileIcon } from "@opencode-ai/ui/file-icon"
import { List } from "@opencode-ai/ui/list" import { List } from "@opencode-ai/ui/list"
import type { ListRef } from "@opencode-ai/ui/list" import type { ListRef } from "@opencode-ai/ui/list"
import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
import fuzzysort from "fuzzysort" import fuzzysort from "fuzzysort"
import { createMemo, createResource, createSignal } from "solid-js" import { createMemo, createResource, createSignal } from "solid-js"
import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSDK } from "@/context/global-sdk"

View file

@ -4,8 +4,8 @@ import { FileIcon } from "@opencode-ai/ui/file-icon"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { Keybind } from "@opencode-ai/ui/keybind" import { Keybind } from "@opencode-ai/ui/keybind"
import { List } from "@opencode-ai/ui/list" import { List } from "@opencode-ai/ui/list"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
import { useNavigate } from "@solidjs/router" import { useNavigate } from "@solidjs/router"
import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid-js" import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid-js"
import { formatKeybind, useCommand, type CommandOption } from "@/context/command" import { formatKeybind, useCommand, type CommandOption } from "@/context/command"

View file

@ -1,13 +1,12 @@
import { useMutation } from "@tanstack/solid-query" import { useMutation, useQueryClient } from "@tanstack/solid-query"
import { Component, createEffect, createMemo, on, Show } from "solid-js" import { Component, createMemo, Show } from "solid-js"
import { createStore } from "solid-js/store"
import { useSync } from "@/context/sync" import { useSync } from "@/context/sync"
import { useSDK } from "@/context/sdk" import { useSDK } from "@/context/sdk"
import { Dialog } from "@opencode-ai/ui/dialog" import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list" import { List } from "@opencode-ai/ui/list"
import { Switch } from "@opencode-ai/ui/switch" import { Switch } from "@opencode-ai/ui/switch"
import { showToast } from "@opencode-ai/ui/toast"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { mcpQueryKey } from "@/context/global-sync"
const statusLabels = { const statusLabels = {
connected: "mcp.status.connected", connected: "mcp.status.connected",
@ -20,48 +19,7 @@ export const DialogSelectMcp: Component = () => {
const sync = useSync() const sync = useSync()
const sdk = useSDK() const sdk = useSDK()
const language = useLanguage() const language = useLanguage()
const [state, setState] = createStore({ const queryClient = useQueryClient()
done: false,
loading: false,
})
createEffect(
on(
() => sync.data.mcp_ready,
(ready, prev) => {
if (!ready && prev) setState("done", false)
},
{ defer: true },
),
)
createEffect(() => {
if (state.done || state.loading) return
if (sync.data.mcp_ready) {
setState("done", true)
return
}
setState("loading", true)
void sdk.client.mcp
.status()
.then((result) => {
sync.set("mcp", result.data ?? {})
sync.set("mcp_ready", true)
setState("done", true)
})
.catch((err) => {
setState("done", true)
showToast({
variant: "error",
title: language.t("common.requestFailed"),
description: err instanceof Error ? err.message : String(err),
})
})
.finally(() => {
setState("loading", false)
})
})
const items = createMemo(() => const items = createMemo(() =>
Object.entries(sync.data.mcp ?? {}) Object.entries(sync.data.mcp ?? {})
@ -71,16 +29,10 @@ export const DialogSelectMcp: Component = () => {
const toggle = useMutation(() => ({ const toggle = useMutation(() => ({
mutationFn: async (name: string) => { mutationFn: async (name: string) => {
const status = sync.data.mcp[name] if (sync.data.mcp[name]?.status === "connected") await sdk.client.mcp.disconnect({ name })
if (status?.status === "connected") { else await sdk.client.mcp.connect({ name })
await sdk.client.mcp.disconnect({ name })
} else {
await sdk.client.mcp.connect({ name })
}
const result = await sdk.client.mcp.status()
if (result.data) sync.set("mcp", result.data)
}, },
onSuccess: () => queryClient.refetchQueries({ queryKey: mcpQueryKey(sync.directory) }),
})) }))
const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length) const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length)

View file

@ -16,6 +16,7 @@ import {
} from "@/context/prompt" } from "@/context/prompt"
import { useLayout } from "@/context/layout" import { useLayout } from "@/context/layout"
import { useSDK } from "@/context/sdk" import { useSDK } from "@/context/sdk"
import { useGlobalSDK } from "@/context/global-sdk"
import { useSync } from "@/context/sync" import { useSync } from "@/context/sync"
import { useComments } from "@/context/comments" import { useComments } from "@/context/comments"
import { Button } from "@opencode-ai/ui/button" import { Button } from "@opencode-ai/ui/button"
@ -102,6 +103,7 @@ const NON_EMPTY_TEXT = /[^\s\u200B]/
export const PromptInput: Component<PromptInputProps> = (props) => { export const PromptInput: Component<PromptInputProps> = (props) => {
const sdk = useSDK() const sdk = useSDK()
const globalSDK = useGlobalSDK()
const sync = useSync() const sync = useSync()
const local = useLocal() const local = useLocal()
@ -270,7 +272,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const buttonsSpring = useSpring(() => (store.mode === "normal" ? 1 : 0), { visualDuration: 0.2, bounce: 0 }) const buttonsSpring = useSpring(() => (store.mode === "normal" ? 1 : 0), { visualDuration: 0.2, bounce: 0 })
const motion = (value: number) => ({ const motion = (value: number) => ({
opacity: value, opacity: value,
transform: `scale(${0.95 + value * 0.05})`, transform: `scale(${0.98 + value * 0.02})`,
filter: `blur(${(1 - value) * 2}px)`, filter: `blur(${(1 - value) * 2}px)`,
"pointer-events": value > 0.5 ? ("auto" as const) : ("none" as const), "pointer-events": value > 0.5 ? ("auto" as const) : ("none" as const),
}) })
@ -345,7 +347,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
promptPlaceholder({ promptPlaceholder({
mode: store.mode, mode: store.mode,
commentCount: commentCount(), commentCount: commentCount(),
example: suggest() ? language.t(EXAMPLES[store.placeholder]) : "", example: suggest() ? (store.mode === "shell" ? "git status" : language.t(EXAMPLES[store.placeholder])) : "",
suggest: suggest(), suggest: suggest(),
t: (key, params) => language.t(key as Parameters<typeof language.t>[0], params as never), t: (key, params) => language.t(key as Parameters<typeof language.t>[0], params as never),
}), }),
@ -1253,7 +1255,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
} }
const [agentsQuery, globalProvidersQuery, providersQuery] = useQueries(() => ({ const [agentsQuery, globalProvidersQuery, providersQuery] = useQueries(() => ({
queries: [loadAgentsQuery(sdk.directory), loadProvidersQuery(null), loadProvidersQuery(sdk.directory)], queries: [
loadAgentsQuery(sdk.directory, sdk.client),
loadProvidersQuery(null, globalSDK.client),
loadProvidersQuery(sdk.directory, sdk.client),
],
})) }))
const agentsLoading = () => agentsQuery.isLoading const agentsLoading = () => agentsQuery.isLoading
@ -1403,12 +1409,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<IconButton <IconButton
data-action="prompt-submit" data-action="prompt-submit"
type="submit" type="submit"
disabled={store.mode !== "normal" || (!working() && blank())} disabled={!working() && blank()}
tabIndex={store.mode === "normal" ? undefined : -1} tabIndex={store.mode === "normal" ? undefined : -1}
icon={stopping() ? "stop" : "arrow-up"} icon={stopping() ? "stop" : store.mode === "shell" ? "arrow-undo-down" : "arrow-up"}
variant="primary" variant="primary"
class="size-8" class="size-8"
style={buttons()}
aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")} aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
/> />
</Tooltip> </Tooltip>
@ -1451,14 +1456,24 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<div class="px-1.75 pt-5.5 pb-2 flex items-center gap-2 min-w-0"> <div class="px-1.75 pt-5.5 pb-2 flex items-center gap-2 min-w-0">
<div class="flex items-center gap-1.5 min-w-0 flex-1 relative"> <div class="flex items-center gap-1.5 min-w-0 flex-1 relative">
<div <div
class="h-7 flex items-center gap-1.5 max-w-[160px] min-w-0 absolute inset-y-0 left-0" class="h-7 flex items-center gap-1.5 min-w-0 absolute inset-0"
style={{ style={{
padding: "0 4px 0 8px", padding: "0 0px 0 8px",
...shell(), ...shell(),
}} }}
> >
<span class="truncate text-13-medium text-text-strong">{language.t("prompt.mode.shell")}</span> <Icon name="console" />
<div class="size-4 shrink-0" /> <span class="truncate text-13-medium text-text-base">{language.t("prompt.mode.shell")}</span>
<div class="flex-1" />
<Button
variant="ghost"
class="text-text-base"
onClick={() => {
setStore("mode", "normal")
}}
>
{language.t("common.cancel")}
</Button>
</div> </div>
<div class="flex items-center gap-1.5 min-w-0 flex-1 h-7"> <div class="flex items-center gap-1.5 min-w-0 flex-1 h-7">
<Show when={!agentsLoading()}> <Show when={!agentsLoading()}>
@ -1565,33 +1580,35 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</TooltipKeybind> </TooltipKeybind>
</Show> </Show>
</div> </div>
<div <Show when={variants().length > 2}>
data-component="prompt-variant-control" <div
style={providersShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined} data-component="prompt-variant-control"
> style={providersShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
<TooltipKeybind
placement="top"
gutter={4}
title={language.t("command.model.variant.cycle")}
keybind={command.keybind("model.variant.cycle")}
> >
<Select <TooltipKeybind
size="normal" placement="top"
options={variants()} gutter={4}
current={local.model.variant.current() ?? "default"} title={language.t("command.model.variant.cycle")}
label={(x) => (x === "default" ? language.t("common.default") : x)} keybind={command.keybind("model.variant.cycle")}
onSelect={(value) => { >
local.model.variant.set(value === "default" ? undefined : value) <Select
restoreFocus() size="normal"
}} options={variants()}
class="capitalize max-w-[160px] text-text-base" current={local.model.variant.current() ?? "default"}
valueClass="truncate text-13-regular text-text-base" label={(x) => (x === "default" ? language.t("common.default") : x)}
triggerStyle={control()} onSelect={(value) => {
triggerProps={{ "data-action": "prompt-model-variant" }} local.model.variant.set(value === "default" ? undefined : value)
variant="ghost" restoreFocus()
/> }}
</TooltipKeybind> class="capitalize max-w-[160px] text-text-base"
</div> valueClass="truncate text-13-regular text-text-base"
triggerStyle={control()}
triggerProps={{ "data-action": "prompt-model-variant" }}
variant="ghost"
/>
</TooltipKeybind>
</div>
</Show>
</Show> </Show>
</Show> </Show>
</div> </div>

View file

@ -1,4 +1,4 @@
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { type AgentPartInput, type FilePartInput, type Part, type TextPartInput } from "@opencode-ai/sdk/v2/client" import { type AgentPartInput, type FilePartInput, type Part, type TextPartInput } from "@opencode-ai/sdk/v2/client"
import type { FileSelection } from "@/context/file" import type { FileSelection } from "@/context/file"
import { encodeFilePath } from "@/context/file/path" import { encodeFilePath } from "@/context/file/path"

View file

@ -2,7 +2,7 @@ import { Component, For, Show } from "solid-js"
import { FileIcon } from "@opencode-ai/ui/file-icon" import { FileIcon } from "@opencode-ai/ui/file-icon"
import { IconButton } from "@opencode-ai/ui/icon-button" import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tooltip } from "@opencode-ai/ui/tooltip" import { Tooltip } from "@opencode-ai/ui/tooltip"
import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/shared/util/path" import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/core/util/path"
import type { ContextItem } from "@/context/prompt" import type { ContextItem } from "@/context/prompt"
type PromptContextItem = ContextItem & { key: string } type PromptContextItem = ContextItem & { key: string }

View file

@ -12,7 +12,7 @@ describe("promptPlaceholder", () => {
suggest: true, suggest: true,
t, t,
}) })
expect(value).toBe("prompt.placeholder.shell") expect(value).toBe("prompt.placeholder.shell:example")
}) })
test("returns summarize placeholders for comment context", () => { test("returns summarize placeholders for comment context", () => {

View file

@ -7,7 +7,7 @@ type PromptPlaceholderInput = {
} }
export function promptPlaceholder(input: PromptPlaceholderInput) { export function promptPlaceholder(input: PromptPlaceholderInput) {
if (input.mode === "shell") return input.t("prompt.placeholder.shell") if (input.mode === "shell") return input.t("prompt.placeholder.shell", { example: input.example })
if (input.commentCount > 1) return input.t("prompt.placeholder.summarizeComments") if (input.commentCount > 1) return input.t("prompt.placeholder.summarizeComments")
if (input.commentCount === 1) return input.t("prompt.placeholder.summarizeComment") if (input.commentCount === 1) return input.t("prompt.placeholder.summarizeComment")
if (!input.suggest) return input.t("prompt.placeholder.simple") if (!input.suggest) return input.t("prompt.placeholder.simple")

View file

@ -1,7 +1,7 @@
import { Component, For, Match, Show, Switch } from "solid-js" import { Component, For, Match, Show, Switch } from "solid-js"
import { FileIcon } from "@opencode-ai/ui/file-icon" import { FileIcon } from "@opencode-ai/ui/file-icon"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
export type AtOption = export type AtOption =
| { type: "agent"; name: string; display: string } | { type: "agent"; name: string; display: string }

View file

@ -74,7 +74,7 @@ beforeAll(async () => {
showToast: () => 0, showToast: () => 0,
})) }))
mock.module("@opencode-ai/shared/util/encode", () => ({ mock.module("@opencode-ai/core/util/encode", () => ({
base64Encode: (value: string) => value, base64Encode: (value: string) => value,
})) }))

View file

@ -1,7 +1,7 @@
import type { Message, Session } from "@opencode-ai/sdk/v2/client" import type { Message, Session } from "@opencode-ai/sdk/v2/client"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { useNavigate, useParams } from "@solidjs/router" import { useNavigate, useParams } from "@solidjs/router"
import { batch, type Accessor } from "solid-js" import { batch, type Accessor } from "solid-js"
import type { FileSelection } from "@/context/file" import type { FileSelection } from "@/context/file"

View file

@ -1,8 +1,8 @@
import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js" import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js"
import type { JSX } from "solid-js" import type { JSX } from "solid-js"
import { useSync } from "@/context/sync" import { useSync } from "@/context/sync"
import { checksum } from "@opencode-ai/shared/util/encode" import { checksum } from "@opencode-ai/core/util/encode"
import { findLast } from "@opencode-ai/shared/util/array" import { findLast } from "@opencode-ai/core/util/array"
import { same } from "@/utils/same" import { same } from "@/utils/same"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { Accordion } from "@opencode-ai/ui/accordion" import { Accordion } from "@opencode-ai/ui/accordion"

View file

@ -7,7 +7,7 @@ import { Keybind } from "@opencode-ai/ui/keybind"
import { Spinner } from "@opencode-ai/ui/spinner" import { Spinner } from "@opencode-ai/ui/spinner"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js" import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { Portal } from "solid-js/web" import { Portal } from "solid-js/web"

View file

@ -5,7 +5,7 @@ import { useSDK } from "@/context/sdk"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { Mark } from "@opencode-ai/ui/logo" import { Mark } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
const MAIN_WORKTREE = "main" const MAIN_WORKTREE = "main"
const CREATE_WORKTREE = "create" const CREATE_WORKTREE = "create"

View file

@ -5,7 +5,7 @@ import { FileIcon } from "@opencode-ai/ui/file-icon"
import { IconButton } from "@opencode-ai/ui/icon-button" import { IconButton } from "@opencode-ai/ui/icon-button"
import { TooltipKeybind } from "@opencode-ai/ui/tooltip" import { TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { Tabs } from "@opencode-ai/ui/tabs" import { Tabs } from "@opencode-ai/ui/tabs"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { useFile } from "@/context/file" import { useFile } from "@/context/file"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { useCommand } from "@/context/command" import { useCommand } from "@/context/command"

View file

@ -11,7 +11,9 @@ import { showToast } from "@opencode-ai/ui/toast"
import { useParams } from "@solidjs/router" import { useParams } from "@solidjs/router"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { usePermission } from "@/context/permission" import { usePermission } from "@/context/permission"
import { usePlatform } from "@/context/platform" import { usePlatform, type DisplayBackend } from "@/context/platform"
import { useGlobalSync } from "@/context/global-sync"
import { useGlobalSDK } from "@/context/global-sdk"
import { import {
monoDefault, monoDefault,
monoFontFamily, monoFontFamily,
@ -40,6 +42,18 @@ type ThemeOption = {
name: string name: string
} }
type ShellOption = {
path: string
name: string
acceptable: boolean
}
type ShellSelectOption = {
id: string
value: string
label: string
}
// To prevent audio from overlapping/playing very quickly when navigating the settings menus, // To prevent audio from overlapping/playing very quickly when navigating the settings menus,
// delay the playback by 100ms during quick selection changes and pause existing sounds. // delay the playback by 100ms during quick selection changes and pause existing sounds.
const stopDemoSound = () => { const stopDemoSound = () => {
@ -75,10 +89,6 @@ export const SettingsGeneral: Component = () => {
const params = useParams() const params = useParams()
const settings = useSettings() const settings = useSettings()
onMount(() => {
void theme.loadThemes()
})
const [store, setStore] = createStore({ const [store, setStore] = createStore({
checking: false, checking: false,
}) })
@ -128,27 +138,25 @@ export const SettingsGeneral: Component = () => {
return return
} }
const actions = const actions = platform.updateAndRestart
platform.update && platform.restart ? [
? [ {
{ label: language.t("toast.update.action.installRestart"),
label: language.t("toast.update.action.installRestart"), onClick: async () => {
onClick: async () => { await platform.updateAndRestart!()
await platform.update!()
await platform.restart!()
},
}, },
{ },
label: language.t("toast.update.action.notYet"), {
onClick: "dismiss" as const, label: language.t("toast.update.action.notYet"),
}, onClick: "dismiss" as const,
] },
: [ ]
{ : [
label: language.t("toast.update.action.notYet"), {
onClick: "dismiss" as const, label: language.t("toast.update.action.notYet"),
}, onClick: "dismiss" as const,
] },
]
showToast({ showToast({
persistent: true, persistent: true,
@ -167,6 +175,70 @@ export const SettingsGeneral: Component = () => {
const themeOptions = createMemo<ThemeOption[]>(() => theme.ids().map((id) => ({ id, name: theme.name(id) }))) const themeOptions = createMemo<ThemeOption[]>(() => theme.ids().map((id) => ({ id, name: theme.name(id) })))
const globalSync = useGlobalSync()
const globalSdk = useGlobalSDK()
const [shells] = createResource(
() =>
globalSdk.client.pty
.shells()
.then((res) => res.data ?? [])
.catch(() => [] as ShellOption[]),
{ initialValue: [] as ShellOption[] },
)
const [displayBackend, { refetch: refetchDisplayBackend }] = createResource(
() => (linux() && platform.getDisplayBackend ? true : false),
() => Promise.resolve(platform.getDisplayBackend?.() ?? null).catch(() => null as DisplayBackend | null),
{ initialValue: null as DisplayBackend | null },
)
onMount(() => {
void theme.loadThemes()
})
const autoOption = { id: "auto", value: "", label: language.t("settings.general.row.shell.autoDefault") }
const currentShell = createMemo(() => globalSync.data.config.shell ?? "")
const shellOptions = createMemo<ShellSelectOption[]>(() => {
const list = shells.latest
const current = globalSync.data.config.shell
const nameCounts = new Map<string, number>()
for (const s of list) {
nameCounts.set(s.name, (nameCounts.get(s.name) || 0) + 1)
}
const options = [
autoOption,
...list.map((s) => {
const ambiguousName = (nameCounts.get(s.name) || 0) > 1
const text = ambiguousName ? s.path : s.name
const label = s.acceptable ? text : `${text} (${language.t("settings.general.row.shell.terminalOnly")})`
return {
id: s.path,
// Prefer name over path - "bash" is much cleaner than the explicit full route even when it may change due to PATH.
value: ambiguousName ? s.path : s.name,
label,
}
}),
]
if (current && !options.some((o) => o.value === current)) {
options.push({ id: current, value: current, label: current })
}
return options
})
const onDisplayBackendChange = (checked: boolean) => {
const update = platform.setDisplayBackend?.(checked ? "wayland" : "auto")
if (!update) return
void update.finally(() => {
void refetchDisplayBackend()
})
}
const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [ const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [
{ value: "system", label: language.t("theme.scheme.system") }, { value: "system", label: language.t("theme.scheme.system") },
{ value: "light", label: language.t("theme.scheme.light") }, { value: "light", label: language.t("theme.scheme.light") },
@ -245,6 +317,28 @@ export const SettingsGeneral: Component = () => {
</div> </div>
</SettingsRow> </SettingsRow>
<SettingsRow
title={language.t("settings.general.row.shell.title")}
description={language.t("settings.general.row.shell.description")}
>
<Select
data-action="settings-shell"
options={shellOptions()}
current={shellOptions().find((o) => o.value === currentShell()) ?? autoOption}
value={(o) => o.id}
label={(o) => o.label}
onSelect={(option) => {
if (!option) return
if (option.value === currentShell()) return
globalSync.updateConfig({ shell: option.value })
}}
variant="secondary"
size="small"
triggerVariant="settings"
triggerStyle={{ "min-width": "180px" }}
/>
</SettingsRow>
<SettingsRow <SettingsRow
title={language.t("settings.general.row.reasoningSummaries.title")} title={language.t("settings.general.row.reasoningSummaries.title")}
description={language.t("settings.general.row.reasoningSummaries.description")} description={language.t("settings.general.row.reasoningSummaries.description")}
@ -653,70 +747,32 @@ export const SettingsGeneral: Component = () => {
<SoundsSection /> <SoundsSection />
{/*<Show when={platform.platform === "desktop" && platform.os === "windows" && platform.getWslEnabled}>
{(_) => {
const [enabledResource, actions] = createResource(() => platform.getWslEnabled?.())
const enabled = () => (enabledResource.state === "pending" ? undefined : enabledResource.latest)
return (
<div class="flex flex-col gap-1">
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.desktop.section.wsl")}</h3>
<SettingsList>
<SettingsRow
title={language.t("settings.desktop.wsl.title")}
description={language.t("settings.desktop.wsl.description")}
>
<div data-action="settings-wsl">
<Switch
checked={enabled() ?? false}
disabled={enabledResource.state === "pending"}
onChange={(checked) => platform.setWslEnabled?.(checked)?.finally(() => actions.refetch())}
/>
</div>
</SettingsRow>
</SettingsList>
</div>
)
}}
</Show>*/}
<UpdatesSection /> <UpdatesSection />
<Show when={linux()}> <Show when={linux()}>
{(_) => { <div class="flex flex-col gap-1">
const [valueResource, actions] = createResource(() => platform.getDisplayBackend?.()) <h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3>
const value = () => (valueResource.state === "pending" ? undefined : valueResource.latest)
const onChange = (checked: boolean) => <SettingsList>
platform.setDisplayBackend?.(checked ? "wayland" : "auto").finally(() => actions.refetch()) <SettingsRow
title={
return ( <div class="flex items-center gap-2">
<div class="flex flex-col gap-1"> <span>{language.t("settings.general.row.wayland.title")}</span>
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3> <Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
<span class="text-text-weak">
<SettingsList> <Icon name="help" size="small" />
<SettingsRow </span>
title={ </Tooltip>
<div class="flex items-center gap-2"> </div>
<span>{language.t("settings.general.row.wayland.title")}</span> }
<Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top"> description={language.t("settings.general.row.wayland.description")}
<span class="text-text-weak"> >
<Icon name="help" size="small" /> <div data-action="settings-wayland">
</span> <Switch checked={displayBackend.latest === "wayland"} onChange={onDisplayBackendChange} />
</Tooltip> </div>
</div> </SettingsRow>
} </SettingsList>
description={language.t("settings.general.row.wayland.description")} </div>
>
<div data-action="settings-wayland">
<Switch checked={value() === "wayland"} onChange={onChange} />
</div>
</SettingsRow>
</SettingsList>
</div>
)
}}
</Show> </Show>
<Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}> <Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}>

View file

@ -3,7 +3,7 @@ import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { Switch } from "@opencode-ai/ui/switch" import { Switch } from "@opencode-ai/ui/switch"
import { Tabs } from "@opencode-ai/ui/tabs" import { Tabs } from "@opencode-ai/ui/tabs"
import { useMutation } from "@tanstack/solid-query" import { useMutation, useQueryClient } from "@tanstack/solid-query"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { useNavigate } from "@solidjs/router" import { useNavigate } from "@solidjs/router"
import { type Accessor, createEffect, createMemo, For, type JSXElement, onCleanup, Show } from "solid-js" import { type Accessor, createEffect, createMemo, For, type JSXElement, onCleanup, Show } from "solid-js"
@ -15,6 +15,7 @@ import { useSDK } from "@/context/sdk"
import { normalizeServerUrl, ServerConnection, useServer } from "@/context/server" import { normalizeServerUrl, ServerConnection, useServer } from "@/context/server"
import { useSync } from "@/context/sync" import { useSync } from "@/context/sync"
import { useCheckServerHealth, type ServerHealth } from "@/utils/server-health" import { useCheckServerHealth, type ServerHealth } from "@/utils/server-health"
import { mcpQueryKey } from "@/context/global-sync"
const pollMs = 10_000 const pollMs = 10_000
@ -137,14 +138,14 @@ const useMcpToggleMutation = () => {
const sync = useSync() const sync = useSync()
const sdk = useSDK() const sdk = useSDK()
const language = useLanguage() const language = useLanguage()
const queryClient = useQueryClient()
return useMutation(() => ({ return useMutation(() => ({
mutationFn: async (name: string) => { mutationFn: async (name: string) => {
const status = sync.data.mcp[name] const status = sync.data.mcp[name]
await (status?.status === "connected" ? sdk.client.mcp.disconnect({ name }) : sdk.client.mcp.connect({ name })) await (status?.status === "connected" ? sdk.client.mcp.disconnect({ name }) : sdk.client.mcp.connect({ name }))
const result = await sdk.client.mcp.status()
if (result.data) sync.set("mcp", result.data)
}, },
onSuccess: () => queryClient.refetchQueries({ queryKey: mcpQueryKey(sync.directory) }),
onError: (err) => { onError: (err) => {
showToast({ showToast({
variant: "error", variant: "error",
@ -162,14 +163,6 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
const dialog = useDialog() const dialog = useDialog()
const language = useLanguage() const language = useLanguage()
const navigate = useNavigate() const navigate = useNavigate()
const sdk = useSDK()
const [load, setLoad] = createStore({
lspDone: false,
lspLoading: false,
mcpDone: false,
mcpLoading: false,
})
const fail = (err: unknown) => { const fail = (err: unknown) => {
showToast({ showToast({
@ -181,40 +174,6 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
createEffect(() => { createEffect(() => {
if (!props.shown()) return if (!props.shown()) return
if (!sync.data.mcp_ready && !load.mcpDone && !load.mcpLoading) {
setLoad("mcpLoading", true)
void sdk.client.mcp
.status()
.then((result) => {
sync.set("mcp", result.data ?? {})
sync.set("mcp_ready", true)
})
.catch((err) => {
setLoad("mcpDone", true)
fail(err)
})
.finally(() => {
setLoad("mcpLoading", false)
})
}
if (!sync.data.lsp_ready && !load.lspDone && !load.lspLoading) {
setLoad("lspLoading", true)
void sdk.client.lsp
.status()
.then((result) => {
sync.set("lsp", result.data ?? [])
sync.set("lsp_ready", true)
})
.catch((err) => {
setLoad("lspDone", true)
fail(err)
})
.finally(() => {
setLoad("lspLoading", false)
})
}
}) })
let dialogRun = 0 let dialogRun = 0

View file

@ -15,6 +15,7 @@ import { terminalFontFamily, useSettings } from "@/context/settings"
import type { LocalPTY } from "@/context/terminal" import type { LocalPTY } from "@/context/terminal"
import { disposeIfDisposable, getHoveredLinkText, setOptionIfSupported } from "@/utils/runtime-adapters" import { disposeIfDisposable, getHoveredLinkText, setOptionIfSupported } from "@/utils/runtime-adapters"
import { terminalWriter } from "@/utils/terminal-writer" import { terminalWriter } from "@/utils/terminal-writer"
import { terminalWebSocketURL } from "@/utils/terminal-websocket-url"
const TOGGLE_TERMINAL_ID = "terminal.toggle" const TOGGLE_TERMINAL_ID = "terminal.toggle"
const DEFAULT_TOGGLE_TERMINAL_KEYBIND = "ctrl+`" const DEFAULT_TOGGLE_TERMINAL_KEYBIND = "ctrl+`"
@ -67,13 +68,6 @@ const debugTerminal = (...values: unknown[]) => {
console.debug("[terminal]", ...values) console.debug("[terminal]", ...values)
} }
const errorName = (err: unknown) => {
if (!err || typeof err !== "object") return
if (!("name" in err)) return
const errorName = err.name
return typeof errorName === "string" ? errorName : undefined
}
const useTerminalUiBindings = (input: { const useTerminalUiBindings = (input: {
container: HTMLDivElement container: HTMLDivElement
term: Term term: Term
@ -478,14 +472,34 @@ export const Terminal = (props: TerminalProps) => {
const gone = () => const gone = () =>
client.pty client.pty
.get({ ptyID: id }) .get({ ptyID: id }, { throwOnError: false })
.then(() => false) .then((result) => result.response.status === 404)
.catch((err) => { .catch((err) => {
if (errorName(err) === "NotFoundError") return true
debugTerminal("failed to inspect terminal session", err) debugTerminal("failed to inspect terminal session", err)
return false return false
}) })
const connectToken = async () => {
const result = await client.pty
.connectToken(
{ ptyID: id, directory },
{
throwOnError: false,
headers: { "x-opencode-ticket": "1" },
},
)
.catch((err: unknown) => {
if (err instanceof Error && err.message.includes("Request is not supported")) return
throw err
})
if (!result) return
if (result.response.status === 200 && result.data?.ticket) return result.data.ticket
if (result.response.status === 404 || result.response.status === 405) return
if (result.response.status === 403)
throw new Error("PTY connect ticket rejected by origin or CSRF checks. Check the server CORS config.")
throw new Error(`PTY connect ticket failed with ${result.response.status}`)
}
const retry = (err: unknown) => { const retry = (err: unknown) => {
if (disposed) return if (disposed) return
if (reconn !== undefined) return if (reconn !== undefined) return
@ -505,22 +519,30 @@ export const Terminal = (props: TerminalProps) => {
}, ms) }, ms)
} }
const open = () => { const open = async () => {
if (disposed) return if (disposed) return
drop?.() drop?.()
const next = new URL(url + `/pty/${id}/connect`) const ticket = await connectToken().catch((err) => {
next.searchParams.set("directory", directory) fail(err)
next.searchParams.set("cursor", String(seek)) return undefined
next.protocol = next.protocol === "https:" ? "wss:" : "ws:" })
if (!sameOrigin && password) { if (once.value) return
next.searchParams.set("auth_token", btoa(`${username}:${password}`)) if (disposed) return
// For same-origin requests, let the browser reuse the page's existing auth.
next.username = username
next.password = password
}
const socket = new WebSocket(next) const socket = new WebSocket(
terminalWebSocketURL({
url,
id,
directory,
cursor: seek,
ticket,
sameOrigin,
username,
password,
authToken: server.current?.type === "http" ? server.current.authToken : false,
}),
)
socket.binaryType = "arraybuffer" socket.binaryType = "arraybuffer"
ws = socket ws = socket

View file

@ -35,6 +35,9 @@ type TauriApi = {
const tauriApi = () => (window as unknown as { __TAURI__?: TauriApi }).__TAURI__ const tauriApi = () => (window as unknown as { __TAURI__?: TauriApi }).__TAURI__
const currentDesktopWindow = () => tauriApi()?.window?.getCurrentWindow?.() const currentDesktopWindow = () => tauriApi()?.window?.getCurrentWindow?.()
const currentThemeWindow = () => tauriApi()?.webviewWindow?.getCurrentWebviewWindow?.() const currentThemeWindow = () => tauriApi()?.webviewWindow?.getCurrentWebviewWindow?.()
const titlebarHeight = 40
const minTitlebarZoom = 0.25
const windowsControlsBaseWidth = 138 // 3 native Windows caption buttons at 46px each.
export function Titlebar() { export function Titlebar() {
const layout = useLayout() const layout = useLayout()
@ -51,7 +54,14 @@ export function Titlebar() {
const windows = createMemo(() => platform.platform === "desktop" && platform.os === "windows") const windows = createMemo(() => platform.platform === "desktop" && platform.os === "windows")
const web = createMemo(() => platform.platform === "web") const web = createMemo(() => platform.platform === "web")
const zoom = () => platform.webviewZoom?.() ?? 1 const zoom = () => platform.webviewZoom?.() ?? 1
const minHeight = () => (mac() ? `${40 / zoom()}px` : undefined) const titlebarZoom = () => (windows() ? Math.max(zoom(), minTitlebarZoom) : zoom())
const counterZoom = () => (windows() && titlebarZoom() < 1 ? 1 / titlebarZoom() : 1)
const minHeight = () => {
if (mac()) return `${titlebarHeight / zoom()}px`
if (windows()) return `${titlebarHeight / Math.min(titlebarZoom(), 1)}px`
return undefined
}
const windowsControlsWidth = () => `${windowsControlsBaseWidth / Math.max(titlebarZoom(), 1)}px`
const [history, setHistory] = createStore({ const [history, setHistory] = createStore({
stack: [] as string[], stack: [] as string[],
@ -165,156 +175,161 @@ export function Titlebar() {
return ( return (
<header <header
class="h-10 shrink-0 bg-background-base relative grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center" class="h-10 shrink-0 bg-background-base relative overflow-hidden"
style={{ "min-height": minHeight() }} style={{ "min-height": minHeight() }}
data-tauri-drag-region data-tauri-drag-region
onMouseDown={drag} onMouseDown={drag}
onDblClick={maximize} onDblClick={maximize}
> >
<div <div
classList={{ class="grid h-full min-h-full w-full grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center"
"flex items-center min-w-0": true, style={{ zoom: counterZoom() }}
"pl-2": !mac(),
}}
> >
<Show when={mac()}> <div
<div class="h-full shrink-0" style={{ width: `${72 / zoom()}px` }} /> classList={{
<div class="xl:hidden w-10 shrink-0 flex items-center justify-center"> "flex items-center min-w-0": true,
<IconButton "pl-2": !mac(),
icon="menu" }}
variant="ghost" >
class="titlebar-icon rounded-md" <Show when={mac()}>
onClick={layout.mobileSidebar.toggle} <div class="h-full shrink-0" style={{ width: `${72 / zoom()}px` }} />
aria-label={language.t("sidebar.menu.toggle")} <div class="xl:hidden w-10 shrink-0 flex items-center justify-center">
aria-expanded={layout.mobileSidebar.opened()} <IconButton
/> icon="menu"
</div> variant="ghost"
</Show> class="titlebar-icon rounded-md"
<Show when={!mac()}> onClick={layout.mobileSidebar.toggle}
<div class="xl:hidden w-[48px] shrink-0 flex items-center justify-center"> aria-label={language.t("sidebar.menu.toggle")}
<IconButton aria-expanded={layout.mobileSidebar.opened()}
icon="menu" />
variant="ghost" </div>
class="titlebar-icon rounded-md" </Show>
onClick={layout.mobileSidebar.toggle} <Show when={!mac()}>
aria-label={language.t("sidebar.menu.toggle")} <div class="xl:hidden w-[48px] shrink-0 flex items-center justify-center">
aria-expanded={layout.mobileSidebar.opened()} <IconButton
/> icon="menu"
</div> variant="ghost"
</Show> class="titlebar-icon rounded-md"
<div class="flex items-center gap-1 shrink-0"> onClick={layout.mobileSidebar.toggle}
<TooltipKeybind aria-label={language.t("sidebar.menu.toggle")}
class={web() ? "hidden xl:flex shrink-0 ml-14" : "hidden xl:flex shrink-0 ml-2"} aria-expanded={layout.mobileSidebar.opened()}
placement="bottom" />
title={language.t("command.sidebar.toggle")} </div>
keybind={command.keybind("sidebar.toggle")} </Show>
> <div class="flex items-center gap-1 shrink-0">
<Button <TooltipKeybind
variant="ghost" class={web() ? "hidden xl:flex shrink-0 ml-14" : "hidden xl:flex shrink-0 ml-2"}
class="group/sidebar-toggle titlebar-icon w-8 h-6 p-0 box-border" placement="bottom"
onClick={layout.sidebar.toggle} title={language.t("command.sidebar.toggle")}
aria-label={language.t("command.sidebar.toggle")} keybind={command.keybind("sidebar.toggle")}
aria-expanded={layout.sidebar.opened()}
> >
<Icon size="small" name={layout.sidebar.opened() ? "sidebar-active" : "sidebar"} /> <Button
</Button> variant="ghost"
</TooltipKeybind> class="group/sidebar-toggle titlebar-icon w-8 h-6 p-0 box-border"
<div class="hidden xl:flex items-center shrink-0"> onClick={layout.sidebar.toggle}
<Show when={params.dir}> aria-label={language.t("command.sidebar.toggle")}
<div aria-expanded={layout.sidebar.opened()}
class="flex items-center shrink-0 w-8 mr-1"
aria-hidden={layout.sidebar.opened() ? "true" : undefined}
> >
<Icon size="small" name={layout.sidebar.opened() ? "sidebar-active" : "sidebar"} />
</Button>
</TooltipKeybind>
<div class="hidden xl:flex items-center shrink-0">
<Show when={params.dir}>
<div <div
class="transition-opacity" class="flex items-center shrink-0 w-8 mr-1"
classList={{ aria-hidden={layout.sidebar.opened() ? "true" : undefined}
"opacity-100 duration-120 ease-out": !layout.sidebar.opened(),
"opacity-0 duration-120 ease-in delay-0 pointer-events-none": layout.sidebar.opened(),
}}
> >
<TooltipKeybind <div
placement="bottom" class="transition-opacity"
title={language.t("command.session.new")} classList={{
keybind={command.keybind("session.new")} "opacity-100 duration-120 ease-out": !layout.sidebar.opened(),
openDelay={2000} "opacity-0 duration-120 ease-in delay-0 pointer-events-none": layout.sidebar.opened(),
}}
> >
<Button <TooltipKeybind
variant="ghost" placement="bottom"
icon={creating() ? "new-session-active" : "new-session"} title={language.t("command.session.new")}
class="titlebar-icon w-8 h-6 p-0 box-border" keybind={command.keybind("session.new")}
disabled={layout.sidebar.opened()} openDelay={2000}
tabIndex={layout.sidebar.opened() ? -1 : undefined} >
onClick={() => { <Button
if (!params.dir) return variant="ghost"
navigate(`/${params.dir}/session`) icon={creating() ? "new-session-active" : "new-session"}
}} class="titlebar-icon w-8 h-6 p-0 box-border"
aria-label={language.t("command.session.new")} disabled={layout.sidebar.opened()}
aria-current={creating() ? "page" : undefined} tabIndex={layout.sidebar.opened() ? -1 : undefined}
/> onClick={() => {
</TooltipKeybind> if (!params.dir) return
</div> navigate(`/${params.dir}/session`)
</div> }}
</Show> aria-label={language.t("command.session.new")}
<div aria-current={creating() ? "page" : undefined}
class="flex items-center shrink-0" />
classList={{ </TooltipKeybind>
"-translate-x-[36px]": layout.sidebar.opened() && !!params.dir, </div>
"duration-180 ease-out": !layout.sidebar.opened(),
"duration-180 ease-in": layout.sidebar.opened(),
}}
>
<Show when={hasProjects() && nav()}>
<div class="flex items-center gap-0 transition-transform">
<Tooltip placement="bottom" value={language.t("common.goBack")} openDelay={2000}>
<Button
variant="ghost"
icon="chevron-left"
class="titlebar-icon w-6 h-6 p-0 box-border"
disabled={!canBack()}
onClick={back}
aria-label={language.t("common.goBack")}
/>
</Tooltip>
<Tooltip placement="bottom" value={language.t("common.goForward")} openDelay={2000}>
<Button
variant="ghost"
icon="chevron-right"
class="titlebar-icon w-6 h-6 p-0 box-border"
disabled={!canForward()}
onClick={forward}
aria-label={language.t("common.goForward")}
/>
</Tooltip>
</div> </div>
</Show> </Show>
<div id="opencode-titlebar-left" class="flex items-center gap-3 min-w-0 px-2" /> <div
{["beta", "dev"].includes(import.meta.env.VITE_OPENCODE_CHANNEL) && ( class="flex items-center shrink-0"
<div class="bg-icon-interactive-base text-[#FFF] font-medium px-2 rounded-sm uppercase font-mono"> classList={{
{import.meta.env.VITE_OPENCODE_CHANNEL.toUpperCase()} "-translate-x-[36px]": layout.sidebar.opened() && !!params.dir,
</div> "duration-180 ease-out": !layout.sidebar.opened(),
)} "duration-180 ease-in": layout.sidebar.opened(),
}}
>
<Show when={hasProjects() && nav()}>
<div class="flex items-center gap-0 transition-transform">
<Tooltip placement="bottom" value={language.t("common.goBack")} openDelay={2000}>
<Button
variant="ghost"
icon="chevron-left"
class="titlebar-icon w-6 h-6 p-0 box-border"
disabled={!canBack()}
onClick={back}
aria-label={language.t("common.goBack")}
/>
</Tooltip>
<Tooltip placement="bottom" value={language.t("common.goForward")} openDelay={2000}>
<Button
variant="ghost"
icon="chevron-right"
class="titlebar-icon w-6 h-6 p-0 box-border"
disabled={!canForward()}
onClick={forward}
aria-label={language.t("common.goForward")}
/>
</Tooltip>
</div>
</Show>
<div id="opencode-titlebar-left" class="flex items-center gap-3 min-w-0 px-2" />
{["beta", "dev"].includes(import.meta.env.VITE_OPENCODE_CHANNEL) && (
<div class="bg-icon-interactive-base text-[#FFF] font-medium px-2 rounded-sm uppercase font-mono">
{import.meta.env.VITE_OPENCODE_CHANNEL.toUpperCase()}
</div>
)}
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="min-w-0 flex items-center justify-center pointer-events-none"> <div class="min-w-0 flex items-center justify-center pointer-events-none">
<div id="opencode-titlebar-center" class="pointer-events-auto min-w-0 flex justify-center w-fit max-w-full" /> <div id="opencode-titlebar-center" class="pointer-events-auto min-w-0 flex justify-center w-fit max-w-full" />
</div> </div>
<div <div
classList={{ classList={{
"flex items-center min-w-0 justify-end": true, "flex items-center min-w-0 justify-end": true,
"pr-2": !windows(), "pr-2": !windows(),
}} }}
data-tauri-drag-region data-tauri-drag-region
onMouseDown={drag} onMouseDown={drag}
> >
<div id="opencode-titlebar-right" class="flex items-center gap-1 shrink-0 justify-end" /> <div id="opencode-titlebar-right" class="flex items-center gap-1 shrink-0 justify-end" />
<Show when={windows()}> <Show when={windows()}>
{!tauriApi() && <div class="w-36 shrink-0" />} {!tauriApi() && <div class="shrink-0" style={{ width: windowsControlsWidth() }} />}
<div data-tauri-decorum-tb class="flex flex-row" /> <div data-tauri-decorum-tb class="flex flex-row" />
</Show> </Show>
</div>
</div> </div>
</header> </header>
) )

View file

@ -3,7 +3,7 @@ import { createStore, produce, reconcile } from "solid-js/store"
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { useParams } from "@solidjs/router" import { useParams } from "@solidjs/router"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { useSDK } from "./sdk" import { useSDK } from "./sdk"
import { useSync } from "./sync" import { useSync } from "./sync"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"

View file

@ -8,23 +8,31 @@ import type {
Todo, Todo,
} from "@opencode-ai/sdk/v2/client" } from "@opencode-ai/sdk/v2/client"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js" import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js"
import { createStore, produce, reconcile } from "solid-js/store" import { createStore, produce, reconcile } from "solid-js/store"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import type { InitError } from "../pages/error" import type { InitError } from "../pages/error"
import { useGlobalSDK } from "./global-sdk" import { useGlobalSDK } from "./global-sdk"
import { bootstrapDirectory, bootstrapGlobal, clearProviderRev } from "./global-sync/bootstrap" import {
bootstrapDirectory,
bootstrapGlobal,
clearProviderRev,
loadGlobalConfigQuery,
loadPathQuery,
loadProvidersQuery,
} from "./global-sync/bootstrap"
import { createChildStoreManager } from "./global-sync/child-store" import { createChildStoreManager } from "./global-sync/child-store"
import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } from "./global-sync/event-reducer" import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } from "./global-sync/event-reducer"
import { createRefreshQueue } from "./global-sync/queue"
import { clearSessionPrefetchDirectory } from "./global-sync/session-prefetch" import { clearSessionPrefetchDirectory } from "./global-sync/session-prefetch"
import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global-sync/session-load" import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global-sync/session-load"
import { trimSessions } from "./global-sync/session-trim" import { trimSessions } from "./global-sync/session-trim"
import type { ProjectMeta } from "./global-sync/types" import type { ProjectMeta } from "./global-sync/types"
import { SESSION_RECENT_LIMIT } from "./global-sync/types" import { SESSION_RECENT_LIMIT } from "./global-sync/types"
import { formatServerError } from "@/utils/server-errors" import { formatServerError } from "@/utils/server-errors"
import { queryOptions, skipToken, useQueryClient } from "@tanstack/solid-query" import { queryOptions, useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/solid-query"
import { createRefreshQueue } from "./global-sync/queue"
import { directoryKey } from "./global-sync/utils"
type GlobalStore = { type GlobalStore = {
ready: boolean ready: boolean
@ -40,8 +48,23 @@ type GlobalStore = {
reload: undefined | "pending" | "complete" reload: undefined | "pending" | "complete"
} }
export const loadSessionsQuery = (directory: string) => export const loadSessionsQueryKey = (directory: string) => [directory, "loadSessions"] as const
queryOptions<null>({ queryKey: [directory, "loadSessions"], queryFn: skipToken })
export const mcpQueryKey = (directory: string) => [directory, "mcp"] as const
export const loadMcpQuery = (directory: string, sdk: OpencodeClient) =>
queryOptions({
queryKey: mcpQueryKey(directory),
queryFn: () => sdk.mcp.status().then((r) => r.data ?? {}),
})
export const lspQueryKey = (directory: string) => [directory, "lsp"] as const
export const loadLspQuery = (directory: string, sdk: OpencodeClient) =>
queryOptions({
queryKey: lspQueryKey(directory),
queryFn: () => sdk.lsp.status().then((r) => r.data ?? []),
})
function createGlobalSync() { function createGlobalSync() {
const globalSDK = useGlobalSDK() const globalSDK = useGlobalSDK()
@ -54,15 +77,38 @@ function createGlobalSync() {
const sessionLoads = new Map<string, Promise<void>>() const sessionLoads = new Map<string, Promise<void>>()
const sessionMeta = new Map<string, { limit: number }>() const sessionMeta = new Map<string, { limit: number }>()
const [configQuery, providerQuery, pathQuery] = useQueries(() => ({
queries: [
loadGlobalConfigQuery(globalSDK.client),
loadProvidersQuery(null, globalSDK.client),
loadPathQuery(null, globalSDK.client),
],
}))
const [globalStore, setGlobalStore] = createStore<GlobalStore>({ const [globalStore, setGlobalStore] = createStore<GlobalStore>({
ready: false, get ready() {
path: { state: "", config: "", worktree: "", directory: "", home: "" }, return bootstrap.isPending
},
project: [], project: [],
session_todo: {}, session_todo: {},
provider: { all: [], connected: [], default: {} },
provider_auth: {}, provider_auth: {},
config: {}, get path() {
reload: undefined, const EMPTY = { state: "", config: "", worktree: "", directory: "", home: "" }
if (pathQuery.isLoading) return EMPTY
return pathQuery.data ?? EMPTY
},
get provider() {
const EMPTY = { all: [], connected: [], default: {} }
if (providerQuery.isLoading) return EMPTY
return providerQuery.data ?? EMPTY
},
get config() {
if (configQuery.isLoading) return {}
return configQuery.data ?? {}
},
get reload() {
return updateConfigMutation.isPending ? "pending" : undefined
},
}) })
const queryClient = useQueryClient() const queryClient = useQueryClient()
@ -88,6 +134,22 @@ function createGlobalSync() {
return (setGlobalStore as (...args: unknown[]) => unknown)(...input) return (setGlobalStore as (...args: unknown[]) => unknown)(...input)
}) as typeof setGlobalStore }) as typeof setGlobalStore
const bootstrap = useQuery(() => ({
queryKey: ["bootstrap"],
queryFn: async () => {
await bootstrapGlobal({
globalSDK: globalSDK.client,
requestFailedTitle: language.t("common.requestFailed"),
translate: language.t,
formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
setGlobalStore: setBootStore,
queryClient,
})
bootedAt = Date.now()
return bootedAt
},
}))
const set = ((...input: unknown[]) => { const set = ((...input: unknown[]) => {
if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) { if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) {
setProjects(input[1] as Project[] | ((draft: Project[]) => Project[])) setProjects(input[1] as Project[] | ((draft: Project[]) => Project[]))
@ -114,10 +176,23 @@ function createGlobalSync() {
const queue = createRefreshQueue({ const queue = createRefreshQueue({
paused, paused,
bootstrap, key: directoryKey,
bootstrap: () => queryClient.fetchQuery({ queryKey: ["bootstrap"] }),
bootstrapInstance, bootstrapInstance,
}) })
const sdkFor = (directory: string) => {
const key = directoryKey(directory)
const cached = sdkCache.get(key)
if (cached) return cached
const sdk = globalSDK.createClient({
directory,
throwOnError: true,
})
sdkCache.set(key, sdk)
return sdk
}
const children = createChildStoreManager({ const children = createChildStoreManager({
owner, owner,
isBooting: (directory) => booting.has(directory), isBooting: (directory) => booting.has(directory),
@ -126,33 +201,28 @@ function createGlobalSync() {
void bootstrapInstance(directory) void bootstrapInstance(directory)
}, },
onDispose: (directory) => { onDispose: (directory) => {
queue.clear(directory) const key = directoryKey(directory)
sessionMeta.delete(directory) queue.clear(key)
sdkCache.delete(directory) sessionMeta.delete(key)
clearProviderRev(directory) sdkCache.delete(key)
clearSessionPrefetchDirectory(directory) clearProviderRev(key)
clearSessionPrefetchDirectory(key)
}, },
translate: language.t, translate: language.t,
getSdk: sdkFor,
global: {
provider: globalStore.provider,
},
}) })
const sdkFor = (directory: string) => {
const cached = sdkCache.get(directory)
if (cached) return cached
const sdk = globalSDK.createClient({
directory,
throwOnError: true,
})
sdkCache.set(directory, sdk)
return sdk
}
async function loadSessions(directory: string) { async function loadSessions(directory: string) {
const pending = sessionLoads.get(directory) const key = directoryKey(directory)
const pending = sessionLoads.get(key)
if (pending) return pending if (pending) return pending
children.pin(directory) children.pin(key)
const [store, setStore] = children.child(directory, { bootstrap: false }) const [store, setStore] = children.child(directory, { bootstrap: false })
const meta = sessionMeta.get(directory) const meta = sessionMeta.get(key)
if (meta && meta.limit >= store.limit) { if (meta && meta.limit >= store.limit) {
const next = trimSessions(store.session, { const next = trimSessions(store.session, {
limit: store.limit, limit: store.limit,
@ -162,14 +232,14 @@ function createGlobalSync() {
setStore("session", reconcile(next, { key: "id" })) setStore("session", reconcile(next, { key: "id" }))
cleanupDroppedSessionCaches(store, setStore, next, setSessionTodo) cleanupDroppedSessionCaches(store, setStore, next, setSessionTodo)
} }
children.unpin(directory) children.unpin(key)
return return
} }
const limit = Math.max(store.limit + SESSION_RECENT_LIMIT, SESSION_RECENT_LIMIT) const limit = Math.max(store.limit + SESSION_RECENT_LIMIT, SESSION_RECENT_LIMIT)
const promise = queryClient const promise = queryClient
.fetchQuery({ .fetchQuery({
...loadSessionsQuery(directory), queryKey: loadSessionsQueryKey(key),
queryFn: () => queryFn: () =>
loadRootSessionsWithFallback({ loadRootSessionsWithFallback({
directory, directory,
@ -199,7 +269,7 @@ function createGlobalSync() {
setStore("session", reconcile(sessions, { key: "id" })) setStore("session", reconcile(sessions, { key: "id" }))
cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo) cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo)
}) })
sessionMeta.set(directory, { limit }) sessionMeta.set(key, { limit })
}) })
.catch((err) => { .catch((err) => {
console.error("Failed to load sessions", err) console.error("Failed to load sessions", err)
@ -214,23 +284,24 @@ function createGlobalSync() {
}) })
.then(() => {}) .then(() => {})
sessionLoads.set(directory, promise) sessionLoads.set(key, promise)
void promise.finally(() => { void promise.finally(() => {
sessionLoads.delete(directory) sessionLoads.delete(key)
children.unpin(directory) children.unpin(key)
}) })
return promise return promise
} }
async function bootstrapInstance(directory: string) { async function bootstrapInstance(directory: string) {
if (!directory) return const key = directoryKey(directory)
const pending = booting.get(directory) if (!key) return
const pending = booting.get(key)
if (pending) return pending if (pending) return pending
children.pin(directory) children.pin(key)
const promise = Promise.resolve().then(async () => { const promise = Promise.resolve().then(async () => {
const child = children.ensureChild(directory) const child = children.ensureChild(directory)
const cache = children.vcsCache.get(directory) const cache = children.vcsCache.get(key)
if (!cache) return if (!cache) return
const sdk = sdkFor(directory) const sdk = sdkFor(directory)
await bootstrapDirectory({ await bootstrapDirectory({
@ -251,39 +322,27 @@ function createGlobalSync() {
}) })
}) })
booting.set(directory, promise) booting.set(key, promise)
void promise.finally(() => { void promise.finally(() => {
booting.delete(directory) booting.delete(key)
children.unpin(directory) children.unpin(key)
}) })
return promise return promise
} }
const unsub = globalSDK.event.listen((e) => { const unsub = globalSDK.event.listen((e) => {
const directory = e.name const directory = e.name
const key = directoryKey(directory)
const event = e.details const event = e.details
const recent = bootingRoot || Date.now() - bootedAt < 1500 const recent = bootingRoot || Date.now() - bootedAt < 1500
if (event.type === "session.error") {
const error = event.properties.error
if (error?.name !== "MessageAbortedError") {
console.error("[global-sync] session error", {
scope: directory === "global" ? "global" : "workspace",
directory: directory === "global" ? undefined : directory,
project: directory === "global" ? undefined : getFilename(directory),
sessionID: event.properties.sessionID,
error,
})
}
}
if (directory === "global") { if (directory === "global") {
applyGlobalEvent({ applyGlobalEvent({
event, event,
project: globalStore.project, project: globalStore.project,
refresh: () => { refresh: () => {
if (recent) return if (recent) return
queue.refresh() bootstrap.refetch()
}, },
setGlobalProject: setProjects, setGlobalProject: setProjects,
}) })
@ -296,9 +355,9 @@ function createGlobalSync() {
return return
} }
const existing = children.children[directory] const existing = children.children[key]
if (!existing) return if (!existing) return
children.mark(directory) children.mark(key)
const [store, setStore] = existing const [store, setStore] = existing
applyDirectoryEvent({ applyDirectoryEvent({
event, event,
@ -307,14 +366,9 @@ function createGlobalSync() {
setStore, setStore,
push: queue.push, push: queue.push,
setSessionTodo, setSessionTodo,
vcsCache: children.vcsCache.get(directory), vcsCache: children.vcsCache.get(key),
loadLsp: () => { loadLsp: () => {
void sdkFor(directory) void queryClient.fetchQuery(loadLspQuery(key, sdkFor(directory)))
.lsp.status()
.then((x) => {
setStore("lsp", x.data ?? [])
setStore("lsp_ready", true)
})
}, },
}) })
}) })
@ -325,27 +379,10 @@ function createGlobalSync() {
}) })
onCleanup(() => { onCleanup(() => {
for (const directory of Object.keys(children.children)) { for (const directory of Object.keys(children.children)) {
children.disposeDirectory(directory) children.disposeDirectory(directoryKey(directory))
} }
}) })
async function bootstrap() {
bootingRoot = true
try {
await bootstrapGlobal({
globalSDK: globalSDK.client,
requestFailedTitle: language.t("common.requestFailed"),
translate: language.t,
formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
setGlobalStore: setBootStore,
queryClient,
})
bootedAt = Date.now()
} finally {
bootingRoot = false
}
}
onMount(() => { onMount(() => {
if (typeof requestAnimationFrame === "function") { if (typeof requestAnimationFrame === "function") {
eventFrame = requestAnimationFrame(() => { eventFrame = requestAnimationFrame(() => {
@ -361,7 +398,6 @@ function createGlobalSync() {
void globalSDK.event.start() void globalSDK.event.start()
}, 0) }, 0)
} }
void bootstrap()
}) })
const projectApi = { const projectApi = {
@ -374,21 +410,10 @@ function createGlobalSync() {
}, },
} }
const updateConfig = async (config: Config) => { const updateConfigMutation = useMutation(() => ({
setGlobalStore("reload", "pending") mutationFn: (config: Config) => globalSDK.client.global.config.update({ config }),
return globalSDK.client.global.config onSuccess: () => bootstrap.refetch(),
.update({ config }) }))
.then(bootstrap)
.then(() => {
queue.refresh()
setGlobalStore("reload", undefined)
queue.refresh()
})
.catch((error) => {
setGlobalStore("reload", undefined)
throw error
})
}
return { return {
data: globalStore, data: globalStore,
@ -401,8 +426,8 @@ function createGlobalSync() {
}, },
child: children.child, child: children.child,
peek: children.peek, peek: children.peek,
bootstrap, // bootstrap,
updateConfig, updateConfig: updateConfigMutation.mutateAsync,
project: projectApi, project: projectApi,
todo: { todo: {
set: setSessionTodo, set: setSessionTodo,

View file

@ -11,14 +11,15 @@ import type {
Todo, Todo,
} from "@opencode-ai/sdk/v2/client" } from "@opencode-ai/sdk/v2/client"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { retry } from "@opencode-ai/shared/util/retry" import { retry } from "@opencode-ai/core/util/retry"
import { batch } from "solid-js" import { batch } from "solid-js"
import { reconcile, type SetStoreFunction, type Store } from "solid-js/store" import { reconcile, type SetStoreFunction, type Store } from "solid-js/store"
import type { State, VcsCache } from "./types" import type { State, VcsCache } from "./types"
import { cmp, normalizeAgentList, normalizeProviderList } from "./utils" import { cmp, normalizeAgentList, normalizeProviderList } from "./utils"
import { formatServerError } from "@/utils/server-errors" import { formatServerError } from "@/utils/server-errors"
import { QueryClient, queryOptions, skipToken } from "@tanstack/solid-query" import { QueryClient, queryOptions } from "@tanstack/solid-query"
import { loadMcpQuery } from "../global-sync"
type GlobalStore = { type GlobalStore = {
ready: boolean ready: boolean
@ -66,6 +67,43 @@ function runAll(list: Array<() => Promise<unknown>>) {
return Promise.allSettled(list.map((item) => item())) return Promise.allSettled(list.map((item) => item()))
} }
function showErrors(input: {
errors: unknown[]
title: string
translate: (key: string, vars?: Record<string, string | number>) => string
formatMoreCount: (count: number) => string
}) {
if (input.errors.length === 0) return
const message = formatServerError(input.errors[0], input.translate)
const more = input.errors.length > 1 ? input.formatMoreCount(input.errors.length - 1) : ""
showToast({
variant: "error",
title: input.title,
description: message + more,
})
}
export const loadGlobalConfigQuery = (sdk: OpencodeClient) =>
queryOptions({
queryKey: ["config"],
queryFn: () => retry(() => sdk.global.config.get().then((x) => x.data!)),
})
export const loadProjectsQuery = (sdk: OpencodeClient) =>
queryOptions({
queryKey: ["project"],
queryFn: () =>
retry(() =>
sdk.project.list().then((x) => {
return (x.data ?? [])
.filter((p) => !!p?.id)
.filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
.slice()
.sort((a, b) => cmp(a.id, b.id))
}),
),
})
export async function bootstrapGlobal(input: { export async function bootstrapGlobal(input: {
globalSDK: OpencodeClient globalSDK: OpencodeClient
requestFailedTitle: string requestFailedTitle: string
@ -74,53 +112,15 @@ export async function bootstrapGlobal(input: {
setGlobalStore: SetStoreFunction<GlobalStore> setGlobalStore: SetStoreFunction<GlobalStore>
queryClient: QueryClient queryClient: QueryClient
}) { }) {
const fast = [
() =>
retry(() =>
input.globalSDK.global.config.get().then((x) => {
input.setGlobalStore("config", x.data!)
}),
),
]
const slow = [ const slow = [
() => input.queryClient.fetchQuery(loadGlobalConfigQuery(input.globalSDK)),
() => input.queryClient.fetchQuery(loadProvidersQuery(null, input.globalSDK)),
() => input.queryClient.fetchQuery(loadPathQuery(null, input.globalSDK)),
() => () =>
input.queryClient.fetchQuery({ input.queryClient
...loadProvidersQuery(null), .fetchQuery(loadProjectsQuery(input.globalSDK))
queryFn: () => .then((data) => input.setGlobalStore("project", data)),
retry(() =>
input.globalSDK.provider.list().then((x) => {
input.setGlobalStore("provider", normalizeProviderList(x.data!))
return null
}),
),
}),
() =>
retry(() =>
input.globalSDK.path.get().then((x) => {
input.setGlobalStore("path", x.data!)
}),
),
() =>
retry(() =>
input.globalSDK.project.list().then((x) => {
const projects = (x.data ?? [])
.filter((p) => !!p?.id)
.filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
.slice()
.sort((a, b) => cmp(a.id, b.id))
input.setGlobalStore("project", projects)
}),
),
] ]
await runAll(fast)
// showErrors({
// errors: errors(await runAll(fast)),
// title: input.requestFailedTitle,
// translate: input.translate,
// formatMoreCount: input.formatMoreCount,
// })
await waitForPaint()
await runAll(slow) await runAll(slow)
// showErrors({ // showErrors({
// errors: errors(), // errors: errors(),
@ -128,7 +128,6 @@ export async function bootstrapGlobal(input: {
// translate: input.translate, // translate: input.translate,
// formatMoreCount: input.formatMoreCount, // formatMoreCount: input.formatMoreCount,
// }) // })
input.setGlobalStore("ready", true)
} }
function groupBySession<T extends { id: string; sessionID: string }>(input: T[]) { function groupBySession<T extends { id: string; sessionID: string }>(input: T[]) {
@ -179,45 +178,22 @@ function warmSessions(input: {
).then(() => undefined) ).then(() => undefined)
} }
export const loadProvidersQuery = (directory: string | null) => export const loadProvidersQuery = (directory: string | null, sdk: OpencodeClient) =>
queryOptions<null>({ queryKey: [directory, "providers"], queryFn: skipToken }) queryOptions({
queryKey: [directory, "providers"],
export const loadAgentsQuery = ( queryFn: () => retry(() => sdk.provider.list().then((x) => normalizeProviderList(x.data!))),
directory: string | null,
sdk?: OpencodeClient,
transform?: (x: Awaited<ReturnType<OpencodeClient["app"]["agents"]>>) => void,
) =>
queryOptions<null>({
queryKey: [directory, "agents"],
queryFn:
sdk && transform
? () =>
retry(() =>
sdk.app
.agents()
.then(transform)
.then(() => null),
)
: skipToken,
}) })
export const loadPathQuery = ( export const loadAgentsQuery = (directory: string | null, sdk: OpencodeClient) =>
directory: string | null, queryOptions({
sdk?: OpencodeClient, queryKey: [directory, "agents"],
transform?: (x: Awaited<ReturnType<OpencodeClient["path"]["get"]>>) => void, queryFn: () => retry(() => sdk.app.agents().then((x) => normalizeAgentList(x.data))),
) => })
export const loadPathQuery = (directory: string | null, sdk: OpencodeClient) =>
queryOptions<Path>({ queryOptions<Path>({
queryKey: [directory, "path"], queryKey: [directory, "path"],
queryFn: queryFn: () => retry(() => sdk.path.get().then((x) => x.data!)),
sdk && transform
? () =>
retry(() =>
sdk.path.get().then(async (x) => {
transform(x)
return x.data!
}),
)
: skipToken,
}) })
export async function bootstrapDirectory(input: { export async function bootstrapDirectory(input: {
@ -241,19 +217,9 @@ export async function bootstrapDirectory(input: {
const seededPath = input.global.path.directory === input.directory ? input.global.path : undefined const seededPath = input.global.path.directory === input.directory ? input.global.path : undefined
if (seededProject) input.setStore("project", seededProject) if (seededProject) input.setStore("project", seededProject)
if (seededPath) input.setStore("path", seededPath) if (seededPath) input.setStore("path", seededPath)
if (input.store.provider.all.length === 0 && input.global.provider.all.length > 0) {
input.setStore("provider", input.global.provider)
}
if (Object.keys(input.store.config).length === 0 && Object.keys(input.global.config).length > 0) { if (Object.keys(input.store.config).length === 0 && Object.keys(input.global.config).length > 0) {
input.setStore("config", input.global.config) input.setStore("config", reconcile(input.global.config, { merge: false }))
} }
if (loading || input.store.provider.all.length === 0) {
input.setStore("provider_ready", false)
}
input.setStore("mcp_ready", false)
input.setStore("mcp", {})
input.setStore("lsp_ready", false)
input.setStore("lsp", [])
if (loading) input.setStore("status", "partial") if (loading) input.setStore("status", "partial")
const rev = (providerRev.get(input.directory) ?? 0) + 1 const rev = (providerRev.get(input.directory) ?? 0) + 1
@ -262,21 +228,20 @@ export async function bootstrapDirectory(input: {
const slow = [ const slow = [
() => Promise.resolve(input.loadSessions(input.directory)), () => Promise.resolve(input.loadSessions(input.directory)),
() => () =>
input.queryClient.ensureQueryData( input.queryClient
loadAgentsQuery(input.directory, input.sdk, (x) => input.setStore("agent", normalizeAgentList(x.data))), .ensureQueryData(loadAgentsQuery(input.directory, input.sdk))
), .then((data) => input.setStore("agent", data)),
() => retry(() => input.sdk.config.get().then((x) => input.setStore("config", x.data!))), () =>
retry(() => input.sdk.config.get().then((x) => input.setStore("config", reconcile(x.data!, { merge: false })))),
() => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))), () => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))),
!seededProject && !seededProject &&
(() => retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id))), (() => retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id))),
!seededPath && !seededPath &&
(() => (() =>
input.queryClient.ensureQueryData( input.queryClient.ensureQueryData(loadPathQuery(input.directory, input.sdk)).then((data) => {
loadPathQuery(input.directory, input.sdk, (x) => { const next = projectID(data.directory ?? input.directory, input.global.project)
const next = projectID(x.data?.directory ?? input.directory, input.global.project) if (next) input.setStore("project", next)
if (next) input.setStore("project", next) })),
}),
)),
() => () =>
retry(() => retry(() =>
input.sdk.vcs.get().then((x) => { input.sdk.vcs.get().then((x) => {
@ -339,33 +304,15 @@ export async function bootstrapDirectory(input: {
}), }),
), ),
() => Promise.resolve(input.loadSessions(input.directory)), () => Promise.resolve(input.loadSessions(input.directory)),
() => input.queryClient.fetchQuery(loadMcpQuery(input.directory, input.sdk)),
() => () =>
retry(() => input.queryClient.fetchQuery(loadProvidersQuery(input.directory, input.sdk)).catch((err) => {
input.sdk.mcp.status().then((x) => { const project = getFilename(input.directory)
input.setStore("mcp", x.data!) showToast({
input.setStore("mcp_ready", true) variant: "error",
}), title: input.translate("toast.project.reloadFailed.title", { project }),
), description: formatServerError(err, input.translate),
() => })
input.queryClient.ensureQueryData({
...loadProvidersQuery(input.directory),
queryFn: () =>
retry(() => input.sdk.provider.list())
.then((x) => {
if (providerRev.get(input.directory) !== rev) return
input.setStore("provider", normalizeProviderList(x.data!))
input.setStore("provider_ready", true)
})
.catch((err) => {
if (providerRev.get(input.directory) !== rev) console.error("Failed to refresh provider list", err)
const project = getFilename(input.directory)
showToast({
variant: "error",
title: input.translate("toast.project.reloadFailed.title", { project }),
description: formatServerError(err, input.translate),
})
})
.then(() => null),
}), }),
].filter(Boolean) as (() => Promise<any>)[] ].filter(Boolean) as (() => Promise<any>)[]

View file

@ -22,6 +22,8 @@ describe("createChildStoreManager", () => {
onBootstrap() {}, onBootstrap() {},
onDispose() {}, onDispose() {},
translate: (key) => key, translate: (key) => key,
getSdk: () => null!,
global: { provider: null! },
}) })
Array.from({ length: 30 }, (_, index) => `/pinned-${index}`).forEach((directory) => { Array.from({ length: 30 }, (_, index) => `/pinned-${index}`).forEach((directory) => {

View file

@ -1,7 +1,7 @@
import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js" import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js"
import { createStore, type SetStoreFunction, type Store } from "solid-js/store" import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
import { Persist, persisted } from "@/utils/persist" import { Persist, persisted } from "@/utils/persist"
import type { VcsInfo } from "@opencode-ai/sdk/v2/client" import type { OpencodeClient, ProviderListResponse, VcsInfo } from "@opencode-ai/sdk/v2/client"
import { import {
DIR_IDLE_TTL_MS, DIR_IDLE_TTL_MS,
MAX_DIR_STORES, MAX_DIR_STORES,
@ -14,8 +14,10 @@ import {
type VcsCache, type VcsCache,
} from "./types" } from "./types"
import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction" import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction"
import { useQuery } from "@tanstack/solid-query" import { useQueries } from "@tanstack/solid-query"
import { loadPathQuery } from "./bootstrap" import { loadPathQuery, loadProvidersQuery } from "./bootstrap"
import { loadLspQuery, loadMcpQuery } from "../global-sync"
import { directoryKey, type DirectoryKey } from "./utils"
export function createChildStoreManager(input: { export function createChildStoreManager(input: {
owner: Owner owner: Owner
@ -24,6 +26,10 @@ export function createChildStoreManager(input: {
onBootstrap: (directory: string) => void onBootstrap: (directory: string) => void
onDispose: (directory: string) => void onDispose: (directory: string) => void
translate: (key: string, vars?: Record<string, string | number>) => string translate: (key: string, vars?: Record<string, string | number>) => string
getSdk: (directory: string) => OpencodeClient
global: {
provider: ProviderListResponse
}
}) { }) {
const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {} const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {}
const vcsCache = new Map<string, VcsCache>() const vcsCache = new Map<string, VcsCache>()
@ -34,30 +40,37 @@ export function createChildStoreManager(input: {
const ownerPins = new WeakMap<object, Set<string>>() const ownerPins = new WeakMap<object, Set<string>>()
const disposers = new Map<string, () => void>() const disposers = new Map<string, () => void>()
const markKey = (key: DirectoryKey) => {
if (!key) return
lifecycle.set(key, { lastAccessAt: Date.now() })
runEviction(key)
}
const mark = (directory: string) => { const mark = (directory: string) => {
if (!directory) return const key = directoryKey(directory)
lifecycle.set(directory, { lastAccessAt: Date.now() }) markKey(key)
runEviction(directory)
} }
const pin = (directory: string) => { const pin = (directory: string) => {
if (!directory) return const key = directoryKey(directory)
pins.set(directory, (pins.get(directory) ?? 0) + 1) if (!key) return
mark(directory) pins.set(key, (pins.get(key) ?? 0) + 1)
markKey(key)
} }
const unpin = (directory: string) => { const unpin = (directory: string) => {
if (!directory) return const key = directoryKey(directory)
const next = (pins.get(directory) ?? 0) - 1 if (!key) return
const next = (pins.get(key) ?? 0) - 1
if (next > 0) { if (next > 0) {
pins.set(directory, next) pins.set(key, next)
return return
} }
pins.delete(directory) pins.delete(key)
runEviction() runEviction()
} }
const pinned = (directory: string) => (pins.get(directory) ?? 0) > 0 const pinned = (directory: string) => (pins.get(directoryKey(directory)) ?? 0) > 0
const pinForOwner = (directory: string) => { const pinForOwner = (directory: string) => {
const current = getOwner() const current = getOwner()
@ -79,30 +92,31 @@ export function createChildStoreManager(input: {
}) })
} }
function disposeDirectory(directory: string) { function disposeDirectory(directory: DirectoryKey) {
const key = directory
if ( if (
!canDisposeDirectory({ !canDisposeDirectory({
directory, directory: key,
hasStore: !!children[directory], hasStore: !!children[key],
pinned: pinned(directory), pinned: pinned(key),
booting: input.isBooting(directory), booting: input.isBooting(key),
loadingSessions: input.isLoadingSessions(directory), loadingSessions: input.isLoadingSessions(key),
}) })
) { ) {
return false return false
} }
vcsCache.delete(directory) vcsCache.delete(key)
metaCache.delete(directory) metaCache.delete(key)
iconCache.delete(directory) iconCache.delete(key)
lifecycle.delete(directory) lifecycle.delete(key)
const dispose = disposers.get(directory) const dispose = disposers.get(key)
if (dispose) { if (dispose) {
dispose() dispose()
disposers.delete(directory) disposers.delete(key)
} }
delete children[directory] delete children[key]
input.onDispose(directory) input.onDispose(key)
return true return true
} }
@ -119,13 +133,14 @@ export function createChildStoreManager(input: {
}).filter((directory) => directory !== skip) }).filter((directory) => directory !== skip)
if (list.length === 0) return if (list.length === 0) return
for (const directory of list) { for (const directory of list) {
if (!disposeDirectory(directory)) continue if (!disposeDirectory(directoryKey(directory))) continue
} }
} }
function ensureChild(directory: string) { function ensureChild(directory: string) {
if (!directory) console.error("No directory provided") const key = directoryKey(directory)
if (!children[directory]) { if (!key) console.error("No directory provided")
if (!children[key]) {
const vcs = runWithOwner(input.owner, () => const vcs = runWithOwner(input.owner, () =>
persisted( persisted(
Persist.workspace(directory, "vcs", ["vcs.v1"]), Persist.workspace(directory, "vcs", ["vcs.v1"]),
@ -134,7 +149,7 @@ export function createChildStoreManager(input: {
) )
if (!vcs) throw new Error(input.translate("error.childStore.persistedCacheCreateFailed")) if (!vcs) throw new Error(input.translate("error.childStore.persistedCacheCreateFailed"))
const vcsStore = vcs[0] const vcsStore = vcs[0]
vcsCache.set(directory, { store: vcsStore, setStore: vcs[1], ready: vcs[3] }) vcsCache.set(key, { store: vcsStore, setStore: vcs[1], ready: vcs[3] })
const meta = runWithOwner(input.owner, () => const meta = runWithOwner(input.owner, () =>
persisted( persisted(
@ -143,7 +158,7 @@ export function createChildStoreManager(input: {
), ),
) )
if (!meta) throw new Error(input.translate("error.childStore.persistedProjectMetadataCreateFailed")) if (!meta) throw new Error(input.translate("error.childStore.persistedProjectMetadataCreateFailed"))
metaCache.set(directory, { store: meta[0], setStore: meta[1], ready: meta[3] }) metaCache.set(key, { store: meta[0], setStore: meta[1], ready: meta[3] })
const icon = runWithOwner(input.owner, () => const icon = runWithOwner(input.owner, () =>
persisted( persisted(
@ -152,19 +167,38 @@ export function createChildStoreManager(input: {
), ),
) )
if (!icon) throw new Error(input.translate("error.childStore.persistedProjectIconCreateFailed")) if (!icon) throw new Error(input.translate("error.childStore.persistedProjectIconCreateFailed"))
iconCache.set(directory, { store: icon[0], setStore: icon[1], ready: icon[3] }) iconCache.set(key, { store: icon[0], setStore: icon[1], ready: icon[3] })
const init = () => const init = () =>
createRoot((dispose) => { createRoot((dispose) => {
const sdk = input.getSdk(directory)
const initialMeta = meta[0].value
const initialIcon = icon[0].value const initialIcon = icon[0].value
const pathQuery = useQuery(() => loadPathQuery(directory)) const [pathQuery, mcpQuery, lspQuery, providerQuery] = useQueries(() => ({
queries: [
loadPathQuery(key, sdk),
loadMcpQuery(key, sdk),
loadLspQuery(key, sdk),
loadProvidersQuery(key, sdk),
],
}))
const child = createStore<State>({ const child = createStore<State>({
project: "", project: "",
projectMeta: undefined, projectMeta: initialMeta,
icon: initialIcon, icon: initialIcon,
provider_ready: false, get provider_ready() {
provider: { all: [], connected: [], default: {} }, return !providerQuery.isLoading
},
get provider() {
const EMPTY = { all: [], connected: [], default: {} }
if (providerQuery.isLoading) return EMPTY
if (providerQuery.data?.all.length === 0 && input.global.provider.all.length > 0)
return input.global.provider
return providerQuery.data ?? EMPTY
},
config: {}, config: {},
get path() { get path() {
if (pathQuery.isLoading || !pathQuery.data) if (pathQuery.isLoading || !pathQuery.data)
@ -181,22 +215,30 @@ export function createChildStoreManager(input: {
todo: {}, todo: {},
permission: {}, permission: {},
question: {}, question: {},
mcp_ready: false, get mcp_ready() {
mcp: {}, return !mcpQuery.isLoading
lsp_ready: false, },
lsp: [], get mcp() {
return mcpQuery.isLoading ? {} : (mcpQuery.data ?? {})
},
get lsp_ready() {
return !lspQuery.isLoading
},
get lsp() {
return lspQuery.isLoading ? [] : (lspQuery.data ?? [])
},
vcs: vcsStore.value, vcs: vcsStore.value,
limit: 5, limit: 5,
message: {}, message: {},
part: {}, part: {},
}) })
children[directory] = child children[key] = child
disposers.set(directory, dispose) disposers.set(key, dispose)
const onPersistedInit = (init: Promise<string> | string | null, run: () => void) => { const onPersistedInit = (init: Promise<string> | string | null, run: () => void) => {
if (!(init instanceof Promise)) return if (!(init instanceof Promise)) return
void init.then(() => { void init.then(() => {
if (children[directory] !== child) return if (children[key] !== child) return
run() run()
}) })
} }
@ -207,6 +249,11 @@ export function createChildStoreManager(input: {
child[1]("vcs", (value) => value ?? cached) child[1]("vcs", (value) => value ?? cached)
}) })
onPersistedInit(meta[2], () => {
if (child[0].projectMeta !== initialMeta) return
child[1]("projectMeta", meta[0].value)
})
onPersistedInit(icon[2], () => { onPersistedInit(icon[2], () => {
if (child[0].icon !== initialIcon) return if (child[0].icon !== initialIcon) return
child[1]("icon", icon[0].value) child[1]("icon", icon[0].value)
@ -215,15 +262,16 @@ export function createChildStoreManager(input: {
runWithOwner(input.owner, init) runWithOwner(input.owner, init)
} }
mark(directory) markKey(key)
const childStore = children[directory] const childStore = children[key]
if (!childStore) throw new Error(input.translate("error.childStore.storeCreateFailed")) if (!childStore) throw new Error(input.translate("error.childStore.storeCreateFailed"))
return childStore return childStore
} }
function child(directory: string, options: ChildOptions = {}) { function child(directory: string, options: ChildOptions = {}) {
const key = directoryKey(directory)
const childStore = ensureChild(directory) const childStore = ensureChild(directory)
pinForOwner(directory) pinForOwner(key)
const shouldBootstrap = options.bootstrap ?? true const shouldBootstrap = options.bootstrap ?? true
if (shouldBootstrap && childStore[0].status === "loading") { if (shouldBootstrap && childStore[0].status === "loading") {
input.onBootstrap(directory) input.onBootstrap(directory)
@ -232,6 +280,7 @@ export function createChildStoreManager(input: {
} }
function peek(directory: string, options: ChildOptions = {}) { function peek(directory: string, options: ChildOptions = {}) {
const key = directoryKey(directory)
const childStore = ensureChild(directory) const childStore = ensureChild(directory)
const shouldBootstrap = options.bootstrap ?? true const shouldBootstrap = options.bootstrap ?? true
if (shouldBootstrap && childStore[0].status === "loading") { if (shouldBootstrap && childStore[0].status === "loading") {
@ -241,8 +290,9 @@ export function createChildStoreManager(input: {
} }
function projectMeta(directory: string, patch: ProjectMeta) { function projectMeta(directory: string, patch: ProjectMeta) {
const key = directoryKey(directory)
const [store, setStore] = ensureChild(directory) const [store, setStore] = ensureChild(directory)
const cached = metaCache.get(directory) const cached = metaCache.get(key)
if (!cached) return if (!cached) return
const previous = store.projectMeta ?? {} const previous = store.projectMeta ?? {}
const icon = patch.icon ? { ...previous.icon, ...patch.icon } : previous.icon const icon = patch.icon ? { ...previous.icon, ...patch.icon } : previous.icon
@ -258,8 +308,9 @@ export function createChildStoreManager(input: {
} }
function projectIcon(directory: string, value: string | undefined) { function projectIcon(directory: string, value: string | undefined) {
const key = directoryKey(directory)
const [store, setStore] = ensureChild(directory) const [store, setStore] = ensureChild(directory)
const cached = iconCache.get(directory) const cached = iconCache.get(key)
if (!cached) return if (!cached) return
if (store.icon === value) return if (store.icon === value) return
cached.setStore("value", value) cached.setStore("value", value)

View file

@ -1,4 +1,4 @@
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { produce, reconcile, type SetStoreFunction, type Store } from "solid-js/store" import { produce, reconcile, type SetStoreFunction, type Store } from "solid-js/store"
import type { import type {
Message, Message,

View file

@ -0,0 +1,46 @@
import { describe, expect, test } from "bun:test"
import { createRefreshQueue } from "./queue"
import { directoryKey } from "./utils"
const tick = () => new Promise((resolve) => setTimeout(resolve, 10))
describe("createRefreshQueue", () => {
test("clears queued directories by normalized key", async () => {
const calls: string[] = []
const queue = createRefreshQueue({
paused: () => false,
key: directoryKey,
bootstrap: async () => {},
bootstrapInstance: (directory) => {
calls.push(directory)
},
})
queue.push("C:\\tmp\\demo")
queue.clear("C:/tmp/demo")
await tick()
expect(calls).toEqual([])
queue.dispose()
})
test("passes the original directory to bootstrapInstance", async () => {
const calls: string[] = []
const queue = createRefreshQueue({
paused: () => false,
key: directoryKey,
bootstrap: async () => {},
bootstrapInstance: (directory) => {
calls.push(directory)
},
})
queue.push("C:\\tmp\\demo")
await tick()
expect(calls).toEqual(["C:\\tmp\\demo"])
queue.dispose()
})
})

View file

@ -2,22 +2,25 @@ type QueueInput = {
paused: () => boolean paused: () => boolean
bootstrap: () => Promise<void> bootstrap: () => Promise<void>
bootstrapInstance: (directory: string) => Promise<void> | void bootstrapInstance: (directory: string) => Promise<void> | void
key?: (directory: string) => string
} }
export function createRefreshQueue(input: QueueInput) { export function createRefreshQueue(input: QueueInput) {
const queued = new Set<string>() const queued = new Map<string, string>()
let root = false let root = false
let running = false let running = false
let timer: ReturnType<typeof setTimeout> | undefined let timer: ReturnType<typeof setTimeout> | undefined
const key = input.key ?? ((directory: string) => directory)
const tick = () => new Promise<void>((resolve) => setTimeout(resolve, 0)) const tick = () => new Promise<void>((resolve) => setTimeout(resolve, 0))
const take = (count: number) => { const take = (count: number) => {
if (queued.size === 0) return [] as string[] if (queued.size === 0) return [] as string[]
const items: string[] = [] const items: string[] = []
for (const item of queued) { for (const [id, directory] of queued) {
queued.delete(item) queued.delete(id)
items.push(item) items.push(directory)
if (items.length >= count) break if (items.length >= count) break
} }
return items return items
@ -33,7 +36,7 @@ export function createRefreshQueue(input: QueueInput) {
const push = (directory: string) => { const push = (directory: string) => {
if (!directory) return if (!directory) return
queued.add(directory) queued.set(key(directory), directory)
if (input.paused()) return if (input.paused()) return
schedule() schedule()
} }
@ -73,7 +76,7 @@ export function createRefreshQueue(input: QueueInput) {
push, push,
refresh, refresh,
clear(directory: string) { clear(directory: string) {
queued.delete(directory) queued.delete(key(directory))
}, },
dispose() { dispose() {
if (!timer) return if (!timer) return

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test" import { describe, expect, test } from "bun:test"
import type { Agent } from "@opencode-ai/sdk/v2/client" import type { Agent } from "@opencode-ai/sdk/v2/client"
import { normalizeAgentList } from "./utils" import { directoryKey, normalizeAgentList } from "./utils"
const agent = (name = "build") => const agent = (name = "build") =>
({ ({
@ -33,3 +33,20 @@ describe("normalizeAgentList", () => {
expect(normalizeAgentList([{ name: "build" }, agent("docs")])).toEqual([agent("docs")]) expect(normalizeAgentList([{ name: "build" }, agent("docs")])).toEqual([agent("docs")])
}) })
}) })
describe("directoryKey", () => {
test("normalizes slashes", () => {
expect(String(directoryKey("C:\\Repos\\sst\\opencode"))).toBe("C:/Repos/sst/opencode")
expect(String(directoryKey("C:/Repos/sst/opencode"))).toBe("C:/Repos/sst/opencode")
})
test("preserves backslashes in posix paths", () => {
expect(String(directoryKey("/tmp/foo\\bar"))).toBe("/tmp/foo\\bar")
})
test("trims trailing slashes without breaking roots", () => {
expect(String(directoryKey("C:/Repos/sst/opencode/"))).toBe("C:/Repos/sst/opencode")
expect(String(directoryKey("C:/"))).toBe("C:/")
expect(String(directoryKey("/"))).toBe("/")
})
})

View file

@ -1,4 +1,5 @@
import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client" import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client"
export { pathKey as directoryKey, type PathKey as DirectoryKey } from "@/utils/path-key"
export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0) export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)

View file

@ -391,7 +391,14 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
? globalSync.data.project.find((x) => x.id === projectID) ? globalSync.data.project.find((x) => x.id === projectID)
: globalSync.data.project.find((x) => x.worktree === project.worktree) : globalSync.data.project.find((x) => x.worktree === project.worktree)
return { ...metadata, ...project } // Preserve local icon override from per-workspace localStorage cache (childStore.icon).
// Without this, different subdirectories of the same git repo would share the same
// icon from the database instead of using their individual overrides.
const base = { ...metadata, ...project }
if (childStore.icon) {
return { ...base, icon: { ...base.icon, override: childStore.icon } }
}
return base
} }
const roots = createMemo(() => { const roots = createMemo(() => {

View file

@ -1,5 +1,5 @@
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { useParams } from "@solidjs/router" import { useParams } from "@solidjs/router"
import { batch, createEffect, createMemo } from "solid-js" import { batch, createEffect, createMemo } from "solid-js"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
@ -382,7 +382,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
setSaved("session", session, { setSaved("session", session, {
agent: msg.agent, agent: msg.agent,
model: msg.model, model: msg.model,
variant: msg.model.variant ?? null, variant: msg.model?.variant ?? null,
}) })
}, },
}, },

View file

@ -7,8 +7,8 @@ import { useGlobalSync } from "./global-sync"
import { usePlatform } from "@/context/platform" import { usePlatform } from "@/context/platform"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { useSettings } from "@/context/settings" import { useSettings } from "@/context/settings"
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { decode64 } from "@/utils/base64" import { decode64 } from "@/utils/base64"
import { EventSessionError } from "@opencode-ai/sdk/v2" import { EventSessionError } from "@opencode-ai/sdk/v2"
import { Persist, persisted } from "@/utils/persist" import { Persist, persisted } from "@/utils/persist"

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test" import { describe, expect, test } from "bun:test"
import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client" import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { autoRespondsPermission, isDirectoryAutoAccepting } from "./permission-auto-respond" import { autoRespondsPermission, isDirectoryAutoAccepting } from "./permission-auto-respond"
const session = (input: { id: string; parentID?: string }) => const session = (input: { id: string; parentID?: string }) =>

View file

@ -1,4 +1,4 @@
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
export function acceptKey(sessionID: string, directory?: string) { export function acceptKey(sessionID: string, directory?: string) {
if (!directory) return sessionID if (!directory) return sessionID

View file

@ -49,11 +49,11 @@ export type Platform = {
/** Storage mechanism, defaults to localStorage */ /** Storage mechanism, defaults to localStorage */
storage?: (name?: string) => SyncStorage | AsyncStorage storage?: (name?: string) => SyncStorage | AsyncStorage
/** Check for updates (Tauri only) */ /** Check for a downloadable desktop update */
checkUpdate?(): Promise<UpdateInfo> checkUpdate?(): Promise<UpdateInfo>
/** Install updates (Tauri only) */ /** Install the downloaded update using the platform restart flow */
update?(): Promise<void> updateAndRestart?(): Promise<void>
/** Fetch override */ /** Fetch override */
fetch?: typeof fetch fetch?: typeof fetch

View file

@ -1,5 +1,5 @@
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { checksum } from "@opencode-ai/shared/util/encode" import { checksum } from "@opencode-ai/core/util/encode"
import { useParams } from "@solidjs/router" import { useParams } from "@solidjs/router"
import { batch, createMemo, createRoot, getOwner, onCleanup } from "solid-js" import { batch, createMemo, createRoot, getOwner, onCleanup } from "solid-js"
import { createStore, type SetStoreFunction } from "solid-js/store" import { createStore, type SetStoreFunction } from "solid-js/store"

View file

@ -0,0 +1,53 @@
import { describe, expect, test } from "bun:test"
import { resolveServerList, ServerConnection } from "./server"
describe("resolveServerList", () => {
test("lets startup auth_token credentials override a persisted same-url server", () => {
const list = resolveServerList({
stored: [{ url: "https://server.example.test" }],
props: [
{
type: "http",
authToken: true,
http: {
url: "https://server.example.test",
username: "opencode",
password: "secret",
},
},
],
})
expect(list).toHaveLength(1)
expect(list[0]?.type).toBe("http")
expect(list[0]?.http).toEqual({
url: "https://server.example.test",
username: "opencode",
password: "secret",
})
expect(list[0]?.type === "http" ? list[0].authToken : false).toBe(true)
expect(ServerConnection.key(list[0]!) as string).toBe("https://server.example.test")
})
test("keeps persisted credentials when startup has no auth_token", () => {
const list = resolveServerList({
stored: [
{
url: "https://server.example.test",
username: "opencode",
password: "saved",
},
],
props: [{ type: "http", http: { url: "https://server.example.test" } }],
})
expect(list).toHaveLength(1)
expect(list[0]?.type).toBe("http")
expect(list[0]?.http).toEqual({
url: "https://server.example.test",
username: "opencode",
password: "saved",
})
expect(list[0]?.type === "http" ? list[0].authToken : true).toBeUndefined()
})
})

View file

@ -33,6 +33,33 @@ function isLocalHost(url: string) {
if (host === "localhost" || host === "127.0.0.1") return "local" if (host === "localhost" || host === "127.0.0.1") return "local"
} }
export function resolveServerList(input: {
props?: Array<ServerConnection.Any>
stored: StoredServer[]
}): Array<ServerConnection.Any> {
const servers = [
...input.stored.map((value) =>
typeof value === "string"
? {
type: "http" as const,
http: { url: value },
}
: value,
),
...(input.props ?? []),
]
const deduped = new Map<ServerConnection.Key, ServerConnection.Any>()
for (const value of servers) {
const conn: ServerConnection.Any = "type" in value ? value : { type: "http", http: value }
const key = ServerConnection.key(conn)
if (deduped.has(key) && conn.type === "http" && !conn.authToken) continue
deduped.set(key, conn)
}
return [...deduped.values()]
}
export namespace ServerConnection { export namespace ServerConnection {
type Base = { displayName?: string } type Base = { displayName?: string }
@ -46,6 +73,7 @@ export namespace ServerConnection {
export type Http = { export type Http = {
type: "http" type: "http"
http: HttpBase http: HttpBase
authToken?: boolean
} & Base } & Base
export type Sidecar = { export type Sidecar = {
@ -113,26 +141,7 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
const url = (x: StoredServer) => (typeof x === "string" ? x : "type" in x ? x.http.url : x.url) const url = (x: StoredServer) => (typeof x === "string" ? x : "type" in x ? x.http.url : x.url)
const allServers = createMemo((): Array<ServerConnection.Any> => { const allServers = createMemo((): Array<ServerConnection.Any> => {
const servers = [ return resolveServerList({ stored: store.list, props: props.servers })
...(props.servers ?? []),
...store.list.map((value) =>
typeof value === "string"
? {
type: "http" as const,
http: { url: value },
}
: value,
),
]
const deduped = new Map(
servers.map((value) => {
const conn: ServerConnection.Any = "type" in value ? value : { type: "http", http: value }
return [ServerConnection.key(conn), conn]
}),
)
return [...deduped.values()]
}) })
const [state, setState] = createStore({ const [state, setState] = createStore({
@ -174,7 +183,7 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
function add(input: ServerConnection.Http) { function add(input: ServerConnection.Http) {
const url_ = normalizeServerUrl(input.http.url) const url_ = normalizeServerUrl(input.http.url)
if (!url_) return if (!url_) return
const conn = { ...input, http: { ...input.http, url: url_ } } const conn: ServerConnection.Http = { ...input, authToken: undefined, http: { ...input.http, url: url_ } }
return batch(() => { return batch(() => {
const existing = store.list.findIndex((x) => url(x) === url_) const existing = store.list.findIndex((x) => url(x) === url_)
if (existing !== -1) { if (existing !== -1) {

View file

@ -1,7 +1,7 @@
import { batch, createMemo } from "solid-js" import { batch, createMemo } from "solid-js"
import { createStore, produce, reconcile } from "solid-js/store" import { createStore, produce, reconcile } from "solid-js/store"
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { retry } from "@opencode-ai/shared/util/retry" import { retry } from "@opencode-ai/core/util/retry"
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { import {
clearSessionPrefetch, clearSessionPrefetch,

View file

@ -1,6 +1,9 @@
import { beforeAll, describe, expect, mock, test } from "bun:test" import { beforeAll, describe, expect, mock, test } from "bun:test"
let getWorkspaceTerminalCacheKey: (dir: string) => string type ServerKey = Parameters<typeof import("./terminal").getTerminalServerScope>[1]
let getWorkspaceTerminalCacheKey: (dir: string, scope?: string) => string
let getTerminalServerScope: typeof import("./terminal").getTerminalServerScope
let getLegacyTerminalStorageKeys: (dir: string, legacySessionID?: string) => string[] let getLegacyTerminalStorageKeys: (dir: string, legacySessionID?: string) => string[]
let migrateTerminalState: (value: unknown) => unknown let migrateTerminalState: (value: unknown) => unknown
@ -17,6 +20,7 @@ beforeAll(async () => {
})) }))
const mod = await import("./terminal") const mod = await import("./terminal")
getWorkspaceTerminalCacheKey = mod.getWorkspaceTerminalCacheKey getWorkspaceTerminalCacheKey = mod.getWorkspaceTerminalCacheKey
getTerminalServerScope = mod.getTerminalServerScope
getLegacyTerminalStorageKeys = mod.getLegacyTerminalStorageKeys getLegacyTerminalStorageKeys = mod.getLegacyTerminalStorageKeys
migrateTerminalState = mod.migrateTerminalState migrateTerminalState = mod.migrateTerminalState
}) })
@ -25,6 +29,45 @@ describe("getWorkspaceTerminalCacheKey", () => {
test("uses workspace-only directory cache key", () => { test("uses workspace-only directory cache key", () => {
expect(getWorkspaceTerminalCacheKey("/repo")).toBe("/repo:__workspace__") expect(getWorkspaceTerminalCacheKey("/repo")).toBe("/repo:__workspace__")
}) })
test("can include a server scope", () => {
expect(getWorkspaceTerminalCacheKey("/repo", "wsl:Debian")).toBe("wsl:Debian:/repo:__workspace__")
})
})
describe("getTerminalServerScope", () => {
test("preserves local server keys", () => {
expect(
getTerminalServerScope(
{ type: "sidecar", variant: "base", http: { url: "http://127.0.0.1:4096" } },
"sidecar" as ServerKey,
),
).toBeUndefined()
expect(
getTerminalServerScope(
{ type: "http", http: { url: "http://localhost:4096" } },
"http://localhost:4096" as ServerKey,
),
).toBeUndefined()
expect(
getTerminalServerScope({ type: "http", http: { url: "http://[::1]:4096" } }, "http://[::1]:4096" as ServerKey),
).toBeUndefined()
})
test("scopes non-local server keys", () => {
expect(
getTerminalServerScope(
{ type: "sidecar", variant: "wsl", distro: "Debian", http: { url: "http://127.0.0.1:4096" } },
"wsl:Debian" as ServerKey,
),
).toBe("wsl:Debian" as ServerKey)
expect(
getTerminalServerScope(
{ type: "http", http: { url: "https://example.com" } },
"https://example.com" as ServerKey,
),
).toBe("https://example.com" as ServerKey)
})
}) })
describe("getLegacyTerminalStorageKeys", () => { describe("getLegacyTerminalStorageKeys", () => {

View file

@ -4,6 +4,7 @@ import { batch, createEffect, createMemo, createRoot, on, onCleanup } from "soli
import { useParams } from "@solidjs/router" import { useParams } from "@solidjs/router"
import { useSDK } from "./sdk" import { useSDK } from "./sdk"
import type { Platform } from "./platform" import type { Platform } from "./platform"
import { ServerConnection, useServer } from "./server"
import { defaultTitle, titleNumber } from "./terminal-title" import { defaultTitle, titleNumber } from "./terminal-title"
import { Persist, persisted, removePersisted } from "@/utils/persist" import { Persist, persisted, removePersisted } from "@/utils/persist"
@ -82,10 +83,31 @@ export function migrateTerminalState(value: unknown) {
} }
} }
export function getWorkspaceTerminalCacheKey(dir: string) { export function getWorkspaceTerminalCacheKey(dir: string, scope?: string) {
if (scope) return `${scope}:${dir}:${WORKSPACE_KEY}`
return `${dir}:${WORKSPACE_KEY}` return `${dir}:${WORKSPACE_KEY}`
} }
export function getTerminalServerScope(conn: ServerConnection.Any | undefined, key: ServerConnection.Key) {
if (!conn) return
if (conn.type === "sidecar" && conn.variant === "base") return
if (conn.type === "http") {
try {
const url = new URL(conn.http.url)
if (
url.hostname === "localhost" ||
url.hostname === "127.0.0.1" ||
url.hostname === "::1" ||
url.hostname === "[::1]"
)
return
} catch {
return key
}
}
return key
}
export function getLegacyTerminalStorageKeys(dir: string, legacySessionID?: string) { export function getLegacyTerminalStorageKeys(dir: string, legacySessionID?: string) {
if (!legacySessionID) return [`${dir}/terminal.v1`] if (!legacySessionID) return [`${dir}/terminal.v1`]
return [`${dir}/terminal/${legacySessionID}.v1`, `${dir}/terminal.v1`] return [`${dir}/terminal/${legacySessionID}.v1`, `${dir}/terminal.v1`]
@ -110,15 +132,16 @@ const trimTerminal = (pty: LocalPTY) => {
} }
} }
export function clearWorkspaceTerminals(dir: string, sessionIDs?: string[], platform?: Platform) { export function clearWorkspaceTerminals(dir: string, sessionIDs?: string[], platform?: Platform, scope?: string) {
const key = getWorkspaceTerminalCacheKey(dir) const key = getWorkspaceTerminalCacheKey(dir, scope)
for (const cache of caches) { for (const cache of caches) {
const entry = cache.get(key) const entry = cache.get(key)
entry?.value.clear() entry?.value.clear()
} }
void removePersisted(Persist.workspace(dir, "terminal"), platform) void removePersisted(Persist.workspace(dir, scope ? `terminal:${scope}` : "terminal"), platform)
if (scope) return
const legacy = new Set(getLegacyTerminalStorageKeys(dir)) const legacy = new Set(getLegacyTerminalStorageKeys(dir))
for (const id of sessionIDs ?? []) { for (const id of sessionIDs ?? []) {
for (const key of getLegacyTerminalStorageKeys(dir, id)) { for (const key of getLegacyTerminalStorageKeys(dir, id)) {
@ -130,12 +153,17 @@ export function clearWorkspaceTerminals(dir: string, sessionIDs?: string[], plat
} }
} }
function createWorkspaceTerminalSession(sdk: ReturnType<typeof useSDK>, dir: string, legacySessionID?: string) { function createWorkspaceTerminalSession(
const legacy = getLegacyTerminalStorageKeys(dir, legacySessionID) sdk: ReturnType<typeof useSDK>,
dir: string,
legacySessionID?: string,
scope?: string,
) {
const legacy = scope ? [] : getLegacyTerminalStorageKeys(dir, legacySessionID)
const [store, setStore, _, ready] = persisted( const [store, setStore, _, ready] = persisted(
{ {
...Persist.workspace(dir, "terminal", legacy), ...Persist.workspace(dir, scope ? `terminal:${scope}` : "terminal", legacy),
migrate: migrateTerminalState, migrate: migrateTerminalState,
}, },
createStore<{ createStore<{
@ -357,8 +385,12 @@ export const { use: useTerminal, provider: TerminalProvider } = createSimpleCont
gate: false, gate: false,
init: () => { init: () => {
const sdk = useSDK() const sdk = useSDK()
const server = useServer()
const params = useParams() const params = useParams()
const cache = new Map<string, TerminalCacheEntry>() const cache = new Map<string, TerminalCacheEntry>()
const scope = createMemo(() => {
return getTerminalServerScope(server.current, server.key)
})
caches.add(cache) caches.add(cache)
onCleanup(() => caches.delete(cache)) onCleanup(() => caches.delete(cache))
@ -382,9 +414,9 @@ export const { use: useTerminal, provider: TerminalProvider } = createSimpleCont
} }
} }
const loadWorkspace = (dir: string, legacySessionID?: string) => { const loadWorkspace = (dir: string, legacySessionID: string | undefined, serverScope: string | undefined) => {
// Terminals are workspace-scoped so tabs persist while switching sessions in the same directory. // Terminals are workspace-scoped so tabs persist while switching sessions in the same directory.
const key = getWorkspaceTerminalCacheKey(dir) const key = getWorkspaceTerminalCacheKey(dir, serverScope)
const existing = cache.get(key) const existing = cache.get(key)
if (existing) { if (existing) {
cache.delete(key) cache.delete(key)
@ -393,7 +425,7 @@ export const { use: useTerminal, provider: TerminalProvider } = createSimpleCont
} }
const entry = createRoot((dispose) => ({ const entry = createRoot((dispose) => ({
value: createWorkspaceTerminalSession(sdk, dir, legacySessionID), value: createWorkspaceTerminalSession(sdk, dir, legacySessionID, serverScope),
dispose, dispose,
})) }))
@ -402,16 +434,16 @@ export const { use: useTerminal, provider: TerminalProvider } = createSimpleCont
return entry.value return entry.value
} }
const workspace = createMemo(() => loadWorkspace(params.dir!, params.id)) const workspace = createMemo(() => loadWorkspace(params.dir!, params.id, scope()))
createEffect( createEffect(
on( on(
() => ({ dir: params.dir, id: params.id }), () => ({ dir: params.dir, id: params.id, scope: scope() }),
(next, prev) => { (next, prev) => {
if (!prev?.dir) return if (!prev?.dir) return
if (next.dir === prev.dir && next.id === prev.id) return if (next.dir === prev.dir && next.id === prev.id && next.scope === prev.scope) return
if (next.dir === prev.dir && next.id) return if (next.dir === prev.dir && next.id && next.scope === prev.scope) return
loadWorkspace(prev.dir, prev.id).trimAll() loadWorkspace(prev.dir, prev.id, prev.scope).trimAll()
}, },
{ defer: true }, { defer: true },
), ),

View file

@ -1,11 +1,13 @@
// @refresh reload // @refresh reload
import * as Sentry from "@sentry/solid"
import { render } from "solid-js/web" import { render } from "solid-js/web"
import { AppBaseProviders, AppInterface } from "@/app" import { AppBaseProviders, AppInterface } from "@/app"
import { type Platform, PlatformProvider } from "@/context/platform" import { type Platform, PlatformProvider } from "@/context/platform"
import { dict as en } from "@/i18n/en" import { dict as en } from "@/i18n/en"
import { dict as zh } from "@/i18n/zh" import { dict as zh } from "@/i18n/zh"
import { handleNotificationClick } from "@/utils/notification-click" import { handleNotificationClick } from "@/utils/notification-click"
import { authFromToken } from "@/utils/server"
import pkg from "../package.json" import pkg from "../package.json"
import { ServerConnection } from "./context/server" import { ServerConnection } from "./context/server"
@ -110,6 +112,13 @@ const getDefaultUrl = () => {
return getCurrentUrl() return getCurrentUrl()
} }
const clearAuthToken = () => {
const params = new URLSearchParams(location.search)
if (!params.has("auth_token")) return
params.delete("auth_token")
history.replaceState(null, "", location.pathname + (params.size ? `?${params}` : "") + location.hash)
}
const platform: Platform = { const platform: Platform = {
platform: "web", platform: "web",
version: pkg.version, version: pkg.version,
@ -125,8 +134,36 @@ const platform: Platform = {
setDefaultServer: writeDefaultServerUrl, setDefaultServer: writeDefaultServerUrl,
} }
if (import.meta.env.VITE_SENTRY_DSN) {
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.VITE_SENTRY_ENVIRONMENT ?? import.meta.env.MODE,
release: import.meta.env.VITE_SENTRY_RELEASE ?? `web@${pkg.version}`,
initialScope: {
tags: {
platform: "web",
},
},
integrations: (integrations) => {
return integrations.filter(
(i) =>
i.name !== "Breadcrumbs" && !(import.meta.env.OPENCODE_CHANNEL === "prod" && i.name === "GlobalHandlers"),
)
},
})
}
if (root instanceof HTMLElement) { if (root instanceof HTMLElement) {
const server: ServerConnection.Http = { type: "http", http: { url: getCurrentUrl() } } const auth = authFromToken(new URLSearchParams(location.search).get("auth_token"))
clearAuthToken()
const server: ServerConnection.Http = {
type: "http",
authToken: !!auth,
http: {
url: getCurrentUrl(),
...auth,
},
}
render( render(
() => ( () => (
<PlatformProvider value={platform}> <PlatformProvider value={platform}>

View file

@ -2,6 +2,10 @@ interface ImportMetaEnv {
readonly VITE_OPENCODE_SERVER_HOST: string readonly VITE_OPENCODE_SERVER_HOST: string
readonly VITE_OPENCODE_SERVER_PORT: string readonly VITE_OPENCODE_SERVER_PORT: string
readonly VITE_OPENCODE_CHANNEL?: "dev" | "beta" | "prod" readonly VITE_OPENCODE_CHANNEL?: "dev" | "beta" | "prod"
readonly VITE_SENTRY_DSN?: string
readonly VITE_SENTRY_ENVIRONMENT?: string
readonly VITE_SENTRY_RELEASE?: string
} }
interface ImportMeta { interface ImportMeta {

View file

@ -210,7 +210,7 @@ export const dict = {
"common.saving": "جارٍ الحفظ...", "common.saving": "جارٍ الحفظ...",
"common.default": "افتراضي", "common.default": "افتراضي",
"common.attachment": "مرفق", "common.attachment": "مرفق",
"prompt.placeholder.shell": "أدخل أمر shell...", "prompt.placeholder.shell": "أدخل أمر shell... {{example}}",
"prompt.placeholder.normal": 'اسأل أي شيء... "{{example}}"', "prompt.placeholder.normal": 'اسأل أي شيء... "{{example}}"',
"prompt.placeholder.simple": "اسأل أي شيء...", "prompt.placeholder.simple": "اسأل أي شيء...",
"prompt.placeholder.summarizeComments": "لخّص التعليقات…", "prompt.placeholder.summarizeComments": "لخّص التعليقات…",
@ -402,6 +402,8 @@ export const dict = {
"error.page.description": "حدث خطأ أثناء تحميل التطبيق.", "error.page.description": "حدث خطأ أثناء تحميل التطبيق.",
"error.page.details.label": "تفاصيل الخطأ", "error.page.details.label": "تفاصيل الخطأ",
"error.page.action.restart": "إعادة تشغيل", "error.page.action.restart": "إعادة تشغيل",
"error.page.action.report": "الإبلاغ عن الخطأ",
"error.page.action.reported": "تم الإبلاغ عن الخطأ",
"error.page.action.checking": "جارٍ التحقق...", "error.page.action.checking": "جارٍ التحقق...",
"error.page.action.checkUpdates": "التحقق من وجود تحديثات", "error.page.action.checkUpdates": "التحقق من وجود تحديثات",
"error.page.action.updateTo": "تحديث إلى {{version}}", "error.page.action.updateTo": "تحديث إلى {{version}}",
@ -721,8 +723,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "جلب محتوى من عنوان URL", "settings.permissions.tool.webfetch.description": "جلب محتوى من عنوان URL",
"settings.permissions.tool.websearch.title": "بحث الويب", "settings.permissions.tool.websearch.title": "بحث الويب",
"settings.permissions.tool.websearch.description": "البحث في الويب", "settings.permissions.tool.websearch.description": "البحث في الويب",
"settings.permissions.tool.codesearch.title": "بحث الكود",
"settings.permissions.tool.codesearch.description": "البحث عن كود على الويب",
"settings.permissions.tool.external_directory.title": "دليل خارجي", "settings.permissions.tool.external_directory.title": "دليل خارجي",
"settings.permissions.tool.external_directory.description": "الوصول إلى الملفات خارج دليل المشروع", "settings.permissions.tool.external_directory.description": "الوصول إلى الملفات خارج دليل المشروع",
"settings.permissions.tool.doom_loop.title": "حلقة الموت", "settings.permissions.tool.doom_loop.title": "حلقة الموت",

View file

@ -210,7 +210,7 @@ export const dict = {
"common.saving": "Salvando...", "common.saving": "Salvando...",
"common.default": "Padrão", "common.default": "Padrão",
"common.attachment": "anexo", "common.attachment": "anexo",
"prompt.placeholder.shell": "Digite comando do shell...", "prompt.placeholder.shell": "Digite comando do shell... {{example}}",
"prompt.placeholder.normal": 'Pergunte qualquer coisa... "{{example}}"', "prompt.placeholder.normal": 'Pergunte qualquer coisa... "{{example}}"',
"prompt.placeholder.simple": "Pergunte qualquer coisa...", "prompt.placeholder.simple": "Pergunte qualquer coisa...",
"prompt.placeholder.summarizeComments": "Resumir comentários…", "prompt.placeholder.summarizeComments": "Resumir comentários…",
@ -403,6 +403,8 @@ export const dict = {
"error.page.description": "Ocorreu um erro ao carregar a aplicação.", "error.page.description": "Ocorreu um erro ao carregar a aplicação.",
"error.page.details.label": "Detalhes do Erro", "error.page.details.label": "Detalhes do Erro",
"error.page.action.restart": "Reiniciar", "error.page.action.restart": "Reiniciar",
"error.page.action.report": "Reportar erro",
"error.page.action.reported": "Erro reportado",
"error.page.action.checking": "Verificando...", "error.page.action.checking": "Verificando...",
"error.page.action.checkUpdates": "Verificar atualizações", "error.page.action.checkUpdates": "Verificar atualizações",
"error.page.action.updateTo": "Atualizar para {{version}}", "error.page.action.updateTo": "Atualizar para {{version}}",
@ -732,8 +734,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Buscar conteúdo de uma URL", "settings.permissions.tool.webfetch.description": "Buscar conteúdo de uma URL",
"settings.permissions.tool.websearch.title": "Pesquisa Web", "settings.permissions.tool.websearch.title": "Pesquisa Web",
"settings.permissions.tool.websearch.description": "Pesquisar na web", "settings.permissions.tool.websearch.description": "Pesquisar na web",
"settings.permissions.tool.codesearch.title": "Pesquisa de Código",
"settings.permissions.tool.codesearch.description": "Pesquisar código na web",
"settings.permissions.tool.external_directory.title": "Diretório Externo", "settings.permissions.tool.external_directory.title": "Diretório Externo",
"settings.permissions.tool.external_directory.description": "Acessar arquivos fora do diretório do projeto", "settings.permissions.tool.external_directory.description": "Acessar arquivos fora do diretório do projeto",
"settings.permissions.tool.doom_loop.title": "Loop Infinito", "settings.permissions.tool.doom_loop.title": "Loop Infinito",

View file

@ -228,7 +228,7 @@ export const dict = {
"common.default": "Podrazumijevano", "common.default": "Podrazumijevano",
"common.attachment": "prilog", "common.attachment": "prilog",
"prompt.placeholder.shell": "Unesi shell naredbu...", "prompt.placeholder.shell": "Unesi shell naredbu... {{example}}",
"prompt.placeholder.normal": 'Pitaj bilo šta... "{{example}}"', "prompt.placeholder.normal": 'Pitaj bilo šta... "{{example}}"',
"prompt.placeholder.simple": "Pitaj bilo šta...", "prompt.placeholder.simple": "Pitaj bilo šta...",
"prompt.placeholder.summarizeComments": "Sažmi komentare…", "prompt.placeholder.summarizeComments": "Sažmi komentare…",
@ -449,6 +449,8 @@ export const dict = {
"error.page.description": "Došlo je do greške prilikom učitavanja aplikacije.", "error.page.description": "Došlo je do greške prilikom učitavanja aplikacije.",
"error.page.details.label": "Detalji greške", "error.page.details.label": "Detalji greške",
"error.page.action.restart": "Restartuj", "error.page.action.restart": "Restartuj",
"error.page.action.report": "Prijavi grešku",
"error.page.action.reported": "Greška prijavljena",
"error.page.action.checking": "Provjera...", "error.page.action.checking": "Provjera...",
"error.page.action.checkUpdates": "Provjeri ažuriranja", "error.page.action.checkUpdates": "Provjeri ažuriranja",
"error.page.action.updateTo": "Ažuriraj na {{version}}", "error.page.action.updateTo": "Ažuriraj na {{version}}",
@ -806,8 +808,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Preuzmi sadržaj sa URL-a", "settings.permissions.tool.webfetch.description": "Preuzmi sadržaj sa URL-a",
"settings.permissions.tool.websearch.title": "Web pretraga", "settings.permissions.tool.websearch.title": "Web pretraga",
"settings.permissions.tool.websearch.description": "Pretražuj web", "settings.permissions.tool.websearch.description": "Pretražuj web",
"settings.permissions.tool.codesearch.title": "Pretraga koda",
"settings.permissions.tool.codesearch.description": "Pretraži kod na webu",
"settings.permissions.tool.external_directory.title": "Vanjski direktorij", "settings.permissions.tool.external_directory.title": "Vanjski direktorij",
"settings.permissions.tool.external_directory.description": "Pristup datotekama izvan direktorija projekta", "settings.permissions.tool.external_directory.description": "Pristup datotekama izvan direktorija projekta",
"settings.permissions.tool.doom_loop.title": "Beskonačna petlja", "settings.permissions.tool.doom_loop.title": "Beskonačna petlja",

View file

@ -226,7 +226,7 @@ export const dict = {
"common.default": "Standard", "common.default": "Standard",
"common.attachment": "vedhæftning", "common.attachment": "vedhæftning",
"prompt.placeholder.shell": "Indtast shell-kommando...", "prompt.placeholder.shell": "Indtast shell-kommando... {{example}}",
"prompt.placeholder.normal": 'Spørg om hvad som helst... "{{example}}"', "prompt.placeholder.normal": 'Spørg om hvad som helst... "{{example}}"',
"prompt.placeholder.simple": "Spørg om hvad som helst...", "prompt.placeholder.simple": "Spørg om hvad som helst...",
"prompt.placeholder.summarizeComments": "Opsummér kommentarer…", "prompt.placeholder.summarizeComments": "Opsummér kommentarer…",
@ -446,6 +446,8 @@ export const dict = {
"error.page.description": "Der opstod en fejl under indlæsning af applikationen.", "error.page.description": "Der opstod en fejl under indlæsning af applikationen.",
"error.page.details.label": "Fejldetaljer", "error.page.details.label": "Fejldetaljer",
"error.page.action.restart": "Genstart", "error.page.action.restart": "Genstart",
"error.page.action.report": "Rapportér fejl",
"error.page.action.reported": "Fejl rapporteret",
"error.page.action.checking": "Tjekker...", "error.page.action.checking": "Tjekker...",
"error.page.action.checkUpdates": "Tjek for opdateringer", "error.page.action.checkUpdates": "Tjek for opdateringer",
"error.page.action.updateTo": "Opdater til {{version}}", "error.page.action.updateTo": "Opdater til {{version}}",
@ -800,8 +802,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Hent indhold fra en URL", "settings.permissions.tool.webfetch.description": "Hent indhold fra en URL",
"settings.permissions.tool.websearch.title": "Websøgning", "settings.permissions.tool.websearch.title": "Websøgning",
"settings.permissions.tool.websearch.description": "Søg på nettet", "settings.permissions.tool.websearch.description": "Søg på nettet",
"settings.permissions.tool.codesearch.title": "Kodesøgning",
"settings.permissions.tool.codesearch.description": "Søg kode på nettet",
"settings.permissions.tool.external_directory.title": "Ekstern mappe", "settings.permissions.tool.external_directory.title": "Ekstern mappe",
"settings.permissions.tool.external_directory.description": "Få adgang til filer uden for projektmappen", "settings.permissions.tool.external_directory.description": "Få adgang til filer uden for projektmappen",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",

View file

@ -215,7 +215,7 @@ export const dict = {
"common.saving": "Speichert...", "common.saving": "Speichert...",
"common.default": "Standard", "common.default": "Standard",
"common.attachment": "Anhang", "common.attachment": "Anhang",
"prompt.placeholder.shell": "Shell-Befehl eingeben...", "prompt.placeholder.shell": "Shell-Befehl eingeben... {{example}}",
"prompt.placeholder.normal": 'Fragen Sie alles... "{{example}}"', "prompt.placeholder.normal": 'Fragen Sie alles... "{{example}}"',
"prompt.placeholder.simple": "Fragen Sie alles...", "prompt.placeholder.simple": "Fragen Sie alles...",
"prompt.placeholder.summarizeComments": "Kommentare zusammenfassen…", "prompt.placeholder.summarizeComments": "Kommentare zusammenfassen…",
@ -410,6 +410,8 @@ export const dict = {
"error.page.description": "Beim Laden der Anwendung ist ein Fehler aufgetreten.", "error.page.description": "Beim Laden der Anwendung ist ein Fehler aufgetreten.",
"error.page.details.label": "Fehlerdetails", "error.page.details.label": "Fehlerdetails",
"error.page.action.restart": "Neustart", "error.page.action.restart": "Neustart",
"error.page.action.report": "Fehler melden",
"error.page.action.reported": "Fehler gemeldet",
"error.page.action.checking": "Prüfen...", "error.page.action.checking": "Prüfen...",
"error.page.action.checkUpdates": "Nach Updates suchen", "error.page.action.checkUpdates": "Nach Updates suchen",
"error.page.action.updateTo": "Auf {{version}} aktualisieren", "error.page.action.updateTo": "Auf {{version}} aktualisieren",
@ -743,8 +745,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Inhalt von einer URL abrufen", "settings.permissions.tool.webfetch.description": "Inhalt von einer URL abrufen",
"settings.permissions.tool.websearch.title": "Web-Suche", "settings.permissions.tool.websearch.title": "Web-Suche",
"settings.permissions.tool.websearch.description": "Das Web durchsuchen", "settings.permissions.tool.websearch.description": "Das Web durchsuchen",
"settings.permissions.tool.codesearch.title": "Code-Suche",
"settings.permissions.tool.codesearch.description": "Code im Web durchsuchen",
"settings.permissions.tool.external_directory.title": "Externes Verzeichnis", "settings.permissions.tool.external_directory.title": "Externes Verzeichnis",
"settings.permissions.tool.external_directory.description": "Zugriff auf Dateien außerhalb des Projektverzeichnisses", "settings.permissions.tool.external_directory.description": "Zugriff auf Dateien außerhalb des Projektverzeichnisses",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",

View file

@ -230,7 +230,7 @@ export const dict = {
"common.default": "Default", "common.default": "Default",
"common.attachment": "attachment", "common.attachment": "attachment",
"prompt.placeholder.shell": "Enter shell command...", "prompt.placeholder.shell": "Enter shell command... {{example}}",
"prompt.placeholder.normal": 'Ask anything... "{{example}}"', "prompt.placeholder.normal": 'Ask anything... "{{example}}"',
"prompt.placeholder.simple": "Ask anything...", "prompt.placeholder.simple": "Ask anything...",
"prompt.placeholder.summarizeComments": "Summarize comments…", "prompt.placeholder.summarizeComments": "Summarize comments…",
@ -465,6 +465,8 @@ export const dict = {
"error.page.description": "An error occurred while loading the application.", "error.page.description": "An error occurred while loading the application.",
"error.page.details.label": "Error Details", "error.page.details.label": "Error Details",
"error.page.action.restart": "Restart", "error.page.action.restart": "Restart",
"error.page.action.report": "Report Error",
"error.page.action.reported": "Error Reported",
"error.page.action.checking": "Checking...", "error.page.action.checking": "Checking...",
"error.page.action.checkUpdates": "Check for updates", "error.page.action.checkUpdates": "Check for updates",
"error.page.action.updateTo": "Update to {{version}}", "error.page.action.updateTo": "Update to {{version}}",
@ -728,6 +730,11 @@ export const dict = {
"settings.general.row.language.title": "Language", "settings.general.row.language.title": "Language",
"settings.general.row.language.description": "Change the display language for OpenCode", "settings.general.row.language.description": "Change the display language for OpenCode",
"settings.general.row.shell.title": "Terminal Shell",
"settings.general.row.shell.description":
"Choose the shell used for your terminal. Compatible shells are also used for agent tool calls.",
"settings.general.row.shell.autoDefault": "Auto (Default)",
"settings.general.row.shell.terminalOnly": "terminal only",
"settings.general.row.appearance.title": "Appearance", "settings.general.row.appearance.title": "Appearance",
"settings.general.row.appearance.description": "Customise how OpenCode looks on your device", "settings.general.row.appearance.description": "Customise how OpenCode looks on your device",
"settings.general.row.colorScheme.title": "Color scheme", "settings.general.row.colorScheme.title": "Color scheme",
@ -915,8 +922,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Fetch content from a URL", "settings.permissions.tool.webfetch.description": "Fetch content from a URL",
"settings.permissions.tool.websearch.title": "Web Search", "settings.permissions.tool.websearch.title": "Web Search",
"settings.permissions.tool.websearch.description": "Search the web", "settings.permissions.tool.websearch.description": "Search the web",
"settings.permissions.tool.codesearch.title": "Code Search",
"settings.permissions.tool.codesearch.description": "Search code on the web",
"settings.permissions.tool.external_directory.title": "External Directory", "settings.permissions.tool.external_directory.title": "External Directory",
"settings.permissions.tool.external_directory.description": "Access files outside the project directory", "settings.permissions.tool.external_directory.description": "Access files outside the project directory",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",

View file

@ -227,7 +227,7 @@ export const dict = {
"common.default": "Predeterminado", "common.default": "Predeterminado",
"common.attachment": "adjunto", "common.attachment": "adjunto",
"prompt.placeholder.shell": "Introduce comando de shell...", "prompt.placeholder.shell": "Introduce comando de shell... {{example}}",
"prompt.placeholder.normal": 'Pregunta cualquier cosa... "{{example}}"', "prompt.placeholder.normal": 'Pregunta cualquier cosa... "{{example}}"',
"prompt.placeholder.simple": "Pregunta cualquier cosa...", "prompt.placeholder.simple": "Pregunta cualquier cosa...",
"prompt.placeholder.summarizeComments": "Resumir comentarios…", "prompt.placeholder.summarizeComments": "Resumir comentarios…",
@ -449,6 +449,8 @@ export const dict = {
"error.page.description": "Ocurrió un error al cargar la aplicación.", "error.page.description": "Ocurrió un error al cargar la aplicación.",
"error.page.details.label": "Detalles del error", "error.page.details.label": "Detalles del error",
"error.page.action.restart": "Reiniciar", "error.page.action.restart": "Reiniciar",
"error.page.action.report": "Informar error",
"error.page.action.reported": "Error informado",
"error.page.action.checking": "Comprobando...", "error.page.action.checking": "Comprobando...",
"error.page.action.checkUpdates": "Buscar actualizaciones", "error.page.action.checkUpdates": "Buscar actualizaciones",
"error.page.action.updateTo": "Actualizar a {{version}}", "error.page.action.updateTo": "Actualizar a {{version}}",
@ -813,8 +815,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Obtener contenido de una URL", "settings.permissions.tool.webfetch.description": "Obtener contenido de una URL",
"settings.permissions.tool.websearch.title": "Búsqueda Web", "settings.permissions.tool.websearch.title": "Búsqueda Web",
"settings.permissions.tool.websearch.description": "Buscar en la web", "settings.permissions.tool.websearch.description": "Buscar en la web",
"settings.permissions.tool.codesearch.title": "Búsqueda de Código",
"settings.permissions.tool.codesearch.description": "Buscar código en la web",
"settings.permissions.tool.external_directory.title": "Directorio Externo", "settings.permissions.tool.external_directory.title": "Directorio Externo",
"settings.permissions.tool.external_directory.description": "Acceder a archivos fuera del directorio del proyecto", "settings.permissions.tool.external_directory.description": "Acceder a archivos fuera del directorio del proyecto",
"settings.permissions.tool.doom_loop.title": "Bucle Infinito", "settings.permissions.tool.doom_loop.title": "Bucle Infinito",

View file

@ -210,7 +210,7 @@ export const dict = {
"common.saving": "Enregistrement...", "common.saving": "Enregistrement...",
"common.default": "Défaut", "common.default": "Défaut",
"common.attachment": "pièce jointe", "common.attachment": "pièce jointe",
"prompt.placeholder.shell": "Entrez une commande shell...", "prompt.placeholder.shell": "Entrez une commande shell... {{example}}",
"prompt.placeholder.normal": 'Demandez n\'importe quoi... "{{example}}"', "prompt.placeholder.normal": 'Demandez n\'importe quoi... "{{example}}"',
"prompt.placeholder.simple": "Demandez n'importe quoi...", "prompt.placeholder.simple": "Demandez n'importe quoi...",
"prompt.placeholder.summarizeComments": "Résumer les commentaires…", "prompt.placeholder.summarizeComments": "Résumer les commentaires…",
@ -406,6 +406,8 @@ export const dict = {
"error.page.description": "Une erreur s'est produite lors du chargement de l'application.", "error.page.description": "Une erreur s'est produite lors du chargement de l'application.",
"error.page.details.label": "Détails de l'erreur", "error.page.details.label": "Détails de l'erreur",
"error.page.action.restart": "Redémarrer", "error.page.action.restart": "Redémarrer",
"error.page.action.report": "Signaler l'erreur",
"error.page.action.reported": "Erreur signalée",
"error.page.action.checking": "Vérification...", "error.page.action.checking": "Vérification...",
"error.page.action.checkUpdates": "Vérifier les mises à jour", "error.page.action.checkUpdates": "Vérifier les mises à jour",
"error.page.action.updateTo": "Mettre à jour vers {{version}}", "error.page.action.updateTo": "Mettre à jour vers {{version}}",
@ -741,8 +743,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Récupérer le contenu d'une URL", "settings.permissions.tool.webfetch.description": "Récupérer le contenu d'une URL",
"settings.permissions.tool.websearch.title": "Recherche Web", "settings.permissions.tool.websearch.title": "Recherche Web",
"settings.permissions.tool.websearch.description": "Rechercher sur le web", "settings.permissions.tool.websearch.description": "Rechercher sur le web",
"settings.permissions.tool.codesearch.title": "Recherche de code",
"settings.permissions.tool.codesearch.description": "Rechercher du code sur le web",
"settings.permissions.tool.external_directory.title": "Répertoire externe", "settings.permissions.tool.external_directory.title": "Répertoire externe",
"settings.permissions.tool.external_directory.description": "Accéder aux fichiers en dehors du répertoire du projet", "settings.permissions.tool.external_directory.description": "Accéder aux fichiers en dehors du répertoire du projet",
"settings.permissions.tool.doom_loop.title": "Boucle infernale", "settings.permissions.tool.doom_loop.title": "Boucle infernale",

View file

@ -209,7 +209,7 @@ export const dict = {
"common.saving": "保存中...", "common.saving": "保存中...",
"common.default": "デフォルト", "common.default": "デフォルト",
"common.attachment": "添付ファイル", "common.attachment": "添付ファイル",
"prompt.placeholder.shell": "シェルコマンドを入力...", "prompt.placeholder.shell": "シェルコマンドを入力... {{example}}",
"prompt.placeholder.normal": '何でも聞いてください... "{{example}}"', "prompt.placeholder.normal": '何でも聞いてください... "{{example}}"',
"prompt.placeholder.simple": "何でも聞いてください...", "prompt.placeholder.simple": "何でも聞いてください...",
"prompt.placeholder.summarizeComments": "コメントを要約…", "prompt.placeholder.summarizeComments": "コメントを要約…",
@ -402,6 +402,8 @@ export const dict = {
"error.page.description": "アプリケーションの読み込み中にエラーが発生しました。", "error.page.description": "アプリケーションの読み込み中にエラーが発生しました。",
"error.page.details.label": "エラー詳細", "error.page.details.label": "エラー詳細",
"error.page.action.restart": "再起動", "error.page.action.restart": "再起動",
"error.page.action.report": "エラーを報告",
"error.page.action.reported": "エラーを報告しました",
"error.page.action.checking": "確認中...", "error.page.action.checking": "確認中...",
"error.page.action.checkUpdates": "アップデートを確認", "error.page.action.checkUpdates": "アップデートを確認",
"error.page.action.updateTo": "{{version}}にアップデート", "error.page.action.updateTo": "{{version}}にアップデート",
@ -727,8 +729,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "URLからコンテンツを取得", "settings.permissions.tool.webfetch.description": "URLからコンテンツを取得",
"settings.permissions.tool.websearch.title": "Web検索", "settings.permissions.tool.websearch.title": "Web検索",
"settings.permissions.tool.websearch.description": "ウェブを検索", "settings.permissions.tool.websearch.description": "ウェブを検索",
"settings.permissions.tool.codesearch.title": "コード検索",
"settings.permissions.tool.codesearch.description": "ウェブ上のコードを検索",
"settings.permissions.tool.external_directory.title": "外部ディレクトリ", "settings.permissions.tool.external_directory.title": "外部ディレクトリ",
"settings.permissions.tool.external_directory.description": "プロジェクトディレクトリ外のファイルへのアクセス", "settings.permissions.tool.external_directory.description": "プロジェクトディレクトリ外のファイルへのアクセス",
"settings.permissions.tool.doom_loop.title": "無限ループ", "settings.permissions.tool.doom_loop.title": "無限ループ",

View file

@ -209,7 +209,7 @@ export const dict = {
"common.saving": "저장 중...", "common.saving": "저장 중...",
"common.default": "기본값", "common.default": "기본값",
"common.attachment": "첨부 파일", "common.attachment": "첨부 파일",
"prompt.placeholder.shell": "셸 명령어 입력...", "prompt.placeholder.shell": "셸 명령어 입력... {{example}}",
"prompt.placeholder.normal": '무엇이든 물어보세요... "{{example}}"', "prompt.placeholder.normal": '무엇이든 물어보세요... "{{example}}"',
"prompt.placeholder.simple": "무엇이든 물어보세요...", "prompt.placeholder.simple": "무엇이든 물어보세요...",
"prompt.placeholder.summarizeComments": "댓글 요약…", "prompt.placeholder.summarizeComments": "댓글 요약…",
@ -401,6 +401,8 @@ export const dict = {
"error.page.description": "애플리케이션을 로드하는 동안 오류가 발생했습니다.", "error.page.description": "애플리케이션을 로드하는 동안 오류가 발생했습니다.",
"error.page.details.label": "오류 세부 정보", "error.page.details.label": "오류 세부 정보",
"error.page.action.restart": "다시 시작", "error.page.action.restart": "다시 시작",
"error.page.action.report": "오류 신고",
"error.page.action.reported": "오류가 신고됨",
"error.page.action.checking": "확인 중...", "error.page.action.checking": "확인 중...",
"error.page.action.checkUpdates": "업데이트 확인", "error.page.action.checkUpdates": "업데이트 확인",
"error.page.action.updateTo": "{{version}} 버전으로 업데이트", "error.page.action.updateTo": "{{version}} 버전으로 업데이트",
@ -722,8 +724,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "URL에서 콘텐츠 가져오기", "settings.permissions.tool.webfetch.description": "URL에서 콘텐츠 가져오기",
"settings.permissions.tool.websearch.title": "웹 검색", "settings.permissions.tool.websearch.title": "웹 검색",
"settings.permissions.tool.websearch.description": "웹 검색", "settings.permissions.tool.websearch.description": "웹 검색",
"settings.permissions.tool.codesearch.title": "코드 검색",
"settings.permissions.tool.codesearch.description": "웹에서 코드 검색",
"settings.permissions.tool.external_directory.title": "외부 디렉터리", "settings.permissions.tool.external_directory.title": "외부 디렉터리",
"settings.permissions.tool.external_directory.description": "프로젝트 디렉터리 외부의 파일에 액세스", "settings.permissions.tool.external_directory.description": "프로젝트 디렉터리 외부의 파일에 액세스",
"settings.permissions.tool.doom_loop.title": "무한 반복", "settings.permissions.tool.doom_loop.title": "무한 반복",

View file

@ -230,7 +230,7 @@ export const dict = {
"common.default": "Standard", "common.default": "Standard",
"common.attachment": "vedlegg", "common.attachment": "vedlegg",
"prompt.placeholder.shell": "Skriv inn shell-kommando...", "prompt.placeholder.shell": "Skriv inn shell-kommando... {{example}}",
"prompt.placeholder.normal": 'Spør om hva som helst... "{{example}}"', "prompt.placeholder.normal": 'Spør om hva som helst... "{{example}}"',
"prompt.placeholder.simple": "Spør om hva som helst...", "prompt.placeholder.simple": "Spør om hva som helst...",
"prompt.placeholder.summarizeComments": "Oppsummer kommentarer…", "prompt.placeholder.summarizeComments": "Oppsummer kommentarer…",
@ -450,6 +450,8 @@ export const dict = {
"error.page.description": "Det oppstod en feil under lasting av applikasjonen.", "error.page.description": "Det oppstod en feil under lasting av applikasjonen.",
"error.page.details.label": "Feildetaljer", "error.page.details.label": "Feildetaljer",
"error.page.action.restart": "Start på nytt", "error.page.action.restart": "Start på nytt",
"error.page.action.report": "Rapporter feil",
"error.page.action.reported": "Feil rapportert",
"error.page.action.checking": "Sjekker...", "error.page.action.checking": "Sjekker...",
"error.page.action.checkUpdates": "Se etter oppdateringer", "error.page.action.checkUpdates": "Se etter oppdateringer",
"error.page.action.updateTo": "Oppdater til {{version}}", "error.page.action.updateTo": "Oppdater til {{version}}",
@ -807,8 +809,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Hent innhold fra en URL", "settings.permissions.tool.webfetch.description": "Hent innhold fra en URL",
"settings.permissions.tool.websearch.title": "Websøk", "settings.permissions.tool.websearch.title": "Websøk",
"settings.permissions.tool.websearch.description": "Søk på nettet", "settings.permissions.tool.websearch.description": "Søk på nettet",
"settings.permissions.tool.codesearch.title": "Kodesøk",
"settings.permissions.tool.codesearch.description": "Søk etter kode på nettet",
"settings.permissions.tool.external_directory.title": "Ekstern mappe", "settings.permissions.tool.external_directory.title": "Ekstern mappe",
"settings.permissions.tool.external_directory.description": "Få tilgang til filer utenfor prosjektmappen", "settings.permissions.tool.external_directory.description": "Få tilgang til filer utenfor prosjektmappen",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",

View file

@ -211,7 +211,7 @@ export const dict = {
"common.saving": "Zapisywanie...", "common.saving": "Zapisywanie...",
"common.default": "Domyślny", "common.default": "Domyślny",
"common.attachment": "załącznik", "common.attachment": "załącznik",
"prompt.placeholder.shell": "Wpisz polecenie terminala...", "prompt.placeholder.shell": "Wpisz polecenie terminala... {{example}}",
"prompt.placeholder.normal": 'Zapytaj o cokolwiek... "{{example}}"', "prompt.placeholder.normal": 'Zapytaj o cokolwiek... "{{example}}"',
"prompt.placeholder.simple": "Zapytaj o cokolwiek...", "prompt.placeholder.simple": "Zapytaj o cokolwiek...",
"prompt.placeholder.summarizeComments": "Podsumuj komentarze…", "prompt.placeholder.summarizeComments": "Podsumuj komentarze…",
@ -403,6 +403,8 @@ export const dict = {
"error.page.description": "Wystąpił błąd podczas ładowania aplikacji.", "error.page.description": "Wystąpił błąd podczas ładowania aplikacji.",
"error.page.details.label": "Szczegóły błędu", "error.page.details.label": "Szczegóły błędu",
"error.page.action.restart": "Restartuj", "error.page.action.restart": "Restartuj",
"error.page.action.report": "Zgłoś błąd",
"error.page.action.reported": "Błąd zgłoszony",
"error.page.action.checking": "Sprawdzanie...", "error.page.action.checking": "Sprawdzanie...",
"error.page.action.checkUpdates": "Sprawdź aktualizacje", "error.page.action.checkUpdates": "Sprawdź aktualizacje",
"error.page.action.updateTo": "Zaktualizuj do {{version}}", "error.page.action.updateTo": "Zaktualizuj do {{version}}",
@ -729,8 +731,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Pobieranie zawartości z adresu URL", "settings.permissions.tool.webfetch.description": "Pobieranie zawartości z adresu URL",
"settings.permissions.tool.websearch.title": "Wyszukiwanie w sieci", "settings.permissions.tool.websearch.title": "Wyszukiwanie w sieci",
"settings.permissions.tool.websearch.description": "Przeszukiwanie sieci", "settings.permissions.tool.websearch.description": "Przeszukiwanie sieci",
"settings.permissions.tool.codesearch.title": "Wyszukiwanie kodu",
"settings.permissions.tool.codesearch.description": "Przeszukiwanie kodu w sieci",
"settings.permissions.tool.external_directory.title": "Katalog zewnętrzny", "settings.permissions.tool.external_directory.title": "Katalog zewnętrzny",
"settings.permissions.tool.external_directory.description": "Dostęp do plików poza katalogiem projektu", "settings.permissions.tool.external_directory.description": "Dostęp do plików poza katalogiem projektu",
"settings.permissions.tool.doom_loop.title": "Zapętlenie", "settings.permissions.tool.doom_loop.title": "Zapętlenie",

View file

@ -227,7 +227,7 @@ export const dict = {
"common.default": "По умолчанию", "common.default": "По умолчанию",
"common.attachment": "вложение", "common.attachment": "вложение",
"prompt.placeholder.shell": "Введите команду оболочки...", "prompt.placeholder.shell": "Введите команду оболочки... {{example}}",
"prompt.placeholder.normal": 'Спросите что угодно... "{{example}}"', "prompt.placeholder.normal": 'Спросите что угодно... "{{example}}"',
"prompt.placeholder.simple": "Спросите что угодно...", "prompt.placeholder.simple": "Спросите что угодно...",
"prompt.placeholder.summarizeComments": "Суммировать комментарии…", "prompt.placeholder.summarizeComments": "Суммировать комментарии…",
@ -448,6 +448,8 @@ export const dict = {
"error.page.description": "Произошла ошибка при загрузке приложения.", "error.page.description": "Произошла ошибка при загрузке приложения.",
"error.page.details.label": "Детали ошибки", "error.page.details.label": "Детали ошибки",
"error.page.action.restart": "Перезапустить", "error.page.action.restart": "Перезапустить",
"error.page.action.report": "Сообщить об ошибке",
"error.page.action.reported": "Об ошибке сообщено",
"error.page.action.checking": "Проверка...", "error.page.action.checking": "Проверка...",
"error.page.action.checkUpdates": "Проверить обновления", "error.page.action.checkUpdates": "Проверить обновления",
"error.page.action.updateTo": "Обновить до {{version}}", "error.page.action.updateTo": "Обновить до {{version}}",
@ -808,8 +810,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Получение контента по URL", "settings.permissions.tool.webfetch.description": "Получение контента по URL",
"settings.permissions.tool.websearch.title": "Web Search", "settings.permissions.tool.websearch.title": "Web Search",
"settings.permissions.tool.websearch.description": "Поиск в интернете", "settings.permissions.tool.websearch.description": "Поиск в интернете",
"settings.permissions.tool.codesearch.title": "Code Search",
"settings.permissions.tool.codesearch.description": "Поиск кода в интернете",
"settings.permissions.tool.external_directory.title": "Внешняя директория", "settings.permissions.tool.external_directory.title": "Внешняя директория",
"settings.permissions.tool.external_directory.description": "Доступ к файлам вне директории проекта", "settings.permissions.tool.external_directory.description": "Доступ к файлам вне директории проекта",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",

View file

@ -227,7 +227,7 @@ export const dict = {
"common.default": "ค่าเริ่มต้น", "common.default": "ค่าเริ่มต้น",
"common.attachment": "ไฟล์แนบ", "common.attachment": "ไฟล์แนบ",
"prompt.placeholder.shell": "ป้อนคำสั่งเชลล์...", "prompt.placeholder.shell": "ป้อนคำสั่งเชลล์... {{example}}",
"prompt.placeholder.normal": 'ถามอะไรก็ได้... "{{example}}"', "prompt.placeholder.normal": 'ถามอะไรก็ได้... "{{example}}"',
"prompt.placeholder.simple": "ถามอะไรก็ได้...", "prompt.placeholder.simple": "ถามอะไรก็ได้...",
"prompt.placeholder.summarizeComments": "สรุปความคิดเห็น…", "prompt.placeholder.summarizeComments": "สรุปความคิดเห็น…",
@ -447,6 +447,8 @@ export const dict = {
"error.page.description": "เกิดข้อผิดพลาดระหว่างการโหลดแอปพลิเคชัน", "error.page.description": "เกิดข้อผิดพลาดระหว่างการโหลดแอปพลิเคชัน",
"error.page.details.label": "รายละเอียดข้อผิดพลาด", "error.page.details.label": "รายละเอียดข้อผิดพลาด",
"error.page.action.restart": "รีสตาร์ท", "error.page.action.restart": "รีสตาร์ท",
"error.page.action.report": "รายงานข้อผิดพลาด",
"error.page.action.reported": "รายงานข้อผิดพลาดแล้ว",
"error.page.action.checking": "กำลังตรวจสอบ...", "error.page.action.checking": "กำลังตรวจสอบ...",
"error.page.action.checkUpdates": "ตรวจสอบการอัปเดต", "error.page.action.checkUpdates": "ตรวจสอบการอัปเดต",
"error.page.action.updateTo": "อัปเดตเป็น {{version}}", "error.page.action.updateTo": "อัปเดตเป็น {{version}}",
@ -796,8 +798,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "ดึงเนื้อหาจาก URL", "settings.permissions.tool.webfetch.description": "ดึงเนื้อหาจาก URL",
"settings.permissions.tool.websearch.title": "ค้นหาเว็บ", "settings.permissions.tool.websearch.title": "ค้นหาเว็บ",
"settings.permissions.tool.websearch.description": "ค้นหาบนเว็บ", "settings.permissions.tool.websearch.description": "ค้นหาบนเว็บ",
"settings.permissions.tool.codesearch.title": "ค้นหาโค้ด",
"settings.permissions.tool.codesearch.description": "ค้นหาโค้ดบนเว็บ",
"settings.permissions.tool.external_directory.title": "ไดเรกทอรีภายนอก", "settings.permissions.tool.external_directory.title": "ไดเรกทอรีภายนอก",
"settings.permissions.tool.external_directory.description": "เข้าถึงไฟล์นอกไดเรกทอรีโปรเจกต์", "settings.permissions.tool.external_directory.description": "เข้าถึงไฟล์นอกไดเรกทอรีโปรเจกต์",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",

View file

@ -232,7 +232,7 @@ export const dict = {
"common.default": "Varsayılan", "common.default": "Varsayılan",
"common.attachment": "ek", "common.attachment": "ek",
"prompt.placeholder.shell": "Kabuk komutu girin...", "prompt.placeholder.shell": "Kabuk komutu girin... {{example}}",
"prompt.placeholder.normal": 'Bir şeyler sorun... "{{example}}"', "prompt.placeholder.normal": 'Bir şeyler sorun... "{{example}}"',
"prompt.placeholder.simple": "Bir şeyler sorun...", "prompt.placeholder.simple": "Bir şeyler sorun...",
"prompt.placeholder.summarizeComments": "Yorumları özetle…", "prompt.placeholder.summarizeComments": "Yorumları özetle…",
@ -452,6 +452,8 @@ export const dict = {
"error.page.description": "Uygulama yüklenirken bir hata oluştu.", "error.page.description": "Uygulama yüklenirken bir hata oluştu.",
"error.page.details.label": "Hata Detayları", "error.page.details.label": "Hata Detayları",
"error.page.action.restart": "Yeniden Başlat", "error.page.action.restart": "Yeniden Başlat",
"error.page.action.report": "Hatayı Bildir",
"error.page.action.reported": "Hata Bildirildi",
"error.page.action.checking": "Kontrol ediliyor...", "error.page.action.checking": "Kontrol ediliyor...",
"error.page.action.checkUpdates": "Güncellemeleri kontrol et", "error.page.action.checkUpdates": "Güncellemeleri kontrol et",
"error.page.action.updateTo": "{{version}} sürümüne güncelle", "error.page.action.updateTo": "{{version}} sürümüne güncelle",
@ -816,8 +818,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Bir URL'den içerik getir", "settings.permissions.tool.webfetch.description": "Bir URL'den içerik getir",
"settings.permissions.tool.websearch.title": "Web Ara", "settings.permissions.tool.websearch.title": "Web Ara",
"settings.permissions.tool.websearch.description": "Web'de ara", "settings.permissions.tool.websearch.description": "Web'de ara",
"settings.permissions.tool.codesearch.title": "Kod Ara",
"settings.permissions.tool.codesearch.description": "Web'de kod ara",
"settings.permissions.tool.external_directory.title": "Harici Dizin", "settings.permissions.tool.external_directory.title": "Harici Dizin",
"settings.permissions.tool.external_directory.description": "Proje dizini dışındaki dosyalara eriş", "settings.permissions.tool.external_directory.description": "Proje dizini dışındaki dosyalara eriş",
"settings.permissions.tool.doom_loop.title": "Sonsuz Döngü", "settings.permissions.tool.doom_loop.title": "Sonsuz Döngü",

View file

@ -249,7 +249,7 @@ export const dict = {
"common.default": "默认", "common.default": "默认",
"common.attachment": "附件", "common.attachment": "附件",
"prompt.placeholder.shell": "输入 shell 命令...", "prompt.placeholder.shell": "输入 shell 命令... {{example}}",
"prompt.placeholder.normal": '随便问点什么... "{{example}}"', "prompt.placeholder.normal": '随便问点什么... "{{example}}"',
"prompt.placeholder.simple": "随便问点什么...", "prompt.placeholder.simple": "随便问点什么...",
"prompt.placeholder.summarizeComments": "总结评论…", "prompt.placeholder.summarizeComments": "总结评论…",
@ -452,6 +452,8 @@ export const dict = {
"error.page.description": "加载应用程序时发生错误。", "error.page.description": "加载应用程序时发生错误。",
"error.page.details.label": "错误详情", "error.page.details.label": "错误详情",
"error.page.action.restart": "重启", "error.page.action.restart": "重启",
"error.page.action.report": "上报错误",
"error.page.action.reported": "错误已上报",
"error.page.action.checking": "检查中...", "error.page.action.checking": "检查中...",
"error.page.action.checkUpdates": "检查更新", "error.page.action.checkUpdates": "检查更新",
"error.page.action.updateTo": "更新到 {{version}}", "error.page.action.updateTo": "更新到 {{version}}",
@ -793,8 +795,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "从 URL 获取内容", "settings.permissions.tool.webfetch.description": "从 URL 获取内容",
"settings.permissions.tool.websearch.title": "网页搜索", "settings.permissions.tool.websearch.title": "网页搜索",
"settings.permissions.tool.websearch.description": "搜索网页", "settings.permissions.tool.websearch.description": "搜索网页",
"settings.permissions.tool.codesearch.title": "代码搜索",
"settings.permissions.tool.codesearch.description": "在网上搜索代码",
"settings.permissions.tool.external_directory.title": "外部目录", "settings.permissions.tool.external_directory.title": "外部目录",
"settings.permissions.tool.external_directory.description": "访问项目目录之外的文件", "settings.permissions.tool.external_directory.description": "访问项目目录之外的文件",
"settings.permissions.tool.doom_loop.title": "死循环", "settings.permissions.tool.doom_loop.title": "死循环",

View file

@ -227,7 +227,7 @@ export const dict = {
"common.default": "預設", "common.default": "預設",
"common.attachment": "附件", "common.attachment": "附件",
"prompt.placeholder.shell": "輸入 shell 命令...", "prompt.placeholder.shell": "輸入 shell 命令... {{example}}",
"prompt.placeholder.normal": '隨便問點什麼... "{{example}}"', "prompt.placeholder.normal": '隨便問點什麼... "{{example}}"',
"prompt.placeholder.simple": "隨便問點什麼...", "prompt.placeholder.simple": "隨便問點什麼...",
"prompt.placeholder.summarizeComments": "摘要評論…", "prompt.placeholder.summarizeComments": "摘要評論…",
@ -445,6 +445,8 @@ export const dict = {
"error.page.description": "載入應用程式時發生錯誤。", "error.page.description": "載入應用程式時發生錯誤。",
"error.page.details.label": "錯誤詳情", "error.page.details.label": "錯誤詳情",
"error.page.action.restart": "重新啟動", "error.page.action.restart": "重新啟動",
"error.page.action.report": "回報錯誤",
"error.page.action.reported": "已回報錯誤",
"error.page.action.checking": "檢查中...", "error.page.action.checking": "檢查中...",
"error.page.action.checkUpdates": "檢查更新", "error.page.action.checkUpdates": "檢查更新",
"error.page.action.updateTo": "更新到 {{version}}", "error.page.action.updateTo": "更新到 {{version}}",
@ -789,8 +791,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "從 URL 取得內容", "settings.permissions.tool.webfetch.description": "從 URL 取得內容",
"settings.permissions.tool.websearch.title": "Web Search", "settings.permissions.tool.websearch.title": "Web Search",
"settings.permissions.tool.websearch.description": "搜尋網頁", "settings.permissions.tool.websearch.description": "搜尋網頁",
"settings.permissions.tool.codesearch.title": "Code Search",
"settings.permissions.tool.codesearch.description": "在網路上搜尋程式碼",
"settings.permissions.tool.external_directory.title": "外部目錄", "settings.permissions.tool.external_directory.title": "外部目錄",
"settings.permissions.tool.external_directory.description": "存取專案目錄之外的檔案", "settings.permissions.tool.external_directory.description": "存取專案目錄之外的檔案",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",

View file

@ -1,6 +1,6 @@
import { DataProvider } from "@opencode-ai/ui/context" import { DataProvider } from "@opencode-ai/ui/context"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { useLocation, useNavigate, useParams } from "@solidjs/router" import { useLocation, useNavigate, useParams } from "@solidjs/router"
import { createEffect, createMemo, createResource, type ParentProps, Show } from "solid-js" import { createEffect, createMemo, createResource, type ParentProps, Show } from "solid-js"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"

View file

@ -1,7 +1,8 @@
import { TextField } from "@opencode-ai/ui/text-field" import { TextField } from "@opencode-ai/ui/text-field"
import * as Sentry from "@sentry/solid"
import { Logo } from "@opencode-ai/ui/logo" import { Logo } from "@opencode-ai/ui/logo"
import { Button } from "@opencode-ai/ui/button" import { Button } from "@opencode-ai/ui/button"
import { Component, Show } from "solid-js" import { Component, createSignal, Show } from "solid-js"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { usePlatform } from "@/context/platform" import { usePlatform } from "@/context/platform"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
@ -244,10 +245,9 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
} }
async function installUpdate() { async function installUpdate() {
if (!platform.update || !platform.restart) return if (!platform.updateAndRestart) return
await platform await platform
.update() .updateAndRestart()
.then(() => platform.restart!())
.then(() => setStore("actionError", undefined)) .then(() => setStore("actionError", undefined))
.catch((err) => { .catch((err) => {
setStore("actionError", formatError(err, language.t)) setStore("actionError", formatError(err, language.t))
@ -271,10 +271,27 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
label={language.t("error.page.details.label")} label={language.t("error.page.details.label")}
hideLabel hideLabel
/> />
<div class="flex items-center gap-3"> <div class="flex flex-row items-center justify-center gap-3 flex-wrap max-w-64">
<Button size="large" onClick={platform.restart}> <Button size="large" onClick={platform.restart}>
{language.t("error.page.action.restart")} {language.t("error.page.action.restart")}
</Button> </Button>
<Show when={Sentry.isEnabled}>
{(_) => {
const [reported, setReported] = createSignal(false)
return (
<Button
size="large"
disabled={reported()}
onClick={() => {
Sentry.captureException(props.error)
setReported(true)
}}
>
{language.t(reported() ? "error.page.action.reported" : "error.page.action.report")}
</Button>
)
}}
</Show>
<Show when={platform.checkUpdate}> <Show when={platform.checkUpdate}>
<Show <Show
when={store.version} when={store.version}

View file

@ -3,7 +3,7 @@ import { Button } from "@opencode-ai/ui/button"
import { Logo } from "@opencode-ai/ui/logo" import { Logo } from "@opencode-ai/ui/logo"
import { useLayout } from "@/context/layout" import { useLayout } from "@/context/layout"
import { useNavigate } from "@solidjs/router" import { useNavigate } from "@solidjs/router"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { usePlatform } from "@/context/platform" import { usePlatform } from "@/context/platform"
import { DateTime } from "luxon" import { DateTime } from "luxon"

View file

@ -17,7 +17,7 @@ import { useLocation, useNavigate, useParams } from "@solidjs/router"
import { useLayout, LocalProject } from "@/context/layout" import { useLayout, LocalProject } from "@/context/layout"
import { useGlobalSync } from "@/context/global-sync" import { useGlobalSync } from "@/context/global-sync"
import { Persist, persisted } from "@/utils/persist" import { Persist, persisted } from "@/utils/persist"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { decode64 } from "@/utils/base64" import { decode64 } from "@/utils/base64"
import { ResizeHandle } from "@opencode-ai/ui/resize-handle" import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
import { Button } from "@opencode-ai/ui/button" import { Button } from "@opencode-ai/ui/button"
@ -25,7 +25,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tooltip } from "@opencode-ai/ui/tooltip" import { Tooltip } from "@opencode-ai/ui/tooltip"
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
import { Dialog } from "@opencode-ai/ui/dialog" import { Dialog } from "@opencode-ai/ui/dialog"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { Session, type Message } from "@opencode-ai/sdk/v2/client" import { Session, type Message } from "@opencode-ai/sdk/v2/client"
import { usePlatform } from "@/context/platform" import { usePlatform } from "@/context/platform"
import { useSettings } from "@/context/settings" import { useSettings } from "@/context/settings"
@ -35,7 +35,7 @@ import type { DragEvent } from "@thisbeyond/solid-dnd"
import { useProviders } from "@/hooks/use-providers" import { useProviders } from "@/hooks/use-providers"
import { showToast, Toast, toaster } from "@opencode-ai/ui/toast" import { showToast, Toast, toaster } from "@opencode-ai/ui/toast"
import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSDK } from "@/context/global-sdk"
import { clearWorkspaceTerminals } from "@/context/terminal" import { clearWorkspaceTerminals, getTerminalServerScope } from "@/context/terminal"
import { dropSessionCaches, pickSessionCacheEvictions } from "@/context/global-sync/session-cache" import { dropSessionCaches, pickSessionCacheEvictions } from "@/context/global-sync/session-cache"
import { import {
clearSessionPrefetchInflight, clearSessionPrefetchInflight,
@ -48,8 +48,8 @@ import {
} from "@/context/global-sync/session-prefetch" } from "@/context/global-sync/session-prefetch"
import { useNotification } from "@/context/notification" import { useNotification } from "@/context/notification"
import { usePermission } from "@/context/permission" import { usePermission } from "@/context/permission"
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { retry } from "@opencode-ai/shared/util/retry" import { retry } from "@opencode-ai/core/util/retry"
import { playSoundById } from "@/utils/sound" import { playSoundById } from "@/utils/sound"
import { createAim } from "@/utils/aim" import { createAim } from "@/utils/aim"
import { setNavigate } from "@/utils/notification-click" import { setNavigate } from "@/utils/notification-click"
@ -64,13 +64,13 @@ import { DebugBar } from "@/components/debug-bar"
import { Titlebar } from "@/components/titlebar" import { Titlebar } from "@/components/titlebar"
import { useServer } from "@/context/server" import { useServer } from "@/context/server"
import { useLanguage, type Locale } from "@/context/language" import { useLanguage, type Locale } from "@/context/language"
import { pathKey } from "@/utils/path-key"
import { import {
displayName, displayName,
effectiveWorkspaceOrder, effectiveWorkspaceOrder,
errorMessage, errorMessage,
latestRootSession, latestRootSession,
sortedRootSessions, sortedRootSessions,
workspaceKey,
} from "./layout/helpers" } from "./layout/helpers"
import { import {
collectNewSessionDeepLinks, collectNewSessionDeepLinks,
@ -164,7 +164,7 @@ export default function Layout(props: ParentProps) {
const editor = createInlineEditorController() const editor = createInlineEditorController()
const setBusy = (directory: string, value: boolean) => { const setBusy = (directory: string, value: boolean) => {
const key = workspaceKey(directory) const key = pathKey(directory)
if (value) { if (value) {
setState("busyWorkspaces", key, true) setState("busyWorkspaces", key, true)
return return
@ -176,7 +176,7 @@ export default function Layout(props: ParentProps) {
}), }),
) )
} }
const isBusy = (directory: string) => !!state.busyWorkspaces[workspaceKey(directory)] const isBusy = (directory: string) => !!state.busyWorkspaces[pathKey(directory)]
const navLeave = { current: undefined as number | undefined } const navLeave = { current: undefined as number | undefined }
const sortNow = () => state.sortNow const sortNow = () => state.sortNow
let sizet: number | undefined let sizet: number | undefined
@ -366,7 +366,7 @@ export default function Layout(props: ParentProps) {
const useUpdatePolling = () => const useUpdatePolling = () =>
onMount(() => { onMount(() => {
if (!platform.checkUpdate || !platform.update || !platform.restart) return if (!platform.checkUpdate || !platform.updateAndRestart) return
let toastId: number | undefined let toastId: number | undefined
let interval: ReturnType<typeof setInterval> | undefined let interval: ReturnType<typeof setInterval> | undefined
@ -384,8 +384,7 @@ export default function Layout(props: ParentProps) {
{ {
label: language.t("toast.update.action.installRestart"), label: language.t("toast.update.action.installRestart"),
onClick: async () => { onClick: async () => {
await platform.update!() await platform.updateAndRestart!()
await platform.restart!()
}, },
}, },
{ {
@ -498,8 +497,8 @@ export default function Layout(props: ParentProps) {
} }
const currentSession = params.id const currentSession = params.id
if (workspaceKey(directory) === workspaceKey(currentDir()) && props.sessionID === currentSession) return if (pathKey(directory) === pathKey(currentDir()) && props.sessionID === currentSession) return
if (workspaceKey(directory) === workspaceKey(currentDir()) && session?.parentID === currentSession) return if (pathKey(directory) === pathKey(currentDir()) && session?.parentID === currentSession) return
dismissSessionAlert(sessionKey) dismissSessionAlert(sessionKey)
@ -557,14 +556,14 @@ export default function Layout(props: ParentProps) {
const currentProject = createMemo(() => { const currentProject = createMemo(() => {
const directory = currentDir() const directory = currentDir()
if (!directory) return if (!directory) return
const key = workspaceKey(directory) const key = pathKey(directory)
const projects = layout.projects.list() const projects = layout.projects.list()
const sandbox = projects.find((p) => p.sandboxes?.some((item) => workspaceKey(item) === key)) const sandbox = projects.find((p) => p.sandboxes?.some((item) => pathKey(item) === key))
if (sandbox) return sandbox if (sandbox) return sandbox
const direct = projects.find((p) => workspaceKey(p.worktree) === key) const direct = projects.find((p) => pathKey(p.worktree) === key)
if (direct) return direct if (direct) return direct
const [child] = globalSync.child(directory, { bootstrap: false }) const [child] = globalSync.child(directory, { bootstrap: false })
@ -597,7 +596,7 @@ export default function Layout(props: ParentProps) {
}) })
const workspaceName = (directory: string, projectId?: string, branch?: string) => { const workspaceName = (directory: string, projectId?: string, branch?: string) => {
const key = workspaceKey(directory) const key = pathKey(directory)
const direct = store.workspaceName[key] ?? store.workspaceName[directory] const direct = store.workspaceName[key] ?? store.workspaceName[directory]
if (direct) return direct if (direct) return direct
if (!projectId) return if (!projectId) return
@ -606,7 +605,7 @@ export default function Layout(props: ParentProps) {
} }
const setWorkspaceName = (directory: string, next: string, projectId?: string, branch?: string) => { const setWorkspaceName = (directory: string, next: string, projectId?: string, branch?: string) => {
const key = workspaceKey(directory) const key = pathKey(directory)
setStore("workspaceName", key, next) setStore("workspaceName", key, next)
if (!projectId) return if (!projectId) return
if (!branch) return if (!branch) return
@ -634,7 +633,7 @@ export default function Layout(props: ParentProps) {
const activeDir = currentDir() const activeDir = currentDir()
return workspaceIds(project).filter((directory) => { return workspaceIds(project).filter((directory) => {
const expanded = store.workspaceExpanded[directory] ?? directory === project.worktree const expanded = store.workspaceExpanded[directory] ?? directory === project.worktree
const active = workspaceKey(directory) === workspaceKey(activeDir) const active = pathKey(directory) === pathKey(activeDir)
return expanded || active return expanded || active
}) })
}) })
@ -645,10 +644,9 @@ export default function Layout(props: ParentProps) {
const projects = layout.projects.list() const projects = layout.projects.list()
for (const [directory, expanded] of Object.entries(store.workspaceExpanded)) { for (const [directory, expanded] of Object.entries(store.workspaceExpanded)) {
if (!expanded) continue if (!expanded) continue
const key = workspaceKey(directory) const key = pathKey(directory)
const project = projects.find( const project = projects.find(
(item) => (item) => pathKey(item.worktree) === key || item.sandboxes?.some((sandbox) => pathKey(sandbox) === key),
workspaceKey(item.worktree) === key || item.sandboxes?.some((sandbox) => workspaceKey(sandbox) === key),
) )
if (!project) continue if (!project) continue
if (project.vcs === "git" && layout.sidebar.workspaces(project.worktree)()) continue if (project.vcs === "git" && layout.sidebar.workspaces(project.worktree)()) continue
@ -701,7 +699,7 @@ export default function Layout(props: ParentProps) {
seen: lru, seen: lru,
keep: sessionID, keep: sessionID,
limit: PREFETCH_MAX_SESSIONS_PER_DIR, limit: PREFETCH_MAX_SESSIONS_PER_DIR,
preserve: params.id && workspaceKey(directory) === workspaceKey(currentDir()) ? [params.id] : undefined, preserve: params.id && pathKey(directory) === pathKey(currentDir()) ? [params.id] : undefined,
}) })
} }
@ -1222,17 +1220,14 @@ export default function Layout(props: ParentProps) {
} }
function projectRoot(directory: string) { function projectRoot(directory: string) {
const key = workspaceKey(directory) const key = pathKey(directory)
const project = layout.projects const project = layout.projects
.list() .list()
.find( .find((item) => pathKey(item.worktree) === key || item.sandboxes?.some((sandbox) => pathKey(sandbox) === key))
(item) =>
workspaceKey(item.worktree) === key || item.sandboxes?.some((sandbox) => workspaceKey(sandbox) === key),
)
if (project) return project.worktree if (project) return project.worktree
const known = Object.entries(store.workspaceOrder).find( const known = Object.entries(store.workspaceOrder).find(
([root, dirs]) => workspaceKey(root) === key || dirs.some((item) => workspaceKey(item) === key), ([root, dirs]) => pathKey(root) === key || dirs.some((item) => pathKey(item) === key),
) )
if (known) return known[0] if (known) return known[0]
@ -1284,7 +1279,7 @@ export default function Layout(props: ParentProps) {
: [root] : [root]
const canOpen = (value: string | undefined) => { const canOpen = (value: string | undefined) => {
if (!value) return false if (!value) return false
return dirs.some((item) => workspaceKey(item) === workspaceKey(value)) return dirs.some((item) => pathKey(item) === pathKey(value))
} }
const refreshDirs = async (target?: string) => { const refreshDirs = async (target?: string) => {
if (!target || target === root || canOpen(target)) return canOpen(target) if (!target || target === root || canOpen(target)) return canOpen(target)
@ -1410,9 +1405,9 @@ export default function Layout(props: ParentProps) {
function closeProject(directory: string) { function closeProject(directory: string) {
const list = layout.projects.list() const list = layout.projects.list()
const key = workspaceKey(directory) const key = pathKey(directory)
const index = list.findIndex((x) => workspaceKey(x.worktree) === key) const index = list.findIndex((x) => pathKey(x.worktree) === key)
const active = workspaceKey(currentProject()?.worktree ?? "") === key const active = pathKey(currentProject()?.worktree ?? "") === key
if (index === -1) return if (index === -1) return
const next = list[index + 1] const next = list[index + 1]
@ -1486,8 +1481,8 @@ export default function Layout(props: ParentProps) {
if (directory === root) return if (directory === root) return
const current = currentDir() const current = currentDir()
const currentKey = workspaceKey(current) const currentKey = pathKey(current)
const deletedKey = workspaceKey(directory) const deletedKey = pathKey(directory)
const shouldLeave = leaveDeletedWorkspace || (!!params.dir && currentKey === deletedKey) const shouldLeave = leaveDeletedWorkspace || (!!params.dir && currentKey === deletedKey)
if (!leaveDeletedWorkspace && shouldLeave) { if (!leaveDeletedWorkspace && shouldLeave) {
navigateWithSidebarReset(`/${base64Encode(root)}/session`) navigateWithSidebarReset(`/${base64Encode(root)}/session`)
@ -1510,7 +1505,7 @@ export default function Layout(props: ParentProps) {
if (!result) return if (!result) return
if (workspaceKey(store.lastProjectSession[root]?.directory ?? "") === workspaceKey(directory)) { if (pathKey(store.lastProjectSession[root]?.directory ?? "") === pathKey(directory)) {
clearLastProjectSession(root) clearLastProjectSession(root)
} }
@ -1530,12 +1525,12 @@ export default function Layout(props: ParentProps) {
if (shouldLeave) return if (shouldLeave) return
const nextCurrent = currentDir() const nextCurrent = currentDir()
const nextKey = workspaceKey(nextCurrent) const nextKey = pathKey(nextCurrent)
const project = layout.projects.list().find((item) => item.worktree === root) const project = layout.projects.list().find((item) => item.worktree === root)
const dirs = project const dirs = project
? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root]) ? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root])
: [root] : [root]
const valid = dirs.some((item) => workspaceKey(item) === nextKey) const valid = dirs.some((item) => pathKey(item) === nextKey)
if (params.dir && projectRoot(nextCurrent) === root && !valid) { if (params.dir && projectRoot(nextCurrent) === root && !valid) {
navigateWithSidebarReset(`/${base64Encode(root)}/session`) navigateWithSidebarReset(`/${base64Encode(root)}/session`)
@ -1562,6 +1557,7 @@ export default function Layout(props: ParentProps) {
directory, directory,
sessions.map((s) => s.id), sessions.map((s) => s.id),
platform, platform,
getTerminalServerScope(server.current, server.key),
) )
await globalSDK.client.instance.dispose({ directory }).catch(() => undefined) await globalSDK.client.instance.dispose({ directory }).catch(() => undefined)
@ -1641,7 +1637,7 @@ export default function Layout(props: ParentProps) {
}) })
const handleDelete = () => { const handleDelete = () => {
const leaveDeletedWorkspace = !!params.dir && workspaceKey(currentDir()) === workspaceKey(props.directory) const leaveDeletedWorkspace = !!params.dir && pathKey(currentDir()) === pathKey(props.directory)
if (leaveDeletedWorkspace) { if (leaveDeletedWorkspace) {
navigateWithSidebarReset(`/${base64Encode(props.root)}/session`) navigateWithSidebarReset(`/${base64Encode(props.root)}/session`)
} }
@ -1868,11 +1864,9 @@ export default function Layout(props: ParentProps) {
const local = project.worktree const local = project.worktree
const dirs = [local, ...(project.sandboxes ?? [])] const dirs = [local, ...(project.sandboxes ?? [])]
const active = currentProject() const active = currentProject()
const directory = workspaceKey(active?.worktree ?? "") === workspaceKey(project.worktree) ? currentDir() : undefined const directory = pathKey(active?.worktree ?? "") === pathKey(project.worktree) ? currentDir() : undefined
const extra = const extra =
directory && directory && pathKey(directory) !== pathKey(local) && !dirs.some((item) => pathKey(item) === pathKey(directory))
workspaceKey(directory) !== workspaceKey(local) &&
!dirs.some((item) => workspaceKey(item) === workspaceKey(directory))
? directory ? directory
: undefined : undefined
const pending = extra ? WorktreeState.get(extra)?.status === "pending" : false const pending = extra ? WorktreeState.get(extra)?.status === "pending" : false
@ -1917,7 +1911,7 @@ export default function Layout(props: ParentProps) {
setStore( setStore(
"workspaceOrder", "workspaceOrder",
project.worktree, project.worktree,
result.filter((directory) => workspaceKey(directory) !== workspaceKey(project.worktree)), result.filter((directory) => pathKey(directory) !== pathKey(project.worktree)),
) )
} }
@ -1943,8 +1937,8 @@ export default function Layout(props: ParentProps) {
setWorkspaceName(created.directory, created.branch, project.id, created.branch) setWorkspaceName(created.directory, created.branch, project.id, created.branch)
const local = project.worktree const local = project.worktree
const key = workspaceKey(created.directory) const key = pathKey(created.directory)
const root = workspaceKey(local) const root = pathKey(local)
setBusy(created.directory, true) setBusy(created.directory, true)
WorktreeState.pending(created.directory) WorktreeState.pending(created.directory)
@ -1955,7 +1949,7 @@ export default function Layout(props: ParentProps) {
setStore("workspaceOrder", project.worktree, (prev) => { setStore("workspaceOrder", project.worktree, (prev) => {
const existing = prev ?? [] const existing = prev ?? []
const next = existing.filter((item) => { const next = existing.filter((item) => {
const id = workspaceKey(item) const id = pathKey(item)
return id !== root && id !== key return id !== root && id !== key
}) })
return [created.directory, ...next] return [created.directory, ...next]

View file

@ -14,8 +14,8 @@ import {
errorMessage, errorMessage,
hasProjectPermissions, hasProjectPermissions,
latestRootSession, latestRootSession,
workspaceKey,
} from "./helpers" } from "./helpers"
import { pathKey } from "@/utils/path-key"
const session = (input: Partial<Session> & Pick<Session, "id" | "directory">) => const session = (input: Partial<Session> & Pick<Session, "id" | "directory">) =>
({ ({
@ -104,16 +104,16 @@ describe("layout deep links", () => {
describe("layout workspace helpers", () => { describe("layout workspace helpers", () => {
test("normalizes trailing slash in workspace key", () => { test("normalizes trailing slash in workspace key", () => {
expect(workspaceKey("/tmp/demo///")).toBe("/tmp/demo") expect(String(pathKey("/tmp/demo///"))).toBe("/tmp/demo")
expect(workspaceKey("C:\\tmp\\demo\\\\")).toBe("C:/tmp/demo") expect(String(pathKey("C:\\tmp\\demo\\\\"))).toBe("C:/tmp/demo")
}) })
test("preserves posix and drive roots in workspace key", () => { test("preserves posix and drive roots in workspace key", () => {
expect(workspaceKey("/")).toBe("/") expect(String(pathKey("/"))).toBe("/")
expect(workspaceKey("///")).toBe("/") expect(String(pathKey("///"))).toBe("/")
expect(workspaceKey("C:\\")).toBe("C:/") expect(String(pathKey("C:\\"))).toBe("C:/")
expect(workspaceKey("C://")).toBe("C:/") expect(String(pathKey("C://"))).toBe("C:/")
expect(workspaceKey("C:///")).toBe("C:/") expect(String(pathKey("C:///"))).toBe("C:/")
}) })
test("keeps local first while preserving known order", () => { test("keeps local first while preserving known order", () => {

View file

@ -1,19 +1,12 @@
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { type Session } from "@opencode-ai/sdk/v2/client" import { type Session } from "@opencode-ai/sdk/v2/client"
import { pathKey } from "@/utils/path-key"
type SessionStore = { type SessionStore = {
session?: Session[] session?: Session[]
path: { directory: string } path: { directory: string }
} }
export const workspaceKey = (directory: string) => {
const value = directory.replaceAll("\\", "/")
const drive = value.match(/^([A-Za-z]:)\/+$/)
if (drive) return `${drive[1]}/`
if (/^\/+$/i.test(value)) return "/"
return value.replace(/\/+$/, "")
}
function sortSessions(now: number) { function sortSessions(now: number) {
const oneMinuteAgo = now - 60 * 1000 const oneMinuteAgo = now - 60 * 1000
return (a: Session, b: Session) => { return (a: Session, b: Session) => {
@ -29,7 +22,7 @@ function sortSessions(now: number) {
} }
const isRootVisibleSession = (session: Session, directory: string) => const isRootVisibleSession = (session: Session, directory: string) =>
workspaceKey(session.directory) === workspaceKey(directory) && !session.parentID && !session.time?.archived pathKey(session.directory) === pathKey(directory) && !session.parentID && !session.time?.archived
export const roots = (store: SessionStore) => export const roots = (store: SessionStore) =>
(store.session ?? []).filter((session) => isRootVisibleSession(session, store.path.directory)) (store.session ?? []).filter((session) => isRootVisibleSession(session, store.path.directory))
@ -72,11 +65,11 @@ export const errorMessage = (err: unknown, fallback: string) => {
} }
export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted?: string[]) => { export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted?: string[]) => {
const root = workspaceKey(local) const root = pathKey(local)
const live = new Map<string, string>() const live = new Map<string, string>()
for (const dir of dirs) { for (const dir of dirs) {
const key = workspaceKey(dir) const key = pathKey(dir)
if (key === root) continue if (key === root) continue
if (!live.has(key)) live.set(key, dir) if (!live.has(key)) live.set(key, dir)
} }
@ -85,7 +78,7 @@ export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted
const result = [local] const result = [local]
for (const dir of persisted) { for (const dir of persisted) {
const key = workspaceKey(dir) const key = pathKey(dir)
if (key === root) continue if (key === root) continue
const match = live.get(key) const match = live.get(key)
if (!match) continue if (!match) continue

View file

@ -4,7 +4,7 @@ import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button" import { IconButton } from "@opencode-ai/ui/icon-button"
import { Spinner } from "@opencode-ai/ui/spinner" import { Spinner } from "@opencode-ai/ui/spinner"
import { Tooltip } from "@opencode-ai/ui/tooltip" import { Tooltip } from "@opencode-ai/ui/tooltip"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { A, useParams } from "@solidjs/router" import { A, useParams } from "@solidjs/router"
import { type Accessor, createMemo, For, type JSX, Match, Show, Switch } from "solid-js" import { type Accessor, createMemo, For, type JSX, Match, Show, Switch } from "solid-js"
import { useGlobalSync } from "@/context/global-sync" import { useGlobalSync } from "@/context/global-sync"
@ -20,9 +20,10 @@ import { childSessionOnPath, hasProjectPermissions } from "./helpers"
const OPENCODE_PROJECT_ID = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750" const OPENCODE_PROJECT_ID = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750"
export function getProjectAvatarSource(id?: string, icon?: { color?: string; url?: string; override?: string }) { export function getProjectAvatarSource(id?: string, icon?: { color?: string; url?: string; override?: string }) {
return id === OPENCODE_PROJECT_ID if (id === OPENCODE_PROJECT_ID) return "https://opencode.ai/favicon.svg"
? "https://opencode.ai/favicon.svg" if (icon?.override) return icon?.override
: (icon?.override ?? (icon?.color ? undefined : icon?.url)) if (icon?.color) return undefined
return icon?.url
} }
export const ProjectIcon = (props: { project: LocalProject; class?: string; notify?: boolean }): JSX.Element => { export const ProjectIcon = (props: { project: LocalProject; class?: string; notify?: boolean }): JSX.Element => {

View file

@ -1,6 +1,6 @@
import { createMemo, For, Show, type Accessor, type JSX } from "solid-js" import { createMemo, For, Show, type Accessor, type JSX } from "solid-js"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { Button } from "@opencode-ai/ui/button" import { Button } from "@opencode-ai/ui/button"
import { ContextMenu } from "@opencode-ai/ui/context-menu" import { ContextMenu } from "@opencode-ai/ui/context-menu"
import { HoverCard } from "@opencode-ai/ui/hover-card" import { HoverCard } from "@opencode-ai/ui/hover-card"

Some files were not shown because too many files have changed in this diff Show more