A comment written with no session tab open opens one, and that panel knows
its directory long before it has loaded the session list and selected its
session. The webview filed the draft on the first snapshot that had a
directory, with the session key falling back to "draft" because no session
was current yet. The panel's composer reads the session's key, so the chip
never appeared, while the editor thread saw the draft in the store snapshot
and reported it attached.
A session panel now stamps its session on every comment it delivers, and
the webview waits until it actually shows that session before filing. The
sidebar files on its current session or an open new-session draft, and no
longer falls back to "draft" merely because nothing is selected yet. The
resolver is a pure module with tests.
Claude-Session: https://claude.ai/code/session_01VqV56Hez25hTxXH4ipJfzH
* feat(chat): render code comments as cards instead of fenced text
* fix(vscode): route Add Comment to the active session editor panel
* fix(chat): persist queued inline comments and tighten file-chip path matching
* feat(vscode): comment on code from the editor
* fix(chat): keep attached context in the message and broadcast comment removal
* fix(vscode): hold every pending comment and gate both entry points on the workspace
* fix(vscode): let only the owning surface decide its comment threads
* fix(vscode): drop a comment removed while its delivery was still in flight
* test(vscode): cover the in-flight comment removal guard
* test(vscode): cover comment removal reaching every chat surface
* fix(vscode): give up on a comment the chat never confirmed holding
* fix(vscode): retract a comment everywhere before reporting it discarded
* fix(chat): preserve queued comment cards
* fix: preserve inline comment context across send paths
* fix(chat): preserve command routing with context
* fix(chat): keep unavailable actions on normal send path
---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
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.
* 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.
- discard an in-flight discovery when the runtime switches, mirroring the
isRequestCurrent pattern, so a late completion cannot repopulate the
cleared map and suppress a fresh scan
- treat a 501 from /api/fs/git-dirs as an explicit 'unsupported' marker
instead of a generic failure; the VS Code webview now answers the route
with unsupportedWebRouteResponse so non-repo roots show the honest
not-a-repository state without a futile Retry
Opens the chat view before focusing the input
Routes a new webview command to focus the composer
Refocuses the input after adding a VS Code selection attachment
OpenCode Go now reads quota usage with a bearer API key from OpenCode auth.json
Removes the old workspace ID and browser cookie credential flow
Deletes legacy OpenCode Go credential files during upgrade
Allow Settings → Providers to define custom providers (id, name, base URL,
API key, models, headers) without code changes. Persist config via OpenCode
layers, store keys through auth.set, and keep web/VS Code parity.
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
Attachment pickers now share an allowlist for supported file types
Local attachments are normalized to consistent MIME types before upload
VS Code file picker now respects extension filters and larger files are allowed
Improve OpenChamber responsiveness under large session workloads while fixing
cache, synchronization, and persistence correctness across runtimes, projects,
directories, and worktrees.
- prioritize selected and visible sessions during bootstrap and defer
non-critical enrichment work
- reduce redundant message loading, event processing, store publication, and
hidden sidebar work
- prevent stale session and message requests from overwriting newer
authoritative state
- preserve existing data when authoritative fetches fail instead of treating
failures as successful empty responses
- scope session materialization, messages, drafts, queues, todos, pins,
permissions, folders, tabs, Git state, and pull request data by runtime and
directory identity
- harden runtime switching, reconnect, cleanup, mutation reconciliation, and
persisted-state ordering
- preserve live subagent Task linkage when metadata arrives after an older
message request or while streaming parts are suspended
- coalesce overlapping tail refreshes without losing newer refresh demand
- improve cold-session loading by moving deferrable work out of the critical
bootstrap path
- isolate URL authentication, mobile credentials, native secrets, and other
runtime-owned state across endpoint changes
- bound long-lived caches and remove avoidable allocations from event and
rendering hot paths
- limit virtualization to archive collections where it improves rendering
without disrupting active sidebar layout
- stabilize session folders, pin ordering, expanded state, and persisted
sidebar behavior
- open skill files through the same secure editor and outside-workspace grant
flow used by file navigation, including worktree sessions
- expand regression coverage for stale completions, runtime collisions,
reconnect behavior, persistence races, authoritative empty results, and
subagent refresh ordering
- document the updated synchronization, cache ownership, performance, and
runtime-isolation invariants
Add a root-session action that creates a generated worktree from the session directory's current branch, transfers uncommitted changes, and moves the parent session plus its descendants through OpenCode's control-plane API.
Reuse existing project/worktree topology and quick-create behavior, keep the UI non-blocking, reconcile live and global session state across directories, and roll back partial moves and failed worktree creation safely.
Split worktree bootstrap readiness into directory-created, git-ready, and setup-ready phases across web and VS Code. Session moves wait for Git readiness while existing setup-aware flows continue waiting for full setup completion, and worktree removal is serialized with active bootstrap tasks.
Expose the move only for idle root sessions, show localized progress and explanatory tooltips in the sidebar, and keep pending/ready worktree metadata synchronized with authoritative session attachments to avoid stale setup indicators.
Add coverage for control-plane payloads, session-state migration, bootstrap phase ordering and compatibility, removal races, progress metadata, and fast-ready attachment races.
Replace the legacy terminal flow with a shared authenticated WebSocket
runtime used across web, desktop, relay, and mobile surfaces.
- introduce the v3 terminal protocol with scoped attachments, snapshots,
ordered output, bounded replay history, reconnects, and explicit lifecycle
- harden PTY creation, restart, resize, close, force-kill, idle cleanup,
shell selection, login mode, environment sanitization, and appearance sync
- add runtime-aware terminal APIs with relay authentication and Electron parity
- add a fullscreen mobile terminal workspace with touch scrolling,
long-press selection, safe-area controls, quick keys, and Ctrl/Alt input
- add terminal selection attachments, preview detection, project actions,
shell settings, and localized UI
- harden Ghostty rendering, resize recovery, Unicode handling, block
characters, line height, and stale-row behavior
- remove the obsolete terminal SSE path and update reverse-proxy guidance
- expand terminal runtime, transport, input, selection, and store coverage
- avoid duplicate web builds when preparing mobile assets in root CI builds
Add a VS Code-specific foreground permission responder while preserving the server-owned implementation for web, desktop, and mobile runtimes.
Persist the authoritative VS Code policy in extension global state and expose matching GET/PUT bridge routes. Broadcast policy updates to the sidebar, session editor panels, and agent manager so every active webview observes the same explicit per-session policy.
Resolve missing child-session lineage through OpenCode, honor nearest explicit ancestor overrides, deduplicate concurrent requests, retry transient replies, and reconcile pending permissions after enablement, bootstrap, and reconnect. Treat resolved requests as handled and route notification suppression through the same responder outcome.
Keep post-toggle reconciliation failures non-fatal after policy persistence and fail closed when lineage or replies cannot be confirmed. Document that auto-accept intentionally cannot run while every OpenChamber webview is closed or suspended.
Use authoritative session.idle and session.error events for notifications
while retaining legacy message.updated handling for compatibility.
Classify sessions through targeted, directory-aware session lookups instead
of fetching the full session list. Cache confirmed root and parent session
relationships without treating failed lookups as root sessions.
Honor subagent completion settings and templates across the server-driven
web, desktop, and mobile paths, and bring the VS Code webview notification
policy to feature parity.
Use freshly synchronized VS Code settings, retry failed settings syncs,
extract session error messages, and deduplicate authoritative and legacy
completion and error events.
Complete rebuild of voice input on a server-authoritative streaming
architecture, replacing the legacy Web Speech / whole-blob / WASM engines
and the dead voice-agent layer (~4k lines removed).
Speech-to-text (dictation):
- Client streams 16 kHz mono PCM16 chunks over /api/dictation/ws with
seq/ack ordering; buffered audio is retained and replayed on reconnect
- Server transcribes and streams live partial transcripts back;
segments auto-commit every ~15s with silence suppression and adaptive
finalization timeouts
- Local provider (default, zero config): sherpa-onnx models in a forked
worker process — auto-download with progress, staged extraction with
verification, corrupt-model auto-recovery, idle shutdown after 5 min
- Model catalog with settings picker (accuracy/speed ratings, sizes,
download/delete): Parakeet TDT v2 (English) and v3 (25 European
languages, auto-detected), Whisper base and tiny (multilingual, light)
- OpenAI-compatible provider for any Whisper endpoint
- Composer overlay with live transcript, volume meter, timer, and
cancel / insert / insert-and-send actions; failed transcriptions keep
their audio for retry or accepting the partial text as-is
- Configurable keyboard shortcut (default mod+alt+v) toggles dictation;
Enter confirms and Escape cancels while recording
- Overlay is pixel-aligned with the composer (measured footer height,
matching paddings/typography/gaps) — no layout shift when toggling
Text-to-speech:
- Local Kokoro provider (English, 11 voices) synthesized in the same
worker via /api/dictation/tts/speak, managed by the shared model
pipeline; sentence-pipelined playback keeps time-to-first-audio at
~1 sentence regardless of message length, and stop cancels in-flight
synthesis
- Sanitizer keeps inline-code content (strips backticks only), reads
interword slashes aloud, and removes only absolute file paths
Settings:
- Voice page unified: a single read-aloud toggle owns all playback
options (the confusing "Enable Voice Mode" is gone); a new "Enable
voice input" toggle (default on, persisted to settings.json) hides
the composer mic entirely when disabled
Mobile and transport:
- iOS/Android microphone permissions added (dictation was previously
impossible on mobile)
- Fixed Android WebSocket upgrades: the Capacitor WebView origin
(https://localhost) was missing from the packaged-client allowlist,
403-ing every WS connection — root cause of the old mobile SSE lock,
which is now removed for all transports
Security and conventions:
- All HTTP routes sit behind the global /api auth gate; the WS upgrade
explicitly validates the UI session and origin, with oc_url_token
narrowly allowlisted and covered by tests; the dictation socket mints
a fresh URL token before connecting
- Routes register before the generic OpenCode proxy; the client goes
through runtimeFetch/getRuntimeUrlResolver, and runtime switches
reset the dictation socket
- VS Code deliberately reports dictation as unavailable (no server
process in that runtime)
CI: workflow Node bumped 20 -> 22 to match the repo engines and fix
better-sqlite3 installs broken by node-gyp@latest on Node 20.
New dependency: sherpa-onnx-node (prebuilt N-API; macOS/Linux x64+arm64,
Windows x64 — Windows-on-ARM falls back to the OpenAI-compatible provider)
* fix: handle non-ISO-8859-1 characters in fetch headers and Content-Disposition
Browser Headers API rejects characters above U+00FF. The x-opencode-directory header carries raw filesystem paths, which breaks when paths contain Chinese/CJK characters. Also fixes Content-Disposition for non-ASCII filenames per RFC 5987.
* refactor: export header sanitization helpers, deduplicate, add tests
Export isLatin1Safe and sanitizeHeadersForBrowser from runtime-fetch.ts so VS Code webview can import them instead of duplicating the logic. Add tests: isLatin1Safe boundary checks, sanitizeHeadersForBrowser encoding/deduplication, runtimeFetch round-trip encode/decode, and Content-Disposition RFC 5987 output for both ASCII and non-ASCII filenames.
* fix: mark encoded directory headers
---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Desktop notifications no longer duplicate when native delivery succeeds
Reasoning chain-of-thought is excluded from notification body text
Untyped message parts are ignored in notification text extraction
* perf(vscode): gate API readiness and coalesce duplicate startup reads
Bring the VS Code bridge runtime to parity with the web startup
optimizations (PR #1650), which were web/desktop-only.
waitForApiUrl now hands out the OpenCode API URL only once the manager
reports 'connected', instead of as soon as getApiUrl() exposes
server.url. The URL is available the moment the process is spawned —
before waitForReady confirms it can serve and during a workspace-switch
restart (stale port) — so URL-presence alone let the bridge forward to a
not-yet-ready OpenCode and surface 502s. Gating on connected status
mirrors the web proxy's isOpenCodeReady hold. Also fail fast on 'error'
status so a missing CLI doesn't burn the full 30s timeout.
Coalesce concurrent identical GET reads (config/path/agents/agent/
project/command) at the bridge proxy so the single OpenCode process
serves them once. On cold start the webview's sync bootstrap and config
store fire these reads in parallel with no shared dedup; this is the
extension-host analog of the runtimeFetch coalescer. Shared reads carry
no AbortController so one caller's abort can't strand the others, and the
entry clears as soon as it settles (never serves stale).
* perf(vscode): fade the startup splash once mounted + connected, not on live fetch
The webview's initial-loading overlay held until a successful live
/api/config/providers AND /api/agent fetch completed. After the cache
hydration work those live reads are the slowest cold-start tail — the UI
underneath already paints pickers and the sidebar from cache and refreshes
in the background — so gating the splash on them kept it spinning long
after the app was usable.
Fade the overlay as soon as the UI is mounted and OpenCode is connected.
Per-widget loaders convey any remaining background refresh, matching how
web/desktop (which have no such splash) already behave. Removes the now
-obsolete bootstrapProvidersReady/AgentsReady/Failed tracking and
recordBootstrapFetch. Connection error/disconnected splash messages are
unchanged.
* fix(vscode): include captured OpenCode output in spawn-timeout error
When the managed OpenCode server fails to emit its 'listening' line within
the start timeout, the error discarded everything the process printed to
stdout/stderr — so the status report showed a bare 'Timeout waiting for
server to start' with no clue whether the process hung, crashed silently,
or printed a config/auth error. The exit path already includes the output;
the timeout path now does too (or notes that nothing was printed).
* feat(vscode): workspace-grouped session list with working folders, pinning, and archived toggle
Replace the flat multi-workspace session list with the grouped project view,
using each open VS Code workspace folder as a header (no per-worktree
subgroups). This restores native folder and pin support, which the flat list
silently dropped, and fixes the clipped left padding on session rows.
- Group sessions strictly by open workspace; funnel all non-archived sessions
into the workspace's group so they no longer fall into the archived bucket.
- Keep the project/group/folder + buttons but make them open a draft in the
correct workspace and navigate to chat; hide the project actions (...) menu,
which isn't relevant in VS Code.
- Force the minimal single-line row layout (the second metadata row is
redundant under workspace headers) and drop the per-row tooltip.
- Add a show/hide archived toggle next to the archive-all control, since the
VS Code header has no display-mode menu.
- Size the hover action reveal so the timestamp clears the row buttons.
Add per-hunk staging, unstaging, and discarding to the Changes diff
view, so a single change region inside a file can be acted on in
isolation instead of forcing whole-file stage/revert. The change is
wired end-to-end across the web server, the shared UI runtime API
contract, and the VS Code extension, with Electron inheriting the web
path unchanged (it boots the server in-process).
Server
------
- New `applyHunk(directory, filePath, { patch, action })` in
packages/web/server/lib/git/service.js. It resolves the repository
context and validates the file path with the same helpers used by
stageFiles/unstageFiles (resolveGitFileContext +
validateRepositoryFilePaths), then writes the single-hunk patch to a
temporary file in the OS temp dir (never inside the repo, so it
cannot show up as an untracked file) and runs `git apply` with flags
chosen per action:
stage -> git apply --cached (working tree -> index)
unstage -> git apply --cached --reverse (index -> working tree)
discard -> git apply --reverse (revert in working tree)
A `git apply --check` runs first with the same flags, so a stale
hunk that no longer applies fails with a clear "Hunk no longer
applies - refresh and try again" message instead of leaving a
partial mutation. The patch's target path is parsed and must match
the requested file (with /dev/null tolerated for new/deleted files),
preventing a patch from silently targeting a different path. The
whole operation runs inside withGitIndexMutationQueue to avoid
racing with concurrent stage/unstage. The temp file is removed in a
finally block.
- New `POST /api/git/apply-hunk` route in routes.js, registered
alongside stage/unstage. Validates directory, path, non-empty patch,
and action before delegating.
- DOCUMENTATION.md updated with the new service entry.
Patch extraction
----------------
- packages/ui/src/lib/diff/patchFileDiff.ts gains
splitPatchIntoHunks(patch) and extractHunkPatch(patch, hunkIndex).
They keep the original file header (diff --git / index / --- / +++)
and emit exactly one @@ hunk per standalone patch, which is what
`git apply` expects. Each emitted patch is guaranteed to end with a
trailing newline (without it git apply reports "corrupt patch").
Runtime API contract
--------------------
- GitAPI (packages/ui/src/lib/api/types.ts) gains optional
stageGitHunk / unstageGitHunk / revertGitHunk, matching the
stageGitFiles? / unstageGitFiles? precedent so runtimes that do not
support it degrade gracefully.
- gitApi.ts delegates to the registered runtime git API, falling back
to gitApiHttp, exactly like the existing whole-file helpers.
- gitApiHttp.ts posts to /api/git/apply-hunk.
- Web runtime composes the three methods in packages/web/src/api/git.ts.
VS Code parity
--------------
- packages/vscode/src/gitService.ts adds applyGitHunk(), implemented
natively with the existing execGit helper + a temp patch file +
`git apply` (--cached / --cached --reverse / --reverse), mirroring
the server's --check-first safety and temp-file cleanup.
- bridge-git-runtime.ts handles the new api:git/apply-hunk bridge
message; webview/api/git.ts sends it. VS Code users get identical
stage/unstage/discard-hunk behavior.
UI
--
- New DiffHunkActions component renders a compact per-hunk strip
above each expanded file diff in the Changes view. Each hunk chip
shows its +additions / -deletions counts and offers:
working scope -> Stage + Discard
staged scope -> Unstage
Clicking extracts that hunk's standalone patch via
extractHunkPatch(patch, hunkIndex) and calls the runtime git API.
Because the chip index comes directly from fileDiff.hunks[] and the
patch is sliced in the same order, the hunk the user sees is always
the hunk that gets applied. While any action is in flight all buttons
disable to prevent conflicting concurrent mutations; the per-hunk
spinner reflects in-flight state.
- DiffView wires DiffHunkActions into InlineDiffViewer (text diffs
only; binary/image and full-file-content modes are excluded since
they have no patch). MultiFileDiffEntry passes directory/staged
through and handles onHunkApplied by bumping the diff reload nonce
(so the file's diff re-fetches and the affected hunk disappears)
and refreshing git status (so file counts and the staged/changed
scope update). Hunk actions are therefore available wherever the
default patch-context diff is shown.
i18n
----
- 10 new keys (diffView.hunk.*) added to all 9 locales (en, es, fr,
ko, pl, pt-BR, uk, zh-CN, zh-TW), including stage/unstage/discard
labels, tooltips with the hunk index, a stale-hunk error message,
and an unsupported-runtime fallback.
Tests
-----
- packages/ui/src/lib/diff/patchFileDiff.test.ts covers
splitHunks/extractHunkPatch: multi-hunk split, header preservation,
single-hunk and empty patches, out-of-range indices.
- service.test.js adds an applyHunk suite that builds real temp repos
with two separate hunks and verifies: staging one hunk leaves the
other unstaged, discarding reverts only the targeted hunk in the
working tree, unstaging removes only one hunk from the index, and a
retargeted patch (different file path) is rejected. Also covers
invalid-action / missing-hunk-header validation.
- packages/web/src/api/git.test.ts mock completed with the new methods
(and previously-missing exports that prevented the test from
loading) and asserts the three hunk methods are exposed.
- routes.test.js continues to pass under bun.
CHANGELOG updated under [Unreleased].
Adds `gh` CLI as a GitHub credential fallback for users who already have
`gh auth login` configured locally. OpenChamber-owned OAuth credentials
remain the primary source of truth; the `gh` token is only used when no
stored OpenChamber GitHub access token exists and the fallback is not
disabled.
The fallback is implemented as a credential provider only: GitHub features
continue to use the existing Octokit/GitHub API paths for issues, pull
requests, checks, merges, and related operations. The PR does not replace
those endpoints with `gh issue` or `gh pr` CLI commands.
Server changes:
- Add `gh-cli-credential.js` to read `gh auth token` with a bounded timeout.
- Cache the `gh` token lookup for 30 seconds, including negative results,
to avoid repeated subprocess spawning on status/polling paths.
- Hide the subprocess window on Windows via `windowsHide: true`.
- Clear the gh CLI token cache when the fallback setting changes.
- Update `getOctokitOrNull()` to prefer stored OpenChamber OAuth tokens and
fall back to the `gh` token only when enabled.
- Add `ghCliDisabled` persistence in the existing settings file with atomic
writes and `0o600` file permissions.
- Add `POST /api/github/auth/gh-cli` to enable or disable the fallback.
- Extend `/api/github/auth/status` with `ghCli` metadata: availability,
disabled state, active state, and active user when applicable.
UI/runtime changes:
- Extend `GitHubAuthStatus` and `GitHubAPI` with gh CLI fallback metadata
and toggle support.
- Add web RuntimeAPI support for toggling the gh CLI fallback through
`runtimeFetch`, preserving active runtime/remote target behavior.
- Add deterministic VS Code unsupported handling for the gh CLI toggle.
- Update GitHub Settings to show gh CLI availability and active status.
- When gh CLI is the active auth source, show it in the connected account
card and offer Disable instead of Disconnect.
- Keep Add Account available so users can still connect an OpenChamber OAuth
account, which then takes priority over gh CLI.
- Add localized gh CLI settings strings across supported settings locales.
Fixes addressed during review:
- Removed unreachable UI branches in the inactive gh CLI card.
- Avoided duplicate and repeated `gh auth token` subprocess calls.
- Hardened settings file permissions for the new persisted flag.
- Routed the gh CLI toggle through the RuntimeAPI/runtimeFetch path instead
of direct browser `fetch`.
- Added targeted tests for hidden subprocess options and negative-result
cache behavior.
- Fixed a VS Code webview Response body typing issue that blocked type-check.
Add proper VS Code multi-root workspace support. New sessions now start in the workspace folder the user chooses instead of always using the first folder.
The VS Code sidebar now shows one shared flat session list for the currently opened workspace folders, keeps that list synced when folders are added or removed, and excludes sessions from worktrees unless that worktree is opened as a workspace folder.
Also keep the OpenCode server process independent from a specific workspace folder so changing the selected folder does not restart or interrupt existing sessions.
* feat: add French locale runtime
Add French to OpenChamber's shared i18n runtime, dictionaries, and parity tests so the existing language picker can load a complete fr locale across shared UI surfaces.
* fix: localize shared UI formatting
Remove remaining shared UI locale hardcodings so dates, numbers, and first-party helper copy follow the active app locale instead of leaking English on French surfaces.
* feat: localize VS Code French surfaces
Localize VS Code bootstrap, native runtime messages, panel titles, and manifest contribution strings so French users get consistent first-party copy across the extension experience.
* fix: TASK-2026-05-30-008 correct French review findings
Fix broken French relative-time and weekday strings reported on PR #1482 and restore proper import order in quota utils without broadening scope.
* fix: TASK-2026-05-30-008 address final PR review comments
Capture the localized More Info label once in the VS Code CLI-missing flow and replace the remaining inline French-only utility strings with dictionary-driven copy plus required locale keys.
* fix: TASK-2026-05-30-008 normalize French glossary
Correct glossary-level French terminology on the live PR branch, keeping canonical technical terms like PR, worktree, stash, HEAD, Mermaid, Markdown, remote, and session while replacing misleading literal translations.
* fix: TASK-2026-05-30-008 refine French terminology pass
Clean up remaining glossary mistakes on the French PR branch, especially around Mermaid, Markdown, PR, worktree, stash, branch, remote, and commit terminology, while keeping behavior unchanged.
* fix: TASK-2026-05-30-008 clean remaining French false friends
Correct the SOCKS5 mistranslation and a final small set of obvious false-friend technical nouns on the French branch without changing behavior.
* fix: TASK-2026-05-30-008 correct French glossary terms
Replace remaining false-friend translations in the French UI dictionaries and normalize technical labels for the French PR branch.
* fix: TASK-2026-05-30-008 remove remaining French Mermaid false friend
Replace the last confirmed Sirène translation with Mermaid and re-run the requested blacklist and build verification on the PR branch.
* fix: TASK-2026-05-30-008 enforce French glossary policy
Keep skill/PR/worktree/remote terminology developer-credible in French and remove remaining machine-translated Git and settings copy.
* fix: TASK-2026-05-30-008 keep prompt terminology in French
Replace remaining technical invite translations with prompt wording across scheduled tasks, multi-run, prompt templates, and Magic Prompts.
* fix: TASK-2026-05-30-008 finalize French terminology cleanup
Polish remaining worktree/remote wording, remove visible metadata leakage, and correct final Git and settings labels on the French PR branch.
* fix: TASK-2026-05-30-008 polish final French strings
Correct the last aria-like artifacts and awkward worktree/remote/GitHub URL phrasing in the French dictionaries.
* fix: TASK-2026-05-30-008 normalize final French glossary framing
Tighten the last worktree/remote/checkout wording and fix remaining French grammar around canonical technical terms.
* fix: TASK-2026-05-30-008 align final developer glossary wording
Normalize the last French framing around canonical developer terms like worktree, remote, prompt, and checkout.
* fix: TASK-2026-05-30-008 harmonize final French sentence framing
Replace the last raw franglais around checkout, remote, worktree, and prompt-facing labels with more natural French framing while keeping the chosen technical terms.
* fix: TASK-2026-05-30-008 add compact relative date keys
Replace French-specific prefix stripping in compact session date labels with dedicated i18n keys across locale dictionaries, preserving existing compact label output while making French wording robust.
* docs: add French documentation
* docs: mention French locale folder
---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Replace local-only filtering in GitHub issue/PR picker dialogs with
server-side GitHub Search API queries. Search text is sent as a query
parameter to the server, which uses the GitHub Search API
(issuesAndPullRequests endpoint) with repo: qualifiers including fork
network support. Results are debounced at 350ms to respect API rate
limits.
- Add query parameter to GitHubAPI issuesList/prsList interface
- Server routes use Search API when query is present, standard list
endpoint when absent
- Fork networks handled via repo:owner/repo OR repo:owner/upstream
- PR search fetches full PR details after Search API for head/base/draft
fields
- Remove local filter memos from all three picker dialogs
- Add debounced search effect with abort controller cleanup
- Update VS Code backend and webview API for parity
- Update search placeholders in all locales
Closes#1350
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Agent settings now correctly save an emptied system prompt instead of silently keeping the old text.
After saving agent, command, or skill settings, OpenChamber refreshes the updated configuration instead of showing stale values from a short-lived cache.
Reload actions now surface refresh failures consistently while avoiding noisy background errors.
Add a directory-created fast path for worktree creation so session and send flows can continue once the target directory exists while Git attachment and bootstrap finish in the background.
Track bootstrap status explicitly in shared UI contracts, including pending, ready, and failed states. Background watchers now surface failures and timeouts, update stored worktree metadata, and keep web and VS Code runtime behavior in parity.
Move GitHub issue/PR worktree sessions and assistant-answer fork sessions onto the unified send path so provider, model, agent, and variant selections are preserved. The assistant-answer fork dialog can optionally create a worktree outside VS Code.
Make worktree deletion dialogs close after linked-session cleanup while removing the worktree in the background, and clean up failed fast-create artifacts safely without recursively deleting user or agent-written files.
Validation: bun test packages/ui/src/lib/worktrees/worktreeBootstrap.test.ts packages/ui/src/lib/worktrees/worktreeManager.test.ts; bun run type-check; bun run lint.
Add a packaged-client runtime boundary so the shared UI can talk to local,
desktop, remote, and VS Code runtimes through the right transport instead of
assuming one same-origin web server.
Centralize OpenChamber-owned API access behind RuntimeAPIs, runtimeFetch, and
runtime URL helpers, while keeping official OpenCode traffic on the SDK path.
Support runtime switching, remote host selection, desktop client credentials,
and headless connection links for pairing packaged clients with remote
OpenChamber servers.
Harden the new auth model by moving long-lived client tokens out of browser
URLs, introducing short-lived scoped URL tokens for browser-owned transports,
restricting URL-token access to explicit readable/realtime routes, and making
client-token management session-scoped or self-scoped as appropriate.
Update browser-owned assets and preview proxy flows to work with the split
runtime model, including authenticated project icons, preview token propagation,
CSP-safe preview bridge injection, and preview proxy auth that survives
short-lived URL-token expiry.
Tighten Electron security boundaries for packaged clients by gating privileged
preload state to trusted origins and requiring explicit confirmation before
connect deep-links import or switch remote runtimes.
Also refresh agent guidance and project skills so future runtime/API, auth,
preview, UI, CLI, settings, locale, and drag-to-reorder work follows the new
architecture.
Loading screens: drop the internal-jargon progress text on the opencode
reload overlay (ConfigUpdateOverlay) and the vscode init splash — show
text only on errors. Replace it with a glow pulse on the OpenCode mark on
the cube's top face; OpenChamberLogo's isAnimated prop was a no-op and now
actually animates. vscode shows the glow on the inline splash logo and the
React app stops writing 'Loading data (… Providers, … Agents)…'.
Restart API: 'OpenChamber: Restart API Connection' now runs the same full
reload flow used after an OpenCode update — the command asks the chat
webview to call reloadOpenCodeConfiguration() (overlay + managed restart
via the bridge + config/data refresh) instead of a bare manager restart,
falling back to the old restart when no webview is open. Mounts
ConfigUpdateOverlay in the vscode app so the overlay actually shows there.
* feat(types): add parents to GitLogEntry and new commit action types
* feat(git): add parent hashes and --all flag to getLog
* fix(git): move record separator to start of log format string
* feat(git): add checkoutCommit server function and route
* feat(git): add cherryPick server function and route
* feat(git): add revertCommit server function and route
* feat(git): add resetToCommit server function and route
* fix(tests): make git service tests branch-name portable, add error path tests
* feat(client): add checkoutCommit, cherryPick, revertCommit, resetToCommit API wrappers
* feat(git-graph): add lane assignment algorithm with tests
* feat(git-graph): add GitGraphSegment per-row SVG renderer
* feat(i18n): add locale strings for git graph action buttons
* fix(git-graph): handle lane convergence, fix SVG path coords, add connector tests
* feat(git-graph): add ref badges and action buttons to HistoryCommitRow
* fix(git-graph): add loading guards to reset actions, use theme tokens for ref badges
* fix(git-graph): conditional hooks, stale graph log, conflict handling, i18n
* fix(types): replace toBeDefined with toBeTruthy, fix toast API usage
* fix(lint): remove unused variables
* fix(git-graph): fix SVG height causing 150px row spacing
* fix(git-graph): smooth bezier curves, fill row height, round line caps
* fix(git-graph): non-scaling-stroke fixes bezier white spaces, sort curves on top
* fix(git-graph): remove viewBox scaling, match SVG height to actual row height
* fix(git-graph): ResizeObserver tracks actual row height, eliminates SVG height mismatch
* feat(git-graph): replace SVG with Canvas for graph rendering
* fix(git-graph): isolate canvas from flex layout to prevent replaced-element height leak
* feat(git-graph): align action buttons, add confirmation popups for all actions
* fix(git-graph): address code review findings CR-001 through CR-005
- CR-001: VS Code getGitLog now forwards 'all' option and parses %P parents
- CR-002: VS Code bridge/gitService implement checkoutCommit, cherryPick,
revertCommit, resetToCommit with conflict detection and hard-reset guard
- CR-003: server-side commit hash validated with /^[0-9a-fA-F]{7,40}$/
in both routes.js and service.js; 12 new rejection tests added
- CR-004: cherry-pick/revert conflict path now refreshes fetchStatus/
fetchBranches/fetchLog; conflict toast uses i18n keys in all 7 locales
- CR-005: corrected O(n) comment to O(n x lanes)
* fix(i18n): add zh-TW locale and common.language.traditionalChinese key to all locales
upstream/main added zh-TW.ts after branch diverged; CI type-check fails
when PR is merged because zh-TW.ts was missing all gitView.history.actions.*
keys and loadMore/loadingMore. Also adds common.language.traditionalChinese
to en.ts and all 6 non-English files to match upstream en.ts.
* fix: harden git history actions
* feat: split git history graph view
* chore: remove git graph planning docs
---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Improve chat session switching and history pagination, with most of the aggressive limits scoped to the VS Code webview where the freezes were observed.
Session history loading and pagination:
- Reduce the VS Code message page size to 30 records so switching sessions does not immediately hydrate large histories into the webview.
- Keep manual Load older messages in VS Code fixed at 30 records per request instead of growing the request size over time.
- Add a bounded VS Code initial-tail expansion path from 30 to 50, 80, and 120 records only when the initial page has no user-message turn boundary, preventing large final turns from rendering as an empty chat.
- Lower the normal web message page size from 200 to 150 for a mild shared optimization without adopting the aggressive VS Code limits.
- Make session pagination metadata reactive per session so ChatContainer receives cursor updates from materialization and reconnect paths without requiring a switch away and back.
- Write pagination metadata before publishing newly materialized messages so the first render sees the correct has-more state.
- Store cursor information from direct materialization and reconnect message fetches in the shared session prefetch metadata cache.
VS Code cache and memory pressure reductions:
- Use a shared per-directory session recency map so cache eviction is based on app-level recency instead of whichever useSync instance happened to run.
- Limit VS Code warm session cache retention to 4 sessions and evict heavy inactive message caches after switching away from a large session.
- Disable sidebar session prefetch in VS Code because warming extra sessions was increasing webview memory and GC pressure during navigation.
- Remove dropdown background message prefetch so opening the switcher does not start additional session materialization work.
- Drop cached session-message-record snapshots when evicting session data so stale derived records do not remain after the raw session cache is cleared.
- Add bounded LRU caching for session message record snapshots, with much smaller VS Code limits and a VS Code cap that avoids caching snapshots above 30 messages.
- Bound the turn-window model cache in VS Code and avoid caching turn models for sessions above the VS Code message-page size.
Chat render-path reductions:
- Reuse ChatContainer's already-materialized message records in plan detection instead of adding a second active-session message subscription.
- Add a no-op guard when marking session plan availability so repeated detections do not create new Map references and fan out renders.
- Add no-op guards for session switcher and dropdown open state updates to avoid unnecessary store updates and renders.
- Convert several session-specific hooks to useSyncExternalStore with empty-session no-subscribe behavior so empty IDs do not subscribe to broad store updates.
- Remount the chat viewport when the current session changes, isolating per-session viewport and list state.
- Change the virtualized message-list fallback to render only a tail window when the virtualizer has not produced rows yet, instead of rendering an entire large history.
VS Code layout and header improvements:
- Remove the broad useSessions subscription from the VS Code layout header path and subscribe only to the active session title and initial-session existence.
- Unmount the compact VS Code session sidebar when the user is in chat view instead of keeping the hidden session list mounted and subscribed.
- Compute the latest assistant model and latest context-token usage in a single reverse scan of current-session messages instead of scanning the same list twice.
- Remove switcher git-status warmup work so the switcher reads already-loaded branch labels without starting extra background git status requests.
Markdown and file-reference safeguards:
- Skip expensive syntax highlighting for very large code blocks, with a 200-line cap in VS Code and a softer 1200-line cap in web.
- Add an LRU cap to file-reference stat lookups so the cache cannot grow without bound across many rendered messages.
- Limit the number of file references annotated per render to 40 in VS Code and 200 in web to prevent large assistant outputs from spawning too many stat checks.
- Clear file-link annotations when file-reference mode is disabled so stale attributes and handlers do not remain on previously annotated nodes.
Assistant-message action and preview reductions:
- Skip preview URL scanning on VS Code, mobile, and mini-chat surfaces so assistant text and tool output are not scanned where the preview action is unavailable.
- Skip Save-as-Plan project lookup on VS Code, mini-chat, and mobile surfaces.
- Hide Save-as-Plan and Start MultiRun assistant-message actions on VS Code, mini-chat, and mobile surfaces.
- Resolve the current session directory on demand for assistant actions instead of subscribing each assistant message to the full session list.
Tool and task rendering optimizations:
- Prefer finalized task metadata summaries without fetching child-session messages when the summary is already present.
- Avoid polling or final-fetching task child sessions once a final metadata summary is available.
- Use VS Code-specific task child fetch limits of 30 records for initial, active, and idle fetches.
- Parse diff stats by scanning patch text line-by-line instead of splitting large patches into arrays.
- Count write-tool lines by scanning content instead of allocating a split array for large files.
- Avoid trimming large patch strings just to test whether they contain content.
- Memoize diff and write statistics so unchanged tool parts do not recalculate them on every render.
VS Code bridge improvements:
- Return JSON and text proxy responses through the VS Code bridge as bodyText instead of base64 so the webview avoids synchronous base64 decoding for common API responses.
- Keep binary responses on the base64 path while making bodyBase64 optional in the bridge contract.
- Strip content-length, content-encoding, and transfer-encoding headers from proxied responses because the bridge reconstructs the Response body.
Validation:
- bun run type-check
- bun run lint
- bun run vscode:build
Prevents duplicate native notifications when a session is open in both sidebar and editor
Uses an extension-host claim step so only one VS Code webview displays each notification
Keeps notification behavior unchanged for single-panel sessions
Targets active editor chat before falling back to sidebar
Adds session open-in-editor action in the VS Code sidebar
Uses attachment badges for VS Code file and selection context
Move VS Code/Cursor desktop notifications onto the webview Notification API instead of the extension-host watcher path, which could not reliably produce native OS notifications.
Route OpenCode runtime events from the shared sync pipeline into the VS Code webview so completion, error, question, and permission notifications use the same live event stream as the UI.
Respect the OpenChamber notification settings in VS Code, including template rendering, completion cooldowns, permission auto-accept suppression, and the notify-while-focused mode.
Use VS Code's window focus signal from the extension host instead of document.hasFocus() inside the webview, so hidden-only notifications are suppressed while Cursor or VS Code is focused across platforms.