Skip to content

Commit 47ca400

Browse files
committed
Fix: Display API errors in conversation buffer (ef-769)
Added efrit-agent--add-error-message function for visible error display. API errors now appear in red in the conversation so users know what happened.
1 parent 2bb2165 commit 47ca400

3 files changed

Lines changed: 26 additions & 3 deletions

File tree

.beads/issues.jsonl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
{"id":"ef-6up","title":"Update efrit-agent-input-send for REPL model","description":"Modify input sending to use REPL session:\n\nCurrent:\n- Calls efrit-do--start-async-session() - creates NEW session\n\nNew:\n- If buffer has active session: call efrit-repl-continue()\n- If no session: create new efrit-repl-session and continue\n\nKey change: The session persists between inputs.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-05T14:29:19.802461-08:00","updated_at":"2025-12-05T14:55:01.439381-08:00","closed_at":"2025-12-05T14:55:01.439381-08:00","dependencies":[{"issue_id":"ef-6up","depends_on_id":"ef-j59","type":"blocks","created_at":"2025-12-05T14:29:31.035941-08:00","created_by":"daemon"}]}
3535
{"id":"ef-6v1","title":"Shell output renderer with ANSI support","description":"Create efrit-agent--render-shell for Bash tool results. Convert ANSI color codes to Emacs faces. Echo command at top of output. Style exit code prominently. Show stderr in warning face.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-03T17:42:26.669765-08:00","updated_at":"2025-12-03T17:50:01.94593-08:00","closed_at":"2025-12-03T17:50:01.94593-08:00","dependencies":[{"issue_id":"ef-6v1","depends_on_id":"ef-wpj","type":"blocks","created_at":"2025-12-03T17:42:54.916754-08:00","created_by":"daemon"}]}
3636
{"id":"ef-71z","title":"Add session resume command","description":"Allow resuming previous sessions:\n\nM-x efrit-resume:\n1. Show list of recent sessions\n2. Display: project, first message, timestamp\n3. Select to restore\n4. Load into new agent buffer\n5. Continue conversation where left off","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-05T14:30:06.910696-08:00","updated_at":"2025-12-05T15:28:08.339331-08:00","closed_at":"2025-12-05T15:28:08.339331-08:00","dependencies":[{"issue_id":"ef-71z","depends_on_id":"ef-a69","type":"blocks","created_at":"2025-12-05T14:30:20.43959-08:00","created_by":"daemon"}]}
37-
{"id":"ef-769","title":"Bug: API errors fail silently - user sees nothing","description":"When an API error occurs, efrit-repl-loop--on-api-error logs it and sets status to 'failed, but the user sees no explanation in the conversation.\n\nFIX: Insert an error message into the conversation so the user knows what happened.","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-05T15:46:23.310005-08:00","updated_at":"2025-12-05T15:47:17.878154-08:00"}
37+
{"id":"ef-769","title":"Bug: API errors fail silently - user sees nothing","description":"When an API error occurs, efrit-repl-loop--on-api-error logs it and sets status to 'failed, but the user sees no explanation in the conversation.\n\nFIX: Insert an error message into the conversation so the user knows what happened.","status":"in_progress","priority":1,"issue_type":"bug","created_at":"2025-12-05T15:46:23.310005-08:00","updated_at":"2025-12-05T15:55:26.428205-08:00"}
3838
{"id":"ef-7cb","title":"Add multi-session support to agent buffer","description":"Support multiple concurrent sessions with queue display and session switching.\n\nIMPLEMENTATION:\n1. Track multiple session-ids in efrit-agent-core.el\n2. Add session selector UI (maybe in header-line or sidebar)\n3. Queue display showing pending/running sessions\n4. Keybindings to switch between sessions\n5. Each session gets its own conversation history\n\nNOTE: This is backlog priority - single session works fine for most use cases.\n\nFILES: lisp/interfaces/efrit-agent-core.el, lisp/interfaces/efrit-agent.el","status":"open","priority":4,"issue_type":"task","created_at":"2025-12-03T12:31:17.056471-08:00","updated_at":"2025-12-03T12:31:17.056471-08:00","dependencies":[{"issue_id":"ef-7cb","depends_on_id":"ef-zq8","type":"blocks","created_at":"2025-12-03T12:31:46.566362-08:00","created_by":"daemon"}]}
3939
{"id":"ef-80t","title":"Define canonical REPL keybindings","description":"Create comprehensive keybindings for agent-mode:\n\nNavigation:\n- n/p: Next/prev tool call\n- TAB: Expand/collapse tool\n- g: Refresh display\n\nSession:\n- C-c C-c or RET: Send input\n- C-c C-k: Pause/interrupt\n- C-c C-n: New session\n- C-c C-r: Resume\n\nHistory:\n- M-p/M-n: Input history\n- C-c C-h: Browse sessions","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-05T14:29:58.378075-08:00","updated_at":"2025-12-05T15:19:43.939106-08:00","closed_at":"2025-12-05T15:19:43.939106-08:00","dependencies":[{"issue_id":"ef-80t","depends_on_id":"ef-c2r","type":"blocks","created_at":"2025-12-05T14:41:05.990661-08:00","created_by":"daemon"}]}
4040
{"id":"ef-81q","title":"Add efrit-agent-open command for idle agent buffer","description":"Create a public command to open or switch to the *efrit-agent* buffer in idle mode, providing a persistent prompt buffer that can be used without an active session.\n\nThis is the foundational task for the new REPL-style UX.\n\n## Acceptance Criteria\n1. `M-x efrit-agent-open` opens `*efrit-agent*` buffer at bottom\n2. Buffer is in `efrit-agent-mode`\n3. Header-line shows \"Idle\" status when no session active\n4. Conversation region is read-only\n5. Input region is editable with `\u003e ` prompt\n6. `efrit-agent-input-mode` activates when point enters input region\n7. No errors when buffer is opened without an active session\n8. Re-running command when buffer exists switches to it without errors\n9. `make compile` passes","design":"## What to Build\n\nA new command `efrit-agent-open` in `lisp/interfaces/efrit-agent.el` that:\n1. Gets or creates the `*efrit-agent*` buffer\n2. Initializes `efrit-agent-mode` if not already active\n3. Sets up conversation/input regions if needed\n4. Sets status to 'idle when no session exists\n5. Displays buffer at bottom and moves point to input region\n\n## Implementation\n\nAdd this function to `efrit-agent.el`:\n\n```elisp\n;;;###autoload\n(defun efrit-agent-open ()\n \"Open or switch to the Efrit agent buffer in idle mode.\nProvides a persistent prompt buffer for interacting with Efrit.\nType at the \u003e prompt to start a session (once Task 2 is done).\"\n (interactive)\n (let ((buffer (efrit-agent--get-buffer)))\n (with-current-buffer buffer\n ;; Initialize mode if not already done\n (unless (derived-mode-p 'efrit-agent-mode)\n (efrit-agent-mode))\n ;; Initialize regions if not set up\n (unless (and efrit-agent--conversation-end\n (marker-position efrit-agent--conversation-end))\n (efrit-agent--init-regions)\n (efrit-agent--setup-regions))\n ;; Set idle state if no active session\n (unless efrit-agent--session-id\n (setq efrit-agent--status 'idle)\n (setq efrit-agent--start-time nil)))\n ;; Display and focus\n (display-buffer buffer '(display-buffer-at-bottom (window-height . 15)))\n (select-window (get-buffer-window buffer))\n (goto-char (point-max))))\n```\n\n## Files to Modify\n- `lisp/interfaces/efrit-agent.el` - Add the new command\n\n## Verification Steps\n1. Run `M-x efrit-agent-open`\n2. Verify buffer `*efrit-agent*` opens at bottom\n3. Header-line shows \"○ Idle\"\n4. Conversation region (above separator) is read-only\n5. Input region (after `\u003e `) is editable\n6. Moving into input region enables `efrit-agent-input-mode` (check mode-line lighter)\n7. No errors when typing in input region with no session\n8. `make compile` passes","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-04T11:44:47.287095-08:00","updated_at":"2025-12-04T14:53:09.767908-08:00","closed_at":"2025-12-04T14:53:09.767908-08:00"}
@@ -50,7 +50,7 @@
5050
{"id":"ef-9z1","title":"Make diff file headers clickable to open files","description":"When viewing expanded diff output, make the file header lines clickable to open the file.\n\nIMPLEMENTATION:\n1. In efrit-agent--format-diff-line, detect file header patterns:\n - \"diff --git a/path b/path\"\n - \"--- a/path\"\n - \"+++ b/path\"\n\n2. Extract the file path from these lines\n\n3. Add a keymap with mouse-1 and RET bindings to:\n - find-file-other-window on the extracted path\n\n4. Apply 'help-echo property showing \"Click to open file\"\n\nThis provides render-type-specific interaction for diffs.\n\nFILES: lisp/interfaces/efrit-agent-tools.el","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-03T20:14:23.26681-08:00","updated_at":"2025-12-03T22:17:13.41037-08:00","closed_at":"2025-12-03T22:17:13.41037-08:00","dependencies":[{"issue_id":"ef-9z1","depends_on_id":"ef-8of","type":"blocks","created_at":"2025-12-03T20:14:36.743318-08:00","created_by":"daemon"},{"issue_id":"ef-9z1","depends_on_id":"ef-3at","type":"parent-child","created_at":"2025-12-03T20:14:36.792878-08:00","created_by":"daemon"}]}
5151
{"id":"ef-9z7","title":"Bug: Duplicate user message in agent buffer on session start","description":"When `efrit-agent--create-buffer` is called, it adds the command as a user message via `efrit-agent--add-user-message`. However, callers may also add the message separately, resulting in the user's command appearing twice.\n\nSeen during dogfooding: the command \"List files in this directory\" appeared twice at the top of the conversation.\n\nFIX: Either remove the `efrit-agent--add-user-message` call from `efrit-agent--create-buffer`, or ensure callers don't duplicate it.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-03T20:47:17.272822-08:00","updated_at":"2025-12-03T20:57:05.346675-08:00","closed_at":"2025-12-03T20:57:05.346675-08:00"}
5252
{"id":"ef-a69","title":"Design session serialization format","description":"Define format for persisting sessions:\n\n```json\n{\n \"version\": 1,\n \"id\": \"session-xxx\",\n \"created\": \"2024-01-15T10:30:00Z\",\n \"project\": \"/path/to/project\",\n \"conversation\": [\n {\"role\": \"user\", \"content\": \"...\", \"ts\": \"...\"},\n {\"role\": \"assistant\", \"content\": \"...\", \"ts\": \"...\"}\n ],\n \"metadata\": {...}\n}\n```\n\nStore in: ~/.emacs.d/efrit/sessions/","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-05T14:30:04.500687-08:00","updated_at":"2025-12-05T15:23:01.075886-08:00","closed_at":"2025-12-05T15:23:01.075886-08:00"}
53-
{"id":"ef-b4q","title":"Bug: Long sessions will crash - no context window limiting","description":"efrit-repl-session-get-api-messages returns ALL messages with no limit. Long sessions will exceed Claude's context window and fail with API errors.\n\nFIX: Cap the returned messages to last N turns in efrit-repl-session-get-api-messages. Use efrit-repl-max-history which already exists but is unused.","status":"in_progress","priority":1,"issue_type":"bug","created_at":"2025-12-05T15:46:14.462073-08:00","updated_at":"2025-12-05T15:54:54.693197-08:00"}
53+
{"id":"ef-b4q","title":"Bug: Long sessions will crash - no context window limiting","description":"efrit-repl-session-get-api-messages returns ALL messages with no limit. Long sessions will exceed Claude's context window and fail with API errors.\n\nFIX: Cap the returned messages to last N turns in efrit-repl-session-get-api-messages. Use efrit-repl-max-history which already exists but is unused.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-05T15:46:14.462073-08:00","updated_at":"2025-12-05T15:55:20.080104-08:00","closed_at":"2025-12-05T15:55:20.080104-08:00"}
5454
{"id":"ef-b9i","title":"Task: Manual interactive dogfood of efrit-agent UI","description":"Do a real interactive test of efrit-agent, not just code review.\n\nActivities to test:\n1. Launch Emacs with efrit loaded\n2. Run M-x efrit-agent to open the agent buffer\n3. Type a multi-step command at the prompt (e.g., \"search for all TODO comments and create a summary\")\n4. Watch Claude execute and observe:\n - Message streaming into conversation\n - Tool calls appearing inline\n - Results showing with proper expansion/collapse\n - Elapsed time updating in header-line\n - Status changes (working → complete/failed)\n5. Test key bindings:\n - n/p for next/previous tool\n - RET to expand/collapse\n - E/C to expand/collapse all\n - v to cycle verbosity\n - M to cycle display mode\n6. Test input handling:\n - Type follow-up command (new session)\n - Try mid-session guidance with 'i' key\n7. Test question/answer flow (if Claude asks for input)\n\nExpected to find bugs around:\n- UI rendering/display updates\n- Real-time streaming behavior\n- Key binding responsiveness\n- Header-line updates\n- Region marker management\n- Input region editing","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-04T20:38:55.761103-08:00","updated_at":"2025-12-04T20:47:27.883789-08:00","closed_at":"2025-12-04T20:47:27.883789-08:00"}
5555
{"id":"ef-bxb","title":"Agent hangs: \"Wrong type argument: integer-or-marker-p, nil\" during directory walk","description":"User ran: \"M-x efrit-do with the command: let's look at the code in ~/ai/mayor/rigs/gastown/elisp/ -- first visit all the non-test elisp files\"\n\nResult: Agent appears to hang (still showing \"Working 0.0s\"), with error in *Messages* buffer:\n```\nERROR: API request failed: Wrong type argument: integer-or-marker-p, nil\n```\n\nThe agent never completes the task. Need to debug the directory traversal logic.","acceptance_criteria":"Agent successfully visits all non-test .el files in a given directory when asked to browse them, returns within reasonable time, and displays results without errors","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-04T11:33:23.978829-08:00","updated_at":"2025-12-04T14:50:31.554359-08:00","closed_at":"2025-12-04T14:50:31.554359-08:00"}
5656
{"id":"ef-byp","title":"Standardize tool naming between chat and do modes","description":"Inconsistent tool names across modes:\n- buffer_create (do schema) vs create_buffer (chat-tool handling)\n- Chat-only tools (create_buffer, edit_buffer, read_buffer, buffer_info, get_context) not in efrit-do-schema.el\n\nThis makes it hard to reason about \"the set of tools Efrit supports\".\n\nOptions:\n1. Decide on canonical names (prefer existing do schema: buffer_create, buffer_read, etc.)\n2. Add aliases in dispatcher (accept both \"create_buffer\" and \"buffer_create\")\n3. Long-term: derive chat and do schemas from same source\n\nStart with option 2 - add aliases without breaking existing code.\n\nFiles affected:\n- lisp/interfaces/efrit-do.el (dispatch table)\n- lisp/core/efrit-do-schema.el (add any missing tools)\n- lisp/core/efrit-chat-api.el (tool handling)","acceptance_criteria":"1. Documented canonical tool naming convention\n2. Both old and new names work via aliases\n3. No behavior change for existing users","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-02T21:58:07.25901-08:00","updated_at":"2025-12-02T23:27:02.989309-08:00","closed_at":"2025-12-02T23:27:02.989309-08:00"}

lisp/core/efrit-repl-loop.el

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,13 @@ Returns tool result string."
289289

290290
(defun efrit-repl-loop--on-api-error (session error)
291291
"Handle API ERROR for REPL SESSION."
292-
(let ((session-id (efrit-repl-session-id session)))
292+
(let ((session-id (efrit-repl-session-id session))
293+
(buffer (efrit-repl-session-buffer session)))
293294
(efrit-log 'error "REPL session %s: API error: %s" session-id error)
295+
(when (and buffer (buffer-live-p buffer))
296+
(with-current-buffer buffer
297+
(when (fboundp 'efrit-agent--add-error-message)
298+
(efrit-agent--add-error-message (format "%s" error)))))
294299
(efrit-repl-loop--end-turn session "api-error")))
295300

296301
(defun efrit-repl-loop--end-turn (session stop-reason)

lisp/interfaces/efrit-agent-render.el

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,24 @@ Claude messages appear inline without a prefix."
6666
;; Start a new message
6767
(efrit-agent--stream-start-message text)))
6868

69+
(defun efrit-agent--add-error-message (text)
70+
"Add an error message with TEXT to the conversation region.
71+
Error messages are displayed in red to make them visible."
72+
(efrit-agent--stream-end-message)
73+
(efrit-agent--hide-thinking)
74+
(let* ((msg-id (format "error-msg-%d" (cl-incf efrit-agent--message-counter)))
75+
(inhibit-read-only t)
76+
(formatted-text (concat "\n⚠ Error: " text "\n\n")))
77+
(save-excursion
78+
(goto-char (marker-position efrit-agent--conversation-end))
79+
(let ((start (point)))
80+
(insert (propertize formatted-text 'face 'efrit-agent-error))
81+
(add-text-properties start (point)
82+
(list 'efrit-type 'error-message
83+
'efrit-id msg-id
84+
'read-only t))
85+
(set-marker efrit-agent--conversation-end (point))))))
86+
6987
(defun efrit-agent--stream-start-message (text)
7088
"Start a new streaming Claude message with TEXT.
7189
Creates markers for tracking the message region for future appends."

0 commit comments

Comments
 (0)