Commit Graph
1017 Commits
Author SHA1 Message Date
Bohdan Triapitsyn eb6f7b0904 fix(terminal): update ghostty-web to a build that clears recycled rows
ghostty-web 0.4.0 hands rows that scroll into view out of recycled WASM
page memory without clearing them, so after a tab or project switch the
new emulator showed the previous terminal's text (upstream #138). The fix
landed only in prereleases, so pin 0.4.0-next.20 and carry the local
block-glyph rendering patch over to the new dist file.

Verified in a production build: creating an emulator after disposing a
full one no longer exposes its rows, and switching between two projects
with live output in each keeps every terminal's content to itself.
2026-09-06 22:57:43 +03:00
Bohdan Triapitsyn c2f36fb5e7 fix(terminal): replay snapshot history at the PTY size it was drawn for
Opening the terminal panel sometimes showed stray fragments on the prompt
row: zsh's end-of-line mark and pieces of the prompt path. The shell had
laid its output out for one PTY width, but the client replayed that
history into an emulator of another width (an early size estimate, a
remount, or a renderer rebuild after fonts loaded). ghostty-web's reflow
then left fragments the shell's SIGWINCH redraw never clears.

The server now reports the PTY cols/rows in every snapshot, the transport
carries them through projections and accepted resizes, and the viewport
replays a sized snapshot chunk at that size before returning to the
fitted size. The container-based size estimate only seeds newly spawned
shells and is no longer sent to a running PTY.

Tests cover the sized replay, the store chunk size, the transport
projection, and the server snapshot; verified in a production build by
reloading with the panel open and switching tabs at a changed width.
2026-09-06 22:57:43 +03:00
alvins82 d8215ef5b3 feat(work-status): add opt-in turn statistics (#3177)
Add optional completed-turn statistics without changing the existing panel layout. Separate final text delivery speed from whole-turn throughput, preserve scope and opt-in settings, and explain each metric with localized delayed tooltips.

Validated focused telemetry, lifecycle, sync and persistence tests, all-workspace type-check and lint, web builds, the 12-locale narrow layout, and full GitHub CI.
2026-09-06 22:56:27 +03:00
Bohdan Triapitsyn b0282b2720 fix(quota): validate Hyper credentials and clean up balance labels
Reject invalid credentials while preserving valid token fallback, parse balances with existing boundary helpers, and keep credit values free of untranslated unit text. Inject auth and HTTP dependencies for focused tests in both runtimes.

Validated web quota and registry tests (35 passed), VS Code quota tests (70 passed), both package type checks and lint, extension build, and changed-line anti-slop checks. Reviewed dead-code output. Live Hyper validation was not run because no API key is available.
2026-09-05 23:26:35 +03:00
Howon Lee 5e7c147785 feat: add Charm Hyper quota provider (#3368) 2026-09-05 23:16:13 +03:00
Bohdan Triapitsyn 7b42208b8c release v1.22.2 2026-09-05 21:39:04 +03:00
𝖎𝖚𝖑𝖎𝖎𝖆 759af5a77d fix(sessions): recover sessions whose directory disappeared (#3365)
* fix(sessions): keep a shared chat directory until its last session is deleted

Deleting a root chat session removed its managed scratch directory even
when forks, side threads, or subagents still lived in it; OpenCode then
failed every prompt in those sessions with FileSystem.realPath NotFound.
The directory is now removed only once no other known session resolves
to it. The deleted subtree does not count, because the server cascade-
deletes it, and an unloaded global cache keeps the directory instead of
guessing.

Closes #3312.

* fix(sessions): relocate a session whose worktree directory disappeared

A worktree removed outside OpenChamber, by the agent or by hand, left its
sessions pointed at a path that no longer exists: every terminal create
and restart failed with "Invalid working directory" and the tab stayed
stuck, while Git, Files, and prompts kept targeting the dead path.

The terminal server now names that one rejection (TERMINAL_CWD_MISSING)
instead of substituting a directory of its own. The shared UI reuses the
archived-restore fallback for live sessions: a server-confirmed missing
directory moves the session and its stranded subtree to the project's
primary directory through the control-plane move, clears the worktree
hint, re-selects the session, and tells the user where it went. It runs
from a terminal failure and on activation of any session whose directory
is neither a project root nor a managed chat directory; available,
unknown, and failed probes leave everything untouched.

Closes #3338.

* fix(scripts): make oc-dev load again after the changelog cleanup

The changelog cleanup referenced fs.existsSync in a module that imports
existsSync by name and never binds fs, so every oc-dev invocation failed
with "fs is not defined" before reaching its action.

* fix(sessions): probe directory availability on disk, not through OpenCode path resolution

OpenCode's /path never checks that a directory exists: it echoes the
requested path and resolves its project through Git discovery that
swallows errors, so a deleted worktree came back as a valid location and
every missing-directory fallback (draft recovery, archived restore,
session relocation) stayed inert on a real server. The probe now asks
OpenChamber's own /api/fs/list, which stats the path and reports
not-found and not-directory explicitly; anything else stays unknown.

* fix(sidebar): keep a worktree whose directory is gone visible as missing

git keeps a worktree registered after its directory is deleted outside
git and marks it prunable; the list parser ignored that line, so a
deleted worktree looked alive, and nothing in the app asked for a new
listing anyway. The server now reports prunable, the UI keeps such a
worktree in the topology with worktreeStatus missing and a warning icon
on its sidebar group, and relocating a session out of a confirmed-
missing directory raises an in-app topology signal the sidebar
rediscovers on. Dropping the worktree instead would hide every session
that lived there, and a hidden session can never be opened or relocated.
No idle polling is added.

* fix(sessions): never relocate a session to the filesystem root

OpenCode files a directory outside any Git repository under its global
project, whose worktree is the filesystem root. A managed chat whose
directory vanished would otherwise be moved to /. The relocation now
refuses a root destination, and the activation probe recognizes chat
directories through the home-based check as well, so it does not depend
on the chats root having been resolved yet.

* test(sessions): mirror the relocation action in the issue-2039 session-actions mock

session-ui-store now imports relocateSessionFromMissingDirectory, and the
mocked module in this test listed every other action but not that one, so
the file failed on import.
2026-09-05 21:26:21 +03:00
Bohdan Triapitsyn f46fb718c5 fix(chat): recall the current session's prompts by default; tidy the six merged PRs
Input history (#3035) shipped with "All projects" as the default scope and
only recorded prompts sent after the upgrade, so ArrowUp showed other
sessions' prompts and, once switched to "Current session", nothing at all.
Default to the current session and merge the visible transcript's prompts
with the persisted bucket. Existing sessions recall as they did before
#3035, while new prompts keep their attachments and stay recallable after
a revert hides them from the transcript.

Cleanup across #1855, #2297, #3072, #3178, #3035 and #3135: drop the
duplicate poll guards in the file content poller, the zod schema the
VS Code package cannot depend on, a copied file-URL helper and stray
whitespace; move the Enter-to-send strings into the settings namespace;
document OPENCHAMBER_CHATS_DIR, resolve the chats root once on the server
and warm it alongside the other bootstrap calls.
2026-09-05 20:16:14 +03:00
Steffen MächtelandBohdan Triapitsyn 3df97908fe feat(chats): relocate managed chat worktrees via OPENCHAMBER_CHATS_DIR (#3135)
* feat(chats): relocate managed chat worktrees via OPENCHAMBER_CHATS_DIR

Projectless-chat worktrees were hard-pinned to
<home>/.config/openchamber/chats: the UI joined the path client-side,
workspace checks allowed only the config root, and identification matched
the literal path segment. When the OpenCode server runs as a separate
user (UID-separated setups), that root is unreachable — every chat
session answered HTTP 500 (EACCES on the session directory).

The server now owns the chats root. OPENCHAMBER_CHATS_DIR relocates it
(default unchanged: <config root>/chats); /api/fs/home answers
{ home, chatsRoot }; fs workspace checks accept the managed chats root
next to the config root; the client resolves the root from the server
(per-runtime cached, warmed at bootstrap so sync classification sees it)
and falls back to the home join for older servers.

Refs #3130

* chore: trim added comments to local precedent

* fix: forward managedChatsRoot through feature-routes-runtime to registerFsRoutes

* fix(chats): await the root warm-up and keep the legacy chats root owned

Review feedback on #3135:

- bootstrapGlobal now awaits warmChatsRootDirectory, so synchronous
  session classification never sees an empty root cache (relocated
  sessions were grouped as project sessions when the session list
  outran /api/fs/home).
- managedProjectRoots keeps the legacy <config root>/chats entry next to
  OPENCHAMBER_CHATS_DIR, so memory ownership of existing chats survives
  relocation.

* fix(chats): distinguish chats-root fetch failure from older servers

* fix(sync): rehydrate managed chat sessions after the chats root warms

* fix(fs): pass managed roots through the symlink and git-dirs path checks after the main merge

* docs: drop changelog edits; changelog is the maintainer's release-time work

* fix(chats): keep legacy chat directories deletable while the root is relocated

* fix(chats): resolve roots before cleanup and initial session loads

* test(chats): type runtime spies against actual SDK contracts

---------

Signed-off-by: Steffen Mächtel <info@steffen-maechtel.de>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-09-05 19:24:52 +03:00
Matt VisnovskyandBohdan Triapitsyn 1d6b15bc04 feat(chat): persist more composer input history with global or session scope; recallable with up/down arrow keys (#3035)
* feat(chat): persist input history

* feat(settings): configure input history scope

* fix(web): keep input history validation packaged

* fix(chat): preserve input history across tabs

* fix(settings): restore prompt history limit

* fix(settings): keep history deletion warning visible

* fix(i18n): restore Turkish Git empty state translations

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-09-05 19:19:03 +03:00
Muhammad ZaimandBohdan Triapitsyn 2bdd9af90a feat(ui): add composer enter-to-send toggle (#3178)
* feat(ui): add composer enter-to-send toggle and native hardware-keyboard detection

Replaces the settings-page "Enter sends with a keyboard attached" checkbox with
an EnterKeyToggle in the composer footer: plain Enter submits / Shift+Enter
inserts a newline when enabled, Shift+Enter submits / Enter inserts a newline
when disabled. Ctrl/Cmd+Enter always submits as the soft-keyboard fallback.
Persisted as enterToSend.

Adds the Android HardwareKeyboardPlugin: scans input devices for an alphabetic
physical keyboard (ignoring phantom key/sensor devices), re-answers on config
changes/foreground, and confirms attachment from real hardware key events.
MainActivity surfaces key events to it before the WebView consumes them. The
composer and draft layout start keyboard-aware instead of inferring one focus
late; ComposerEditor preserves Enter modifiers through CodeMirror's deferred
re-dispatch so the toggle can tell Shift/Ctrl+Enter from plain Enter.

Removes the settings search entry and i18n keys for the old checkbox.

* refactor(ui): keep enter-to-send branch focused

* fix(ui): preserve enter-toggle taps on touch

* fix(ui): preserve enter key defaults and move setting

* fix(ui): keep enter setting lint-clean

* fix(i18n): preserve current Turkish message parity

* fix(settings): persist enter-to-send preference

* fix(ui): clarify enter-to-send setting

* fix(ui): apply enter preference on desktop

* fix(ui): match enter setting focus mode default

* test(ui): cover enter key policy matrix

* fix(ui): harden deferred enter handling

* fix(chat): preserve untouched Enter policy and validate settings

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-09-05 19:07:28 +03:00
Ibrahim KhanandBohdan Triapitsyn 7ea24e3c50 fix(files): stop file viewer reload loop from sub-ms mtime jitter (#1489) (#2297)
* fix(files): guard file polling races

- Ignore sub-millisecond mtime jitter on a same-size file so an unchanged
  open file no longer loops through reload and flickers.
- Swap externally changed text content into the open editor in place
  instead of clearing the loaded path and showing the load spinner.
- Read content only after metadata changed, confirm it with a second
  read, and skip the swap when the file is unchanged, the buffer is
  dirty, or a newer local write landed.
- Keep the stat baseline unchanged when a poll cannot observe content so
  a failed read is retried rather than treated as unchanged.
- Fall back to a full reload for images, PDFs, binaries, and files above
  the content-poll byte limit, and when a poll returns binary content.
- Serialize polls and dispose the poller on unmount, file switch, and
  directory change.
- Add a `fresh` file read option that bypasses the content cache and the
  HTTP cache.

* fix(files): invalidate stale polls after diagram saves

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-09-05 18:31:35 +03:00
Andrea VandBohdan Triapitsyn a005215458 feat(chat): remember JSON output view mode (#3072)
* feat(chat): remember JSON output view mode

* perf(chat): skip JSON preference reads for text output

* fix(chat): persist JSON view in UI settings

* fix(chat): round-trip JSON view preference

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-09-05 18:27:38 +03:00
Ibrahim Khan 3a96c7ce64 fix(cli): warn when systemd user lingering is disabled (#1855) 2026-09-05 18:12:43 +03:00
Bohdan Triapitsyn 3d2bcf7ed6 chore(changelog): retire CHANGELOG.md as a dependency
The update dialog (server and desktop) now reads changelog/index.json and
renders title, intro and groups; the release workflow builds the GitHub
Release body and name from changelog/<version>.md; oc-dev, the issue-intake
agent, AGENTS.md and the changelog skill no longer point at the file.

CHANGELOG.md stays as a legacy copy for installs up to 1.22.1, which fetch
it for update notes. The generator refreshes it while it exists and never
recreates it, so deleting it after 2026-09-19 retires it for good.

Generated outputs hold released versions only, so editing unreleased.md
never makes them stale: agents write that file and nothing else, and
oc-dev create-release does the generation.

Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
2026-09-05 17:02:28 +03:00
Bohdan Triapitsyn 9eb8a92169 fix(cli): probe bun.exe under BUN_INSTALL on Windows
isBunInstalled() spawned <BUN_INSTALL>/bin/bun, which does not exist on
Windows where the installer ships bun.exe. spawnSync adds no extension to
an explicit path, so the probe failed and the CLI quietly started the
server under Node even with Bun installed. The probe now names bun.exe
on win32.

Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
2026-09-05 14:46:06 +03:00
Bohdan Triapitsyn f02dfbd4cb fix(web): tidy the XDG config root resolution
The web server read XDG_CONFIG_HOME through a redundant typeof guard on a
value that is already string|undefined, which tripped the anti-slop lint on
its own new line and diverged from the VS Code helper. Both now read the
same way. The VS Code provider test also still hard-coded ~/.config/opencode,
so it silently stopped asserting whenever XDG_CONFIG_HOME was set; it now
uses the shared constant, like the web test already does.
2026-09-05 14:45:20 +03:00
Bohdan Triapitsyn ac23e23db7 docs(providers): name the supported adapters in the web module doc
The web route accepts the OpenAI Chat Completions, OpenAI Responses and
Anthropic Messages adapters, but the module doc still described the write
path as OpenAI-compatible only. The VS Code doc already names all three.
2026-09-05 14:44:55 +03:00
Bohdan Triapitsyn b7a63af1b9 test(providers): cover legacy alias migration with several entries
The legacy `providers` block is deleted whole when its last entry migrates
to `provider`. Nothing covered the case where other legacy entries remain,
so a regression there would silently drop unrelated providers. Adds the
case to both the web server and VS Code parity suites.
2026-09-05 14:44:55 +03:00
Bohdan Triapitsyn 7a3244bab2 test(web): remove two timing races that hung CI for a minute each
Both failures showed up as "Test timed out in 30000ms" followed by "Hook
timed out in 30000ms" on loaded PR runners, in files nobody had touched.

relay host-client: the scripted client dialed its WebSocket before awaiting
its WebCrypto setup and only then attached the open/message listeners. On a
loaded runner the loopback socket opened while key generation was still
queued on the threadpool, the open event found no listener, no hello was
sent, and the client waited forever. The socket is now dialed after the key
material is ready, in the same tick as the listeners. The fake relay also
terminates leftover sockets on stop so a failure is reported once, not
twice.

walkthrough routes: each test slept 20 ms and assumed the request had
reached the route by then. The tests now wait until the service has been
asked to generate one more time than before the request, and afterEach
closes idle keep-alive connections so server.close() cannot hang on a
response a failed test never received.

Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
2026-09-05 12:40:07 +03:00
Bohdan Triapitsyn d323b51a0a fix(queue): deliver a queued slash command with context as a prompt
OpenCode's command route accepts file parts only. The server queue was
attaching captured context as text parts to POST /session/:id/command, so
a slash command queued with a comment, quote, or PR diff was rejected with
400 and retried forever.

A command queued without context still takes the command route with its
files. One queued with context now takes the prompt route the way the
composer does: the command's template is expanded with its arguments, a
skill keeps its text and gets the explicit skill-invocation instruction,
and the context rides along.

Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
2026-09-05 12:27:55 +03:00
Bohdan Triapitsyn 0b39efb0ae chore: bump @opencode-ai/sdk to 1.18.29 2026-09-05 12:11:20 +03:00
Bohdan Triapitsyn d37ce34a2e fix(terminal): reconcile project action executions across clients (#3362) 2026-09-05 12:05:39 +03:00
Matt Visnovsky 4e0eed717d fix: Project action terminal lifecycle (#3287)
* fix(terminal): make command sessions own action lifecycle

* fix(ui): reconcile project action terminal state

* feat(ui): show running project actions in terminal tabs

* feat(ui): run project actions from linked worktrees

* fix(ui): guard project action reconciliation

* fix(ui): scope project action preview fallback

* fix(ui): default project actions to worktrees

* fix(ui): reveal project action terminals

* fix(ui): retain terminal output after snapshot replay

* fix(ui): restore running action terminals on revisit
2026-09-05 12:04:36 +03:00
Bohdan Triapitsyn 58dfc789a2 fix(queue): clear the queue card after the last queued message is sent
When the server delivered the last item of a session's queue it deleted
the session entry and broadcast the update with an empty directory. The
UI keys its projection by directory, could not build the key, and dropped
the event, so the delivered message stayed in the "Queued messages" card
until the next full hydration.

The server now remembers a session's directory beyond the emptying of its
queue so that broadcast names it. The UI additionally treats an empty
session without a directory as "this session's queue is done" for every
projection keyed under that session id, which covers clients talking to
an older server.

Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
2026-09-05 11:21:39 +03:00
Bohdan Triapitsyn 7c9fdd1ee5 fix: complete goal resume and comment input follow-ups (#3361) 2026-09-05 10:53:34 +03:00
Leonidandalvins82 c5086e0250 fix(session-goal): bound length recovery after truncation (#3278)
Thanks to alvins82 for tracing the truncation failure and to bashrusakh for adding bounded recovery and error precedence. We will finish the explicit Resume behavior in the same batch.

Closes #3255

Co-authored-by: alvins82 <alvins82@gmail.com>
2026-09-05 10:46:18 +03:00
Bohdan Triapitsyn 260bfe666b fix: complete config path and review follow-ups (#3348) 2026-09-05 02:19:59 +03:00
tdoherty 0543b954c3 fix(web): honor XDG_CONFIG_HOME for OpenCode paths (#3305)
Closes #3304. Thanks for consolidating the global OpenCode paths. VS Code path parity and effective-path display follow-ups are maintainer-owned.
2026-09-05 02:14:26 +03:00
Bohdan Triapitsyn 74a065fd10 Merge pull request #3332 from hehuaiyu/fix/windows-bun-shim-resolution
fix(tooling): resolve Bun executable on Windows
2026-09-05 01:24:34 +03:00
Bohdan Triapitsyn d9f7bc199e Merge pull request #3329 from hehuaiyu/fix/preserve-custom-provider-model-metadata
fix(providers): preserve custom model metadata on edit
2026-09-05 01:24:23 +03:00
Bohdan Triapitsyn 05e2903815 release v1.22.1 2026-09-04 23:51:03 +03:00
Bohdan Triapitsyn f42988c9bf chore: bump @opencode-ai/sdk to 1.18.28 2026-09-04 23:43:54 +03:00
Bohdan Triapitsyn 34bf631157 feat(queue): queue a message with everything the composer had attached
Queueing captured only the text and files. Context chips (inline comments,
terminal selections, browser annotations, PR comments and checks, quotes,
linked issue/PR/Linear references, pending synthetic parts) stayed in the
composer and only left with the next manual send, so a queued message the
server delivered went out without them and the chips rode an unrelated
message later.

A queued message now carries what the composer would have sent: the text
with its agent mention stripped and file mentions resolved into
attachments, the attached context as structured parts, and the skill
instruction derived from the text. The server delivers those parts in the
composer's order, the VS Code auto-send does the same, and editing a queued
message puts the chips and linked references back. A failed queue restores
the composer completely. Snapshots and broadcasts omit the captured
context like attachment payloads; a take returns it.

Claude-Session: https://claude.ai/code/session_01HB9wdLQoZX2vfyDjwv6Rso
2026-09-04 22:56:27 +03:00
Bohdan Triapitsyn ce5c0c068e feat(mcp): reconnect failed MCP servers in managed OpenCode
OpenCode marks an MCP server failed when it does not come up at startup
or when a live connection drops, and never retries. A new managed plugin
reconnects those servers with a per-server backoff (1s doubling to a 30s
cap), wakes early on mcp.tools.changed, and stops on dispose. Only the
failed state is retried; disabled and auth states stay untouched.

The OPENCODE_CONFIG_CONTENT merge that agent-tool and system-prompt each
carried is now one shared helper so the three managed plugins compose.

Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
2026-09-04 22:56:27 +03:00
Bohdan Triapitsyn f160f3aac4 Merge remote-tracking branch 'origin/main' into fix/ui-thinking-effort-draft-project-rename 2026-09-04 18:58:08 +03:00
Iuliia Ivashko 0ef189f3c8 perf(sessions): archive a worktree's sessions through one server batch
Removing a worktree archived its sessions one SDK call at a time and
then re-rendered the whole sidebar once per streamed session.updated
echo. On a worktree with 121 sessions that meant 14.8s of main-thread
work, 121 requests, and 328 localStorage writes.

- Add POST /api/openchamber/sessions/archive: validates a batch (max
  500 ids, per-request archivedAt), archives sequentially, and reports
  partial failures instead of dropping the batch. VS Code serves no
  such route and answers 501; the shared UI then falls back to the
  per-session path.
- Plan batches from the sessions this client actually holds, live
  directory stores first, so worktree-only sessions still batch.
- Claim (id, archivedAt) pairs before the request and consume the
  matching session.updated echoes, so the server's own confirmations
  no longer fan out into 121 store publications. Runtime-scoped, TTL
  30s, released on response or fallback; non-matching updates pass.
- Make the managed-chats persistence a real trailing debounce instead
  of a 50ms throttle, so a burst of publications coalesces into one
  localStorage write.

Benchmark (121 sessions, production build, real Chrome): 14785ms ->
~1030ms, long tasks 100 -> 1, global store publications 236 -> 1,
persistence writes 328 -> 3.
2026-09-04 16:50:03 +03:00
Bohdan Triapitsyn 07fa83cc72 feat(queue): deliver queued messages from the server
Messages queued while a session is busy used to live in the browser tab and
were sent by that tab once the session went idle, so closing the tab (or
losing the connection) stranded them. The web server now owns the queue:
it persists to <data-dir>/message-queue.json, watches session.status on the
global event hub, re-verifies idleness against OpenCode before sending, and
delivers the head of the queue via prompt_async (or /command for slash
commands) with the model, agent, variant, attachments, and agent mention
captured at queue time. Failed sends stay queued and retry with backoff; a
user abort holds delivery briefly; every change is broadcast so all clients
see one queue.

The shared UI store becomes a projection of the server queue outside VS
Code (hydrate on connect, apply broadcasts, optimistic mutations settled on
the server's copy, one-time upload of locally queued messages from older
builds). Edit / send-now take the full message back from the server. A
UI-driven auto-review run asks the server to hold that session's queue.
VS Code keeps its local queue and foreground auto-send.

Claude-Session: https://claude.ai/code/session_01HB9wdLQoZX2vfyDjwv6Rso
2026-09-04 14:08:08 +03:00
Bohdan Triapitsyn 8afec51480 fix: switch new installs to OpenChamber themes
New installs now default to OpenChamber light and dark themes.
Existing Flexoki theme preferences continue to be preserved during migration.
2026-09-04 12:51:49 +03:00
hehuaiyu 0dafc512a6 fix(tooling): resolve Bun executable on Windows 2026-09-04 14:34:34 +08:00
hehuaiyu c6755768b8 fix(providers): preserve custom model metadata on edit 2026-09-04 13:11:55 +08:00
𝖎𝖚𝖑𝖎𝖎𝖆 0d8709a72e fix(worktrees): remove worktrees in the background (#3319)
* refactor(worktrees): fetch source once during creation

* fix(worktrees): remove worktrees in background

* fix(worktrees): show background removal progress

* fix(worktrees): name the worktree in removal toasts
2026-09-03 15:17:28 +03:00
James Tatum 5995802fe3 feat(worktrees): fetch remote source branch before worktree creation (#3296)
* feat(worktrees): fetch remote source branch before worktree creation

New worktrees based on a local branch that is behind its upstream now
fetch first and branch from the remote-tracking ref, so they are not
born stale. A global setting (on by default) in Settings > Behavior
controls this, and fetch failures toast a warning and fall back to
local state instead of blocking creation.

* fix(worktrees): wire fetch-source toggle to store and honor failed runtime fetches

The Behavior toggle only persisted the setting; the consumer reads the
config store at creation time, so a just-toggled-off setting kept
fetching until the next hydration. Update the store optimistically on
toggle and on page load, and roll it back when the save fails.

The VS Code runtime bridge resolves git fetches with { success: false }
instead of throwing, which the consumer read as success and silently
based the worktree on the stale remote ref. Treat any non-success
result as a failed fetch: warn and fall back to local state, matching
the web/desktop/mobile path.

* fix(worktrees): stop new remote-based worktrees from tracking the base branch

Creating a worktree with a remote start ref made git auto-track the
base branch (branch.autoSetupMerge), so with the new remote fetch every
behind-root worktree was born with upstream origin/<base> and plain
git push refused under push.default=simple.

The new branch's own upstream does not exist until its first push, and
the bootstrap deliberately refuses to write tracking config for refs
that were never fetched, so --set-upstream-to cannot re-point it.
Suppress the auto-track with --no-track on new-mode creation from a
remote ref: the branch ships with no upstream, matching the behavior
before the remote fetch until the first push sets it. Explicit
upstream keys now also win over the remote start ref inference,
aligning the create path with the validate path and the VS Code
runtime.

* fix(worktrees): keep the pre-create remote ref refresh soft

The client fetch and the server's pre-create fetchRemoteBranchRef both
refresh the same branch, and the second fetch throws on failure — so a
connection dropped between the two turned the promised soft fallback
into a rejected creation even though the remote-tracking ref was
already available locally.

The refresh is now best-effort when the ref exists locally (creation
proceeds from it) and still mandatory when the ref was never fetched,
preserving the materialization behavior for remote-only branches.
Applied to both the web server and the VS Code runtime.

* chore: ignore the .openchamber app runtime state directory
2026-09-03 14:03:32 +03:00
Bohdan Triapitsyn 85bf0a99de fix: identify OpenCode Go requests by session 2026-09-03 11:49:35 +03:00
Bohdan Triapitsyn 17fd46d4e5 fix: keep instance statuses between switcher opens
The switcher held reachability in component state and replaced the whole map at
the end of a probe run. It ran once per open before the config had loaded — with
Local as the only host — so that pass wiped every other instance's status and
each open started on "Checking", including for the instance the app was
connected to and actively talking to.

Statuses move to their own module: startup warms them so the switcher opens on
real values, a re-probe replaces each value in place as it lands rather than
blanking them first, and stale entries are dropped against the loaded config
instead of a partial host list. The connected instance never reads "Checking" —
the live connection already answers what the probe would ask.
2026-09-03 11:49:34 +03:00
Bohdan Triapitsyn a87f068256 fix: support dev server previews over relay 2026-09-03 11:49:34 +03:00
Bohdan Triapitsyn da6ab52a65 feat: add exe.dev usage tracking 2026-09-03 11:49:34 +03:00
Bohdan Triapitsyn dfb3c614c8 fix: authenticate terminal WebSockets synchronously on Bun 2026-09-03 11:49:34 +03:00
𝖎𝖚𝖑𝖎𝖎𝖆 e885afbe89 Improve branch switch safety and recent branch status (#3302)
* feat(ui): block branch switches on dirty trees

* feat(ui): show unpushed commits in git branch selector

* feat(ui): show recent branches in git selector

* fix(ui): persist recent branch status

* feat(ui): add mobile branch picker

* fix(ui): guard mobile branch checkout

* fix(i18n): restore Turkish git empty state labels

* feat(ui): flag dirty draft directories on the branch selector

Replaces the draft dirty-directory banner with an indicator on the branch
selector: a warning icon plus a hover tooltip that opens by itself for five
seconds when the dirty state first appears, then stays hover-only. The copy
states the situation and the options (commit or worktree) without prescribing
either.

* feat(ui): optional push in the dirty branch switch dialog

Commit-and-switch gains an opt-in "Push after commit" checkbox. When the
push fails the commit stands but the switch is cancelled with an explicit
toast, so the user is never moved off a branch without knowing its push did
not happen. Without the checkbox the toast states the commit is local only.

* fix(i18n): align dirty-directory copy across locales

* fix(a11y): name the unpushed-commit badge in the branch picker

The badge showed a bare arrow and number with no accessible name or tooltip.
Both the desktop recents list and the mobile picker now carry a localized
"N commits not pushed" title and aria-label.

* fix(mobile): push before switching dirty branches

Honor the dirty-switch dialog's push option on the mobile Changes surface.
A failed push leaves the new commit on its source branch, refreshes state, and
cancels checkout. Mobile branch selection now also shows the existing dirty
switch notice.
2026-09-03 01:42:50 +03:00
Bohdan Triapitsyn 40e4b6f857 fix: allow external host origin behind HTTP proxy
Accept browser https origins when TLS terminates before an HTTP proxy hop
Honor forwarded external host while rejecting mismatched origins
Add tests for proxy and host matching behavior
2026-09-01 14:38:44 +03:00