Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Bugfix: resuming a previous session after a server restart no longer reopens it as a `*Closed session*` (mode-line) / `This chat is closed` buffer that rejects new prompts. `eca-chat-exit` (run on restart) marks the most-recent chat buffer `eca-chat--closed` but leaves it in the session registry; `eca-chat-opened` was treating that live-but-closed buffer as "already known" and replaying the resumed history into it. It now treats a registered buffer marked `eca-chat--closed` as stale, killing the leftover closed buffer and creating a fresh, writable one in its place.
- Color the Doom Emacs workspace tabline by ECA session status: orange when a chat waits for approval/question, dim yellow while running. Enabled automatically on Doom (`:ui workspaces` module), disable with `eca-doom-workspace-tabs`.
- Add `eca-chat-go-to-next-attention` and `eca-chat-go-to-next-attention-in-project` commands to jump to the next chat waiting on the user (pending tool call approval or unanswered question), cycling across all sessions or only the current one.
- Make the chat history/output, the `---` separator and the task area read-only so only the progress, `@`-context and prompt input lines stay editable, preventing accidental edits to previous messages and assistant output. The `read-only` text property is applied up to the progress area (with stickiness tuned so typing still works) and kept in sync as content streams in and as blocks are expanded. Toggle off via the new `eca-chat-read-only-history` (default `t`).
Expand Down Expand Up @@ -52,7 +53,7 @@

## 0.6.0

- Add `eca-chat-save-to-file` command. #95
- Add `eca-chat-save-to-file` command. #95
- Fix chat not being closed. #89
- Fix: check existing eca sessions when opening chat. #88
- Add rewrite feature.
Expand Down
13 changes: 11 additions & 2 deletions eca-chat.el
Original file line number Diff line number Diff line change
Expand Up @@ -3466,18 +3466,27 @@ update the title and return without creating a duplicate buffer.
Otherwise, creates a new chat buffer for a server-initiated chat
\(e.g. /fork or replay via chat/open\) and registers it under the
real chat-id so subsequent `chat/contentReceived' notifications
render into it."
render into it.

A registered buffer marked `eca-chat--closed' (left behind by
`eca-chat-exit' on restart) is treated as stale, not reused, so a
resumed chat gets a fresh writable buffer."
(let* ((chat-id (plist-get params :chatId))
(title (plist-get params :title))
(existing (eca-get (eca--session-chats session) chat-id)))
(cond
((and existing (buffer-live-p existing))
((and existing (buffer-live-p existing)
(not (buffer-local-value 'eca-chat--closed existing)))
;; Already known: propagate title (if any) but do not duplicate.
(when title
(with-current-buffer existing
(setq-local eca-chat--title title)))
(eca-chat--force-tab-line-update))
(t
;; Any live buffer reaching here is a stale closed one; kill it so
;; it doesn't linger as an orphan `:closed' buffer.
(when (and existing (buffer-live-p existing))
(kill-buffer existing))
(cl-incf eca-chat--new-chat-id)
(let ((new-buffer (eca-chat--create-buffer session)))
(with-current-buffer new-buffer
Expand Down
52 changes: 52 additions & 0 deletions test/eca-chat-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -467,4 +467,56 @@ does not treat the first line as metadata. Returns FN's value."
(expect 'markdown-follow-thing-at-point :to-have-been-called)
(expect 'browse-url :not :to-have-been-called)))))

(describe "eca-chat-opened"
;; Regression: resuming after a restart must not replay into a stale
;; closed buffer left in the registry by `eca-chat-exit'.
(it "creates a fresh buffer when the registered chat buffer is closed"
(spy-on 'eca-chat--force-tab-line-update)
(let* ((session (make-eca--session))
(closed-buf (generate-new-buffer " *test-closed-chat*")))
(unwind-protect
(progn
(with-current-buffer closed-buf
(setq major-mode 'eca-chat-mode)
(setq-local eca-chat--id "chat-AAA")
(setq-local eca-chat--closed t))
(setf (eca--session-chats session)
(eca-assoc (eca--session-chats session) "chat-AAA" closed-buf))
(eca-chat-opened session (list :chatId "chat-AAA" :title "My chat"))
(let ((registered (eca-get (eca--session-chats session) "chat-AAA")))
;; The registry now points at a brand new, live, writable buffer.
(expect (buffer-live-p registered) :to-be-truthy)
(expect registered :not :to-be closed-buf)
(expect (buffer-local-value 'eca-chat--closed registered) :to-be nil)
(expect (buffer-local-value 'eca-chat--id registered)
:to-equal "chat-AAA")
;; The stale closed buffer is cleaned up, not left lingering.
(expect (buffer-live-p closed-buf) :to-be nil)
(when (and (buffer-live-p registered)
(not (eq registered closed-buf)))
(kill-buffer registered))))
(when (buffer-live-p closed-buf)
(kill-buffer closed-buf)))))

(it "reuses the existing buffer when it is live and not closed"
(spy-on 'eca-chat--force-tab-line-update)
(let* ((session (make-eca--session))
(live-buf (generate-new-buffer " *test-open-chat*")))
(unwind-protect
(progn
(with-current-buffer live-buf
(setq major-mode 'eca-chat-mode)
(setq-local eca-chat--id "chat-BBB")
(setq-local eca-chat--closed nil)
(setq-local eca-chat--title "old title"))
(setf (eca--session-chats session)
(eca-assoc (eca--session-chats session) "chat-BBB" live-buf))
(eca-chat-opened session (list :chatId "chat-BBB" :title "new title"))
(let ((registered (eca-get (eca--session-chats session) "chat-BBB")))
;; No duplicate buffer; the title is propagated in place.
(expect registered :to-be live-buf)
(expect (buffer-local-value 'eca-chat--title live-buf)
:to-equal "new title")))
(when (buffer-live-p live-buf)
(kill-buffer live-buf))))))
;;; eca-chat-test.el ends here
Loading