Commit Graph
3564 Commits
Author SHA1 Message Date
Bohdan Triapitsyn 214d9a916b Merge main and preserve custom provider protocols 2026-09-05 01:29:16 +03:00
Bohdan Triapitsyn f742775be4 feat: streamline release preparation and changelog checks
Exports the release package list so release prep stages the same versioned files as the bump script.
Validates that CHANGELOG.md contains the target release section before preparing a release.
Updates the suggested release steps to commit only release files and push the tag explicitly.
2026-09-05 01:26:10 +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 d6f0f2f23c feat(settings): retire the third-party plugin integrations
Settings → Integrations offered install cards for the Claude Code and
Cursor provider plugins. They are gone: the section, its plugin catalog,
its own i18n module and tests, the settings search entries, the page
keywords, and the two sprite icons only it used. The page now holds the
built-in GitHub and Linear cards, so it is hidden in VS Code where neither
applies; its title and description live in the settings dictionaries.

Docs follow: the Integrations page in every locale now documents GitHub
(pointing at its own page) and Linear in full, the GitHub page names
Settings → Integrations as the place to connect and covers linking an
issue or PR to a message, and the Providers pages no longer promise Claude
or Cursor subscriptions.

Claude-Session: https://claude.ai/code/session_01HB9wdLQoZX2vfyDjwv6Rso
2026-09-04 23:34:15 +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 caac24ac3d Merge pull request #3320 from openchamber/fix/ui-thinking-effort-draft-project-rename
Various session UI fixes: drafts, rename, worktree creation, thinking effort
2026-09-04 20:00:42 +03:00
Bohdan Triapitsyn f81bc26fe5 docs: clarify uncommitted changes warning text 2026-09-04 19:54:16 +03:00
Bohdan Triapitsyn b4a38061bc fix(ui): route answer worktrees from source session 2026-09-04 19:46:21 +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 IvashkoandClaude Opus 5 16f6f27027 fix(ui): stop the echo of a Default send from erasing the recorded Default
The send write was fixed, but the message that send echoes back arrives with
model metadata and no effort, and its model matches the one the send just
saved. That is exactly when `shouldPreserveManualModelOverride` declines to
protect the selection, so the history branch ran with no variant and recorded
"no choice" over the user's explicit `Default`. Picking Default, sending, then
switching agent and back still put the settings default in the picker.

A message carrying no effort is not evidence that the session has none. The
history branch now keeps whatever the session already recorded when the message
carries no variant, and a concrete historical effort still replaces it.

The behavior test ran the real guard through a mock that returned a fixed
answer, so the failing branch had no coverage. It now calls the real function
unless a test opts out, and the new case fails without this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZuVVgziiLjD81W5vaxdH2
2026-09-04 18:39:05 +03:00
Iuliia IvashkoandClaude Opus 5 9299e2a28d fix(ui): keep an explicit Default effort across the send that follows it
A send carries no effort both when nothing was chosen and when the user picked
"Default", so `sendMessage` could not tell the two apart and recorded the raw
value, which clears the entry. Picking "Default", sending, then switching agent
and back put the settings default back in the picker — the shape of the bug
this branch set out to fix.

`materializeOpenDraftSession` already read the live selection to keep that
distinction on the draft path. Both paths now share `resolveVariantToRecord`,
which prefers `currentVariantSelection.override` while the live selection still
describes the agent and model being sent to, and falls back to the sent value
when it does not.

Three tests go through the real `sendMessage`; two of them fail without this
change. The existing ones seeded the record directly, which is why the send
path was never covered.

Also documents the `oc.chatInput.lastDraftTarget` record in the owning sync
documentation: its three `target` values, what a pre-`target` record and a
removed project fall back to, and why a chat scratch directory is not a project
target.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZuVVgziiLjD81W5vaxdH2
2026-09-04 17:53:11 +03:00
Iuliia IvashkoandClaude Opus 5 4dfbb5bd1c test(ui): pin who may record an explicit Default effort
Nothing covered ModelControls, which is where the effort restore decides
between "the user chose Default" and "nothing was found". Three cases: a
concrete effort in the session history is restored, history without an effort
records no choice, and a preserved manual override keeps a recorded Default.
The middle one fails on the previous commit's parent — it recorded `null` and
then `undefined` for the same restore.

Also drops the `fetchAll` binding GitView stopped using when the post-bootstrap
refresh moved to a forced `fetchStatus`; it was failing `bun run lint:ui`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZuVVgziiLjD81W5vaxdH2
2026-09-04 17:41:35 +03:00
Iuliia IvashkoandClaude Opus 5 025cd3d46c fix(ui): let restore paths report no effort without pinning Default
`commitVariantSelectionForModel` turned every `undefined` into an explicit
`Default` (`null`), but it serves two kinds of caller. The picker means "the
user chose Default"; the history and manual-override restores mean "nothing
was found". Restoring a session therefore recorded a choice nobody made, and
`resolveModelVariantSelection` collapsed that `null` back to `undefined`, so
the next restore recorded it again. Because an explicit `Default` outranks the
agent and settings defaults by design, the session latched onto `Default` and
the concrete effort its own history carried could not come back.

Move the decision to the callers: the four picker paths pass `variant ?? null`,
the restore paths pass their result through, and the resolver returns the
selection store's three states instead of two. The follow-up write in the
history restore goes with it — the apply above it already recorded the same
agent and model, and a second write could only disagree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZuVVgziiLjD81W5vaxdH2
2026-09-04 17:32:40 +03:00
Iuliia Ivashko cb3bc3bd1b merge: bring bulk session archiving and background worktree removal into the thinking-effort branch 2026-09-04 16:54:28 +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
Iuliia Ivashko 94c90a16b6 fix(ui): reconcile git state after worktree changes 2026-09-04 16:41:39 +03:00
Bohdan Triapitsyn 8a58054551 docs: refresh README wording and product messaging 2026-09-04 14:22:49 +03:00
Iuliia Ivashko bde6fed8a2 Revert "revert(ui): restore inherited semantics for Default thinking effort"
This reverts commit 35875e818c.
2026-09-04 14:12:56 +03:00
Iuliia Ivashko 3cd31a090a Revert "fix(ui): stop pinning inherited effort as a session choice on send"
This reverts commit 0a47b306d6.
2026-09-04 14:12:56 +03:00
Iuliia Ivashko 0b803468a3 Revert "fix(ui): stop restoring inherited effort from history as an explicit choice"
This reverts commit 7519167031.
2026-09-04 14:12:56 +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 63df14e985 fix(vscode): support custom provider protocols 2026-09-04 13:23:39 +08:00
hehuaiyu c6755768b8 fix(providers): preserve custom model metadata on edit 2026-09-04 13:11:55 +08:00
Iuliia Ivashko 74e8d522b9 fix(ui): hold the changed-files gate until post-bootstrap status lands
Refresh hints fired while setup commands run can cache a mid-creation
dirty snapshot; lifting the gate the moment bootstrap settles flashed
that stale snapshot until the forced fetch resolved. The gate now stays
down until the post-bootstrap status fetch completes.
2026-09-04 07:10:23 +03:00
Iuliia Ivashko 7519167031 fix(ui): stop restoring inherited effort from history as an explicit choice
Message metadata records the effective effort, inherited defaults
included. The history-restore effect re-applied it as an explicit
override and re-pinned it per session one render after every send, so
the picker still jumped from Default to the settings default despite the
send-time fix. Only an effort deviating from what the model would
inherit is restored as a choice; the preserve-manual-override branch
follows the same rule.
2026-09-04 07:10:23 +03:00
Iuliia Ivashko f2853490de fix(ui): open a chat draft when a chat scratch directory is forwarded
'New session in the current directory' callers (shortcut, menu, composer)
forward the current session's directory even when that session is a
chat. Its managed scratch directory names no project, but it counted as
an explicit project target, so a plus pressed inside a chat session
opened a project draft. Chat scratch overrides now resolve to a chat
draft.
2026-09-04 07:10:23 +03:00
Iuliia Ivashko acdf0c2e1f fix(ui): hide transient dirty state while a worktree bootstraps
A freshly created worktree transiently looks dirty until its setup
commands and initial git reset finish. The work status panel showed
those files as changes on the branch and the draft's worktree dropdown
flashed its dirty warning, and both then froze on that state because
nothing refetched after bootstrap. Make the bootstrap state (the
existing authority on unfinished creation) subscribable, suppress the
dirty probe and the changed-files row while it is pending, and force one
status fetch when it settles so the lifted gate shows the reset tree.
2026-09-04 02:35:56 +03:00
Iuliia Ivashko 0a47b306d6 fix(ui): stop pinning inherited effort as a session choice on send
Sending recorded the effective variant - inherited settings/agent
defaults included - as the session's explicit per-session choice, so the
picker jumped from Default to the inherited effort right after the send
and the session stopped following later default changes. Record only an
explicit picker override when the send reflects the live selection;
sends carrying a captured configuration for another session keep their
captured variant.
2026-09-04 02:16:21 +03:00
Iuliia Ivashko bbb97f7e66 fix(ui): keep New Worktree form state when worktree topology changes
The reset-on-open effect also depended on generateUniqueSlug, which is
derived from the available-worktree list, so a worktree appearing or
disappearing while the dialog was open wiped the form (branch name,
linked issue). Initialize once per open via a ref guard, pinned by a
behavior test. Also tightens CreateWorktreeArgs/GitHub selection typing
in place of type assertions.
2026-09-04 02:10:22 +03:00
Iuliia Ivashko 35875e818c revert(ui): restore inherited semantics for Default thinking effort
The explicit-Default behavior from 77d756aeb (Default = send no effort)
was based on a misdiagnosis: the tester did not know a default effort was
configured in settings, so the settings default applying under Default is
the intended behavior, not a bug. Restores the pre-fix effort resolution,
tests, and documentation.
2026-09-04 02:10:17 +03:00
Iuliia Ivashko 77d756aebb fix(ui): preserve draft and effort selections 2026-09-03 17:32:24 +03: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
bot-hermes e67987533e ci: add deploy workflow for custom branch (OpenChamber-CUSTOM) 2026-09-03 11:36:34 +00:00
bot-hermes 057e451068 Merge pull request 'fix: remove stray v1.22.0 merge marker in ContextPanelRail.tsx' (#6) from fix/v1.22.0-marker into custom 2026-09-03 07:19:42 -04:00
bot-hermes 12e36f9f7f fix(ui): remove stray v1.22.0 merge-conflict marker from ContextPanelRail.tsx 2026-09-03 11:17:49 +00: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
mbatchelder 4042fc53a6 Merge remote-tracking branch 'origin/release/v1.22.0' into custom
# Conflicts:
#	bun.lock
#	packages/ui/src/components/chat/message/normalizeUserDisplayParts.ts
#	packages/ui/src/components/chat/work-status/WorkStatusPrimaryGroup.tsx
#	packages/ui/src/components/layout/ContextPanelRail.tsx
#	packages/ui/src/hooks/useKeyboardShortcuts.ts
#	packages/ui/src/lib/i18n/messages/de.ts
#	packages/ui/src/lib/surfaces/DOCUMENTATION.md
#	packages/web/server/lib/fs/routes.test.js
2026-09-03 06:10:05 -04:00
mbatchelder d13bd5e1f7 Merge remote-tracking branch 'origin/release/v1.21.1' into custom
# Conflicts:
#	packages/ui/src/components/chat/message/normalizeUserDisplayParts.ts
#	packages/ui/src/hooks/useKeyboardShortcuts.ts
#	packages/ui/src/lib/i18n/messages/de.ts
2026-09-03 06:07:44 -04:00
Bohdan Triapitsyn 40f5ed73c9 Merge pull request #3274 from kydorn/fix/theme-save-echo-clobber
fix(ui): stop settings save echoes from clobbering theme preferences
2026-09-03 13:01:27 +03:00
Bohdan Triapitsyn 2a0672e3a6 fix(ui): preserve VS Code themes during settings broadcasts 2026-09-03 12:41:59 +03:00
Bohdan Triapitsyn 85bf0a99de fix: identify OpenCode Go requests by session 2026-09-03 11:49:35 +03:00
Bohdan Triapitsyn 76128b615f feat: add Fixel font support
Adds Fixel Text as a new UI font option
Allows font sources to load from direct URLs
Keeps existing fontsource-based loading working
2026-09-03 11:49:35 +03:00
Bohdan Triapitsyn 0107abb32d fix: order concurrent instance probes and honour the probe budget
Startup warm-up, opening the switcher and the refresh button can all probe at
once, and a relay host working through tunnel retries takes an order of
magnitude longer than a loopback one — so a slow older run landed last and
replaced a fresh "ok" with its own stale "unreachable". Each host now records
which run owns its status; a status from the switch flow outranks any probe
still running for it.

Each relay attempt is also capped by what is left of the 15s budget rather than
the full per-request timeout, so an attempt started just under the deadline can
no longer run the whole 8s past it.
2026-09-03 11:49:35 +03:00
Bohdan Triapitsyn f0ced9d361 fix: reject instance-served responses that outlive their instance
MCP status and skills were cleared on a switch but their in-flight requests
were not, so a response for the previous instance could still write itself over
the new one's — the same race the quota store already guards. Both now carry a
generation.

Usage also claimed an instance as loaded before it had answered, so a load that
failed on a cold or briefly unreachable instance was never attempted again, and
the previous instance's display mode and provider selection survived a switch —
which decided what the new instance was even asked for.
2026-09-03 11:49:34 +03:00