Commit Graph
100 Commits
Author SHA1 Message Date
Bohdan Triapitsyn 3362ebbfa3 release v1.16.0 2026-07-13 02:30:40 +03:00
Bohdan Triapitsyn 6ed2ef750b fix: hide craft-goal starter in VS Code runtime
Omits the craft-goal command from draft starters in VS Code
Prevents the starter from being resolved or pinnable in that runtime
Keeps non-VS Code behavior unchanged
2026-07-13 02:25:18 +03:00
Bohdan Triapitsyn 7253924eb8 feat: add VS Code Insiders to open-in-apps
Recognizes Visual Studio Code Insiders as an available app
Maps the new option to the correct macOS app name
2026-07-13 02:15:00 +03:00
Bohdan Triapitsyn fd66e8c112 fix(files): scope preview defaults to previewable files 2026-07-13 02:07:46 +03:00
Bohdan Triapitsyn 9dd389fe8e feat: group chat visual settings into labeled sections
Adds section headers and tooltip help for the goal settings area
Reorganizes chat settings into Session Assistance, Reasoning, Message Appearance, Tools & Files, and Composer groups
Adds localized labels and search entries for the new section headings
2026-07-13 01:33:19 +03:00
Bohdan Triapitsyn 9b645f0e53 perf: fast relay connect on mobile and desktop + connect splash + edit-safe instances
Relay connect used to serialize a dead LAN probe (up to 8s per stale address
on mobile, 2-4s on desktop) in front of the relay attempt, then paid a second
WebSocket connect + E2EE handshake because the probe tunnel was thrown away.

- mobile probeConnectionCandidates: race the relay probe against the direct
  chain with a 1.5s direct headstart; a live LAN still wins, a dead one no
  longer delays startup
- relay probes adopt their tunnel as the runtime tunnel (adoptRelayTunnel)
  instead of dialing a fresh one — applies to auto-connect, pairing redeem,
  password login, and the desktop host switcher's relay fallback
- relay probe drops the /health round-trip: the E2EE handshake already proves
  the server identity, /auth/session alone proves liveness and auth
- desktop restoreDesktopRelayRuntime: same headstart race; a late direct
  success hot-switches back (stable runtimeKey); startup probe now passes
  expectedServerId so a re-leased LAN address never sees the token
- launch splash shows 'Connecting to device: <label>' with animated dots
  under the (still centered) logo, translated in all locales
- editing a saved instance no longer rebuilds it from the URL field alone:
  the id is passed through, relay/https candidates are preserved, and a
  token-key change migrates the Keychain token instead of orphaning it
2026-07-13 01:29:35 +03:00
Bohdan Triapitsyn 08e4ce5b99 fix(chat): sync subagent prompting in context panel 2026-07-13 01:21:18 +03:00
Bohdan Triapitsyn 7e12a0dcef feat(chat): allow prompting subagent sessions 2026-07-13 01:06:44 +03:00
Bohdan Triapitsyn 04307e163b fix: single relay host per machine via cooperative claim lock
All local instances share the data dir and therefore the relay identity
(serverId), so concurrent relay hosts evicted each other at the relay worker
(4001: Control replaced) and paired devices landed on whichever local process
won last — often a stale dev server, surfacing as 'Unable to reach server'
and devices stuck on relay with 503s on newer endpoints.

- relay/host-lock.js: per-machine claim file (relay-host.lock, {pid}); stale
  claims from dead pids are ignored; unwritable data dir falls back to
  pre-lock behavior
- relay/service.js: start only when the claim is free or ours, otherwise
  'standby' with the holder pid in lastError; 30s watcher takes over when the
  claimant dies and stands down when another process claims; pairing-link
  creation and explicit /relay/enable force-claim (user intent wins)
- mobileConnections.ts: log candidate-refresh skip reasons and the refresh
  result instead of failing silently
2026-07-13 00:36:00 +03:00
Bohdan Triapitsyn e247343423 fix(quota): derive Codex windows from duration 2026-07-12 23:54:07 +03:00
Bohdan Triapitsyn afb368e11b feat: connection candidates refresh + relay identity hardening
Candidates refresh (server + mobile + desktop clients):
- GET /api/client-auth/connection/candidates returns the server's current
  LAN URLs, relay candidate, and serverId for already-paired devices
- /health and /api/version expose serverId so clients can verify a learned
  address belongs to the expected server before sending their bearer token
- mobile: refresh saved candidates over the live transport after every
  connect/wake, hot-switch relay->LAN when a fresh address is reachable;
  serverId gate on direct probes; token no longer sent to /health
- desktop: refresh stored host apiUrl after a relay connect and hot-switch
  back to direct; electron probe verifies serverId before authenticated fetch

Fixes found while debugging a dead pairing:
- settings: strict reader that throws on corrupt/unreadable file instead of
  returning {}; relay signing/encryption key generation is now gated on it,
  so a swallowed read failure can no longer mint a new server identity and
  orphan every paired device (loud log when a keypair IS generated)
- SessionAuthGate: bounded auto-retry for transient session-check failures
  (initial request racing the relay tunnel's first WS attempt, startup 5xx)
2026-07-12 18:09:54 +03:00
Bohdan Triapitsyn 22d5ad3814 fix: mobile goal UX — bottom-sheet dialog, keyboard-safe target button, capacitor safe-top for overlays
- the goal dialog renders as the shared MobileOverlayPanel bottom sheet
  on mobile instead of a centered dialog
- tapping the target button to ARM keeps the soft keyboard open (the next
  message is the objective; same guard as the attachment/mic buttons),
  while opening the manage sheet lets the keyboard close as usual
- capacitor: bottom-sheet overlays cap their height by the keyboard inset
  AND the top safe area — with the keyboard raised while typing inside a
  sheet, 100dvh does not shrink (native resize is off) and the panel could
  slide under the notch/status bar; applies to every MobileOverlayPanel
2026-07-12 17:01:40 +03:00
Bohdan Triapitsyn 8d956f5f9c feat: distill any oversized goal objective, not just plans
The distillation moved from PlanView into setSessionGoal — the shared
choke point for every goal source (composer, fork dialog, plans) — and
into the scheduled-tasks goal creation server-side. Any objective over
5000 chars is distilled into completion criteria by the small model; on a
transient failure a head+tail excerpt keeps the intent (top) and the
acceptance criteria (bottom) with a trim marker, plus a toast. The
working agent always received the full prompt in chat — only the auditor
is bound by the limit.
2026-07-12 16:34:16 +03:00
Bohdan Triapitsyn 19b2a3d0d3 feat: distill oversized plan goals into audit criteria
Plan-goal objectives are capped at 5000 chars for the auditor while the
working agent reads the full plan from its file. Plans over the limit are
now distilled by the small model into completion criteria (end goals +
per-phase verification, no implementation steps), prefixed with a header
pointing back at the plan file so every continuation re-anchors on the
live source of truth. If distillation fails (transient small-model
hiccup), a head+tail excerpt keeps the plan's intent (top) and acceptance
criteria (bottom) with a trim marker between — sacrificing the
implementation middle the agent reads from the file anyway — and a toast
tells the user the objective is degraded.
2026-07-12 16:28:48 +03:00
Bohdan Triapitsyn b09614fd68 refactor(quota): secure managed provider credentials (#2160)
- add shared owner-only credential storage for OpenCode Go, Ollama Cloud, and Cursor
- validate credentials before atomic writes using 0700 directories and 0600 files
- replace provider-specific credential routes with an allowlisted lifecycle API
- stop automatically reading Ollama's legacy cookie file
- stop reading or modifying Cursor's database during regular quota requests
- add explicit one-time Cursor credential import without mutating Cursor storage
- persist refreshed Cursor credentials only in OpenChamber-managed storage
- add Ollama Cloud and Cursor credential controls to provider settings
- preserve OpenCode Go tracking through the shared credential flow
- add VS Code credential management and Cursor quota parity
- reject authentication redirects, enforce request timeouts, and fail on unparseable usage pages
- mask stored secrets in API responses and extend quota security coverage
- update quota provider documentation
2026-07-12 16:21:38 +03:00
Bohdan Triapitsyn 3b92d97795 refactor: simplify sidebar project sorting
Removes recent-project grouping from the sidebar project list
Keeps project ordering based on manual and default sort rules only
Reduces memoized session-derived state in the sidebar
2026-07-12 15:25:07 +03:00
Bohdan Triapitsyn 1c1456892a feat: add sort-desc icon for sidebar project sorting
Replaces the sidebar sort action icon with a dedicated descending sort glyph
Adds the new icon to the shared sprite set
2026-07-12 15:18:17 +03:00
Bohdan Triapitsyn d738d41574 feat: persist permission auto-accept on server (#2158)
Move per-session permission auto-accept policy ownership from the UI to the
OpenChamber server so enabled sessions continue running when clients disconnect
or the server restarts.

- persist explicit per-session policies in OpenChamber settings
- inherit the nearest explicit policy across subagent session hierarchies
- allow child sessions to opt out of an inherited parent policy
- immediately accept matching global and directory-scoped pending requests
- process future requests without requiring a connected UI client
- reconcile pending permissions after startup and event-stream reconnects
- deduplicate concurrent requests and retry transient reply failures
- synchronize policy updates across connected clients
- migrate existing browser-persisted policies to server storage
- suppress auto-accepted permission cards before they enter UI state
- show deduplicated permission toasts for inactive sessions
- preserve foreground-only permission handling in VS Code
- integrate directory-aware notification routing from main
- add coverage for persistence, inheritance, retries, reconciliation, pending
  requests, client hydration, and inactive-session toasts
2026-07-12 15:03:16 +03:00
Bohdan Triapitsyn 3d90eddcaf feat(quota): add OpenCode Go usage tracking (#2155)
* feat(quota): add OpenCode Go usage tracking

* fix(quota): align OpenCode Go VS Code parsing
2026-07-12 14:48:50 +03:00
Bohdan Triapitsyn b4f50e0a01 fix(notifications): handle subagents and session errors (#2156)
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.
2026-07-12 14:47:57 +03:00
Bohdan Triapitsyn a0bdcae54c feat: add craft-goal session starter and command
Adds /craft-goal autocomplete and chat handling for starting a Goal crafting session.
Introduces new Magic Prompts content and localized labels/descriptions for Goal crafting.
Migrates desktop draft starters to include Craft a Goal once and persists the migration marker.
2026-07-12 10:58:58 +03:00
Bohdan Triapitsyn c9ac8676e7 feat: file-backed goal objectives + compaction accounting fixes
Compaction fixes (observed in a real long run):
- the summary message's zeroed tokens froze the goal counter at its
  pre-compaction value; segments now close with the previously displayed
  total as a continuity floor
- audits and continuations after a summary tail now take execution params
  (provider/model/agent/variant) from the newest non-summary assistant
  turn instead of inheriting agent 'compaction' and the summarize model

File-backed objectives:
- the objective text lives in <data-dir>/goals/<sessionId>.md, keyed by
  session id (one goal per session, a new goal overwrites the file);
  metadata carries only an objectiveFile flag so session.updated fanout
  stays light, and never a path — ids are pattern-validated before any
  filesystem access
- limit raised to 5000 chars, no snapshot field: the UI fetches content
  via PUT/GET/DELETE /api/goals/objective/:sessionId (behind the blanket
  /api auth gate), writes the file before stamping metadata, and falls
  back to an inline objective when the write fails
- the loop reads the file fresh on every tick, so objectives are
  live-editable mid-goal; a missing file falls back to the inline text
- scheduled goal tasks write the objective file server-side; VS Code
  degrades to the audit note (route unavailable there by design)
2026-07-12 02:49:56 +03:00
Bohdan Triapitsyn 2c4b40893c fix(sidebar): keep recent project ids referentially stable 2026-07-12 02:02:42 +03:00
Bohdan Triapitsyn bb45164ae8 feat: session goals - server-driven goal loop with independent small-model audit (#2148)
Arm the target button in the composer and the next prompt becomes a goal:
the server keeps the session working toward it (idle tick -> small-model
audit -> continuation) until the objective is verifiably complete, blocked,
or out of budget — even with the UI closed.

Server (packages/web/server/lib/session-goal):
- event-driven loop on the global SSE hub; goal state lives in
  session.metadata.openchamber.goal (merge-safe patches, stale-write guard
  by goal id), so it survives restarts and syncs to every client for free
- the small-model audit (objective + last assistant turn only, language
  pinned to the objective) is the sole termination authority; blocked needs
  3 consecutive verdicts, audit outages tolerate one unaudited continuation
  then stop the goal as resumable-blocked
- hard stops: optional token budget, auto-continuation cap (Resume grants a
  fresh allowance), turn errors; user abort pauses the goal instead of
  blocking it, and resuming over an aborted tail nudges immediately
- token accounting as a snapshot of the latest turn (input + cache.read +
  output), goal-relative via a creation baseline and segmented across
  compactions; a compaction summary skips the audit and continues
- continuations reuse the session's own provider/model/agent/variant

UI:
- three-mode target button (arm / disarm / manage dialog), informational
  goal strip with inline pause/resume and an Evaluating indicator, sidebar
  state glyph, objective length counter (2000-char server clamp),
  read-only completed goals
- goal entry points: composer (sessions and drafts), start-new-session-
  from-answer dialog, plan implement dialog (plan content becomes the
  objective), scheduled tasks (Run as goal + budget)
- Settings -> Chat -> Goal: feature toggle + default token budget with
  three-layer parity (web server, client persistence, VS Code bridge);
  VS Code renders goal state but hides the entry points (the loop runs in
  the web server only)

Notifications: per-turn "ready" notifications are suppressed while a goal
is active; settling sends one final notification (desktop, web-push, APNs
generic titles with the session name as body) honoring the completion
toggle. Error/question/permission notifications are untouched.

Docs: user guide (session-goals) in all 9 locales + sidebar entry,
scheduled-tasks cross-reference, server module DOCUMENTATION.md.
2026-07-12 01:23:22 +03:00
Bohdan Triapitsyn 82c039117a fix(queue): back off failed queued auto-sends 2026-07-12 01:12:58 +03:00
Bohdan Triapitsyn 72aaa166f7 fix(session): do not block draft creation on auto-accept 2026-07-12 00:54:46 +03:00
Bohdan Triapitsyn 0daad180a1 fix(sidebar): preserve pins after partial session loads 2026-07-12 00:51:36 +03:00
Bohdan Triapitsyn fb173067f5 fix(worktree): refresh changed discovery metadata 2026-07-12 00:49:28 +03:00
Bohdan Triapitsyn 711289a606 fix(chat): retain latest overlapping message data 2026-07-12 00:48:21 +03:00
Bohdan Triapitsyn f214af15fc fix(sidebar): avoid duplicate refresh loads 2026-07-12 00:47:22 +03:00
Bohdan Triapitsyn a8953ad6f9 fix(auth): reject spoofed local host headers 2026-07-12 00:46:41 +03:00
Bohdan Triapitsyn 4f65e01a63 fix(sync): defer incomplete assistant-only pages 2026-07-12 00:45:26 +03:00
Bohdan Triapitsyn d0bcb8106a fix(sync): preserve optimistic entries for stale loads 2026-07-12 00:44:58 +03:00
Bohdan Triapitsyn 5c65462454 refactor: remove unused editor font size styling from chat input
Stops reading editor font size from UI state in ChatInput
Removes inline font-size styling from the text area
2026-07-11 18:17:21 +03:00
Bohdan Triapitsyn b0200bb3f2 chore: remove implementation plan 2026-07-11 18:01:47 +03:00
Bohdan Triapitsyn 0a89f05d07 feat: mark embedded session chat as desktop surface
Adds a surface=desktop URL param to embedded session chat links
Uses the surface override to classify embedded chat as desktop
Covers the new URL parameter in tests
2026-07-11 10:48:20 +03:00
Bohdan Triapitsyn 5a91c08413 fix: treat embedded session chat as desktop surface
Prevents narrow embedded session chat panels from being misdetected as mobile
Uses the ocPanel=session-chat query parameter to पहचान desktop-like layout
Keeps device detection aligned with other desktop shell runtimes
2026-07-11 09:40:35 +03:00
Bohdan Triapitsyn 33cbd0b836 fix(mobile): pad Android app below the status bar on Android 15
Android 15 enforces edge-to-edge and ignores the StatusBar overlay:false
inset the app relied on, while every --oc-safe-area-* CSS definition was
gated behind iOS-only conditions. Read the Capacitor-injected
--safe-area-inset-* vars (with env() fallback) on the Android native
shell so the header, top toasts, and connect screen clear the status
bar; both sources report 0 where the native inset still applies.
2026-07-10 20:25:48 +03:00
Bohdan Triapitsyn e9102df5f7 release v1.15.0 2026-07-10 19:41:49 +03:00
Bohdan Triapitsyn 3c0fb49c46 chore: add changelog entry for workspace-relative filenames in Editor Integration 2026-07-10 19:15:41 +03:00
Bohdan Triapitsyn 6ec1797583 feat(cli): make connect-url --relay a full anywhere pairing link
- --relay links now carry both routes: direct LAN plus relay fallback,
  matching the UI's Anywhere pairing; devices prefer the direct route
- pairing sessions created by the CLI are marked with usesRelay, and the
  server reconciles relay demand on a timer, so a headless instance
  brings the relay up on its own after connect-url --relay
- warn with LAN_UNREACHABLE when the link's direct route points at
  loopback and other devices cannot use it
- document the --relay flow and the --lan binding caveat in Connect a
  Device and Remote Instances across all locales
2026-07-10 18:29:15 +03:00
Bohdan Triapitsyn 7ea974d89b docs: centralize device connection guides, add private relay docs
- new Connect a Device page: one-time QR pairing, transport choices, device management
- new Private Relay page: E2EE guarantees, demand-driven lifecycle, relay vs tunnel
- rewrite mobile page around the native iOS/Android apps (TestFlight + APK)
- update remote-instances, security, tunnels, and remote-access troubleshooting to point at the new pairing flow
- translate everything across all 8 locales and update the sidebar
2026-07-10 15:30:50 +03:00
Bohdan Triapitsyn b3fa19fe3e feat: add navigable JSON summaries for tool output
Tool JSON output now starts with a compact navigable summary view.
Expandable tool output includes quick open-file and diff actions for changed files.
Reasoning headers strip stray HTML comments, and navigation tools stay compact.
2026-07-10 14:51:33 +03:00
Bohdan Triapitsyn 6c2e657511 chore: draft unreleased changelog, bump opencode sdk to 1.17.18
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.
2026-07-10 13:42:52 +03:00
Bohdan Triapitsyn 51e6ae7e3f feat(desktop): multi-transport hosts with relay fallback, card-style services dropdown
- A saved host now keeps every transport its pairing link carried: direct URL
  plus the relay descriptor, with one token for both (the mobile connection
  model). Switching tries the direct leg and falls back to the E2EE tunnel;
  list probes report Connected · Relay when only the tunnel reaches the host;
  relaunch restore picks direct first
- Host switching trusts the dropdown's fresh probe instead of re-probing on
  click (no doubled latency, no transient Unreachable flashes); statuses are
  written once with the final outcome, survive the dropdown closing via a
  last-known cache, and an unprobed host reads Checking — never Unknown
- Open-in-new-window works for relay hosts: a new IPC command boots the local
  UI with the host id injected and the renderer picks the transport; the app
  render holds on the relay restore so the splash shows instead of a transient
  auth screen (10s safety valve)
- Relay host control socket gained protocol-level keepalive: a missed pong
  window terminates and reconnects, so the relay can no longer hold a ghost
  registration that leaves every client tunnel hanging; the desktop relay
  probe also hard-times-out at 8s instead of hanging status flows
- Services dropdown restyled with mobile-style cards: per-provider usage
  cards, per-host instance cards with a selected highlight and a toned
  status line, MCP servers grouped in a card
2026-07-10 12:24:50 +03:00
Bohdan Triapitsyn ba32518b88 feat(desktop): live status in the servers list and a cleaner section header
- Each saved server row shows live reachability (Connected · Nms ping /
  Unreachable / Auth required) with a status dot, probed once per list change
  through the shared HTTP/relay probe (relay probing moved to desktopHosts as
  probeRelayDesktopHost, reused by the host switcher)
- Section header: one short description, Import Link promoted to the primary
  action; the token-storage note moved into the Add Server dialog next to the
  token field it describes, and the dialog got its own description
2026-07-10 03:36:10 +03:00
Bohdan Triapitsyn 26e88355e1 fix(desktop): relay host status, display, and server-side LAN candidate
- Probe relay hosts through a throwaway E2EE tunnel in the host switcher
  instead of an HTTP probe against the relay:// pseudo-URL, which always
  reported Unreachable
- Show 'via OpenChamber Relay' for relay hosts in the switcher and the
  servers list instead of the raw relay:// pseudo-URL; hide the URL-centric
  edit action for relay hosts (saving it would drop the tunnel descriptor)
- Pairing LAN candidate prefers the address the requesting client actually
  reached the server on; interface scanning could pick an unroutable virtual
  bridge (docker0), producing links whose LAN leg silently failed and forced
  devices onto the relay
2026-07-10 03:22:57 +03:00
Bohdan Triapitsyn cc4de243c6 fix(chat): sticky user headers float mid-list in the virtualized timeline
The tanstack rows sit in a wrapper offset with transform: translateY(), and a
transformed ancestor becomes the sticky containing block — turn headers stuck
to the wrapper's overscan-dependent top edge instead of the scroll container,
floating over the previous turn. Offset the wrapper with padding-top instead:
identical geometry, sticky computes against the scroll container again, and
the padding only changes when the virtual window shifts, not per scroll frame.
2026-07-10 02:28:31 +03:00
Bohdan Triapitsyn 848f552767 fix(ios): actually hide the iOS 26 scroll edge effect
The compile fix replaced the direct topEdgeEffect API with KVC casting the
effect to UIView — but UIScrollEdgeEffect is not a UIView, so the cast
silently returned nil and the system's dark edge band stayed visible behind
the status bar. Keep the KVC (compiles with pre-26 SDKs) but toggle the
ObjC 'hidden' key on the effect as a plain NSObject, guarded by respondsTo.
2026-07-10 02:20:41 +03:00
Bohdan Triapitsyn 3184afafdf feat(pairing): auto-close the QR/link dialog once the device connects
The pairing session is single-use, so it leaving the pending list (polled
every 5s) means it was redeemed — close the dialog and toast success. Armed
only after the pairing has been seen in the pending list, so the stale list
at result-phase open can't blink the dialog shut; expired/cancelled sessions
close it silently. Pending-list polling now preserves the previous list on a
transient fetch failure instead of blanking it (which would also have faked
the redeem signal).
2026-07-10 02:01:00 +03:00
Bohdan Triapitsyn e74834a739 feat(mobile): redesign connect screen and instances sheet
- Connect screen leads with Scan QR code plus a plain-words hint of where the
  code lives; manual URL entry is collapsed behind Connect by address (expanded
  automatically on web where scanning is unavailable); saved connections show a
  per-row connecting spinner
- Instances sheet is list-first: the active instance shows a live status dot
  and transport (Connected - Local network / Private relay), rows connect on
  tap with an inline spinner, and the add/edit form hides behind Scan QR code /
  Add by address
- Deleting the last instance returns to the connect screen: without a runtime
  endpoint the native app no longer bootstraps against the webview's own origin
  (which faked a successful connection), and the connect screen renders
  regardless of a stale isConnected flag
2026-07-10 01:44:42 +03:00
Bohdan Triapitsyn ac93a52e21 feat: iPad split layout for the Capacitor app (#2104)
* fix: open mobile model/agent panels on tablet-width Capacitor shells and keep composer taps from dismissing the keyboard

* feat: add iPadOS-style split layout to the Capacitor app

- classify the Capacitor shell as mobile in device detection so shared
  surfaces (draft starters, panels) stop falling into tablet branches
- add isIPadApp() and useOrientation() helpers
- iPad: persistent full-height sessions sidebar (mobile sessions surface
  inline), Changes/Files in a right sidebar with header shortcut toggles
- animate sidebar open/close like the desktop sidebars and add
  finger-sized drag-resize with persisted widths
- anchor the overflow menu and the usage/metadata popover next to their
  header buttons regardless of open sidebars

* fix: re-anchor metadata popover on layout shifts and untangle sidebar toggle updates

- recompute the iPad metadata popover anchor via a ResizeObserver on its
  wrapper so sidebar toggles/resizes while it is open cannot leave it
  misplaced
- move the portrait right-panel close out of the setIpadSidebarOpen
  updater into plain sequential state updates
2026-07-08 21:52:18 +03:00
Bohdan Triapitsyn dfed121bf1 fix: defer markdown code line number sync during streaming
Avoids syncing code line numbers while markdown is still streaming
Keeps code block wrapping and line numbers stable after render
Updates code block layout to support deferred gutter insertion
2026-07-08 20:06:33 +03:00
Bohdan Triapitsyn 6d0977fc4e fix: accept looser unified diff patches
Normalizes bare ---/+++ headers and paths before rendering
Repairs loosely formatted hunk bodies for patch display
Recounts hunk ranges so diff headers stay accurate
2026-07-08 15:10:40 +03:00
Bohdan Triapitsyn f49f3a0d88 feat: add synced code block line numbers
Adds line-number gutters for markdown code blocks
Keeps gutter heights in sync when wrapping or resizing changes
Applies wrap styles directly to pre and code for better overflow handling
2026-07-08 14:59:11 +03:00
Bohdan Triapitsyn 8b7448bcf0 feat: add code block line wrap toggle
Adds a chat code block wrap toggle in markdown code block headers
Persists and restores the setting across desktop/web settings
Adds localized labels and OpenChamber search entry for the new option
2026-07-08 14:46:44 +03:00
Bohdan Triapitsyn 859b4529da feat: add private relay for end-to-end-encrypted remote access (#2087)
Adds OpenChamber Relay — an opt-in way to reach an instance from a phone,
browser, or another desktop from anywhere, with no open inbound ports, no
tunnel, and no shared LAN. The instance dials outbound to a relay; all app
traffic (HTTP, the event stream, terminal, dictation) is multiplexed and
encrypted through a single connection per client, so the relay only ever
forwards opaque ciphertext.

Transport
- End-to-end-encrypted channel over WebCrypto (ECDH P-256 -> HKDF ->
  AES-256-GCM) with a capability-negotiated handshake and a small
  HTTP/SSE/WebSocket multiplexing protocol. A byte-compatible JS host mirror
  is cross-checked by tests.
- Host: outbound connection manager, per-client tunnel dispatcher to the local
  server over loopback, reuse of the existing instance identity key, and
  management routes. Disabled by default; explicit opt-in.
- Client: plugs into the existing runtime layer (runtime-fetch/-url/-switch/
  -auth, event pipeline, terminal, dictation) so features work over the relay
  unchanged; direct-URL and Electron realtime-proxy paths are untouched.

Pairing & UX
- Relay section in Settings -> Remote Instances (live status, QR/link pairing,
  revocation via the existing client-token list) and the mobile connect flow.
- Frame batching and idle-gated keepalive keep tunnel message volume low
  without affecting streaming smoothness.

Security
- The tunnel is transport only; the server authenticates every tunneled
  request exactly as for a direct remote client.
  fragments only. The relay stores no keys, tokens, or payloads.

Operability
- The endpoint can be pinned to a self-hosted rel
  paired clients inherit it from the offer automatically.
- Relay module DOCUMENTATION.md and a relay-trans
  invariants that future WebSocket/streaming changes must follow.

The relay transport is complete and tested; the UI for enabling and pairing
is gated behind openchamber_relay_gate and stays
2026-07-08 03:44:02 +03:00
Bohdan Triapitsyn 42e470cefa fix: keep browser tab session navigation stable
Keep the browser pane loaded URL separate from the in-frame current URL so SPA navigation updates the address bar and history without remounting the iframe or resetting the Electron webview src.

Preserve parsed ?session= route params during initial URL normalization and pass a directory hint when applying deep links, preventing embedded OpenChamber sessions from collapsing back to / while bootstrap catches up.
2026-07-07 20:58:54 +03:00
Bohdan Triapitsyn 44f44da212 ci: add friendly android release asset names 2026-07-07 20:38:05 +03:00
Bohdan Triapitsyn d447952344 ci: allow manual mobile release tag uploads 2026-07-07 20:26:20 +03:00
Bohdan Triapitsyn 7a508f1b14 ci: add mobile platform release toggles 2026-07-07 20:20:47 +03:00
Bohdan Triapitsyn 2f4eb1d112 fix: simplify context raw message rows 2026-07-07 20:05:54 +03:00
Bohdan Triapitsyn 40dfff4a9a fix: handle ambiguous prompt transport failures 2026-07-07 19:49:11 +03:00
Bohdan Triapitsyn c801c7a74b fix: clear right sidebar resize constraints 2026-07-07 18:13:46 +03:00
Bohdan Triapitsyn 292e78f067 feat: add last-turn diff view
Adds a Last turn scope to DiffView that renders OpenCode snapshot diffs from the latest user message summary without re-fetching git contents. The view hides Review in that mode and carries the selected diff scope through main and context-panel navigation.

Connects latest-turn changed-file chips in chat to the snapshot diff view on desktop and mobile, while keeping older turn chips static/read-only to avoid misleading affordances and extra subscriptions. Updates localized labels and empty states plus changelog.

Validation: bun run type-check (packages/ui); bun run lint (packages/ui).
2026-07-07 14:44:20 +03:00
Bohdan Triapitsyn 1c44146a4f feat: show android update toast 2026-07-07 13:49:26 +03:00
Bohdan Triapitsyn 4a62a2aeab feat: support mobile update checks 2026-07-07 13:39:50 +03:00
Bohdan Triapitsyn d45cae485c feat: trigger mobile release from the main release workflow
Adds a reusable mobile release workflow call from the release pipeline
Passes version, build number, and release tag into Android artifact publishing
Tightens release workflow shell quoting and version handling
2026-07-07 13:00:11 +03:00
Bohdan Triapitsyn 00346fa6a1 ci: build ios release with macos 26 2026-07-07 12:37:42 +03:00
Bohdan Triapitsyn 0f6e769715 fix: compile ios scroll edge handling 2026-07-07 12:32:47 +03:00
Bohdan Triapitsyn 10869094e1 fix: set ios app deployment target 2026-07-07 12:27:07 +03:00
Bohdan Triapitsyn a92c4a335b ci: match ios extension signing blocks 2026-07-07 12:22:39 +03:00
Bohdan Triapitsyn ef9f8a01e2 ci: patch ios signing per target 2026-07-07 12:18:36 +03:00
Bohdan Triapitsyn 1434034dbf ci: install ios profiles by uuid 2026-07-07 12:13:27 +03:00
Bohdan Triapitsyn 4d13253cfe ci: use ios provisioning profile uuids 2026-07-07 12:07:19 +03:00
Bohdan Triapitsyn 8645802dc2 ci: force ios distribution signing 2026-07-07 12:02:10 +03:00
Bohdan Triapitsyn 29483997ea ci: match ios release signing blocks 2026-07-07 11:57:12 +03:00
Bohdan Triapitsyn ab3ce3354f ci: fix ios release signing patch 2026-07-07 11:51:25 +03:00
Bohdan Triapitsyn 1db8f5cc80 ci: use app store signing for mobile ios 2026-07-07 11:46:46 +03:00
Bohdan Triapitsyn e9ab5618c3 ci: add mobile release workflows 2026-07-07 11:39:17 +03:00
Bohdan Triapitsyn 5101fab962 fix: keep local file picker mounted during composer swaps
Moves the hidden file input out of the attachment controls so it stays mounted.
Prevents file selections from being lost when the composer variant changes.
Restores reliable local attachment uploads after opening the OS picker.
2026-07-07 10:32:13 +03:00
Bohdan Triapitsyn 9ceb027e75 fix: preserve composer taps while the keyboard dismisses
Keeps mobile composer controls from missing taps during keyboard blur/reflow
Applies the deferred blur behavior to mobile browsers and installed PWAs
Leaves Capacitor behavior unchanged
2026-07-07 10:20:14 +03:00
Bohdan Triapitsyn 624be556f6 release v1.14.1 2026-07-07 01:56:59 +03:00
Bohdan Triapitsyn 856b312da1 fix: grow composer with dictation transcript like typed text
The dictation overlay is absolutely positioned over the composer, so the
transcript could not expand it — long dictations clipped after two lines.
ComposerDictation now measures the transcript text block (not the flex-1
container, which would feed the composer's own height back and creep a few
px per update) and reports it to ChatInput, which feeds it into the
textarea autosize: same line cap as typing, transcript area scrolls past
it and follows the newest words. Idle/unmount releases the height, and an
idle sibling instance (mobile footer + wrapper engine) can no longer zero
the active one's report.
2026-07-07 01:40:00 +03:00
Bohdan Triapitsyn 24c40b4930 fix: iOS PWA safe areas, viewport stability, and keyboard/overlay choreography
- Add standalone-only safe-area padding for the composer (bottom floor +
  fullscreen top inset) and top toast offset; env() reports 0 on iOS 26
  standalone so a fixed floor is required
- Pin the mobile shell to 100lvh: WebKit leaves 100dvh stuck at the
  keyboard-shrunk value after dismissal
- Clamp visual-viewport pinning to documentElement.clientHeight to guard
  against stale visualViewport metrics
- Defer the composer blur flip (120ms) so taps on composer controls
  survive the keyboard-resize reflow; transition the bottom padding so
  the late flip reads as a slide, not a dip
- Restore the keyboard after mobile overlays close: MobileOverlayPanel
  dispatches synchronous open/close events, ChatInput refocuses within
  the same gesture, holds focus through iOS's tap-settle dismissal,
  guards the pill collapse via DOM focus, and reveals the composer form
  above the keyboard (programmatic focus skips iOS's native reveal)
2026-07-07 01:25:28 +03:00
Bohdan Triapitsyn 182fc89b8d feat: split session recap and suggestion settings 2026-07-06 22:54:31 +03:00
Bohdan Triapitsyn 1824b51155 fix: scope session assist to session directory 2026-07-06 20:00:53 +03:00
Bohdan Triapitsyn 18d1c9111a feat: add load earlier support to chat timeline
Adds a load older button when earlier history is available
Preserves scroll position while older messages are loaded
Shows date-grouped messages with clearer per-message timestamps
2026-07-06 17:24:01 +03:00
Bohdan Triapitsyn d8f0ef074b fix: compute first changed diff line from patch hunks
Uses hunk contents to find the first modified line instead of the hunk start
Handles added, removed, and binary-only patches more accurately
Adds tests for patch parsing edge cases
2026-07-06 01:26:56 +03:00
Bohdan Triapitsyn 708cd723f7 fix: update button removal notice duration in ShareOpinionDialog 2026-07-06 00:45:17 +03:00
Bohdan Triapitsyn 81c9e6eaa2 fix: preserve chat composer focus on mobile keyboard
Keeps the textarea reference available during mobile viewport adjustments
Ensures the composer scrolls back into view after keyboard interactions
Updates text selection menu dependencies to include the current session
2026-07-06 00:39:49 +03:00
Bohdan Triapitsyn 086982c1b3 feat: add temporary sidebar share opinion prompt
Adds a dialog with call booking and survey actions for user feedback
Shows a one-time sidebar toast prompting users to share what’s useful or missing
Adds localized copy for the new feedback entry points
2026-07-06 00:37:24 +03:00
Bohdan Triapitsyn a6edc7baee fix(capacitor): validate mobile connection on app resume
Checks the runtime session before restoring a mobile connection
Disconnects and resets state when the session is no longer valid
Adds tests for reachable, unreachable, and unauthenticated runtimes
2026-07-05 23:51:11 +03:00
Bohdan Triapitsyn 0a7807a9dc fix: run deployed web CLI from Bun global install
Detects the OpenChamber CLI from Bun's global install directory
Starts the global instance via the installed CLI path instead of relying on PATH
Fails fast if the global CLI was not installed
2026-07-05 23:21:59 +03:00
Bohdan Triapitsyn 28f0736d69 feat: small-model utility calls on existing OpenCode providers (#2049)
Adds a server-side "small model" capability: direct, cheap LLM calls that
reuse the user's existing OpenCode provider logins — the mechanism OpenCode
uses internally for titles and summaries but does not expose through the
SDK or plugins. Zero new dependencies; plain fetch with per-provider wire
formats, credentials never leave the server.

Core (packages/web/server/lib/small-model):
- Resolution mirrors OpenCode's session scoping: explicit settings override
  → small_model from the OpenCode config → family scan within the session's
  provider → the session's own model. The global provider scan only serves
  callers without a session context, and background callers forbid it
  entirely (restrictToPreferredProvider), so conversation content never
  reaches a provider the user didn't pick — explicit choices excepted.
- Per-provider auth replicating OpenCode's plugin loaders: GitHub Copilot
  (device token as bearer, no exchange), ChatGPT plan via the codex
  Responses API (single-flight OAuth refresh written back to auth.json),
  Anthropic messages, Google generateContent, generic OpenAI-compatible.
- OpenCode's free models (opencode/big-pickle, *-free) are never called
  directly; unauthenticated providers are skipped by design.
- Prompt clamping to the model's catalog context limit; thinking disabled
  where a wire switch exists (Z.AI/GLM, MiniMax-M3, Gemini Flash); robust
  content parsing with a clear error when a thinking model spends its whol
  budget on reasoning.
- Settings → Sessions gains a Small Model group: use-default checkbox plus
  an override picker limited to authenticated providers, persisted with
  web/desktop/VS Code sanitization parity.

Consumers:
- Session assist: a server-side watcher on the global SSE hub generates a
  short recap and one suggested follow-up after a session idles quietly fo
  a minute, stored on session metadata (openchamber.assist). Freshness is
  keyed to the last assistant message id, so new activity invalidates the
  payload everywhere with no extra writes. The chat shows the recap under
  the last message after five quiet minutes and the suggestion as a
  dismissible chip above the composer (tap fills the input, never sends).
  Gated by a new Chat setting (default on) that is a hard generation
  switch. Language is anchored to the conversation itself, with a
  script-mismatch guard against model/backend language hallucination.
- TTS: a third input mode, summarized — long replies are condensed to
  spoken prose before playback on any TTS engine.
- Git: commit-message and PR generation moved off the active chat session
  onto the small model fed with real diffs and the commit list (bodies
  included), with a session-transport fallback for free-model-only setups.
- Notes: Add to notes distills long selections into 1-3 dense sentences
  preserving exact identifiers, with verbatim fallback on failure.

Fixes along the way:
- The global event watcher now starts unconditionally; it was gated behind
  the desktop-notify env, leaving the server-side event hub dead in
  packaged apps.
- OpenCode re-emits message.updated for old user messages after idle; the
  watcher no longer mistakes those for new activity.
- Session metadata merges from a fresh read right before the PATCH, so
  writes made during the generation window (suggestion dismissals, review
  links) are preserved; the assist runtime stops during graceful shutdown.
2026-07-05 23:19:10 +03:00
Bohdan Triapitsyn e5b03493da release v1.14.0 2026-07-05 12:58:59 +03:00
Bohdan Triapitsyn f3c36eb96e fix(tooltip): update styles for tooltip content and arrow to use elevated surface variables 2026-07-05 12:55:04 +03:00
Bohdan Triapitsyn 02dc59b788 docs: changelog for the abort, desktop auth, and Windows CLI fixes 2026-07-05 10:48:18 +03:00
Bohdan Triapitsyn 81b8218d7c fix(chat): route abort to the session's own OpenCode instance
The stop button sent the abort with the UI's active directory, but
OpenCode dispatches the request to the per-directory instance — for a
session running under a different project, worktree, or a mapped docker
path the abort hit an instance that didn't own the prompt, cancelled
nothing, and still returned 200. The abort now resolves the session's
own directory, like the revert flow's aborts always did.
2026-07-05 10:47:09 +03:00
Bohdan Triapitsyn 997116c736 fix(opencode): robust Windows CLI discovery and launch across surfaces
Launching: the VS Code extension spawned .cmd shims with shell:true,
which builds an unquoted command line — a path like
"C:\Program Files\nodejs\opencode.cmd" broke with "'C:\Program' is not
recognized". It now spawns cmd.exe directly with the shim path as its
own argv element (the web server's existing cmd-wrapper pattern), and the
web server's final launch-spec fallback routes raw .cmd/.bat through the
same wrapper instead of handing them to spawn.

Configured paths: wrapping quote pairs (Windows "Copy as path" pastes)
are now stripped everywhere a binary path enters — the VS Code setting
and shared settings.json, env vars on both surfaces, the server's
directory-path normalization, and the desktop settings input.

Discovery: added the system-wide npm prefix (Program Files\nodejs) and
scoop's .exe shim to the Windows candidates, Linuxbrew on Linux, and the
where-probe now runs with windowsHide. The macOS desktop-app exclusion
also covers the OpenCode Dev/Beta app bundles.
2026-07-05 10:39:09 +03:00