branch: externals/ellama
commit 22fd8f4aee215ee430abc0d7c2c8c829f591f20d
Author: Sergey Kostyaev <[email protected]>
Commit: Sergey Kostyaev <[email protected]>

    Append user prompt after finished agent loops
    
    Add a shared plan-and-act finish path that restores tools and appends the 
next user heading to the session buffer. Use it for normal completion, 
blocked/max-step exits, already-completed loops, and unrecoverable compaction 
errors. Mark compaction failures as blocked and cover both completion and 
unrecoverable error paths in tests.
---
 ellama-tools.el            | 79 ++++++++++++++++++++++++++++++++++++++--------
 tests/test-ellama-tools.el | 16 ++++++++--
 2 files changed, 80 insertions(+), 15 deletions(-)

diff --git a/ellama-tools.el b/ellama-tools.el
index e620691df3..2021b65e51 100644
--- a/ellama-tools.el
+++ b/ellama-tools.el
@@ -58,6 +58,7 @@
 (defvar ellama-provider)
 (defvar ellama-coding-provider)
 (defvar ellama-assistant-nick)
+(defvar ellama-user-nick)
 (defvar ellama--current-session)
 (defvar ellama--current-session-id)
 
@@ -4074,6 +4075,35 @@ TEMPLATE-BASE, ROLE and ARGUMENTS are used for template 
rendering and hints."
      (ellama-tools--session-extra-with
       session :tools (and had-tools tools)))))
 
+(defun ellama-tools--agent-append-user-header (buffer)
+  "Append user heading to BUFFER after a finished plan-and-act loop."
+  (when (buffer-live-p buffer)
+    (with-current-buffer buffer
+      (let ((header
+             (concat (ellama-get-nick-prefix-for-mode)
+                     " " ellama-user-nick ":")))
+        (unless (save-excursion
+                  (goto-char (point-max))
+                  (skip-chars-backward " \t\n")
+                  (beginning-of-line)
+                  (looking-at-p (regexp-quote header)))
+          (save-excursion
+            (goto-char (point-max))
+            (unless (bobp)
+              (unless (bolp)
+                (insert "\n"))
+              (unless (save-excursion
+                        (forward-line -1)
+                        (looking-at-p "[[:space:]]*$"))
+                (insert "\n")))
+            (insert header "\n")))))))
+
+(defun ellama-tools--agent-finish (session buffer)
+  "Restore SESSION tools and append a user heading in BUFFER."
+  (ellama-tools--agent-restore-tools session)
+  (ellama-tools--agent-append-user-header
+   (ellama-tools--agent-session-buffer session buffer)))
+
 (defun ellama-tools-agent-active-p (session)
   "Return non-nil when SESSION has a running plan-and-act loop."
   (when-let* ((state (ellama-tools--agent-state session)))
@@ -4128,7 +4158,7 @@ TEMPLATE-BASE, ROLE and ARGUMENTS are used for template 
rendering and hints."
     (setq state (ellama-tools--agent-state-put state :result result))
     (ellama-tools--agent-put-state session state)
     (ellama-tools--agent-insert-state session nil "Done")
-    (ellama-tools--agent-restore-tools session)
+    (ellama-tools--agent-finish session nil)
     "Result received. Plan-and-act loop completed."))
 
 (defun ellama-tools--agent-system-message (system)
@@ -4225,28 +4255,29 @@ ERROR-CB is called on non-tool LLM errors to track 
consecutive failures."
      ((not (ellama-session-p session))
       (message "Plan-and-act session is not available."))
      (done
-      (message "Plan-and-act loop finished."))
+      (message "Plan-and-act loop finished.")
+      (ellama-tools--agent-finish session buffer))
      ((eq phase 'done)
       (setq state (ellama-tools--agent-state-put state :completed t))
       (ellama-tools--agent-put-state session state)
       (ellama-tools--agent-insert-state session buffer "Done")
-      (ellama-tools--agent-restore-tools session))
+      (ellama-tools--agent-finish session buffer))
      ((eq phase 'blocked)
       (ellama-tools--agent-insert-state session buffer "Blocked")
-      (ellama-tools--agent-restore-tools session))
+      (ellama-tools--agent-finish session buffer))
      ((>= steps max)
       (setq state (ellama-tools--agent-state-put state :phase 'blocked))
       (setq state (ellama-tools--agent-state-put
                    state :blocked (format "Max steps (%d) reached." max)))
       (ellama-tools--agent-put-state session state)
       (ellama-tools--agent-insert-state session buffer "Blocked")
-      (ellama-tools--agent-restore-tools session))
+      (ellama-tools--agent-finish session buffer))
      ((ellama-tools--agent-plan-complete-p state)
       (setq state (ellama-tools--agent-state-put state :phase 'done))
       (setq state (ellama-tools--agent-state-put state :completed t))
       (ellama-tools--agent-put-state session state)
       (ellama-tools--agent-insert-state session buffer "Done")
-      (ellama-tools--agent-restore-tools session))
+      (ellama-tools--agent-finish session buffer))
      (t
       (setq state (ellama-tools--agent-state-put state :step-count (1+ steps)))
       (ellama-tools--agent-put-state session state)
@@ -4302,12 +4333,31 @@ ERROR-CB is called on non-tool LLM errors to track 
consecutive failures."
              (setq continued t)
              (if (ellama--session-extra-get
                   session :auto-compact-last-error)
-                 (message "Agent loop stopped after compaction error: %s"
-                          (ellama--session-extra-get
-                           session :auto-compact-last-error))
+                 (let* ((error (ellama--session-extra-get
+                                session :auto-compact-last-error))
+                        (state (ellama-tools--agent-state session))
+                        (buffer (ellama-tools--agent-session-buffer
+                                 session nil)))
+                   (message "Agent loop stopped after compaction error: %s"
+                            error)
+                   (when state
+                     (setq state
+                           (ellama-tools--agent-state-put
+                            state :phase 'blocked))
+                     (setq state
+                           (ellama-tools--agent-state-put
+                            state :blocked
+                            (format "Context compaction failed: %s" error)))
+                     (ellama-tools--agent-put-state session state)
+                     (ellama-tools--agent-insert-state
+                      session buffer "Blocked"))
+                   (ellama-tools--agent-finish session buffer))
                (ellama-tools--continue-agent-after-error session)))))
-      (ellama--session-compact
-       session :automatic t :on-done #'continue))))
+      (unless (ellama--session-compact
+               session :automatic t :on-done #'continue)
+        (when (ellama--session-extra-get
+               session :auto-compact-last-error)
+          (continue))))))
 
 (defun ellama-tools--make-agent-error-callback (session)
   "Return error callback for plan-and-act loop of SESSION.
@@ -4696,8 +4746,11 @@ ERROR-CB is called on non-tool LLM errors to track 
consecutive failures."
                           (ellama--session-extra-get
                            session :auto-compact-last-error))
                (ellama-tools--continue-subagent-after-error session)))))
-      (ellama--session-compact
-       session :automatic t :on-done #'continue))))
+      (unless (ellama--session-compact
+               session :automatic t :on-done #'continue)
+        (when (ellama--session-extra-get
+               session :auto-compact-last-error)
+          (continue))))))
 
 (defun ellama-tools--make-subagent-error-callback (session)
   "Return error callback for subagent loop of SESSION.
diff --git a/tests/test-ellama-tools.el b/tests/test-ellama-tools.el
index efa1fadebf..e3fcd68663 100644
--- a/tests/test-ellama-tools.el
+++ b/tests/test-ellama-tools.el
@@ -3223,7 +3223,16 @@ Return list with result and prompt."
             (should-not stream-calls)
             (should (= (plist-get (ellama-tools--agent-state session)
                                   :consecutive-error-count)
-                       2))))
+                       2))
+            (should (eq (plist-get (ellama-tools--agent-state session)
+                                   :phase)
+                        'blocked))
+            (with-current-buffer buffer
+              (should (string-match-p "Ellama Agent Blocked:"
+                                      (buffer-string)))
+              (should (string-suffix-p
+                       "\n\n** User:\n"
+                       (buffer-string))))))
       (when (buffer-live-p buffer)
         (kill-buffer buffer)))))
 
@@ -3515,7 +3524,10 @@ END_ELLAMA_AGENT_STATE"))
                            (list base-tool)))
             (with-current-buffer buffer
               (should (string-match-p "Ellama Agent Done:"
-                                      (buffer-string))))))
+                                      (buffer-string)))
+              (should (string-suffix-p
+                       "\n\n** User:\n"
+                       (buffer-string))))))
       (when (buffer-live-p buffer)
         (kill-buffer buffer)))))
 

Reply via email to