66 Commits
Author SHA1 Message Date
Bohdan Triapitsyn 85c4320825 Settings storage with scopes, and project setup that can live in the repository (#3413)
* refactor(settings): settings registry and intent-gated writes

Problem: every setting lived in a flat document with ten hand-maintained
key lists that had drifted (three keys the server silently dropped, five
it kept that nothing read), and three code paths wrote to the server
without a person changing anything: the theme persist effect on mount,
bootstrap seeding of server-missing keys, and the auto-save echoing
values just adopted from the server.

Approach: one registry (packages/ui/src/lib/settings/registry.ts) names
every key with its scope (instance / profile / device), a boundary parser
and its store binding; DesktopSettings, the sanitizer, the mirror, the
apply step and the auto-save derive from it. A generated JSON snapshot
carries the key list to the server and the VS Code bridge. Writes carry
intent: the theme context writes only from its user-facing setters, a
missing server key leaves the local store alone instead of resetting it,
updateDesktopSettings drops values the server already holds, and the
auto-savers treat values applied from the server as a new baseline.

Testing: bun test packages/ui (registry + persistence suites cover zero
writes on load, dedup, toggle-back cancellation, failed-save retry, and
snapshot freshness); tsc for every workspace.

* refactor(ui): read and write settings through the shared path only

Problem: fourteen pages and stores fetched /api/config/settings on their
own and re-parsed the raw document by hand, so the registry could not
guard them and two of them treated a failed load as an empty list.

Approach: loadDesktopSettings() and updateDesktopSettings() (which now
resolves { ok }) replace every direct call; SkillsCatalogPage and
AddCatalogDialog refuse to write the catalog list until it is known.

Testing: bun test packages/ui (403 files), eslint on the changed files.

* refactor(server): validate settings writes against the registry snapshot

Problem: the server whitelist was the only guard on PUT /api/config/settings
and had drifted from the client; dead keys were still persisted.

Approach: settings-helpers.js drops any key the generated registry
snapshot does not list as persistable and strips secret keys from
responses; the dead keys (markdownDisplayMode, toolCallExpansion,
typographySizes, expandedEditorToolbar, gitProviderId/gitModelId) are
gone; the profile keys that were client-only now round-trip. A drift
test requires a valid sample for every persistable registry key.

Testing: vitest run in packages/web (182 files), including the packed
tarball import.

* refactor(vscode): gate bridge settings writes by the registry

Problem: the extension host wrote any key the webview sent straight into
settings.json, and commit-message generation read the dead
gitProviderId/gitModelId pair instead of the small-model setting.

Approach: filterPersistableSettingsChanges applies the registry snapshot
before the file write; chooseBridgeGitGenerationModel honours
smallModelUseDefault/smallModelOverride ahead of the zen fallback.

Testing: bun test packages/vscode (37 files), tsc, build:extension.

* feat(settings): split the user's profile into preferences.json

Problem: one flat settings.json held instance facts, the user's
preferences and device state together, so device state travelled between
installs and the profile had no document of its own to sync from.

Approach: the server keeps one merged document for clients but routes
each key by registry scope on disk (settings-files.js): profile keys go to
preferences.json as { value, updatedAt } entries stamped when the value
changes, everything else stays in settings.json, device keys are dropped
from writes. A missing preferences.json is seeded once from settings.json,
which is left intact; an unreadable one is a failure that pauses profile
writes and never gets overwritten. Server modules that read a profile key
off the disk use the merged sync read. Electron main reads the theme mode
from both files and now owns the splash colours, handed over the
window-theme IPC instead of the settings document. Clients stop sending
device keys, seed them once from a pre-split document, and persist
inputBarOffset locally. The PWA manifest keys are instance facts.

Testing: vitest in packages/web (seed, split write, timestamp retention,
unreadable file), bun test in packages/ui and packages/electron, tsc for
every workspace.

* feat(vscode): write the profile to preferences.json from the extension host

Problem: the extension host writes the shared settings files directly and
had to follow the server's split, and its file writes reported success on
failure.

Approach: settings-files.ts mirrors the server's format and split rules
(seed once, unreadable preferences.json is a failure); persistSettings
routes profile keys to preferences.json and the rest to settings.json,
and the atomic writers now throw so a failed save reaches the webview.
Clearing a key now actually removes it from the owning file.

Testing: bun test packages/vscode (38 files), tsc, build:extension.

* feat(settings): store the per-surface profile fields by surface kind

Problem: theme, chat-layout switches and typography sizes are one value
for every client of an instance, so the phone and the desktop cannot
disagree without a hard-coded runtime branch.

Approach: every settings request carries the client's surface kind in the
x-openchamber-surface header (web, desktop, vscode, mobile — the phone app
and the hosted mobile shell are one kind). For the registry's perSurface
keys the store writes a changed value under fields[key].surfaces[kind] in
preferences.json and never touches the base from a surface; reads resolve
the kind's own value, then the base, then nothing. Writes without a
surface (migrations, the seed) set the base. The VS Code host is always
vscode; Electron main resolves desktop for the native window theme. The
Settings UI is unchanged.

Testing: vitest in packages/web (surface write/read, no base copy, unknown
surface falls back to base), bun test in packages/vscode and packages/ui,
tsc for every workspace, build:extension.

* fix(settings): keep a legacy copy of the profile in settings.json

The first write after the split rewrote settings.json with the instance
part only, and that write happens on startup (relay reconcile). A build
from before the split reads only settings.json, so rolling back would
have lost every preference: theme, default model, all of it.

Every write now stores the profile's base values in settings.json next
to the instance part (`legacySettingsDocumentOf`), on the server and in
the VS Code extension host alike. Current builds ignore the copy because
preferences.json wins in the merged read. When preferences.json is
unreadable the copy already on disk is kept rather than dropped.

Testing: settings-runtime tests updated for the copy; full web suite
(182 files), VS Code tests and extension build, tsc clean. Verified live
on a scratch OPENCHAMBER_DATA_DIR: all 136 keys survive startup, theme
changes land per surface, plain keys land in the base.

* feat(settings): make the UI password and tunnel preset tokens write-only

GET /api/config/settings returned desktopUiPassword and the managed
remote tunnel preset tokens to every authenticated client, including
paired phones and the VS Code webview that never need them.

Both keys are now `secret` in the registry: accepted on write, withheld
from reads. The server answers with a hasDesktopUiPassword flag; the
desktop network page shows "Password set" and sends a value only when
the user types a new one or presses "Remove password" (an empty string
clears it and turns LAN access off). The tunnel page already learned
token presence from the status endpoint. The VS Code bridge strips
secret keys from what it hands the webview while still merging them
from disk on write.

Testing: registry, i18n parity, server settings, VS Code gate tests and
tsc; workspace type-check. Verified against a scratch server: GET
carries the flag and no password, PUT with '' clears, PUT with a value
sets. The desktop-only page itself awaits the owner's run.

* fix(settings): send the surface kind as a query parameter, not a header

The packaged desktop shell (openchamber-ui://app) and the phone app are
cross-origin to the OpenChamber server, so the x-openchamber-surface
header turned every settings request into a CORS preflight the server
did not allow. Settings looked reset and every save reported "Save
failed" without reaching persistSettings. An older remote instance would
refuse the header the same way even with the allow-list fixed.

The client now sends ?surface=<kind>, which keeps the request
CORS-simple on every server version; the server reads the query
parameter and still honours the header. The header is also in the CORS
allow-list for completeness.

Testing: workspace type-check, persistence and registry tests, server
opencode tests. On a scratch server: PUT with ?surface=vscode lands
under surfaces.vscode, GET without or with an unknown surface serves the
base, the header fallback resolves. Confirmed in the owner's rebuilt
desktop and on the phone.

* refactor(settings): drop the show-password toggle from the desktop network page

With the password write-only, the field only ever holds a value the user
is typing right now; the reveal toggle and its strings are gone from
every locale.

* refactor(projects): serve project setup through the server, drop the legacy migration

The shared UI read and wrote ~/.config/openchamber/projects/<id>.json
itself: it resolved the home directory, composed the path, and used the
Files API, which only desktop and VS Code have natively and which cannot
see a remote instance's file at all. It also still carried the months-old
migration from <repo>/.openchamber/openchamber.json, which deleted files in
the folder the upcoming shared project config will use.

The client-owned keys (worktree setup commands, project actions, draft
starters) now live behind GET/PUT /api/projects/:projectId/config.
project-setup.js sanitizes and builds the view; the project-config runtime
merges a patch under the same cross-process lock the scheduled-task writers
hold, so unknown and server-owned keys survive. A wrongly shaped key is a
400, not a silent drop. openchamberConfig.ts keeps its exported functions
and is now an HTTP client. The VS Code webview handles the route locally
and bridges to the extension host, which owns the file with a TS mirror of
the sanitizers.

Testing: server tests for sanitizers, round trip, lock, and invalid patch;
client tests against a mocked route; VS Code sanitizer and bridge tests;
workspace type-check, both VS Code builds, UI isolated suite (409 files),
server projects and project-context suites. Live GET/PUT against a
running server with the owner's real project config.

* feat(projects): read the team's shared config and merge it with the personal one

A project can now carry <repo>/.openchamber/project.json (version 1:
setupWorktree, setupWorktreeWait, projectActions, draftStarters,
plansDir). The server finds the checkout from the path-derived project
id, parses the file, and answers GET /api/projects/:id/config with one
merged view: what runs at the top level, plus shared and personal blocks
so a page can edit the personal file without copying a teammate's entry
into it.

Merge rules: shared setup commands run first (a personal
setupWorktreeMode of "replace" uses the personal list only); the
personal wait flag wins when set; actions union by id with a personal
action replacing the shared one and personal hiddenSharedActionIds
dropping shared ones; starters union by type:name; the primary action is
personal only. A shared file that exists but cannot be parsed, or that
names a plansDir outside the repo, is reported as invalid with a reason
and never treated as "no shared setup". Nothing writes the repo file yet.

Client: getProjectSetup exposes the view; the existing helpers return
effective values, while the Projects page sections and the draft
starters hook edit the personal block only. Shared entries show a quiet
"shared" mark in the actions dropdown and read-only lists above the
editable ones on the Projects page; shared starter chips have no remove
handle. The VS Code extension host mirrors the parser and merge.

Testing: server tests for the parser, plansDir guard, merge table, id
round trip, and a runtime test against a temp checkout; client tests
against a mocked route; VS Code sanitizer, merge, and bridge tests; the
section test covers the shared row; locale parity; workspace type-check;
UI isolated suite (409 files). Live: GET against a temp repo with a
shared file and with a broken one.

* feat(projects): ask before the team's shared commands run, once per set of commands

Shared setup commands and shared actions come from a file a git pull can
change, and they run on the machine of whoever pulls. The first time one
would run, a dialog now shows exactly what would run and asks: "Trust and
run" or "Not this time". A "trust" answer is recorded in the personal
config against a SHA-256 of the executable parts (setup commands and each
action's id, command, and runIn; renames and icons do not count), so a
pull that changes a command brings the prompt back. Nothing asks when the
shared file has nothing that executes.

Worktree creation (session creator, new-worktree dialog, session store,
multi-run launcher, agent-manager empty state) resolves its commands
through the prompt; "not this time" runs only the user's own commands.
The actions dropdown asks before a shared action runs. The Projects page
shows "Trusted on this instance" with a "Reset trust" button next to the
shared actions. The dialog is mounted beside the app-link confirmation on
every shell. The VS Code extension host mirrors the hash and the record.

Testing: server tests for hash stability, ordering, and the trusted flag,
plus a runtime test that changes the shared file and sees trust drop;
client tests for the confirmation store (ask, trust, skip, replace mode,
newer request, failed record, reset); VS Code mirror tests; the actions
button, new-worktree dialog, and issue-2039 tests updated for the trust
path; locale parity; workspace type-check; UI isolated suite (410 files).

* feat(projects): share and unshare setup with the team from the Projects page

The repo file <repo>/.openchamber/project.json is now written by the app,
and only when the user shares something: nothing appears in a repository
until then. PUT /api/projects/:id/config/shared replaces the keys it
names over the current file, writes it pretty-printed with version first
and only the keys that carry something, removes the file (and an empty
.openchamber folder) when nothing is left, refuses a missing checkout or
a plansDir outside the repo, and records trust for the writer, who has
seen what they shared.

On the Projects page, actions and setup commands get "Share with team"
and "Make personal"; shared actions can be hidden for this user; a
checkbox switches to "Use only my setup commands". Project starter chips
get share and make-personal hover buttons. A new "Shared config" block
shows the file's path and status, the shared plans folder, and the trust
status with "Reset trust". A share is a repo write followed by a personal
write; a failure after the first leaves the item visible once, as
personal. The VS Code extension host mirrors the writer.

Testing: server tests for the patch, serialization, emptiness, the write
and removal round trip, the writer's trust record, and the refusals;
client test for the shared route; VS Code bridge test for write and
removal; locale parity; workspace type-check; UI isolated suite (410
files). Live on a scratch server: share, invalid plansDir (400), unshare
to removal of file and folder.

* feat(projects): list, edit, and move plans in the team's shared plans folder

When the shared config names a plansDir, every markdown file in that
folder is a plan on the Plans tab: listed after the user's own plans,
marked shared, addressed as shared:<file>, read and edited in place
(the raw document is written verbatim, so a plan another tool wrote
keeps its shape), and deletable. Share moves one of the user's plans
into the folder; make personal moves it back under a new id; a name
collision gets a numeric suffix. Sharing is refused, with a hint in the
panel, until a shared plans folder is set in Project settings. This
answers the request to read plans from an existing folder such as
docs/plans.

Server: the project-context runtime takes resolveSharedPlansDir from the
project-config runtime; readContext reports sharedPlansDir; POST
.../plans/:id/share and /unshare. Client: movePlan in the context store,
a shared badge and a share / make-personal button per plan row. Session
attachments reference plan ids, so an attached plan that moves has to be
attached again.

Testing: runtime tests for listing, foreign markdown titles, id
traversal, in-place update and delete, share and unshare with a
collision, and the refusal without a folder; HTTP route tests; store and
locale parity tests; workspace type-check; full web suite (183 files);
UI isolated suite (410 files). Live on a scratch server against a temp
repo: list, share, read, unshare.

* fix(server): make OPENCHAMBER_DATA_DIR move every folder, not just the flat files

The variable is documented as the OpenChamber data directory, but only
settings, preferences, auth, and push files followed it; projects,
themes, speech models, and the chats default stayed under
~/.config/openchamber. A second instance started with a custom
directory therefore read and wrote the default instance's project
configs.

Every folder now hangs off the one root. An instance that already used
a custom directory gets projects, themes, and speech-models copied in
once at startup; copied, not moved, so a second instance beside the
default one cannot strip it, and nothing is merged into a folder that
already exists. Existing managed chats are not copied, as with
OPENCHAMBER_CHATS_DIR.

Testing: migration tests for copy-once, no-merge, and same-root no-op;
full web suite; a scratch server with an empty data dir copied the real
project configs and kept its writes in the copy.

* fix(projects): keep a plan's id when it moves into or out of the repository folder

A plan moved into the repository plans folder used to be listed under a
new shared:<file> id, so a session that had attached it lost the
attachment. The manifest entry now stays with a `shared` flag that says
which folder holds the file; the id survives both directions. Only a
plan that never had an entry (one written by another tool) gets an id
when it is brought in. A personal file and a repository file may share
a name because they live in different folders.

Testing: runtime tests for share and unshare with a stable id, reading
and editing the moved plan, the suffix on a name collision, and the
adoption of a foreign file.

* feat(projects): default repository plans folder, "move to repository" wording, tooltips

Plans now have a repository folder without any setup: .openchamber/plans
by default. A custom plansDir replaces the default outright (only that
folder is read and written; moving files between the two is the user's
job), and the field's placeholder and hint say so. The move buttons on
plans are therefore always available.

The word "share" is gone from the UI: it read like publishing, while
the action stores an item in the repository so everyone who pulls it
gets it. Labels are "Move to repository" / "Move to my settings", the
badge is "In repo", the block is "Repository config", and every button
on the Projects page carries a tooltip that says what happens (the
"Move to repository" button explains that edits save first while the
form is dirty). The trust status with "reset trust" moved from the
repository block into the Worktree section next to the commands it
guards; the plan row's badge sits beside the title.

Testing: locale parity, section test, workspace type-check, UI isolated
suite (410 files), full web suite.

* fix(projects): leave the icon key out of the repository file when an action has none

Actions without an icon were written as "icon": null into
.openchamber/project.json. The key is now omitted; readers already fall
back to the play icon. Server and VS Code serializers, tests updated.

* docs: describe the repository config file and how items move into it

A new page in every locale: what stays personal and what can move into
the repository, the .openchamber/project.json format with an example
and every key explained (setup commands, actions with the supported icon
names, starters, plansDir), the merge rules, the trust prompt, and plans
in the repository. Linked from the sidebar and from Project Actions.
Translations written by hand.
2026-09-07 17:50:55 +03:00
Bohdan Triapitsyn 60a4dccb32 fix(vscode): file an editor comment under the session it was delivered to
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
2026-09-05 14:18:51 +03:00
Felipe GenéandBohdan Triapitsyn a12b9be443 feat(chat): Comments and review in VS Code, like the OpenChamber desktop app (#1724)
* 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>
2026-09-05 12:28:17 +03:00
Iuliia Ivashko 0ef189f3c8 perf(sessions): archive a worktree's sessions through one server batch
Removing a worktree archived its sessions one SDK call at a time and
then re-rendered the whole sidebar once per streamed session.updated
echo. On a worktree with 121 sessions that meant 14.8s of main-thread
work, 121 requests, and 328 localStorage writes.

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

Benchmark (121 sessions, production build, real Chrome): 14785ms ->
~1030ms, long tasks 100 -> 1, global store publications 236 -> 1,
persistence writes 328 -> 3.
2026-09-04 16:50:03 +03:00
Bohdan Triapitsyn 2a0672e3a6 fix(ui): preserve VS Code themes during settings broadcasts 2026-09-03 12:41:59 +03:00
jaygupta17 5e3f9d1ba2 fix(git): harden nested repository discovery
- 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
2026-08-25 19:15:35 +05:30
Bohdan Triapitsyn 1b02310a28 fix (vscode): focus chat input after opening the chat view
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
2026-08-13 23:50:54 +03:00
Bohdan Triapitsyn 5917325d49 feat: switch OpenCode Go usage to API
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
2026-08-12 01:49:22 +03:00
Cursor AgentandSerhii Dziupin be87e25c7d feat: add custom/other OpenAI-compatible LLM providers
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>
2026-08-02 09:12:28 +00:00
Bohdan Triapitsyn d69ee1033e fix(vscode): prevent early SSE replay loss 2026-07-30 00:31:15 +03:00
𝖎𝖚𝖑𝖎𝖎𝖆andBohdan Triapitsyn c88dd16d2a fix: prevent bundled OpenCode self-upgrades (#2525)
* fix: prevent bundled OpenCode self-upgrades

* feat(vscode): support OpenCode upgrades

* fix: refresh OpenCode update status on runtime switch

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-07-29 19:59:41 +03:00
Bohdan Triapitsyn 85400459e9 perf: overhaul session loading, caching, and runtime isolation (#2360)
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
2026-07-21 20:52:20 +03:00
Bohdan Triapitsyn 0202e88eb4 fix(vscode): restore permission auto-accept parity
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.
2026-07-17 12:59:41 +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 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 de1b85ac56 feat(voice): first-class voice input and local TTS across web, desktop, and mobile (#2018)
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)
2026-07-04 02:48:07 +03:00
FanFan4204andBohdan Triapitsyn efd621b087 fix: handle non-ISO-8859-1 characters in fetch headers and Content-Disposition (#1673)
* 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>
2026-06-23 19:49:44 +03:00
Bohdan Triapitsyn 91de51d1a5 fix: deduplicate desktop notifications and tighten notification text extraction
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
2026-06-16 15:21:27 +03:00
Bohdan Triapitsyn c73ab9cbd5 feat(vscode): startup parity + workspace-grouped session list (#1658)
* 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.
2026-06-15 13:02:26 +03:00
Maksym Mospanenko 7b33805ea0 feat(vscode): support multi-root workspaces (#1493)
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.
2026-06-10 23:58:40 +03:00
Pascal AndréandBohdan Triapitsyn 49a1424e5f feat: add complete French localization (#1482)
* 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>
2026-06-10 20:19:30 +03:00
Bohdan Triapitsyn e3f3da4cb4 fix: restore dependency update compatibility
Fix VS Code webview type-check with TypeScript 5.9
Align ghostty-web to 0.4.0 across the workspace
Refresh ghostty-web patch for the updated package
2026-06-10 17:05:10 +03:00
Bohdan Triapitsyn ef71a997a8 fix: save agent prompt and reload changes reliably
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.
2026-06-08 13:43:45 +03:00
Bohdan Triapitsyn e192359da9 fix: improve startup readiness performance 2026-06-05 15:09:24 +03:00
Bohdan Triapitsyn 2031e3b4a8 Decouple bundled UI from runtime API and add remote instance tooling (#1228)
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.
2026-06-02 00:43:05 +03:00
Bohdan Triapitsyn a4314c189b feat: streamline opencode loading screens and reuse the reload flow for vscode Restart API
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.
2026-05-30 02:04:32 +03:00
Bohdan Triapitsyn 51c8d52ab5 perf(ui): improve VS Code chat session switching
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
2026-05-21 15:45:44 +03:00
Bohdan Triapitsyn 04f4b71396 fix: dedupe vscode notifications across panels
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
2026-05-19 16:41:16 +03:00
Bohdan Triapitsyn c81441ecfc feat: improve VS Code editor chat actions
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
2026-05-19 15:53:54 +03:00
Bohdan Triapitsyn a57b02a308 fix: restore vscode native notification behavior
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.
2026-05-19 02:06:32 +03:00
Bohdan Triapitsyn 4cc2f1bff6 refactor: split vscode into a dedicated app root
Renders VS Code through its own app entrypoint
Keeps shared app effects focused and reusable
Avoids importing the full desktop app root in VS Code
2026-05-07 01:31:17 +03:00
David SazandBohdan Triapitsyn 5475ef2db3 Feat/add ide opened file to chat context (#1106)
* feat(chat): add active editor file context and related functionality

* feat(chat): improve active editor file context handling and update translations

* feat(i18n): standardize quotation marks in file attachment messages

* update Korean translation for image removal action in file attachment

* feat(chat): refine active editor file handling and optimize broadcast logic

* fix(chat): stabilize VS Code editor context chips

---------

Signed-off-by: David Saz <david.saz.g@gmail.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-05 01:24:03 +03:00
Islam NoflandBohdan Triapitsyn a23f5e7545 fix: cross-verify update API claims against npm registry (#1082)
* fix: cross-verify update API claims against npm registry

* Update packages/web/server/lib/package-manager.js

Signed-off-by: Islam Nofl <islamnofl.official@gmail.com>

* fix: show live server version in AboutDialog instead of stale build-time constant

* fix: add comment to empty catch block to satisfy lint no-empty rule

* fix: preserve live about dialog version in electron

* fix: scope update checks by runtime

---------

Signed-off-by: Islam Nofl <islamnofl.official@gmail.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-01 12:23:58 +03:00
Shyamalan KannanandBohdan Triapitsyn bcbf34b1d1 feat: add Behavior settings page for global AGENTS.md (#1079)
* feat: add Behavior settings page for global AGENTS.md

- Add new 'Behavior' settings page to manage global system prompt
- Sync global behavior prompt to ~/.config/opencode/AGENTS.md
- Add GET/PUT /api/behavior/agents-md endpoints with 1MB size limit
- Add AbortController guards and parallel fetches in BehaviorPage
- Add i18n translations for en, es, ko, pt-BR, uk, zh-CN
- Add globalBehaviorPrompt to DesktopSettings type and sanitizer
- Add /api/behavior to express.json() body-parser whitelist
- Ensure atomic save: AGENTS.md written before settings updated

* fix: address Greptile review feedback

- Rename misleading 'trimmed' variable to 'value' in settings-helpers.js
- Add Content-Length guard for /api/behavior before 50mb JSON parser
- Use express.json({ limit: '1mb' }) for behavior endpoints
- Add actual translations for es, ko, pt-BR, uk, zh-CN locales

* fix(vscode): support behavior settings endpoint

* fix(behavior): wait for settings persistence

* fix(behavior): end agents file with newline

* fix(behavior): avoid duplicate textarea resize handles

* fix(behavior): clarify global rules copy

* fix(behavior): move rules note into tooltip

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-05-01 00:39:55 +03:00
Bohdan Triapitsyn a9c51f115e refactor(text): extract shared summarization service with tts/note/notification modes
Move server-side summarization out of lib/tts/summarization.js into a shared
lib/text module exposing tts, note and notification modes. TTS and notification
runtimes now delegate to the shared service, note mode is used to distill a
selected excerpt into a short project note. Expose the new endpoint via
/api/text/summarize and route it through the common request middleware.

Client voice/summarize.ts accepts a mode option and points at the new
endpoint with an absolute URL that respects the desktop server origin.
VS Code webview swaps the stubbed tts summarize path for /api/text/summarize
and hardens URL parsing with window.location.href as the base.
2026-04-18 13:46:51 +03:00
Bohdan Triapitsyn b3f2732dff fix(vscode): make session markdown export save/reveal work 2026-04-17 18:46:13 +03:00
Bohdan Triapitsyn 5f0d1623ae feat: expand magic prompts coverage and split generation prompts (#835)
- Add configurable visible/instructions prompt families for commit/PR generation, PR checks/comments flows, and git conflict resolution helpers.
- Refactor prompt sending to explicit visible + synthetic parts instead of newline-based splitting, with legacy override migration for old keys.
- Polish Magic Prompts settings UX with grouped sidebar entries, tooltip-based descriptions, AI icon, and validation that visible prompts cannot be empty across web and VS Code runtimes.
2026-04-07 22:51:14 +03:00
Bohdan TriapitsynandIuliia Ivashko c9e31a0e6c perf: harden sync architecture and modularize runtimes (#803)
* fix: added desktop app background throttling

* perf: add streaming debug metrics panel

- Show streaming performance metrics in the debug panel
- Auto-enable stream profiling while the panel is open
- Add JSON export for sharing UI and VS Code metrics

* perf: batch streaming updates more aggressively

- Buffer message deltas and metadata updates to cut render churn
- Skip no-op part updates before they touch the message store
- Fix the desktop debug panel shortcut binding

* perf: split streaming event handling and coalesce deltas

- Move streaming content events onto a dedicated fast path
- Defer non-critical stream side effects off the hot path
- Merge repeated message delta events before they reach the UI

* perf: isolate streaming rows from chat rerenders

- Memoize chat rows against render-relevant message changes only
- Read live assistant text directly from store to narrow streaming updates
- Split the active streaming entry from the stable message list path

* perf: streamline chat streaming and SSE proxying

- Reduce chat rerenders around the active streaming path
- Simplify server SSE forwarding to avoid duplicate proxy work

* fix: preserve the first streaming text chunk

- Show the initial text chunk immediately before batched deltas arrive
- Bypass batching for the first text or reasoning part update
- Keep later streaming updates buffered for performance

* perf: align streaming/render hot paths with opencode parity

* perf: harden turn/cache stability and stale delta suppression

* fix: stabilize chat rendering and disable timeline interactions

- Disabled timeline dialog access from shortcuts, commands, and chat input
- Reduced chat render churn by simplifying message list and turn staging behavior
- Improved session-switch stability to prevent update-depth crashes

* perf: track static message rerenders during streaming

* perf: reduce sorted-mode activity rerender fanout

* perf: reduce chat rerender fanout and add active-turn metrics

- Reduced sorted-mode rerender coupling by tightening turn context propagation
- Added a metric for static rerenders outside the active turn during streaming
- Exposed new chat render counters in the debug panel for parity tracking

* fix: keep sorted activity mounted while stream grows

* fix: stabilize session and history scroll rendering

* refactor: decouple server routes from index

* refactor: extract fs module from server index

* refactor: move opencode route ownership into module

* refactor: extract notification route registration

* refactor: extract opencode and notification runtimes from index

* refactor: extract settings runtime and complete server modularization pass

* refactor: modularize server config, skills, icons, and tunnel routes

* refactor: extract server modules from monolithic index.js

Split proxy, routes, runtime helpers, and notification emitter
into dedicated modules under packages/web/server/lib/.

* refactor: replace session/message stores with SSE-driven sync layer

Delete ~9200 lines of old architecture (useEventStream, messageStore,
sessionStore, useSessionStore, questionStore, useTodoStore, client SSE).

New sync layer: event pipeline with coalescing + 16ms flush, pure event
reducer, per-directory child stores with LRU eviction, cursor pagination,
optimistic updates, deferred timeline staging, text throttle.

Migrate all UI consumers to sync hooks (useSessionMessages,
useSessionMessageRecords, useSessionStatus, useSessionPermissions, etc).

Strip session-ui-store to UI-only state, delegate SDK ops to
session-actions with abort-if-busy, optimistic store updates, and
response merging for revert/fork/archive/delete.

Add notification-store for SSE-driven session attention tracking,
cross-directory GlobalSessionStatusStore for sidebar indicators,
client-side diff snapshot sanitization to prevent memory bloat,
and revert message filtering via useVisibleSessionMessages.

* feat: notification store, session actions, activity detection

Add notification-store.ts for SSE-driven attention tracking.
Add sanitize.ts to strip diff snapshot memory bloat.
Add session-actions.ts with optimistic revert/fork/archive/delete.
Improve useSessionActivity with incomplete-message fallback.
Delete useServerSessionStatus polling hook.

* fix: add directory param to all SDK calls, fix command/shell/abort routing

All SDK calls in session-actions.ts now pass directory parameter —
required by OpenCode server to scope session operations. Without it,
abort, commands, revert, fork, and other operations returned 500.

Add routeMessage() in session-ui-store for shell mode (session.shell),
slash commands (session.command), and normal prompts. Command lookup
checks both sync child store and useCommandsStore. Handle /compact
locally via session.summarize().

Implement getContextUsage() to restore header context usage display —
reads token counts from last assistant message in sync store.

* refactor: replace custom API proxy with http-proxy-middleware

Remove ~280 lines of custom proxy code: forwardSseRequest,
forwardGenericApiRequest, collectRequestBodyBuffer, header
manipulation, hop-by-hop filtering, SSE block buffering.

Replace with single createProxyMiddleware() call that handles
SSE streaming, large bodies, and timeouts out of the box.
Dynamic router for OpenCode port changes after restarts.
Auth headers injected via proxyReq hook.

Keep: readiness gate, Windows session merge, API prefix detection.

* perf: targeted event draft cloning to fix streaming render cascade

Event handler was eagerly cloning all state slices on every event,
breaking Zustand selector referential equality. During streaming
(~60 events/sec), this caused every subscriber to re-render regardless
of which slice actually changed.

Now only clones fields the specific event type mutates. Also extracts
StatusRowContainer to isolate high-frequency useAssistantStatus
subscription, removes dead messageStreamStatesMap subscription from
ChatContainer, and narrows useAssistantStatus to only track last
assistant message parts.

MessageList renders: 1972 → 296 per streaming session (-85%).

* fix: null safety for sync state slices

Add defensive ?? {} guards on permission, question, session_status,
and message record access. Prevents crashes when child store state
is partially initialized during bootstrap.

* perf: dedup inflight SDK calls, extract concurrency util, delay PR tracking

Extract mapWithConcurrency to shared lib/concurrency.ts. Add in-flight
dedup for loadProviders/loadAgents to prevent concurrent duplicate SDK
calls. Delay initial PR background tracking by 5s to reduce startup
CPU burst.

* fix: header session lookup across all child stores

Session title and context panel click failed when session belonged to
a different directory than the current child store. Fall back to
getAllSyncSessions() to search all initialized stores.

* chore: bump @opencode-ai/sdk to 1.3.5

* docs: add sync event handling guide

* Optimize session prefetch and improve delete/archive UX

- Add settlement delay to session prefetch to avoid race conditions on
  rapid session switches
- Reduce git diff prefetch and session cache limits for better performance
- Implement optimistic UI updates for session delete/archive operations
  with proper rollback on failure
- Wire session prefetch hook into SessionSidebar with sync integration

* Add file content cache and sync optimizations

- Wrap FilesAPI with in-memory LRU cache for file content with dual
  constraints (entry count and byte size)
- Optimize chat timeline scroll restoration using useLayoutEffect
- Preserve React references in message and part arrays to prevent
  unnecessary re-renders when prepending history
- Add session prefetch TTL cache to prevent redundant fetches
- Integrate session prefetch cache clearing with eviction flow

* Improve session sidebar error handling and add diff prefetch filtering

Load active and archived sessions independently using Promise.allSettled
to prevent one failure from blocking the other. Add retry logic to session
API calls and skip large files during diff prefetch to improve performance.

* Replace sendMessage with optimisticSend wrapper

Introduces optimistic UI updates for normal chat messages to provide
instant feedback. Messages appear immediately in the UI while the API
call executes in the background, with automatic rollback on errors.

* perf: split stores, proper optimistic send, fix revert/directory bugs

- split session-ui-store into voice/input/selection/viewport stores
  to reduce subscriber re-evaluation during streaming
- wire optimisticSend through useSync shadow Map infrastructure
  matching OpenCode's pattern (no heuristic part detection)
- port OpenCode Identifier.ascending ID format for correct sorting
- pass messageID to promptAsync to prevent duplicate messages
- fix worktree directory not propagating to session actions
  (dynamic dir() via opencodeClient.getDirectory)
- fix setCurrentSession accepting directoryHint for new sessions
- fix revert not hiding messages (session limit was 5, bumped to match loaded count)
- fix revert optimistic message removal from store
- fix load-more flicker (useLayoutEffect scroll compensation)
- add prefetch TTL cache, file content LRU cache
- add session prefetch for adjacent sessions
- add instant archive/delete (optimistic before SDK call)
- migrate legacy window.__zustand_session_store__ to session-ui-store
- add retry + independent error handling for archived sessions
- add AGENTS.md performance rules

* perf: startup optimization — dedup, caching, light git status, diff rendering gates

- defer diff prefetch to git tab open, reduce concurrency 4→2, skip >500 changed lines
- cap project git checks concurrency (2), directory status probe (3)
- dedup provider/agent loading, github auth, worktree list (in-flight + TTL caches)
- delay PR tracking 5s, cache 403 search failures per-repo
- coalesce settings PUT (200ms debounce), cache settings GET (2s TTL)
- cache canonical directory resolution (60s TTL)
- persist missing directory status to localStorage (10min TTL)
- light/heavy git status: polling skips numstat+line counting+rev-list
- large diff rendering gate (>500 lines → "render anyway" button)
- tokenization degradation for >500KB files in Pierre
- parallelize main.tsx pre-render awaits
- batch sidebar file tree expanded paths restoration (3 at a time)
- remove bare useConfigStore() subscription in AgentsPage
- sync worktree sandboxes to OpenCode SQLite DB
- fix RightSidebarTabs ternary → explicit tab matching
- defensive guards on sync state (session_status, permission, question, message)

* fix: add defensive guards on remaining sync state field accesses

guard session_status, permission, message, todo, part, config with ?? {}
in useDirectorySync selectors, session-cache, and bootstrap

* fix: add missing directory dep to useCallback in use-sync.ts

* fix: preserve diffStats when light-mode polling overwrites status

* perf: optimize startup git status polling and diff rendering

Preserves diff stats when lightweight polling updates repository status
Reduces startup overhead with smarter git polling and store updates
Adds detailed optimization and migration docs for next performance steps

* fix: keep chat diff stats stable during git status updates

Prevents lightweight git polling from dropping diff statistics
Keeps MessageList diff indicators consistent while status refreshes
Improves reliability of git-aware chat rendering

* fix: user animation replay, queued message variant, startup provider loading

- consume animation ID after first play to prevent re-animation
  on neighbor assistant message completion
- capture send config (model/agent/variant) at queue time matching
  OpenCode's FollowupDraft pattern instead of re-resolving at send time
- replace one-shot startup recovery effect with polling interval
  that retries every 2s until providers and agents load
- fix optimistic bridge to avoid re-render loop (stable ref wrappers)

* chore: update tauri to 2.10.3 and all plugins to latest

- tauri 2.9.4 → 2.10.3
- tauri-build 2.5.3 → 2.5.6
- tauri-plugin-dialog 2.4.2 → 2.6.0
- tauri-plugin-log 2.7.1 → 2.8.0
- tauri-plugin-shell 2.3.3 → 2.3.5
- tauri-plugin-updater 2 (floating) → 2.10.0 (pinned)
- @tauri-apps/api ^2.9.0 → ^2.10.1
- wry 0.53.5 → 0.54.4 (transitive)

* refactor: decouple web server index orchestration runtimes

* fix: align VS Code runtime behavior with web and reduce draft view CPU load

- Queue VS Code bridge and SSE startup requests until API readiness to avoid false bootstrap failures
- Make agent manager actions directory-aware and remove real worktrees with safer partial-failure handling
- Replace heavy logo animation path with a lightweight pulse to cut draft-session CPU usage

* fix: restore auto-selected file sending in chat input

- Send server-selected files as proper file URLs in the message payload
- Include server-backed attachments in submit flow instead of dropping them
- Restore queued-message attachments through the refactored input store

* fix: restore session model selection consistently on session switch

- Restore agent, model, and variant from the latest loaded user message for each session
- Wait for session messages before applying restored selections to avoid stale or missing state
- Remove legacy session-choice inference paths that caused overlap and instability

* fix: restore permission replies and auto-accept across sessions

- Scope permission and question replies to the target session directory so answers take effect reliably
- Make permission auto-accept immediately handle pending requests and react to new permission prompts
- Keep parent-session handling working for child-session requests through the shared response path

* feat: add reusable fuzzy branch fuzzy-search helper and dialog integration (#798)

* feat: add reusable fuzzy branch search for worktrees

* chore: drop planning docs from feature branch

* feat: make worktree branch refresh manual

* feat: add configurable session retention action

* refactor: centralize global session state in ui store

* fix: cancel debounced permission push after reply

* docs: clarify global and directory session store architecture

* docs: refine agent development rules and session activity guidance

- Clarify agent code of conduct and durable development patterns
- Add explicit shared-store rerender and live-state guidance
- Narrow session activity fallback to avoid stale working state

* chore: updated .gitignore

---------

Co-authored-by: Iuliia Ivashko <yulia.ivashko@gmail.com>
2026-03-31 18:47:00 +03:00
Bohdan Triapitsyn 1231fd773e feat: improve VS Code dev flow and stabilize sidebar/chat behavior (#754)
* fix: improve session sidebar tooltip and truncation behavior

- Keep new-draft tooltip anchored to its trigger button
- Fix minimal-mode worktree/group header text truncation
- Tune minimal-mode right padding to reduce early label clipping

* fix: render reasoning through markdown pipeline

- Use Streamdown rendering for reasoning in live chat mode
- Remove italic styling from reasoning text
- Render expanded reasoning content with MarkdownRenderer

* chore: remove legacy electron dependencies

- Removed unused Electron packages from root and UI manifests
- Deleted obsolete Electron context menu type declaration
- Regenerated lockfile after dependency cleanup

* fix: handle non-repository folders in git status API

- Prevent 500 errors when status is requested outside a valid Git repo
- Improve repository detection using `git rev-parse --git-dir`
- Reduce noisy server logs for expected non-repo status checks

* fix unloaded session chat layout flicker

* fix: reduce noisy TTS status polling

Cache and dedupe TTS status requests, and only check provider availability when the related voice features are enabled so disabled voice setups stay quiet.

* perf: throttle background PR git status refreshes

* fix: improve VS Code Explorer file drop mentions in chat

- Add Explorer context action to insert selected files as @mentions.
- Handle Explorer drag-and-drop to prefill @file mentions instead of attachments.
- Prevent duplicate plain-path text when dropping multiple files.

* fix: deduplicate recent sessions in VS Code sidebar

- Hide sessions from main list when already shown in recent
- Apply dedup only in VS Code runtime
- Keep session search behavior unchanged

* feat: add true HMR dev flow for VS Code extension

- Load VS Code webview from Vite dev server with React refresh preamble
- Add `vscode:dev` runner that starts watchers and opens Extension Development Host
- Update VS Code dev docs and scripts to use the new HMR startup flow

* feat: polish VS Code session sidebar and attachment UX

- Add resizable sessions sidebar in VS Code layout
- Tighten session list spacing and hover behavior in VS Code
- Remove bulk file/image attach success toasts while keeping error toasts
2026-03-23 23:51:55 +02:00
Bohdan Triapitsyn 64b55025e1 feat: Add opt-out setting for anonymous usage reporting (#743)
* refactor: align VS Code update checks with web runtime parity

- VS Code now uses file-based installId (shared with web server)
- Accepts platform/arch from webview for consistent behavior
- Usage data collection now matches web implementation

* feat: Add opt-out setting for anonymous usage reporting in Appearance

- Add privacy control in Appearance settings to opt-out of anonymous usage reports
- Usage data includes only app version, platform, and runtime - no personal data or code collected
- Setting persists across all runtimes and controls the reportUsage parameter in update checks
2026-03-22 23:02:53 +02:00
Bohdan Triapitsyn 7356090e3d fix: improve cross-runtime session UX and platform config handling (#725)
* fix: make textarea focus highlight render inside

Apply inset focus ring to shared textarea component
Prevent focus border from appearing clipped near container edges

* fix: build desktop sidecar with target-matched architecture

Map Tauri target triples to Bun compile targets
Pass explicit Bun compile target for sidecar builds
Prevent x86_64 releases from shipping arm64 sidecar binaries

* fix: allow Windows git custom binary paths

Enable safe use of resolved custom git executable paths
Prevent git status failures when path contains restricted characters
Keep default behavior unchanged for plain git invocations

* fix: allow toggling diff line wrap on mobile

Stops forcing wrapped lines in mobile diff view
Line-wrap button now reflects and applies user preference

* fix: align VS Code managed server env with shell settings

Import login-shell environment variables before starting managed OpenCode
Apply Windows and Unix shell snapshot resolution for parity
Improve proxy-dependent provider connectivity in VS Code extension

* fix: respect user scope when adding MCP servers

Prevent user-scope MCP entries from being written to project config
Keep project writes only for explicit project scope

* fix: show linked GitHub issues and PRs as user message attachments

Preserve synthetic issue/PR context parts during message filtering.
Convert synthetic GitHub context JSON into attachment-style user parts.
Open issue/PR attachment links via shared external URL helper.

* fix: restore and polish project notes in sessions sidebar

Restored the Notes button in the left sessions sidebar header
Improved notes panel layout with wider dialog, larger notes area, and project name in the header
Refined todo rows with inline expand/collapse text and stable action/checkbox alignment

* fix: hide sidebar footer actions in VS Code runtime

Remove Settings, About, and Shortcuts buttons from the sessions sidebar footer in VS Code
Keep update button behavior unchanged across runtimes

* fix: normalize Windows paths for VS Code session loading

Canonicalize drive-letter casing in session path normalization
Align VS Code workspace path persistence with the same Windows path format
Normalize client directory context before API calls to keep session filtering consistent

* fix: open linked GitHub attachments with shared URL helper

Use runtime-aware external URL opening for issue/PR attachment links.
Keep GitHub attachment labels readable without altering normal file name rendering.

* fix: keep user MCP config writes out of project files

Respect user scope when selecting config write target
Prevent MCP user entries from being written to project opencode.json

* fix: prevent project menu from overlapping new session button

Align project menu positioning for non-git and git project rows
Avoid kebab-menu and plus-button overlap in sessions sidebar
2026-03-20 18:58:13 +02:00
Bohdan Triapitsyn 3123de5f43 fix: improve Windows UX and stabilize chat/session behavior across runtimes (#693)
* fix: preserve unsent prompt when adding editor context in VS Code

* fix: append Add to chat selections as markdown blocks with stable spacing

Convert selected assistant content to markdown before appending
Wrap each Add to chat selection in an `md` fenced block
Preserve multiline composer formatting across repeated appends

* fix: normalize persisted Windows paths to prevent identity mismatches

* fix: hide Windows subprocess console popups across server tasks

Hide OpenCode startup and shell command child windows in the web server
Apply windowsHide to cloudflared and skills-catalog git subprocesses
Cover remaining git service exec paths that could surface console windows

* fix: restore chat auto re-pin when reaching bottom

Re-pin now triggers when scrolling back into the bottom zone, not only via the button.
Upward user scroll intent still unpins immediately and is not overridden by re-pin.
Unified bottom/re-pin threshold logic to reduce sensitivity mismatches.

* fix: restore chat scroll release on mobile during streaming

Restores pinned-scroll release on touch scroll up so mobile users can leave auto-follow while streaming.
Improves re-pin behavior near bottom to avoid sticky or inconsistent pin states.
Includes related chat UI and dependency updates in the same change set.

* fix: hide daemon startup probe consoles on Windows

* fix: prevent pinned scroll tug-of-war during streaming

* fix: prefer git.exe to avoid Windows diff popup flashes

* fix: prefer git.exe discovery in Windows git flows

* fix: avoid where probes in Windows git resolution

* fix: avoid update-check subprocess flashes on Windows

* fix: normalize read file path labels

* feat: add OpenChamber defaults and improve theme ports

Add new OpenChamber light and dark themes
Regenerate imported themes with stronger surface mapping
Set OpenChamber themes as the default top options

* fix: stabilize chat pin and unpin behavior during streaming

Restores reliable unpin on upward wheel and touch gestures while auto-follow is active.
Prevents immediate re-pin while the user is actively scrolling upward near the bottom.
Keeps smooth follow-to-bottom behavior while reducing scroll tug-of-war.

* fix: suppress Windows command popups in VSCode runtime processes

Hide spawned git and server process windows in VS Code runtime
Extend hidden-window handling to server port cleanup and reveal commands
Keep behavior unchanged on non-Windows platforms
2026-03-17 13:18:54 +02:00
Bohdan Triapitsyn 7f37256e96 fix: unify update-check API flow across runtimes (#672)
* fix: align update flow across web, desktop, and vscode

Unify runtime update handling in shared UI update state
Adjust desktop and VS Code bridge integration for consistent behavior
Improve server package-manager update plumbing for safer checks

* fix: sync update checks between vscode and server

Align VS Code bridge behavior with server package-manager logic
Reduce mismatches in update detection across runtimes

* fix: stabilize update checks across ui and server
2026-03-15 23:14:37 +02:00
Bohdan Triapitsyn fab32ca992 refactor(chat): complete turn-based pipeline and stabilize streaming, scroll, and tool UX (#629)
* refactor: add canonical turn projection pipeline for chat

Centralizes turn/activity/summary derivation, migrates render consumers to shared projection sources, and adds projector regression coverage for retries, malformed parent fallbacks, and activity segmentation.

* refactor: stream assistant text live in turn-first render path

* refactor: complete phase-3 turn timeline history/navigation

* refactor: complete phase-4 turn pipeline cutover

Remove legacy grouping adapters and lock key edge cases with executable regression tests so turn-centric rendering stays stable across retry/history/navigation flows.

* fix: align chat history loading path with opencode parity

* fix: stabilize chat streaming and reduce render churn

* feat: add blurIn streaming text animation and smooth lerp-based auto-scroll

Streaming markdown now uses Streamdown blurIn animation (150ms, ease-out) for per-word reveal
Auto-scroll during streaming uses continuous exponential smoothing (lerp) instead of restarting spring animations
One-shot scroll-to-bottom (button click) uses motion spring for natural deceleration

* fix: respect user scroll-up during streaming and re-pin on scroll back to bottom

* fix: stabilize activity stream and tool progress rendering

Activity now stays chronologically consistent and avoids duplicate reasoning/justification.
Tool rows show running state and duration earlier with smoother pending behavior.
Removed legacy trim/timestamp paths to prevent message drops and simplify Activity UI.

* fix: reverted regression of revert functionality

* fix: stabilize chat activity layout and tool progress behavior

Stopped user messages from re-animating when new user messages appear.
Made Activity and aggregated tool rows wrap inline consistently without misaligned headers/icons.
Fixed tool part matching so duration and shine no longer reset across pending/in-progress status updates.

* fix: restore activity mode behavior and stabilize tool rendering

Re-enabled Collapsed/Summary/Detailed/Changes defaults for Activity and tool expansion.
Fixed tool row wrapping/alignment, removed user fade re-animations, and disabled FadeInOnReveal animations.
Improved tool status handling to reduce timer/shine resets and made reasoning bold render as normal text.

* refactor: simplify chat tool and message rendering

Replace activity-group rendering with flat inline part rendering
Remove obsolete tool output and justification activity settings
Keep user message animation and improve edit/apply-patch file path headers

* style: narrow chat and composer columns equally

* fix: Tool status checks and abort flow regression

* fix: improve multi-file apply patch header rendering

* fix: make timeline message jump scroll to selected chat entry

Conversation Timeline clicks now reliably scroll to the selected message in chat
Timeline dialog waits for navigation result before closing to avoid silent failures
Chat message scrolling now resolves the active chat scroller consistently

* chore: remove branch-added chat tests and clean lint leftovers

Removes newly added chat and message test files from this branch
Fixes lint blockers in ProgressiveGroup, ToolPart, and messageStore
Keeps type-check, lint, and build passing after cleanup

* fix: keep composer status row closest to the input

Status/todo row now renders below queued messages and attachments.
Linked GitHub issue/PR chips stay above status so input context order is consistent.
Improves chat composer readability while assistant is working.

* fix: disable assistant header animations and polish chat status behavior

Assistant message header now renders without transition effects
Removed a lint issue from the working placeholder props
Chat status and message flow updates improve consistency during active turns

* feat: increase bottom spacer in mobile chat for better scrolling

* feat: add sorted chat mode with progressive activity rendering

Adds global chat render mode settings for sorted and live behavior
Renders non-stop assistant output in Activity as messages complete
Disables streaming text animation in sorted mode and restores classic reasoning/justification blocks

* fix: stabilize chat timeline navigation and older history loading

Prevents auto-loading older history from scroll and keeps loading behind the button
Fixes timeline jump-to-message behavior that snapped back to the bottom
Reduces chat list flicker and empty-state glitches on large session pagination

* fix: stabilize sorted activity text and hide reasoning timers

Prevented sorted justification from appearing before message completion by requiring explicit non-stop finish.
Removed duration values for sorted Thinking and Justification blocks.
Kept smooth sorted auto-follow behavior tuned to reduce bottom-jump jitter.

* refactor: simplify chat tool rendering paths and add parts docs

Centralized tool icon mapping into a shared presentation helper used by grouped and expandable rows.
Removed static-tool-specific branches from ToolPart to keep it focused on expandable tools.
Added a new DOCUMENTATION.md explaining where to change tool descriptions and rendering behavior.

* feat: enhance event stream handling for message parts

- Refactor event key building functions to improve clarity and reduce redundancy.
- Introduce `applyPartDelta` method in message store for handling delta updates to message parts.
- Update `useEventStream` to utilize the new `applyPartDelta` method for processing incoming deltas.
- Enhance text extraction logic to accommodate new part types and improve merging behavior.
- Implement logic to skip delta-driven updates for ongoing text/reasoning parts to prevent overwriting.
- Adjust server-side text merging logic to handle trimmed text comparisons and improve consistency.

* feat: enhance OpencodeService with delta handling and stale delta management

- Introduced globalSseStaleDeltas to track stale deltas.
- Added methods to generate keys for delta and updated part events.
- Updated global SSE loop to skip processing stale deltas.
- Refactored global SSE event handling to improve performance and reliability.

refactor: remove unused streaming part handling in messageStore

- Eliminated streaming part queue and associated logic.
- Simplified message merging logic by removing redundant checks.
- Streamlined message handling to improve clarity and maintainability.

chore: clean up unused streaming text normalization functions

- Removed deprecated functions related to streaming text identity and merging.
- Simplified normalizeStreamingTextPayload to focus on current functionality.

* refactor: simplify streaming event passthrough

Use SDK-native global event streaming in the UI client
Remove dead server-side SSE normalization helpers
Keep streaming payloads closer to upstream behavior

* revert: remove message memory settings UI

Drop the restored message memory settings screen
Keep session settings focused on current defaults
Preserve chat history limits as internal behavior

* feat: add compact collapsed Activity preview in sorted mode

Collapsed Activity now shows a live preview of the latest 7 entries
Older activity rows roll up behind a +N more indicator
The +N more indicator is clickable to expand the full Activity

* fix: adjusted activity changed files text prominence

* fix: make sorted mode snap to bottom when not streaming

* perf: show chat scrollbar only on user-driven scrolling

Chat scrollbar now stays hidden during automatic follow-to-bottom
Scrollbar visibility is gated by real user scroll intent
Reduced scroll overhead by removing timer-based intent tracking

* refactor: align web and vscode session activity streaming

Route VS Code SSE through SDK-based proxy flow
Restore controlled web session activity tracking and cooldowns
Keep chat activity behavior consistent across runtimes

* fix: adjusted muted foreground themes color

* fix: stabilize session activity and attention updates

Keep streamed chat state in sync as sessions complete
Restore reliable attention indicators for inactive sessions
Tighten server and UI event handling for chat updates

* feat: add chat render previews and file icon visibility setting

Added animated chat render mode previews in Chat settings.
Added a new toggle to show or hide file icons across tool rows.
Persisted chat render, activity, and mermaid modes to shared settings file.

* fix: update default GitHub OAuth client ID

Use the new OpenChamber org GitHub OAuth app ID by default.
Keep web server and VS Code auth defaults in sync.

* fix: sync chat settings across VS Code chat views

Broadcast settings changes to both sidebar and editor chat webviews.
Apply synced settings in each webview via shared settings refresh.
Ensures chat render mode updates appear immediately in editor tabs.

* fix: stop writing session folders file in VS Code workspaces

Disable disk persistence for session folders in VS Code webview runtime
Keep session folder state in VS Code/webview storage instead of project files
Preserve existing behavior for non-VS Code runtimes

* style: polish expanded tool panels and bash output readability

Removed extra borders/labels in expanded tool cards for a cleaner UI
Added softer scroll shadows and full-height decorative activity line
Switched bash command/output to code-style typography with tighter spacing

* feat: add default-open tool controls for chat activity

Added Chat settings toggles to open Bash and Edit tools by default
Default-open behavior now applies across both sorted and live activity rendering
Users can still manually collapse default-open tools, plus bash output height was reduced

* fix: align chat loading skeleton with message layout

Match skeleton width and horizontal alignment to chat message column
Add one-line tool-style skeleton rows with subtle circular placeholders
Increase top spacing so loading state no longer touches chat header

* fix: prevent blank screen when adding inline diff comments

Sanitize persisted inline comment drafts to ignore invalid data
Harden diff annotations against malformed line numbers
Fail safely when annotation updates error instead of crashing the view

* fix: streamline VS Code archived sessions sidebar

Show archived sessions in VS Code only for the active workspace directory.
Render archived sessions as a flat list in VS Code without project-root subfolders.
Hide the "Open in Side Panel" session action in VS Code.
2026-03-12 23:45:45 +02:00
Bohdan Triapitsyn 79143bff4c feat: massive chat reliability + UX pass (web/desktop/mobile/vscode) (#593)
## Added Features
- Add VS Code save-as-image flow for assistant messages via webview bridge + native save dialog.
- Add hourly desktop update checks after startup.
- Add new tool output display mode: `Changes` (auto-expand edit/write/patch only; keep activity expanded; mode guidance text).
- Add GitHub PR attachment flow in chat input with PR picker + attached PR chip/details.
- Add mobile overlay presentation for GitHub Issue and PR pickers (shared with desktop picker content).

## Fixes
- Save-gate project icon updates until explicit Save; allow icon removal with same save-gated behavior.
- Restore clickable chat action buttons in sticky header mode (desktop + Firefox hit-target issue).
- Clamp sticky user messages to bounded chat height and allow internal scrolling.
- Prevent drawer context crash during iPad/tablet orientation switching.
- Improve text-selection action menu placement on narrow screens.
- Move assistant message time into clock tooltip; keep duration display clean.
- Hide `Link GitHub Issue` row in VS Code chat input area (GitHub flow is not yet ready there).
- Remove laggy close animation in text-selection popover; keep open motion/positioning behavior.
- Fetch branches when picker opens and cache empty; show loading state instead of false “No branches found”.
- Fix share-image export metadata rendering (theme background resolution, timestamp rendering, footer alignment).
- Scope MCP services status/toggles to active directory to avoid cross-project leakage.
- Improve long user-message clamp behavior (40% cap variant, hidden scrollbar, scroll shadows, expansion detection).
- Fix desktop `Check for Updates` menu handler; prevent duplicate checks; show clear success/error toasts.
- Stabilize long user-message scrolling behavior (follow-up hardening).
- Avoid premature web update failure on slower servers.
- Restore user message image previews + fullscreen gallery navigation payload.
- Repair desktop chat drag-and-drop image attachments when native drop coords are missing.
- Move GitHub issue linking entry into Add attachment menu.
- Align header context usage percentage visuals with context panel.
- Align `@` file search with active project in all runtimes.
- Route `@` file discovery through OpenCode SDK `find.files`; remove legacy `/api/fs/search` reliance.
- Make chat `@` mention behavior consistent with files-style behavior.
- Keep status-row todos in stable order after status changes; add compact status icons; replace noisy priority labels.

## Refactors / UX Consistency
- Simplify chat attachment model and remove project file picker path.
- Keep composer focused on `@` mention file flow.
- Use direct `Attach files` action in VS Code instead of attachment dropdown path.
- Unify issue/PR picker behavior between desktop and mobile overlays.
2026-03-04 01:41:01 +02:00
Asuta b68a10571c feat(vscode): support drag-and-drop file attachments in chat (#535)
* feat(vscode): implement file drop API for handling dropped files in VS Code

* refactor(vscode): deduplicate attachment parsing for picked and dropped files
2026-02-27 20:21:12 +02:00
Nguyễn Ngô ThượngandBohdan Triapitsyn d0e4dc2704 feat(mcp): add MCP Config Manager UI (#473)
* feat(session-folders): drag-to-folder DnD, sort by activity, and UX improvements

- Add DraggableSessionRow wrapping each session row so the whole row is
  draggable; stopPropagation prevents outer group-reorder DnD from firing
- Add DroppableFolderWrapper + SessionFolderDndScope (inner DndContext
  scoped per group) with closestCenter collision detection
- DragOverlay matches exact width/height of dragged row so cursor stays
  aligned
- Folder header highlights (ring + primary colour) when a session hovers
  over it during drag
- + button on folder header opens a dropdown: 'New session' / 'New folder'
- + button on each folder row creates a session scoped to that folder
- Empty folders are no longer auto-deleted (removed .filter(sessionIds.length>0)
  from addSessionToFolder / removeSessionFromFolder / cleanupSessions)
- Sessions inside a folder are sorted by most-recent activity (same
  compareSessionsByPinnedAndTime logic used everywhere else)
- Sort comparator now takes sessionAttentionStates so lastUserMessageAt /
  lastStatusChangeAt is used when newer than session.time.updated; all
  sort call-sites and their useMemo/useCallback deps updated accordingly
- Remove foldersMap from cleanup effect deps to prevent cascade re-renders
  when folders change; read current value via getState() instead

* fix(session-folders): new session is placed into the correct folder

sendMessage() was calling useSessionManagementStore.createSession()
directly, bypassing the targetFolderId logic in useSessionStore.createSession.

Fix: read targetFolderId from draft at the top of the draft branch in
sendMessage, then call addSessionToFolder immediately after the session
is created and before the draft is closed. Also propagate targetFolderId
through openNewSessionDraft options and NewSessionDraftState type.

* feat(session-folders): add sub-folder support (one level deep)

- SessionFolder gains optional parentId field for hierarchy
- createFolder accepts parentId to create sub-folders
- deleteFolder cascades to remove all child sub-folders
- SessionFolderItem renders sub-folders before sessions in body;
  new sub-folder button (RiFolderAddLine) visible at depth 0 only
- renderOneFolderItem in SessionSidebar builds the tree recursively;
  sub-folders are indented via depth prop (ml-3 on root's children)
- Persist/hydrate parentId correctly from localStorage

* feat(session): add delete confirm dialogs and improve subtitle UX

- Add confirmation dialogs before deleting sessions or folders
- Show relative time (e.g., '2h ago', '35min ago') for recent sessions
- Replace +/- diff numbers with file change count (e.g., '3 files changed')
- New folders use default name without forcing rename
- Cleaner, less cluttered session list UI

* fix(session-folders): skip folder cleanup while sessions are loading

Prevents race condition on reload where cleanupSessions() runs before
the server returns the full session list, causing folder-session
assignments to be incorrectly wiped from localStorage.

* feat(mcp): add MCP Config Manager UI

- Backend: CRUD lib (mcp.js) + 5 REST routes (GET/POST/PATCH/DELETE /api/config/mcp/:name)
- Frontend: Zustand store (useMcpConfigStore), McpSidebar with status dots, McpPage with redesigned UX
  - Textarea command editor: paste full shell commands, auto-split into args, one-arg-per-line view
  - Compact env editor: wide value column, show/hide toggle, paste .env format support
  - Header card: name, type badge, enabled toggle, connect/disconnect button
- Navigation: 'mcp' added to sidebar sections in SettingsView
- TypeScript: all packages pass type-check clean

* fix(mcp): remove constant truthiness lint error in McpPage

Replace '(isNewServer || true) &&' with unconditional render — type
selector should always be visible so the user can switch between
stdio and remote without recreating the server.

* fix: add MCP server management to VS Code backend

- Implement CRUD operations for MCP servers via bridge API
- Support local and remote MCP server configurations with validation
- Add VS Code webview endpoints for MCP server management

* feat: add project-level MCP server configuration

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-02-22 23:22:33 +02:00
Iuliia Ivashko 138772e66e fix(managed-runtime): secure auth and lifecycle control across runtimes (#437)
* feat: add OpenCode server authentication with auto-generated passwords

* fix(auth): separate user env and managed OpenCode password state

* fix(auth): enforce env precedence and managed password rotation across runtimes

* fix(vscode): rotate managed auth on startup and harden webview proxy

* build: add dev icons and config for Tauri desktop development

* fix(runtime): start managed OpenCode via CLI and expose active API port

* fix(managed-runtime): control OpenCode lifecycle and surface secure diagnostics

* docs: remove VS Code plugin test runbook
2026-02-17 18:01:57 +02:00
Bohdan Triapitsyn 844562749d feat(ui): enable drag-and-drop attachments and image previews in chat (#390)
* feat(BottomTerminalDock): add close button next to the fullscreen toggle in the dock

* style: replace hardcoded gradient with theme value in shine text variant

* fix(header): adapt instance button for desktop only

* refactor(chat): polish sticky turn UX and message action rows for better readability

Switch to stable sticky-only turn behavior and redesign user/assistant action controls (placement, hover rules, ordering, spacing, selection-safe clamp) to reduce visual noise and improve interaction flow.

* feat(chat/message): refactor buttons in messages footer

* feat: enhance image preview functionality in chat messages

- Added a new ImagePreviewDialog component to handle image previews with navigation support.
- Updated ToolOutputDialog to utilize the new ImagePreviewDialog for displaying images.
- Modified the ToolPopupContent type to include a gallery of images and an index for the current image.
- Removed the old inline image display logic from ToolOutputDialog.
- Improved file handling in the file store, including better MIME type guessing and handling of server paths.
- Introduced a new API endpoint for handling large session message payloads, allowing for better management of multi-file attachments.
- Updated the VSCode bridge to support session message requests with appropriate headers and body handling.

* feat(proxy): implement SSE forwarding and enhance generic API request handling

* feat(chat): support submitting only queued messages

* feat: add image preview transition state

* fix: default VSCode view to draft and fixed sessions list regression
2026-02-11 19:28:22 +02:00