Skip to content
Open
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
32 changes: 32 additions & 0 deletions ai-code-backends-infra-ghostel.el
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
(require 'ai-code-editor-viewport)
(require 'ai-code-ghostel-image-preview)
(require 'ai-code-session-link)
(require 'ai-code-startup-diagnostics)

;; Prefer `ghostel-exec' for Ghostel backend startup when available, as
;; it simplifies process startup integration.
Expand Down Expand Up @@ -879,6 +880,34 @@ ORIG-FILTER is Ghostel's original process filter."
(ai-code-backends-infra-ghostel--render-output
buffer orig-filter process output)))))

(defun ai-code-backends-infra-ghostel--capture-native-child-exit-status
(process output)
"Record a native Ghostel child exit status from PROCESS OUTPUT."
(when (and (processp process) (stringp output))
(let* ((fragment
(or (process-get
process 'ai-code-backends-infra-ghostel--event-fragment)
""))
(input (concat fragment output))
(input-length (length input))
(offset 0)
pending)
(while (< offset input-length)
(let ((parsed
(condition-case nil
(read-from-string input offset)
(end-of-file nil))))
(if parsed
(progn
(when (numberp (car parsed))
(ai-code-startup-diagnostics-record-child-exit-status
process (car parsed)))
(setq offset (cdr parsed)))
(setq pending (substring input offset)
offset input-length))))
(process-put
process 'ai-code-backends-infra-ghostel--event-fragment pending))))

(defun ai-code-backends-infra-ghostel--wrap-process-filter
(buffer process)
"Wrap PROCESS output for the AI Code Ghostel session in BUFFER."
Expand All @@ -896,6 +925,9 @@ ORIG-FILTER is Ghostel's original process filter."
(set-process-filter
process
(lambda (proc output)
(when (eq orig-filter 'ghostel--events-filter)
(ai-code-backends-infra-ghostel--capture-native-child-exit-status
proc output))
(let ((target (or (ignore-errors (process-buffer proc))
buffer)))
(when (buffer-live-p target)
Expand Down
115 changes: 74 additions & 41 deletions ai-code-backends-infra.el
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
(require 'ai-code-editor-viewport)
(require 'ai-code-session)
(require 'ai-code-session-link)
(require 'ai-code-startup-diagnostics)
;; Terminal-specific implementations live in dedicated modules so this
;; file can stay focused on shared session orchestration.
(require 'ai-code-backends-infra-vterm)
Expand Down Expand Up @@ -1141,7 +1142,7 @@ were non-nil and append that UUID to the default CLI args."
found-resume-switch
(ai-code-backends-infra--selected-session-id)))
(prompt-p (or arg selected-session-id))
(default-args (mapconcat #'identity
(default-args (mapconcat #'shell-quote-argument
(append switches
(and selected-session-id
(list selected-session-id)))
Expand All @@ -1152,8 +1153,11 @@ were non-nil and append that UUID to the default CLI args."
(resolved-args (if prompt-p
(split-string-shell-command prompt-args)
switches))
(command (mapconcat #'identity
(cons program resolved-args)
(resolved-program (if (string-prefix-p "~" program)
(expand-file-name program)
program))
Comment on lines +1156 to +1158

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve tilde paths against the selected session host

When a backend program is configured as ~/bin/codex and a prefix invocation selects a remote working directory, this expands ~ before ai-code-backends-infra--session-working-directory has chosen that directory. The resulting command therefore contains a path resolved for the source buffer—often a local home path or a different TRAMP host—which is then executed in the selected remote session and fails unless both homes happen to match. Defer expansion until the working directory is known or leave remote shell expansion intact.

Useful? React with 👍 / 👎.

(command (mapconcat #'shell-quote-argument
(cons resolved-program resolved-args)
" ")))
(list :command command :args resolved-args)))

Expand Down Expand Up @@ -1311,14 +1315,19 @@ behavior."
buffer working-dir prefix task-file)
(ai-code-backends-infra--display-buffer-in-side-window buffer))

(defun ai-code-backends-infra--handle-session-start-failure (buffer session-key process-table)
"Handle startup failure for BUFFER and SESSION-KEY in PROCESS-TABLE."
(remhash session-key process-table)
(if (buffer-live-p buffer)
(progn
(pop-to-buffer buffer)
(message "CLI failed to start - see buffer for error details"))
(message "CLI failed to start - process exited immediately")))
(defun ai-code-backends-infra--handle-session-start-failure
(buffer session-key process-table prefix command)
"Handle startup failure for BUFFER and SESSION-KEY in PROCESS-TABLE.
PREFIX identifies the backend that attempted to start.
COMMAND is the launch command supplied to the terminal backend."
(let ((process (gethash session-key process-table))
(backend (or prefix
(when (buffer-live-p buffer)
(with-current-buffer buffer
ai-code-backends-infra--session-prefix)))))
(remhash session-key process-table)
(ai-code-startup-diagnostics-report
buffer process backend command)))

(defun ai-code-backends-infra--start-cli-session (options arg)
"Start a generic CLI session described by OPTIONS and prefix ARG.
Expand Down Expand Up @@ -1474,36 +1483,60 @@ TASK-FILE and SOURCE-BUFFER preserve file-to-session binding."
(if (file-remote-p working-dir)
env-vars
(ai-code-editor-viewport-environment env-vars)))
(buffer-and-process
(ai-code-backends-infra--create-terminal-session
resolved-buffer-name working-dir command editor-environment))
(new-buffer (car buffer-and-process))
(process (cdr buffer-and-process)))
(puthash session-key process process-table)
;; Wait for initialization before checking process status
(sleep-for ai-code-backends-infra-terminal-initialization-delay)
;; Check if process is still alive after initialization delay
(if (and process (process-live-p process))
(progn
(ai-code-backends-infra--finalize-started-session
new-buffer
process
working-dir
resolved-buffer-name
process-table
resolved-instance
prefix
escape-fn
cleanup-fn
multiline-input-sequence
post-start-fn
task-file)
(ai-code-backends-infra--remember-file-session-buffer
prefix source-buffer new-buffer))
(ai-code-backends-infra--handle-session-start-failure
new-buffer
session-key
process-table))))
buffer-and-process
startup-failed)
(condition-case err
(setq buffer-and-process
(ai-code-backends-infra--create-terminal-session
resolved-buffer-name working-dir command editor-environment))
(error
(unwind-protect
(let ((failure-buffer (get-buffer resolved-buffer-name)))
(if failure-buffer
(progn
(ai-code-backends-infra--handle-session-start-failure
Comment on lines +1495 to +1497

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve terminal setup errors

When Ghostel is installed but lacks ghostel-exec, ai-code-backends-infra--start-ghostel-process deliberately raises an actionable version error after the Ghostel session buffer has been created. Because failure-buffer is therefore non-nil, this branch suppresses that error and reports a generic CLI startup failure with unknown process status instead, incorrectly directing the user to troubleshoot the CLI rather than upgrade Ghostel. Re-signal terminal configuration errors or safely retain their diagnostic reason.

Useful? React with 👍 / 👎.

failure-buffer
session-key
process-table
prefix
command)
(setq startup-failed t))
(signal (car err) (cdr err))))
(when cleanup-fn
(funcall cleanup-fn)))))
(unless startup-failed
(let ((new-buffer (car buffer-and-process))
(process (cdr buffer-and-process)))
(puthash session-key process process-table)
;; Wait for initialization before checking process status
(sleep-for ai-code-backends-infra-terminal-initialization-delay)
;; Check if process is still alive after initialization delay
(if (and process (process-live-p process))
(progn
(ai-code-backends-infra--finalize-started-session
new-buffer
process
working-dir
resolved-buffer-name
process-table
resolved-instance
prefix
escape-fn
cleanup-fn
multiline-input-sequence
post-start-fn
task-file)
(ai-code-backends-infra--remember-file-session-buffer
prefix source-buffer new-buffer))
(unwind-protect
(ai-code-backends-infra--handle-session-start-failure
new-buffer
session-key
process-table
prefix
command)
(when cleanup-fn
(funcall cleanup-fn))))))))

(defun ai-code-backends-infra--toggle-or-create-session (working-dir buffer-name process-table command
&optional escape-fn cleanup-fn
Expand Down
Loading
Loading