Commit Graph
100 Commits
Author SHA1 Message Date
Serhii Dziupin bfcb555aa5 Merge pull request #2928 from openchamber/feat/stale-last-directory-fallback-1be0
fix(sessions): recover new chats from a deleted lastDirectory
2026-08-15 15:48:25 +03:00
Serhii Dziupin 8c91c82b62 Merge pull request #2929 from openchamber/feat/fix-stale-merged-pr-status-3a1e 2026-08-15 08:07:14 +03:00
Serhii Dziupin 11a136bba8 Merge pull request #2913 from Gautam0507/fix/2803-session-retention-persist 2026-08-15 08:05:48 +03:00
Serhii Dziupin 13b25eea64 Merge pull request #2920 from alohaninja/fix/ui-unused-runtimefetch-import 2026-08-15 08:03:17 +03:00
Serhii Dziupin 6b1e677aaf Merge pull request #2925 from makeittech/feat/fix-issue-2903-embedded-subagent-2cbc
fix(ui): restore embedded subagent history (#2892, #2903, #2919, #2922)
2026-08-15 07:07:39 +03:00
Serhii Dziupin 47acc48300 docs(changelog): note context-panel subagent history fix
Co-authored-by: serkraser <serkraser@gmail.com>
2026-08-15 03:51:35 +00:00
Serhii Dziupin 4e6ac40801 fix(ui): load embedded history while visibility stays inactive
Keep session-message loads and retries on messagesEnabled so a mounted
session-chat panel can materialize history even before the visibility
handshake, and cover the enabled-gate with the real hook.

Co-authored-by: serkraser <serkraser@gmail.com>
2026-08-15 03:51:33 +00:00
Serhii Dziupin 11537de734 Merge pull request #2924 from makeittech/feat/fix-clawhub-label-typo-5c30
Fix ClawHub display name typo in Skills Catalog (#2895)
2026-08-15 06:43:56 +03:00
Serhii DziupinandSerhii Dziupin de0455e10e test(ui): drop tautological #2903 enabled-gate helper
The helper reimplemented `if (!enabled) return []` locally, so those
cases never exercised the real hook. Keep the snapshot-builder and
source-contract coverage instead.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-15 03:42:45 +00:00
Serhii DziupinandSerhii Dziupin 0a07bc7e03 test(ui): assert #2903 records through the real snapshot builder
Replace the fake enabled-gate helper's store fixture with
buildSessionMessageRecordsSnapshot so the regression covers the same
record shape ChatContainer renders.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-15 03:40:37 +00:00
Serhii DziupinandSerhii Dziupin 903638db94 fix(ui): show embedded subagent history while visibility stays inactive
Busy context-panel session chats could render only the working-status row
when the iframe booted inactive or lost its visibility handshake, because
message reads shared the composer/background-work gate. Keep message
subscriptions enabled in the mounted session-chat panel so materialized
history remains visible (#2903, #2892).

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-15 03:40:37 +00:00
Serhii DziupinandSerhii Dziupin a216b6871f Fix ClawHub display name typo in Skills Catalog (#2895)
The Skills Catalog source dropdown showed "ClawdHub"; the registry brands
itself as ClawHub. Update curated/fallback labels across web, UI, and VS Code,
align docs, and add regression tests.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-14 18:49:31 +00:00
Serhii Dziupin 9761f5ff3c Merge pull request #2910 from openchamber/feat/third-party-integrations-dashboard-6ead 2026-08-14 19:53:13 +03:00
Serhii Dziupin 3ae8d3fe45 feat: include restart notice in integration success toasts 2026-08-14 15:19:26 +00:00
Serhii Dziupin 10b928a786 feat: refresh third-party integration descriptions 2026-08-14 15:19:26 +00:00
Serhii Dziupin 6e55df9bed feat(integrations): point third-party plugins at OpenChamber packages 2026-08-14 15:19:26 +00:00
Serhii DziupinandSerhii Dziupin 0e0580f95c fix(integrations): remove unnecessary Refresh control
Data already reloads on mount, after mutations, and after Apply & Restart,
so the section Refresh button was redundant noise.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-14 15:19:26 +00:00
Serhii DziupinandSerhii Dziupin 22ffc4c87f fix(integrations): drop duplicate accordion status; reword copy
Remove the repeated installed/status block inside expanded third-party
cards, and phrase page/plugin descriptions as adding a subscription to
use as an OpenChamber provider.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-14 15:19:26 +00:00
Serhii DziupinandSerhii Dziupin 70f8f2e15b fix(icons): inject missing sprite symbols for newly added icons
The icon sprite was injected once and never updated, so HMR/new glyphs
like telegram-fill left <use> refs empty. Append missing symbols on render.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-14 15:19:25 +00:00
Serhii DziupinandSerhii Dziupin a838f62b41 feat(integrations): add Discord/Telegram Coming soon placeholders
Show greyed non-interactive messenger cards with a Coming soon badge
(matching integration card chrome, no expandable controls), and refresh
third-party statuses immediately after Apply & Restart clears pending
plugin restarts.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-14 15:19:25 +00:00
Serhii DziupinandSerhii Dziupin 2303740b4a fix(integrations): restore brand icons and provider logo fallbacks
The Integrations cards render Icon(plugin.icon), so Claude Code /
Command Code / Cursor brand glyphs belong in the sprite. Also restore
claude-code.svg and the Command Code ProviderLogo fallback used after
Set up opens the Providers page. Leave out unused opencode.svg.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-14 15:19:25 +00:00
Serhii DziupinandSerhii Dziupin 53ab67ea35 refactor(integrations): drop unused logos, brand icons, and card wrapper
Keep only the Settings Integrations page wiring: three plugins, i18n,
search/metadata, and the plugins-store registry boolean needed for
failure status. Remove ProviderLogo fallbacks, SVG assets, custom sprite
icons, and the separate IntegrationCard layer.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-14 15:19:25 +00:00
Serhii DziupinandSerhii Dziupin 0aba428e95 fix(i18n): strip BOM from zh-TW settings after import insert
Adding the third-party integrations import shifted the existing UTF-8 BOM
onto line 2 and tripped no-irregular-whitespace.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-14 15:19:25 +00:00
Serhii DziupinandSerhii Dziupin af380081c2 feat(settings): add third-party integrations dashboard
Add a Settings → Integrations page for installing and managing the three
supported OpenCode provider plugins (Claude Code, Command Code, Cursor),
with search, i18n, and plugin-registry status wiring.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-14 15:19:25 +00:00
Serhii DziupinandSerhii Dziupin e99f6560be fix(scheduled-tasks): prevent dual-server double dispatch of daily tasks (#2713)
* fix(scheduled-tasks): claim schedule occurrences across server instances

Two OpenChamber servers sharing project config each armed timers and both
dispatched the same daily/weekly/cron/once slot (#2710). Claim the occurrence
in shared config under a cross-process write lock before creating a session.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* fix(scheduled-tasks): harden occurrence claim failure and lock ownership

Address PR review blockers: release running-slot bookkeeping when claim
throws, avoid silently dropping an armed occurrence after a due-slack sync,
verify lock-file ownership on release, and cover real on-disk lock behavior.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* fix(scheduled-tasks): always release running slot on state-write failures

Wrap runTask bookkeeping in finally so claim, manual-start, and completion
lock timeouts cannot stuck-run a task; drop the diskNext claim guard that
suppressed later occurrences; recover unparseable locks via mtime age.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* fix(scheduled-tasks): stop re-arming past nextRunAt and clear stuck running

Only schedule future nextRunAt values so once-task losers and claim-failed
paths cannot spin delay-0 retries. Clear past once nextRunAt on claim, and on
completion-write failure retry terminal status so manual runNow still returns
the session instead of a hard 500 with lastStatus stuck running.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* fix(scheduled-tasks): release write chain on lock acquire timeout

withProjectWriteLock left the in-process promise chain pending when
acquireProjectFileLock timed out, wedging every later project write and
stranding runTask before finally. Always release the chain; surface
persistError on run; record once claim failures in task state.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

---------

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-13 16:14:42 +03:00
Serhii DziupinandSerhii Dziupin 86e6a2ae76 Remove verified dead declarations (#2714)
* chore: remove verified dead declarations

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* chore: narrow unused internal exports

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* chore: remove newly exposed dead helpers

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* chore: remove unused deep-link serializer

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* test: drop two tests that assert on copies of the code

mainLayoutMobileSidebarMount read MainLayout.tsx and SessionSidebar.tsx as
strings and asserted on source substrings down to exact indentation, so it
failed on formatting rather than behaviour. useProjectSessionSelection.test
reimplemented the hook's visitNodes logic inside the test file and asserted
against that copy, so it could not observe the hook at all.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* test: repair sync suites that had rotted while unrunnable

No runner executed packages/ui, so these drifted from the source unnoticed:
two imported helpers that are no longer exported, one directory-store stub
predated the session field routeMessage reads, and the WebSocket fake missed
the mandatory url-token mint plus the close event the socket wrapper reads.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* test: stop the web suite failing on timeouts and a hand-copied mock

The Git suites drive a real git binary, so the 5s default made a valid suite
fail differently per run. The gitApiHttp mock listed ~70 export names by hand
and fell behind the source; it now derives every stub from the real module,
which the added shared-UI aliases make resolvable.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* test: run every suite from one command and in CI

packages/ui (232 files) and packages/vscode (22) had no test script at all, CI
ran neither, and 9 vscode files could never run because Node cannot resolve
their extensionless TypeScript imports. Three electron files sat outside every
script list, one of them importing vitest, which that package does not depend
on. A runner gives each file its own process, since these suites keep
module-level singletons and fail by load order when sharing one.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* chore: delete a superseded repro harness and a completed plan

The issue-2638 harness needed lsof, overrode process.platform and spawned real
servers, and nothing referenced it; event-stream/rebind.test.js now covers the
same hub-pinned-to-the-old-port behaviour. The pairing v2 plan described relay
and the pairing UI as out of scope, both of which shipped.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* docs: point at the theme tools and record the github barrel invariant

convert-vscode-theme and harmonize-theme were referenced nowhere, so the
theme-authoring reference now names them. The github barrel is loaded through
await import('./index.js') and destructured per route, which no static report
can see; documenting that is what stops the next cleanup from deleting it.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* test: repair merge drift in bridge and route-registry mocks

upstream/main gained upsertProviderConfig on bridge-system-runtime and a
PATCH scheduled-task route after this branch forked. Their test doubles
were never updated to match:
- bridge-system-runtime.test.js: add upsertProviderConfig to the
  opencodeConfig mock so the import resolves.
- sse-routes.test.js: add app.patch to the route registry stub.

---------

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-13 15:30:54 +03:00
Serhii Dziupin 61533ed881 fix(desktop): keep minimize on the taskbar and send only close to the tray (#2874)
With tray background mode on, the in-app minimize button hid the window,
so the taskbar entry disappeared while the native title-bar and taskbar
minimize still performed a normal minimize. Minimize now always minimizes;
the setting gates the close path only.

The persisted key stays `desktopMinimizeToTrayEnabled` so existing settings
keep working; the visible label becomes "Close to the system tray" in every
locale.

Closes #2857
2026-08-13 15:10:48 +03:00
Serhii Dziupin d3011a6247 fix(sessions): snapshot send target so a project switch cannot reroute a pending send (#2871)
Snapshot the new-session draft (and keep the existing-session target captured) at
submit time, then use that snapshot for draft materialization and routing instead
of re-reading live selection state after async preparation.

Fixes #2222
Fixes #2315
2026-08-13 12:31:33 +03:00
Serhii Dziupin 8a6eca5597 fix(sessions): select the active project using session ownership (#2865)
* fix(sessions): select the active project using session ownership

Keep a same-project worktree session while its rendered map is stale, but switch to the remembered or fallback session when the current session is known to belong to another project.

Fixes #2317

* test(sessions): pin project-switch ownership recovery in the selection hook
2026-08-13 10:51:40 +03:00
Serhii Dziupin 87f2d21054 chore: remove terminal debug screenshots 2026-08-10 10:14:46 +03:00
Serhii Dziupin f8b841c922 chore: remove PR evidence screenshots for rail badge background 2026-08-10 10:14:00 +03:00
Serhii DziupinandUbuntu 2d96454fc9 fix(i18n): shorten pending-restart applying label across locales (#2791)
Co-authored-by: Ubuntu <ubuntu@watcher.tail9db222.ts.net>
2026-08-10 09:51:31 +03:00
Serhii Dziupin 5e6d1f8b01 fix(ui): restore rail badge background via surface theme tokens (#2790)
* fix(ui): expose surface.* tokens so bg-surface-muted renders

* docs: add PR evidence screenshots for rail badge background
2026-08-10 08:23:06 +03:00
10606d79d3 fix(git): enable core.longpaths for worktree population (#2746) (#2747)
* fix(git): enable core.longpaths for worktree population

Worktrees live under a deep OpenCode data-dir path, so Windows checkouts
of deeply nested repos failed bootstrap with "Filename too long". Enable
Git core.longpaths before git reset --hard (web + VS Code) and surface
clearer path-length guidance when the filesystem still rejects a path.

Fixes #2746

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* chore(vscode): keep ensureWorktreeLongpaths private

Avoid an unused export in the VS Code git service; the helper stays
local to populateWorktreeWithLockRecovery.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-07 09:52:48 +03:00
Serhii Dziupin 87dbc59bf1 fix(chat): do not replay entry animations for already-seen fresh messages (#2124) (#2732) 2026-08-07 09:16:34 +03:00
Serhii Dziupin 3fc136c95e fix(terminal): keep default terminal tab names unique after closing tabs (#2718) (#2731) 2026-08-07 09:09:32 +03:00
Serhii DziupinandSerhii Dziupin bc380e6e1b perf: cut cold-start download 58% and startup heap 22% via measured chunk-graph fixes (#2742)
* fix(ui): update session-switch-resync test to current handleEvent/setSessionTodos signatures

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* perf(build): split Shiki grammars/themes, CodeMirror legacy modes, and @pierre/diffs into on-demand chunks

Merging @shikijs/langs into one manual vendor chunk made the first language
request download every grammar (7.4 MB raw / 1 MB gzip). Letting Rollup split
these packages per dynamically imported module downloads only the languages,
themes, and modes actually used — matching how the worker build already
behaves. @pierre/diffs is split the same way so its pure patch parser (used by
the eager tool renderer) no longer drags the Shiki-importing render stack into
the startup graph.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* perf(ui): restore lazy heavy views and stop eager settings-graph loading

- MainLayout: DiffView/FilesView/GitView/PlanView return to lazyWithChunkRecovery
  (they were silently made static in 2031e3b4 while their Suspense wrappers
  remained), keeping the CodeMirror and @pierre/diffs stacks out of startup.
- ContextPanel: same lazy treatment for its Diff/Files/Git/Plan/Walkthrough
  tabs, with null Suspense fallbacks.
- CommandPalette imported getSettingsNavIcon from SettingsView, statically
  pulling the entire settings surface (SkillsPage -> CodeMirrorEditor -> vim
  mode, theme registry -> @pierre/diffs) into the eager graph; the helper now
  lives in lib/settings/metadata.
- The windowed SettingsWindow mounts only after its first open: rendering the
  lazy component closed made React fetch the SettingsView chunk graph at
  startup.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* perf(ui): keep @pierre/diffs + Shiki out of the eager chat graph and defer diff worker warmup

- DiffWorkerProvider no longer statically imports @pierre/diffs/worker or the
  theme registry, and no longer spawns 3 workers plus a main-thread shared
  highlighter during mount. Pools are created on demand through a dynamic
  module load, warmed via requestIdleCallback after startup settles, and
  useWorkerPool notifies consumers when a pool becomes available.
- ToolPart's rich diff preview moves to lazily loaded ToolPartDiffPreview;
  the plain-text patch (PlainDiffFallback) renders while the chunk loads,
  mirroring the existing error fallback. Theme registration happens during
  render inside the lazy module so PatchDiff never renders unregistered ids.
- ChatInput mounts its lazy ToolOutputDialog only after the first attachment
  preview opens instead of fetching the dialog chunk on the draft screen.
- getMarkdownSyntaxVars moves to a pierre-free markdownSyntaxVars module so
  eager code-rendering consumers stop importing the registration module.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* perf(web): load ghostty-web and Nerd Fonts on first terminal use

- ghostty-web (638 KB raw JS + WASM VT) is dynamically imported when a
  terminal actually mounts; TerminalView stays eagerly importable for the
  bottom dock.
- The ~2 MB of CDN Nerd Fonts are no longer preloaded and force-loaded on
  every cold start. index.html exposes an idempotent
  __openchamberEnsureNerdFonts hook; TerminalViewport requests it on mount
  and waits up to 2s so a cached font is in place before the glyph atlas is
  built, while a cold CDN fetch never blocks the terminal. Runtimes without
  the hook (VS Code, mobile) resolve immediately, matching their existing
  fallback-font behavior.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

---------

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-07 09:01:31 +03:00
Serhii Dziupin 834d2edb87 feat(ui,server): surface active instance service URLs in About settings (#2669)
Show the running instance's local server URL and tunnel URL (when a
tunnel is active) as labeled, click-to-open buttons on the About page.
/api/system/info now reports the instance port and tunnel URL, resolved
lazily from the tunnel runtime so each Git-worktree instance identifies
itself in the UI without parsing terminal output.

Refs OPE-194
2026-08-07 00:25:57 +03:00
Serhii Dziupin fe331c093b fix(ui): never auto-send the message queue into a streaming turn (#2642)
The queue auto-send gate treated a missing session status entry as idle,
but the server's /session/status map only lists busy/retry sessions — a
missed busy event leaves no entry while a turn is still streaming. On a
client that missed the busy event (e.g. mobile reconnect window), queued
follow-ups were dispatched into the running turn; OpenCode then merged
both prompts into one model response instead of serializing them.

Extract resolveQueuedSessionStatusType and mirror useSessionActivity's
fallback: a trailing in-flight assistant message means the session is
still busy, so the queue waits for the real idle edge. Subscribe the
effect to messages so the queue drains as soon as the turn completes
even when status events were missed.
2026-08-06 22:19:50 +03:00
Serhii Dziupin 9368d44b99 Merge pull request #2685 from makeittech/fix/gh-2558-ios-shift-enter
fix(composer): restore Shift+Enter newline on iOS
2026-08-06 18:13:29 +03:00
Serhii Dziupin 7a9e5099d7 Merge pull request #2256 from bashrusakh/fix/issue-2244-todo-event-resilience
fix(sync): route directory-less todo updates
2026-08-06 18:09:24 +03:00
Serhii Dziupin 43f03608ec Merge pull request #2682 from makeittech/feat/gh-2634-pending-question
feat(sessions): show a pending-question indicator on sessions
2026-08-06 13:06:24 +03:00
Serhii Dziupin 99a6dcd052 Merge branch 'main' into feat/gh-2634-pending-question 2026-08-06 13:04:16 +03:00
Serhii Dziupin 319e58686b Merge pull request #2663 from makeittech/fix/ope-236-question-tool-stuck
fix(sync): route question/permission replies by the request's own session directory
2026-08-06 11:39:53 +03:00
Serhii Dziupin f36bfee0d5 Merge pull request #2660 from makeittech/fix/ope-216-ui-password-daemon
fix(cli): generate a UI password for bare --ui-password in daemon/serve mode
2026-08-06 10:34:19 +03:00
Serhii Dziupin c9f39f7604 Merge pull request #2695 from makeittech/fix/gh-2638-chat-ui-freeze
fix(server): rebind message-stream upstreams after a managed OpenCode restart (#2638)
2026-08-06 10:15:51 +03:00
Serhii Dziupin aa5f37a25a Merge pull request #2661 from makeittech/feat/ope-231-opencode-hostname
feat(server): validate OPENCHAMBER_OPENCODE_HOSTNAME bind hostname
2026-08-06 10:10:10 +03:00
Serhii Dziupin e2fb4f8f21 Merge pull request #2665 from makeittech/fix/ope-178-yaml-frontmatter
fix(web): parse agent frontmatter as leniently as OpenCode
2026-08-06 10:07:17 +03:00
Serhii Dziupin 5964a7d8ba Merge pull request #2698 from makeittech/feat/gh-2583-markdown-loops
feat(tasks): support markdown scheduled-task loops in .agents/loops
2026-08-06 09:57:49 +03:00
makeittech 0a4fd7c5fb docs(tasks): add loops quick-start to the scheduled-tasks page
User-facing onboarding for markdown loop tasks: where .agents/loops
files live (project + user scope), a copy-paste sample file, the
frontmatter field table, and the behavior contract (file authoritative,
off by default, rename/malformed semantics, run-now still available).
Also lists the cron schedule type in the UI task creation steps, which
the page previously omitted.
2026-08-06 09:56:11 +03:00
makeittech 9b6b90504c fix(tasks): cover syncProject wiring and allow deleting orphans after file removal
Review follow-up:

- runtime.test.js: add syncProject wiring tests with a real temp-dir
  project and real project-config runtime — asserts reconcileLoopTasks is
  driven with the discovered loops when the project path is known (task
  created, nextRunAt computed) and that plain listing is used when the
  path cannot be resolved (reconcile not called).
- service.js: DELETE on a loop-owned task is rejected with a 400 only
  while its loop file still exists on disk; once the file is gone the
  orphan task can be deleted directly instead of waiting for the next
  reconcile. Tests use real temp files for both branches.
- DOCUMENTATION.md: delete semantics updated accordingly.
- PR description refreshed for the final HEAD (test counts, reconciliation
  contract, evidence wording).
2026-08-06 09:49:31 +03:00
makeittech 59a6c1b70d fix(tasks): guard loop name length and surface loop ownership in the UI
Review follow-up:

- Reject loop files whose frontmatter name exceeds MAX_TASK_NAME_LENGTH
  (80): task names are clamped at storage time, so a raw name longer than
  the limit could never match the stored task identity. The file is treated
  as malformed (definition: null) instead of creating an unreachable
  definition; MAX_TASK_NAME_LENGTH is now exported from project-config.js
  and shared with loops.js.
- Surface loop-sourced tasks in the scheduled-tasks dialog: tasks carrying
  loopFile show a 'Managed by loop file <path>' note, and the enable
  toggle / edit / delete actions are disabled with an explanatory tooltip,
  since the file remains authoritative and would revert any such change.
  run-now stays available. New locale keys added to all 11 message files
  (i18n parity test enforces exact key sets).
- ScheduledTask type gains an optional loopFile field (additive, unknown
  to older clients).
2026-08-06 09:38:18 +03:00
makeittech 359225d363 fix(tasks): harden loop reconciliation against renames and malformed files
Review fixes for the markdown loop feature:

- Loop-owned tasks now adopt by loop file path, not task name, so renaming
  a loop (frontmatter name or UI rename) renames the task in place instead
  of leaving a stale duplicate that keeps running the old definition;
  orphan duplicates of the same file are unscheduled.
- Unparseable loop files are reported to the scheduler as
  definition:null entries: a task whose file still exists is kept with its
  last good definition, and only a genuinely removed file unschedules it.
  Transiently malformed files (mid-edit, bad merge) no longer delete tasks
  or their runtime state.
- Adoption preserves UI-only execution fields (goalEnabled, goalTokenBudget,
  permissionAutoAccept, variant) that the portable format does not define.
- DELETE on a loop-sourced task now returns 400 with guidance to remove the
  loop file, instead of being silently undone by the next reconcile.
- Loops default to enabled: false; discovery of repository content never
  auto-executes scheduled sessions unless the file explicitly enables them.

Regression tests for each fix; DOCUMENTATION.md updated.
2026-08-06 09:28:31 +03:00
Serhii Dziupin 352cd2e1e2 Merge pull request #2699 from makeittech/fix/gh-2577-stale-running-ui
fix(sync): finalize tool parts orphaned by an interrupted turn after settlement (#2577)
2026-08-06 09:00:54 +03:00
Serhii Dziupin 4fa753773e Merge pull request #2707 from openchamber/feat/file-tree-depth-limit-497d
fix(fs): keep file-tree list paths through symlinks (#2627)
2026-08-06 08:33:05 +03:00
Serhii DziupinandSerhii Dziupin ee57088dfe fix(fs): keep list paths in requested space through symlinks
Closes openchamber/openchamber#2627

Listing a directory through a workspace symlink was returning realpath
entry paths. The file tree then rejected nested expand toggles because
those paths fall outside the workspace root.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-05 17:40:01 +00:00
Serhii Dziupin 22766fe0ac Merge pull request #2706 from openchamber/feat/terminal-escape-key-behavior-ba7f
fix(ui): Escape in terminal reaches PTY instead of closing panel
2026-08-05 20:21:09 +03:00
Serhii Dziupin 23a677b45e Merge pull request #2679 from makeittech/feat/gh-2364-git-changes-count
feat(ui): show the changed-files count badge on the Git rail surface
2026-08-05 17:06:24 +03:00
Serhii Dziupin a0436b3aa8 Merge pull request #2678 from makeittech/feat/gh-2447-focus-after-context
feat(chat): refocus composer after adding message to context
2026-08-05 16:40:32 +03:00
Serhii Dziupin f7157d3137 Merge pull request #2487 from pascalandr/fix/2405-settings-persistence
fix(settings): persist collapsed message preference
2026-08-05 16:39:05 +03:00
Serhii DziupinandSerhii Dziupin 341b4b45c8 fix(ui): stop painting git activity dots on non-git rail surfaces
The badge PR inverted showActivityDot to !== 'git', so editor/terminal/diff
picked up the blue activity dot whenever git had changes. Git already shows
a numeric badge; other surfaces should stay quiet. Also split the count
aria/tooltip strings into singular/plural keys.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-05 13:26:46 +00:00
Serhii Dziupin 856ab452dc docs(server): document onOpenCodeRestarted lifecycle dependency 2026-08-05 14:16:18 +03:00
Serhii Dziupin e3202df037 fix(sync): finalize tool parts orphaned by an interrupted turn after settlement
When a managed OpenCode process dies mid-turn (crash, health-check
restart), the persisted turn never settles: the trailing assistant
message has no time.completed and its tool parts stay pending/running
forever — the server never finalizes them (anomalyco/opencode#19023).
The existing settle-triggered tail refresh refetches the same stale
records, so the UI kept running tool timers and working styling
indefinitely (#2577).

Now, when a session is authoritatively settled (session.idle/
session.error event, or an authoritative status snapshot lowering a
previously busy session) and the trailing assistant message is still
unfinished with active tool parts and no pending question/permission,
the orphaned parts are finalized locally as error/"Interrupted" with
an end time — the same shape OpenCode itself writes for cancelled
tools. The mark is gated on an explicit idle status (absent status is
"unknown", never judged), never applies while busy (including
question/permission waits), and a later terminal event or refresh
supersedes it while a stale running refresh cannot regress it (the
reducer and materializer already preserve final statuses).

Fixes #2577
2026-08-05 14:15:03 +03:00
Serhii Dziupin 2fcfe511a5 feat(tasks): support markdown scheduled-task loops in .agents/loops
Adds markdown-based scheduled-task definitions ("loops") discovered from
.agents/loops/*.md (project scope, ancestor directories up to the
worktree root) and ~/.agents/loops/*.md (user scope), mirroring the
skills discovery pattern.

File format: YAML frontmatter (name, schedule cron, enabled, model as
provider/model, optional agent/timezone) plus the markdown body as the
execution prompt. Discovery and parsing live in
scheduled-tasks/loops.js; project-config gains reconcileLoopTasks which
runs inside the project write lock on every syncProject:
- identity by task name; a loop takes over a matching task, preserving
  its id and runtime state (markdown wins on conflict with JSON)
- tasks whose loopFile is gone are unscheduled; JSON tasks are never
  removed
- new loops are created under deterministic loop:<scope>:<name> ids
- project scope shadows user scope on name collisions
- malformed files are skipped with a warning and never block valid ones

Runtime state stays in the project config/state store; it is never
written to the markdown files. Module documentation updated with the
file format and reconciliation rules.

Fixes #2583
2026-08-05 14:11:28 +03:00
Serhii Dziupin 13f6a0280d fix(server): rebind message-stream upstreams after a managed OpenCode restart
When the managed OpenCode process exits but a server survives on the old
port (Windows: killProcessOnPort is a no-op, so the orphaned process tree
keeps the port), restartOpenCode() times out waiting for the port and
spawns a fresh server on a NEW port. HTTP/proxy traffic follows the new
port, but the global message-stream hub's upstream SSE reader stays pinned
to the old server's /global/event stream — that connection never closes —
so new events never reach the UI and the chat stops updating until the
app is restarted (#2638).

Lifecycle now fires an optional onOpenCodeRestarted hook after a
successful managed restart; index.js wires it to the new
messageStreamRuntime.rebindUpstream(), which restarts the shared hub
(its reader re-dials buildOpenCodeUrl → the current port) and closes
directory-scoped sockets so their per-connection readers rebuild against
the new port. External servers are untouched (their port cannot change).

Fixes #2638
2026-08-05 13:59:17 +03:00
Serhii Dziupin 7ff86a3bc7 fix(composer): restore Shift+Enter newline on iOS
CodeMirror defers Enter on iOS (and Chrome Android): the real keydown is
captured without running the keymaps and the keymaps then run against a
synthetic keydown that dispatchKey builds from the key name alone, with
no modifier keys. The composer's Shift+Enter thus arrived as a plain
Enter, and on devices where Enter sends (iPad Safari/PWA, where the
desktop layout applies) it submitted the message instead of inserting a
newline.

Record the real Enter keydown's shift state on the view's contentDOM and
restore it onto the deferred synthetic event before the caller's
onKeyDown policy runs, so Shift+Enter means newline again on every
runtime. Plain Enter behavior is untouched: on iOS it still follows the
same deferred path it used before this change.

Fixes #2558
2026-08-05 13:44:36 +03:00
Serhii Dziupin c1ba631964 feat(sessions): show a pending-question indicator on session rows
Adds a per-session pending-question badge to sidebar rows, driven by the
live directory-store question state through a dedicated per-session
subscription channel so unrelated streaming never re-renders rows.

Collapsed parent rows roll up pending questions of hidden descendants
from their owning directory stores without bootstrapping them. Question
state is cloned on session delete/archive so badges clear when sessions
disappear. Adds the questionChangeCallbacks sync performance counter,
i18n keys for all locales, and unit tests for the subscription channel
and scope selection.

Fixes #2634
2026-08-05 13:41:48 +03:00
Serhii Dziupin 9b7c032524 feat(ui): show the changed-files count badge on the Git rail surface
Replaces the plain activity dot on the context panel rail's Git button with
a numeric badge of the changed-files count from the git store status, so the
count is visible at a glance without opening the Git surface. Large counts
cap at 99+ to keep the pill within the 36px button. The badge is reflected
in the button's accessible label and the hover tooltip.

Fixes #2364
2026-08-05 13:37:46 +03:00
Serhii Dziupin 654e9cdb64 feat(chat): refocus composer after adding message to context
After the add-to-context (context pin) action completes successfully, move
focus back to the chat input so the user can keep typing immediately. Uses
the existing focusChatInput helper and the requestAnimationFrame refocus
pattern already used by the model/agent selectors.

Fixes #2447
2026-08-05 13:37:20 +03:00
Serhii Dziupin ff5814a731 fix(web): parse agent frontmatter as leniently as OpenCode
parseMdFile now matches gray-matter (used by OpenCode) for file shapes
OpenChamber previously failed to parse: frontmatter whose closing '---'
sits at end-of-file without a trailing newline, a UTF-8 BOM prefix, and
YAML with unquoted colons in scalar values (via the same sanitizer
OpenCode applies). OpenCode parses these files, so OpenChamber must
too: otherwise the whole file was treated as the prompt body and a
save rewrote the existing YAML block into the body, prepending a
duplicate frontmatter block.

Refs OPE-178
2026-08-05 11:48:31 +03:00
Serhii Dziupin 0116739111 fix(sync): route question/permission replies by the request's own session directory
Answering a question tool (or a permission prompt) could leave the session
permanently stuck on "asking question": resolveDirectoryForBlockingRequest
returned the containing child-store key, which only proves containment.
For a worktree session (or any session whose record is grouped under a
parent project store), the reply was addressed to the parent directory's
OpenCode instance, where the pending request does not exist - the server
answered QuestionNotFoundError, the local request was removed, and the
trailing question-tool part stayed running with no recovery until Stop.

Resolve the directory from the request's own session record (server-
confirmed ownership: session.directory, then project.worktree) before
falling back to the containing store key. When a reply/reject comes back
not-found, also enqueue the settled-running-tool tail materialization so
the tool part converges to the server's actual state instead of leaving
the UI stuck.

Refs OPE-236
2026-08-05 11:41:40 +03:00
Serhii Dziupin ddae6f2545 feat(server): validate OPENCHAMBER_OPENCODE_HOSTNAME bind hostname
The env var was already read and passed to the managed OpenCode server
spawn, but any non-empty string was accepted. Reject values that are
not a valid IP (IPv4/IPv6, brackets allowed) or DNS-style hostname with
a clear [config] error and fall back to the secure loopback default so
a typo can never silently bind a non-loopback address.

Refs OPE-231
2026-08-05 11:24:14 +03:00
Serhii Dziupin 41a2e3781d fix(cli): generate a UI password for bare --ui-password in daemon/serve mode
The grand tunnel restructuring removed the CLI's auto-generated UI
password, so `openchamber -d --ui-password` (no value) silently started
an unauthenticated server instead of creating a password as in 1.8.1.

Restore generation for an explicit --ui-password flag without a value:
the password is generated before either launch path, passed to the
daemon/foreground process via OPENCHAMBER_UI_PASSWORD, persisted in the
instance state file, and surfaced once in human/quiet/json output.

Refs OPE-216
2026-08-05 11:24:10 +03:00
Serhii Dziupin 746e0d4abd Merge pull request #2617 from openchamber/feat/unexpected-message-content-edbc
fix(walkthrough): replace raw provider-login error with a readiness blocker
2026-08-04 17:33:48 +03:00
Serhii Dziupin 1765322ab6 Merge pull request #2622 from openchamber/feat/model-override-persistence-5865
fix(ui): persist manual model override across delegated subtask completion (#2404)
2026-08-04 17:27:03 +03:00
Serhii Dziupin fcd4eead56 Merge pull request #2621 from openchamber/feat/oauth-only-provider-auth-a542
fix(providers): hide API key form for OAuth-only providers
2026-08-04 15:45:52 +03:00
Serhii Dziupin f47110c66f Merge pull request #2616 from makeittech/feat/restore-archived-sessions-2346
feat: add restore/unarchive for archived sessions
2026-08-04 13:41:00 +03:00
Serhii Dziupin 932d224916 feat: add german translations for session restore 2026-08-04 13:29:03 +03:00
Serhii Dziupin 577aca0437 feat: add restore/unarchive for archived sessions
Archived sessions had no way back to the active list: the only available
action was "Delete permanently". Add restore per session (sidebar context
menu, Archive page row) and in bulk (sidebar selection bar).

The OpenCode server cannot clear time.archived over HTTP — session.update
only applies the field for a finite number, so an omitted key is a no-op
and null is silently ignored (verified against opencode 1.18.12). Restore
therefore writes time.archived = 0: every client-side reader classifies
archive state by truthiness, so 0 reads as active in the UI, the event
reducer, and the OpenCode app/TUI.

The server's time_archived IS NULL list filter still excludes such rows,
so the global session cache no longer issues an archived:false request
for its active list. Full and per-directory loads now fetch once with
the inclusive flag and split client-side via splitGlobalSessionsByArchived,
which also halves per-directory refresh requests. Directory bootstrap
keeps the server filter because live child stores must not hold archived
sessions; a restored session re-enters its live store through the
authoritative session.updated event.

unarchiveSession/unarchiveSessions follow the archiveSession contract:
wait for server confirmation before reconciling stores, runtime-guard
every reconciliation, preserve partial batch results, and fail loudly
when the server keeps the session archived instead of toasting a
successful no-op.

Closes #2346
2026-08-04 13:21:04 +03:00
Serhii Dziupin 397b9840b7 Merge pull request #2595 from openchamber/feat/ctrl-l-add-selection-to-chat-58c2
feat(ui): Ctrl/Cmd+L adds selected text to chat
2026-08-04 11:33:51 +03:00
Serhii Dziupin f710b49854 Merge pull request #2600 from kydorn/fix/linux-terminal-launcher-misattribution
fix(electron): require TerminalEmulator category for terminal appId on Linux
2026-08-04 10:22:25 +03:00
Serhii DziupinandClaude Sonnet 5 f02969548a fix(files): hide desktop-only reveal action and label download in browser clients
Reveal-in-file-manager was always offered whenever the server exposed
revealPath, including in a plain browser tab where there is no local
file manager to reveal into. Gate it behind a new isBrowserClientRuntime
check (web platform, no Electron shell) and relabel the save action to
"Download" for that case, since it triggers a browser-style file
download rather than an in-place save.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 17:11:41 +03:00
Serhii Dziupin 166b89d8db Merge pull request #2596 from openchamber/fix-Kimi-for-Coding-usage
fix: Kimi for Coding usage showing 0% despite full consumption
2026-08-03 17:02:43 +03:00
Serhii Dziupin 635a70b24f fix: compute Kimi quota usage from used or remaining field 2026-08-03 16:39:14 +03:00
Serhii Dziupin 5414bad539 Merge pull request #2589 from openchamber/feat/opencode-argv-0-path-a7e7
fix(desktop): strip AppImage ARGV0 leak corrupting zsh argv[0] (#2588)
2026-08-03 16:37:54 +03:00
Serhii Dziupin 9289dea4a0 Merge pull request #2586 from openchamber/feat/skill-renaming-content-preservation-c1d5
fix(skills): preserve SKILL.md content when renaming
2026-08-03 15:04:48 +03:00
Serhii Dziupin 94c9ac3153 Merge pull request #2592 from openchamber/terminal-open-debug
fix(terminal): start PTY before viewport mounts without dropping output or replies
2026-08-03 12:50:19 +03:00
Serhii Dziupin 88937ade72 fix(terminal): start PTY before viewport mounts without dropping output or startup replies
Terminal creation no longer waits for the Ghostty viewport to report its
size: it starts the PTY immediately with a container/font-derived
provisional size (falling back to 80x24), then resizes once the real
viewport dimensions are known, with a dedupe guard while sizing settles.

Starting the shell earlier means it can emit device/theme queries before
a browser terminal is attached to answer them, so the server now answers
primary device attribute queries itself (Fish blocks ~10s on this at
startup) and bun-pty buffers output emitted before a data subscriber
attaches. Also fixes a few WebSocket transport reconnect races surfaced
by session creation now overlapping renderer setup.
2026-08-03 12:29:32 +03:00
Serhii Dziupin 42eb18f82a Merge pull request #2576 from openchamber/feat/repository-local-skills-discovery-41dc
fix: discover repository-local .agents skills (#1159)
2026-08-03 12:20:42 +03:00
Serhii Dziupin 824d1fbbf4 Merge pull request #2584 from openchamber/docs/credit-bestsithineu-linux-desktop
docs(changelog): credit @BestSithInEU for Linux desktop AppImage work
2026-08-03 08:44:04 +03:00
Serhii Dziupin 97156eef87 docs(changelog): credit @BestSithInEU for Linux desktop AppImage work
path-open-utils.mjs and its smoke test are byte-for-byte identical to
his PR #1335 (opened 2026-05-19, review findings addressed the same
day, community-verified on CachyOS but never given a maintainer
review), and linux-app-discovery.mjs retains most of that PR's
implementation. The [1.17.0] changelog entry credited the PR that
carried this work forward but omitted the original author.
2026-08-03 08:42:58 +03:00
Serhii Dziupin aa1875b6f0 perf: start terminal sessions before renderer mounts 2026-08-02 16:03:32 +03:00
Serhii Dziupin 7afec99f80 Merge pull request #2551 from openchamber/feat/git-session-context-5ef9
fix(git): pin simple-git to opened project path for session discovery
2026-08-02 13:05:15 +03:00
Serhii Dziupin 5d24d6cb2a fix: dedupe shared worktree ownership
Cherry-picked from claude branch 7b3bec48 (fix: dedupe shared worktree ownership and title sessions immediately).

Only the worktree-dedupe half applies to main: partitionWorktreesByRegisteredProject and its callers (SessionSidebar, ElectronMiniChatApp, MobileApp, MobileSessionsSheet) plus tests and sidebar docs.

The session-title half (title sessions immediately) remains on the claude branch: the session-title runtime and its claude-code transcript translator imports do not exist on main and cannot build there until the Claude harness lands.
2026-08-02 09:34:49 +03:00
Serhii Dziupin 4ae3debf54 Merge pull request #2534 from openchamber/feat/bc-38abb61a-bce6-4a01-a189-c569346927fa-33b6
fix(files): stop autosave data loss on load lag and binary files
2026-07-30 16:21:59 +03:00
Serhii Dziupin f1e52b0cbc fix: stop terminal open/switch from rewriting state per output chunk (#2536)
Opening a terminal rebuilt the WASM terminal twice and rewrote the
persisted session snapshot on every streamed output chunk, so the cost
grew with each open terminal and could crash under load.

- Move PTY scrollback to a standalone buffers map keyed by directory and
  tab id; output no longer touches sessions, so the tab strip, the
  project-action monitor and the persist projection stay referentially
  stable while chunks stream.
- Memoize partialize and add a dedup storage adapter that skips writes
  when the persisted projection is unchanged.
- Reuse the idle terminal WebSocket across tab switches (15s grace)
  instead of re-authenticating and replaying the snapshot on every attach.
- Key the viewport by directory + tab only so createSession no longer
  tears down and rebuilds the Ghostty terminal.
- Reset the terminal in place on replay discontinuities instead of
  bumping the renderer generation.
- Scan the chunk array from the end (O(1) per write) instead of findIndex.

Adds regression tests for the buffer map, socket reuse and viewport key,
and documents the store invariants.
2026-07-30 13:48:24 +03:00
Serhii Dziupin 0d6ecbfc12 Merge pull request #2498 from kydorn/feat/linux-window-controls-style
feat(desktop): gate traffic-lights behind window-controls style setting
2026-07-30 10:47:39 +03:00
Serhii Dziupin 12802a540c Merge pull request #1608 from IbrahimKhan12/fix/dedupe-skill-commands-slash-menu
fix: dedupe skills shadowed by commands in the slash menu
2026-07-30 10:06:15 +03:00
Serhii Dziupin c84305bf7e Merge pull request #2501 from openchamber/feat/linux-appimage-tray-icons-b6fc
fix(desktop): Linux AppImage tray Show/Hide/Close and system file-manager icons
2026-07-29 16:22:39 +03:00
Serhii Dziupin d4e4dbc0ec Merge pull request #2520 from sergiofspedro/fix/settings-scrollbar
Fix/settings scrollbar
2026-07-29 14:37:44 +03:00