-
Bugfix:
eca-chat-resumeno longer silently no-ops when the picker is shown from a fresh welcome buffer. Filter out chats the server can't open (id is nil, which happens for legacy DB rows that pre-date the per-chat:idfield) so they aren't offered, and surface auser-errorwhenchat/openreports:found? falseor no buffer was registered for the chat-id, instead of falling through thewhen-let*. -
Add
eca-chat-resumecommand to pick and resume a past chat viacompleting-readannotated with model, message count and relative update time. AResume a previous sessionlink in the welcome area triggers it, andeselects it in the transient menu. The originating empty welcome buffer is killed once the resumed chat is in place, so no stray tab is left behind. -
Bugfix: avoid
(wrong-number-of-arguments color-rgb-to-hsl 0)crash on chat startup in a no-window/TTY Emacs.face-backgroundreturns the literal sentinel"unspecified-bg"(not nil) on non-graphic frames, which was fed intocolor-lighten-name/color-darken-namefromeca-chat--update-expandable-block-facesandeca-table-update-faces. Neweca-safe-face-backgroundhelper ineca-util.elnormalizes the"unspecified-bg"/"unspecified-fg"sentinels to nil so thewhen-let*guards short-circuit and the faces remain at their default appearance on TTY. #244 -
Add inline
[+ Add MCP server]form and per-rowremovebutton (with two-step inline confirmation) on the MCPs settings tab. Handles the newtool/serverRemovednotification so the UI updates live without a server restart. -
eca-doctornow emits a### Self-checksection probing whether the loaded code carries the chat-RET expand fix:eca-chat-mode-mapRET still points ateca-chat--key-pressed-return,eca-chat--key-pressed-returnreferences the expandable clause before the markdown-link clause, the nearest expandable block label has RET bound in its text-propertykeymap, and the loadedeca-chat-expandablefile's path (with a stale-.elcwarning when the on-disk.elis newer). Each failing probe pushes a targeted hint pointing at commitdac33d8and the appropriate remediation (pull master, restart Emacs,M-x byte-recompile-directory, or start a new chat so blocks get the new label keymap). -
Bugfix: restore RET expanding/collapsing
>blocks in the chat. The recent markdown-link-on-RET clause was intercepting the toggle when the label text was fontified as a link; the expandable check now runs before the link follow, and the label's text-property keymap also binds RET so it works in evil normal state without evil-collection wiring. -
Add
eca-doctorcommand that prints a markdown-formatted diagnostic report to*eca-doctor*for bug reports: a## Versionssection (same aseca-version) plus a## Chatsection sourced from the active session's auto-detected chat buffer. #242 -
Bugfix: stop the
mergedworktree fallback ineca-sessionfrom auto-adding an unrelated ancestor (typically$HOME) as a second workspace folder. The fallback previously declared two paths to be the "same repo" purely ongit rev-parse --git-common-direquality, which is also true for any subdirectory of a git-tracked HOME (yadm/chezmoi/git init ~); a buffer whosedefault-directoryis~(e.g. the doom scratch buffer) would then triggerworkspace/didChangeWorkspaceFoldersand append~to the session created withC-u M-x ecaon~/.config/eca, showing both roots in the chat modeline. The match ineca--session-for-worktreenow additionally requires that ROOT and the candidate folder are not in an ancestor/descendant relationship (real worktrees are always sibling directories, never nested), via the neweca--paths-nested-phelper. True sibling worktrees continue to merge as before. #238 -
Generate
chatIdclient-side at chat-buffer creation. #231 -
Scope model/agent/variant selection to the active chat:
eca-chat-select-model,eca-chat-select-variantandeca-chat--set-agentnow write the local change to the chat buffer the user is interacting with (current buffer when invoked from inside a chat, falling back to the session's last chat) and include:chatIdin the resultingchat/selectedModelChanged/chat/selectedAgentChangednotification.eca-chat-config-updatedhonors the newchatIdfield onconfig/updated: when present, per-chat fields (selectModel,selectAgent,selectVariant,selectTrust) apply only to that chat's buffer; when absent, the legacy session-wide path applies to every chat (still used for the initialconfig/updatedafterinitialize). Fixes a long-standing leak where changing the model in one chat would silently overwrite another chat's display. #231 -
Add
eca-process-wrapper-function, an optional function called with the resolved server command and the list of workspace folder paths just beforemake-processis invoked. It must return a (possibly transformed) command list, letting users run the eca server inside any command-prefix-style sandbox (firejail, bubblewrap, jai, docker, ...) directly from elisp — without a separate launcher script and with the workspace roots dynamically injected into the sandbox's directory whitelist. New "Sandboxing" section in the README documents the recommended pattern withjaiandfirejailexamples. -
Expand the
eca-send-process-iddocstring to explicitly cover sandbox tools that hide or remap the host PID (firejail, bubblewrap, jai, containers): in those setups the server's parent-process watchdog would otherwise see an invalid PID and shut down right after startup, so the variable must be set to nil. Cross-referenceseca-process-wrapper-function. -
Bugfix: avoid
void-function flymake-diagnostic-codeerrors ineca-editor--flymake-diagnostics(which surfaced as a 30sTimeout waiting for editor diagnostics responseoneditor/getDiagnosticswhenever any flymake diagnostic was processed) byfboundp-guarding theflymake-diagnostic-codeaccessor;:codeis simply omitted when the accessor isn't shipped with the running Emacs'sflymake. The existingdeclare-functionis preserved so the byte-compiler stays quiet on Emacsen that do expose it. #228 -
Hide the
[-](remove workspace) mode-line button when only a single workspace folder is active in the session, since removing the last workspace is disallowed anyway; the[+](add workspace) button is still always shown. The button reappears automatically as soon as a second workspace is added. -
Stop the CPU peg when watching a long uncollapsed write-tool argument body stream in (#234). The dominant cost was
gfm-mode's markdown matchers (markdown-match-italic/markdown-match-bold/markdown-match-code) re-fontifying the whole growing body on every chunk, becausetoolCallPrepareupdates go througheca-chat--update-expandable-content'sdelete-region+ reinsert branch (which marks the entire body as needing fontification and lets jit-lock then run the matchers over it on the next redisplay). Introduce a buffer-local customfont-lock-fontify-region-function(eca-chat--fontify-region) that walks the requested region ineca-no-fontifytext-property runs and only delegates the un-tagged sub-ranges tofont-lock-default-fontify-region; tagged sub-ranges are skipped entirely, so jit-lock has nothing to do on them. ThetoolCallPreparearm now tags the streaming body witheca-no-fontifyat the source string, which travels naturally through(propertize content 'line-prefix ...)insideeca-chat--update-expandable-contentand through the toggle-open path'seca-chat--insert, so every code path that lands the body in the buffer carries the tag. Cleanup is automatic: whentoolCalledarrives it reinserts un-tagged final content via the samedelete-region+ reinsert path, after-change-functions clearfontified, and jit-lock then refontifies normally on the next redisplay (no manualfont-lock-flushneeded). Measured via a newtool-prep-stream-{skip,full}benchmark inbenchmarks/eca-chat-bench.el(synchronous fontify per chunk to simulate jit-lock under--batch): at body 1000/5000/20000 chars, per-chunk cost drops from ~4.5ms / ~62ms / ~908ms to ~173µs / ~144µs / ~225µs respectively (26× / 432× / 4030×). The post-fix path is FLAT in body size (only the existing buffer churn remains), and the 20000-char run drops from 265 GCs to 0. -
Make long chats fast again. Replaced linear
(overlays-in (point-min) (point-max))scans in singleton-overlay accessors and the expandable-block id lookup with O(1) buffer-local caches (a per-overlay slot for the chat singletons; a per-buffer hash table for expandable blocks). Region-scoped the per-turnfont-lock-ensureand end-of-streameca-chat--align-tables/eca-chat--beautify-tablesto start ateca-chat--last-user-message-posinstead ofpoint-min, so cost is bounded by the current turn rather than the full chat history. Measured on a synthetic 500-turn fixture (benchmarks/eca-chat-bench.el): per-streamed-chunk insert dropped from ~426µs to ~6µs (71×), expandable-id lookup from ~324µs to ~0.4µs (790×), and the end-of-stream finalize block from ~2.6s to ~9ms (274×). -
Add
benchmarks/eca-chat-bench.el, a chat-rendering benchmark harness runnable viaM-x eca-chat-bench-runoreask emacs --batch -l benchmarks/eca-chat-bench.el -f eca-chat-bench-run. Builds synthetic chat buffers of varying size and times the streaming hot paths (insertion-point lookup, streamed chunk insert, expandable lookup/update, end-of-stream finalize); prints a markdown table with per-call µs / wall-ms / GC counts so future changes can be validated quantitatively. -
Bugfix: avoid corrupted redisplay in
emacs -nwby using single-cell BMP glyphs (●/○) for the chat mode-line trust indicator on terminal frames; emoji defaults are preserved on graphic frames. Configurable viaeca-chat-trust-on-symbol-tty/eca-chat-trust-off-symbol-tty. #229 -
Expire the cached GitHub releases list used for eca server update detection. Previously
eca-process--releases-cachewas populated once per Emacs session and never invalidated, so long-running Emacs instances never noticed new eca server releases (andeca-install-server, despite its docstring, was bound by the same stale cache). Neweca-server-releases-cache-ttldefcustom (default 3600 seconds;nildisables expiry;0always refetches) refreshes the cache on access once expired. New autoloaded interactive commandeca-server-check-updatesclears the cache and reports whether the installed server is up to date, has an available upgrade, or whether the GitHub check failed.eca-install-servernow also clears the cache before fetching the latest version, matching its "Force download the latest eca server." docstring. #230 -
Render assistant-emitted
ChatImageContentfromchat/contentReceivedinline in the chat buffer (e.g. images returned by the OpenAIimage_generationtool). Decodes the base64 payload and displays it via an overlay carryingcreate-imagedata (overlay rather thandisplaytext-property somarkdown-mode/gfm-modefont-lock cannot strip it onfont-lock-ensure), falling back to a[Image: <mediaType>, <N> bytes]placeholder on TTY frames or when the image type is unavailable in the current Emacs build. Sizing is configurable viaeca-chat-image-max-width(defaultfit-window— scales to the chat window body width, hard-capped byeca-chat-image-window-fit-cap, default 800px) andeca-chat-image-max-height(defaultnil); a fixed integer ornil(unconstrained) are also accepted foreca-chat-image-max-width. Per-chat zoom:eca-chat-image-zoom-in(C-c C-z +/C-c C-z =),eca-chat-image-zoom-out(C-c C-z -), andeca-chat-image-zoom-reset(C-c C-z 0) mutate the buffer-localeca-chat-image-scale(multiplier on the resolved width cap, default1.0) byeca-chat-image-scale-step(default1.2) and re-rasterize already-rendered images viaimage-flushso existing images snap to the new size without re-fetching. Save inline images to disk viaeca-chat-save-image-at-point(C-c C-z s), also reachable viaRETormouse-2directly on the image overlay (with a tooltip describing media type / byte count), with the destination customizable viaeca-chat-save-image-directory(defaultworkspace-root, resolving to<root>/.eca/images/; also accepts a literal path string ornilfordefault-directory) andeca-chat-save-image-filename-format(defaulteca-image-<YYYYMMDD-HHMMSS>.<ext>); saved bytes are pulled directly from the existing overlay viaimage-property :data, so display zoom does not affect the file and no server round-trip is needed. -
Syntax-highlight the inline
eca-completeghost-text overlay using the buffer'smajor-mode, with optional foreground dimming for a "ghost" look similar to Cursor / Copilot. Configurable viaeca-completion-syntax-highlight(defaultt) andeca-completion-overlay-dim-ratio(default0.5,nildisables dimming).eca-completion-overlay-facenow composes with APPEND priority on top of font-lock faces instead of overriding them. #227 -
Sync the mode-line trust indicator on chat resume by honoring the new
selectTrustfield onconfig/updated: the shield/flame icon now matches the server's persisted trust state for the resumed chat instead of always defaulting to off (eca #426). -
Defer fontification during chat streaming: replace the per-chunk
font-lock-ensurewith a debounced idle-timer scheduler plus a single guaranteedfont-lock-ensureat end-of-stream, configurable viaeca-chat-fontify-debounce-interval(default 0.15s,nildisables intermediate fontify). Cuts font-lock CPU by ~99% on fast-streaming models and ~84% on slow-streaming ones. -
Improve mode-line trust indicator: 🔥 (fire) when ON, 🛡 (shield) when OFF. Customizable via
eca-chat-trust-on-symbol/eca-chat-trust-off-symbol. -
Add
eca-chat-remove-workspace-rootcommand and[-]mode-line button to remove a workspace folder from a running session. Both 'add' and 'remove' is in the transient menu (W a/W r). -
Add
eca-chat-mode-line-formatfor customizable chat mode line layout. #184 -
Fix top-level
(require 'tab-line)causing side effects wheneca-chat-tab-lineis nil. #195 -
Add support for
chat/openedserver notification, enabling the/forkcommand to open forked chats as new tabs. -
Fix
eca--uri-to-pathreturning wrong path on Windows (leading slash before drive letter). #200 -
Add
eca-settings-tab-lineoption to disabletab-line-modein settings buffers. #201 -
Replace
helpful-headingface dependency witheca-settings-headingdefface. #201 -
Fix settings tab re-registration reordering tabs. #201
-
Add support for
chat/askQuestionserver request, enabling tools to ask users questions with selectable options in the chat buffer. #338
- Add
eca-chat-save-to-filecommand. #95 - Fix chat not being closed. #89
- Fix: check existing eca sessions when opening chat. #88
- Add rewrite feature.
- Fix issue with
view_ediffdisplaying empty buffers in ediff. #86 - Fix user message contexts and file paths not being sent. #112