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
1 change: 1 addition & 0 deletions pi-coding-agent-menu.el
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ Used when starting a new session."
(when-let* ((chat-buf (pi-coding-agent--get-chat-buffer)))
(with-current-buffer chat-buf
(let ((inhibit-read-only t))
(pi-coding-agent--clear-render-artifacts)
(erase-buffer)
(insert (pi-coding-agent--format-startup-header))
(insert "\n")
Expand Down
11 changes: 11 additions & 0 deletions pi-coding-agent-render.el
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,16 @@ Returns a plist with:
:hidden-lines (if (and truncated-first-line (= hidden 0)) 1 hidden)
:line-map line-map-vec)))))

(defun pi-coding-agent--clear-render-artifacts ()
"Delete pi-owned render overlays in the current chat buffer.
This removes completed/pending tool overlays and diff overlays before
buffer reset or history rebuild, then clears the pending overlay slot so
buffer state and overlay state stay consistent. Tree-sitter overlays are
left alone."
(remove-overlays (point-min) (point-max) 'pi-coding-agent-tool-block t)
(remove-overlays (point-min) (point-max) 'pi-coding-agent-diff-overlay t)
(setq pi-coding-agent--pending-tool-overlay nil))

(defun pi-coding-agent--tool-overlay-create (tool-name &optional path)
"Create overlay for tool block TOOL-NAME at point.
Optional PATH stores the file path for navigation.
Expand Down Expand Up @@ -1831,6 +1841,7 @@ Note: When called from async callbacks, pass CHAT-BUF explicitly."
(when (and chat-buf (buffer-live-p chat-buf))
(with-current-buffer chat-buf
(let ((inhibit-read-only t))
(pi-coding-agent--clear-render-artifacts)
(erase-buffer)
(insert (pi-coding-agent--format-startup-header) "\n")
(when (vectorp messages)
Expand Down
31 changes: 31 additions & 0 deletions test/pi-coding-agent-menu-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,37 @@
;; Tool args cache should be empty
(should (= 0 (hash-table-count pi-coding-agent--tool-args-cache)))))

(ert-deftest pi-coding-agent-test-clear-chat-buffer-removes-pi-owned-render-overlays ()
"Clearing chat buffer removes stale pi-owned tool and diff overlays."
(with-temp-buffer
(pi-coding-agent-chat-mode)
(let ((inhibit-read-only t))
(insert "tool\n+ 1 added\n- 2 removed\n"))
(let ((tool-ov (make-overlay 1 5 nil nil nil))
(tool-count 0)
(diff-count 0))
(overlay-put tool-ov 'pi-coding-agent-tool-block t)
(setq pi-coding-agent--pending-tool-overlay tool-ov)
(pi-coding-agent--apply-diff-overlays 6 (point-max))
(dolist (ov (overlays-in (point-min) (point-max)))
(when (overlay-get ov 'pi-coding-agent-tool-block)
(setq tool-count (1+ tool-count)))
(when (overlay-get ov 'pi-coding-agent-diff-overlay)
(setq diff-count (1+ diff-count))))
(should (= tool-count 1))
(should (= diff-count 4)))
(pi-coding-agent--clear-chat-buffer)
(let ((tool-count 0)
(diff-count 0))
(dolist (ov (overlays-in (point-min) (point-max)))
(when (overlay-get ov 'pi-coding-agent-tool-block)
(setq tool-count (1+ tool-count)))
(when (overlay-get ov 'pi-coding-agent-diff-overlay)
(setq diff-count (1+ diff-count))))
(should (= tool-count 0))
(should (= diff-count 0))
(should-not pi-coding-agent--pending-tool-overlay))))

(ert-deftest pi-coding-agent-test-new-session-clears-buffer-from-different-context ()
"New session clears buffer and updates state even when callback runs elsewhere.
This tests that the async callback properly captures the chat buffer reference,
Expand Down
27 changes: 27 additions & 0 deletions test/pi-coding-agent-render-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,33 @@ agent_end + next section's leading newline must not create triple newlines."
(should (cl-some (lambda (ov) (overlay-get ov 'pi-coding-agent-tool-block))
(overlays-in (point-min) (point-max))))))

(ert-deftest pi-coding-agent-test-display-session-history-does-not-accumulate-stale-tool-overlays ()
"Rebuilding session history removes stale pi-owned tool overlays first."
(with-temp-buffer
(pi-coding-agent-chat-mode)
(let ((messages [(:role "assistant"
:content [(:type "text" :text "Let me check.")
(:type "toolCall" :id "tc1"
:name "bash"
:arguments (:command "ls -la"))]
:timestamp 1704067200000)
(:role "toolResult" :toolCallId "tc1"
:toolName "bash"
:content [(:type "text" :text "total 42")]
:isError :json-false
:timestamp 1704067201000)]))
(pi-coding-agent--display-session-history messages (current-buffer))
(pi-coding-agent--display-session-history messages (current-buffer))
(let ((tool-count 0)
(zero-tool-count 0))
(dolist (ov (overlays-in (point-min) (point-max)))
(when (overlay-get ov 'pi-coding-agent-tool-block)
(setq tool-count (1+ tool-count))
(when (= (overlay-start ov) (overlay-end ov))
(setq zero-tool-count (1+ zero-tool-count)))))
(should (= tool-count 1))
(should (= zero-tool-count 0))))))

(ert-deftest pi-coding-agent-test-history-renders-multiple-tools-in-order ()
"Multiple tool calls render with headers and output in order."
(with-temp-buffer
Expand Down
Loading