The terminal ran on the ghostty-web npm package plus a hand-written patch,
and every rendering bug (recycled rows, duplicated reflow fragments, prompt
artifacts) had to be worked around from outside. The emulator now is the
official libghostty-vt C ABI compiled to WebAssembly, driven by a browser
adapter ported from T3 Code (MIT, notice in LICENSE-T3CODE) and owned in
packages/ui/src/lib/ghostty. The artifact is reproducible with
scripts/build-libghostty-wasm.sh, including a workaround for Zig 0.15.2 on
macOS 27 SDKs.
On top of the port: one WASM instance per page with every tab kept mounted
and hidden tabs paused; history replayed at the PTY size it was drawn for;
shells spawned only after the first fitted grid so zsh never prints the
PROMPT_SP marker; box drawing, block elements and Powerline arrows drawn
procedurally to the exact cell so TUI borders and block logos have no gaps
between rows; a software-rasterized canvas so Gecko renders every tab's text
with the same smoothing; the symbols-only Nerd Font bundled instead of a CDN
fetch; touch selection and scrolling driven through the surface API; a copy
button in the tab strip for touch hosts; localized aria labels.
Testing: bun tests run the real WASM (reflow, palette, replay isolation,
recycled rows, box glyph geometry); viewport and view tests use a surface
double; verified in Chromium and Zen (windowed and headless) for crisp text,
new tabs, panel reopen, resize and box glyph rendering; package type-check,
oxlint/eslint on new files, web build.
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.
Two mitigations for local multi-instance contention over the shared relay
identity:
- A standby instance now waits a 2-minute grace period after the host claim
frees before taking over, so a cleanly restarting host (app update or
relaunch) — which reclaims at boot with no wait — always wins the restart
window instead of stranding paired devices on another process.
- Dev instances never host the relay passively: dev scripts set
OPENCHAMBER_RELAY_HOST=off and the Electron dev shell is detected via
OPENCHAMBER_ELECTRON_DEV. Explicit enable/pairing on such an instance still
force-claims; OPENCHAMBER_RELAY_HOST=on overrides.
* feat(skills): remove ClawHub catalog integration
Drop the ClawHub registry as a skills catalog source across web server,
shared UI, VS Code, docs, and locales. The catalog now serves git-based
sources only: the curated Anthropic repo and user-defined repositories.
Also removes the now-unused adm-zip dependency.
* feat(skills): redesign catalog around curated GitHub repositories
Replace the single-source dropdown with a card grid of curated GitHub
repositories (Anthropic, OpenAI, Cursor pstack/skills, Matt Pocock) plus
user-defined sources. Source cards show skill counts, GitHub stars, and
last-updated time; a global search covers all loaded sources.
Server: curated sources gain GitHub repo metadata (stars, pushed_at)
fetched best-effort with a 3-hour in-memory and on-disk cache; scans
run through a concurrency-limited, deduplicated cache with 3-hour TTL
persisted across restarts. Refresh still bypasses the cache.
Shared UI: source cards, global search with clear button, per-skill
GitHub links, install/installed states. VS Code curated list updated
to match. All new copy translated across 12 locales.
* fix(skills): address catalog review findings
- GitHub metadata fetch timeout drops to 1.5s (under the catalog
client's 3s deadline) and failed lookups cache briefly (5 min) so
repeated catalog loads do not re-hit a failing API.
- Disk cache files are written with owner-only permissions (0o600);
rename preserves the mode.
- loadSource deduplicates concurrent in-flight requests per source and
the shared isLoadingSource flag now clears only when the last active
source load finishes.
adm-zip <0.6.0 allows a crafted ZIP to trigger a ~4GB memory
allocation (GHSA-xcpc-8h2w-3j85). Bump the dependency in the web
and vscode packages to ^0.6.0. The new AdmZip(buffer) and
extractAllTo(dir, overwrite) APIs are unchanged, so no call-site
adaptation is needed.
Add a vitest regression test for the ClawdHub install path that
builds a real ZIP with adm-zip and asserts extractAllTo restores
files (including nested subdirectories) into the target skill dir.
The SQLite write into OpenCode's database was the only consumer of
better-sqlite3 in the repository. Everything that existed to ship its native
binary went with it:
- the dependency in @openchamber/web and @openchamber/electron
- the afterPack hook staging better_sqlite3.node into app.asar.unpacked
- a dedicated @electron/rebuild pass (onlyModules) and its binary assertion,
so desktop packaging now runs one native rebuild instead of two
- the bundler external entry and the AppImage required-native-module check
Desktop packaging, the AppImage verification tests, and the extension bundle
were re-validated after a clean reinstall, so no stale module could satisfy a
missed import.
Updates the SDK dependency across web, UI, and VS Code packages.
Refreshes the lockfile and package versions to match the release.
Adds unreleased changelog notes for the current set of user-facing changes.
Changelog leads with the private relay and the native mobile apps (TestFlight
beta + Android APK links), followed by pairing v2 and the device management,
desktop multi-transport, and chat items; VS Code changelog gets the shared
chat-render entries.
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)
Bundle the official OpenCode CLI into Electron desktop builds instead of relying on whichever opencode executable happens to be first on PATH. Pin @opencode-ai/sdk to an exact version and use that version as the source of truth for the downloaded CLI artifact.
Add an Electron prepare script that maps the current platform/arch to the official OpenCode release artifact, downloads it from GitHub releases, caches the archive under packages/electron/.cache, stages the binary under resources/opencode-cli, verifies opencode --version, and skips work when the staged binary already matches.
Prefer explicit OpenCode binary overrides first, then the bundled Electron CLI, then PATH/system installs. Keep rejecting the Windows OpenCode desktop app executable as a CLI candidate and add resolver tests for bundled priority, explicit override priority, resourcesPath lookup, and desktop-app rejection.
Suppress OpenCode CLI update prompts when the active CLI source is bundled. The server now reports upgrade-status as unavailable for bundled CLI while still returning the current OpenCode version for About, and rejects direct upgrade attempts with a 409 instead of trying to mutate the bundled binary.
Update desktop release, smoke, and manual macOS DMG workflows to prepare and verify the bundled CLI before packaging, verify the packaged app contains the expected CLI, cache downloads by OS/arch/OpenCode version, and align the Windows smoke runner with production windows-2022.
Document desktop bundling behavior, ignore generated CLI/cache files, add oc-dev helpers, and keep Web/VS Code behavior dependent on installed OpenCode CLI rather than desktop bundled resources.
Resyncs active sessions after hidden upstream stream reconnects
Recovers orphaned streaming parts with active-session snapshots
Adds coverage for event-stream reconnect behavior