Skip to content

Commit 77d6bee

Browse files
committed
feat executor registation improvements and result parsing
1 parent 75ffcde commit 77d6bee

3 files changed

Lines changed: 317 additions & 98 deletions

File tree

agent-shell-queue-db.el

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -135,43 +135,34 @@ to store the database at a custom location.")
135135
;;; Save
136136

137137
(defun agent-shell-queue-db--save ()
138-
"Persist queue items to SQLite, excluding done and running items.
138+
"Persist all queue items to SQLite.
139139
Called as `agent-shell-queue-save-function' when the DB backend is active."
140-
(let* ((conn (agent-shell-queue-db--ensure-connection))
141-
(items-to-save
142-
(cl-remove-if
143-
(lambda (pair) (null (cdr pair)))
144-
(mapcar (lambda (pair)
145-
(cons (car pair)
146-
(cl-remove-if
147-
(lambda (item)
148-
(memq (agent-shell-queue-item-status item)
149-
'(done running)))
150-
(cdr pair))))
151-
agent-shell-queue--items))))
140+
(let ((conn (agent-shell-queue-db--ensure-connection)))
152141
(condition-case err
153142
(progn
154143
(sqlite-execute conn "BEGIN TRANSACTION")
155144
(sqlite-execute conn "DELETE FROM items")
156-
(dolist (pair items-to-save)
157-
(let ((pos 0))
158-
(dolist (item (cdr pair))
159-
(sqlite-execute
160-
conn
161-
agent-shell-queue-db--insert-item
162-
(list (agent-shell-queue-item-id item)
163-
(car pair)
164-
(agent-shell-queue-item-args item)
165-
(symbol-name (agent-shell-queue-item-status item))
166-
(symbol-name (or (agent-shell-queue-item-kind item) 'prompt))
167-
(if (agent-shell-queue-item-background item) 1 0)
168-
(agent-shell-queue-item-created item)
169-
(agent-shell-queue-item-dispatched item)
170-
(agent-shell-queue-item-completed item)
171-
(agent-shell-queue-item-response item)
172-
pos
173-
(agent-shell-queue--executor-name (agent-shell-queue-item-executor item))))
174-
(cl-incf pos))))
145+
(seq-do (lambda (pair)
146+
(let ((pos 0))
147+
(seq-do (lambda (item)
148+
(sqlite-execute
149+
conn
150+
agent-shell-queue-db--insert-item
151+
(list (agent-shell-queue-item-id item)
152+
(car pair)
153+
(agent-shell-queue-item-args item)
154+
(symbol-name (agent-shell-queue-item-status item))
155+
(symbol-name (or (agent-shell-queue-item-kind item) 'prompt))
156+
(if (agent-shell-queue-item-background item) 1 0)
157+
(agent-shell-queue-item-created item)
158+
(agent-shell-queue-item-dispatched item)
159+
(agent-shell-queue-item-completed item)
160+
(agent-shell-queue-item-response item)
161+
pos
162+
(agent-shell-queue--executor-name (agent-shell-queue-item-executor item))))
163+
(setq pos (1+ pos)))
164+
(cdr pair))))
165+
agent-shell-queue--items)
175166
(sqlite-execute conn "COMMIT"))
176167
(error
177168
(ignore-errors (sqlite-execute conn "ROLLBACK"))

agent-shell-queue.el

Lines changed: 78 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -245,40 +245,56 @@ Generates constructor TYPE-NAME--make plus:
245245

246246
;;; Executor registry
247247

248-
(defvar agent-shell-queue--executor-registry (make-hash-table :test #'equal)
249-
"Registry mapping executor name strings to executor functions.
250-
Only registered executors survive serialization. Register entries with
248+
(cl-defstruct (agent-shell-queue-executor
249+
(:constructor agent-shell-queue-executor--make)
250+
(:copier nil))
251+
"Registry entry pairing a serializable NAME with an EXECUTOR function and
252+
an optional CAPTURE function.
253+
EXECUTOR: (item args) — called by `agent-shell-queue-send-item' to dispatch.
254+
CAPTURE: () — called during item creation to produce the args value;
255+
nil means fall back to the standard text-capture buffer."
256+
name executor capture)
257+
258+
(defvar agent-shell-queue--executors nil
259+
"List of `agent-shell-queue-executor' entries.
260+
Only executors present here survive serialization. Register entries with
251261
`agent-shell-queue-register-executor'.")
252262

253-
(defun agent-shell-queue-register-executor (name fn)
254-
"Register executor function FN under NAME.
255-
NAME may be a string or symbol; it is stored as a string via `symbol-name'
256-
when a symbol is passed. The name is what gets written into serialized
263+
(defun agent-shell-queue-register-executor (name executor &optional capture)
264+
"Register EXECUTOR (and optional CAPTURE) under NAME.
265+
NAME may be a string or symbol; it is coerced to a string. Re-registering
266+
an existing name replaces the entry. The name is written into serialized
257267
queue state, so it must be stable across Emacs restarts.
258-
Returns FN."
259-
(puthash (if (symbolp name) (symbol-name name) name)
260-
fn agent-shell-queue--executor-registry)
261-
fn)
268+
Returns EXECUTOR."
269+
(let ((name-str (if (symbolp name) (symbol-name name) name)))
270+
(setq agent-shell-queue--executors
271+
(cons (agent-shell-queue-executor--make
272+
:name name-str :executor executor :capture capture)
273+
(seq-remove (lambda (e) (equal name-str (agent-shell-queue-executor-name e)))
274+
agent-shell-queue--executors)))
275+
executor))
276+
277+
(defun agent-shell-queue--find-executor (name)
278+
"Return the `agent-shell-queue-executor' entry for NAME, or nil."
279+
(seq-find (lambda (e) (equal name (agent-shell-queue-executor-name e)))
280+
agent-shell-queue--executors))
262281

263282
(defun agent-shell-queue--executor-name (fn)
264-
"Return the registry name for FN, or nil if FN is not registered.
265-
Performs a reverse lookup (value → key) over the registry, so the
266-
registered name need not match the function's symbol name. Returns nil
267-
for nil, or for any function not found in the registry."
268-
(when fn
269-
(map-some (lambda (name registered-fn)
270-
(when (eq fn registered-fn) name))
271-
agent-shell-queue--executor-registry)))
283+
"Return the registry name for FN, or nil if not registered."
284+
(when-let* ((e (seq-find (lambda (e) (eq fn (agent-shell-queue-executor-executor e)))
285+
agent-shell-queue--executors)))
286+
(agent-shell-queue-executor-name e)))
272287

273288
(defun agent-shell-queue--executor-from-plist (name)
274289
"Deserialize executor NAME via the registry.
275290
Returns nil (kind-dispatch) when NAME is nil or not found; emits a
276291
warning for non-nil names that have no registry entry."
277292
(when name
278-
(or (gethash name agent-shell-queue--executor-registry)
279-
(progn
280-
(message "agent-shell-queue: unknown executor %S — item will use kind dispatch" name)
281-
nil))))
293+
(if-let* ((e (agent-shell-queue--find-executor name)))
294+
(agent-shell-queue-executor-executor e)
295+
(progn
296+
(message "agent-shell-queue: unknown executor %S — item will use kind dispatch" name)
297+
nil))))
282298

283299
;;; Data model
284300

@@ -539,11 +555,15 @@ the session queue is paused until the mode changes.")
539555
(apply #'string
540556
(seq-map (lambda (_it) (aref chars (random 36))) (make-list 4 nil))))))
541557

558+
(defun agent-shell-queue--clean-args (args)
559+
"Remove trailing whitespace from every line of ARGS."
560+
(string-join (seq-map #'string-trim-right (split-string args "\n")) "\n"))
561+
542562
(defun agent-shell-queue--make-item (prompt &optional background kind)
543563
"Return a new active queue item for PROMPT."
544564
(agent-shell-queue-item--make
545565
:id (agent-shell-queue--gen-id)
546-
:args prompt
566+
:args (agent-shell-queue--clean-args prompt)
547567
:status 'active
548568
:kind (or kind 'prompt)
549569
:background background
@@ -1087,9 +1107,8 @@ Returns t to proceed, nil to skip. When user answers \\='a\\=', sets
10871107

10881108
(defun agent-shell-queue-edit (id new-prompt)
10891109
"Replace the args of item ID with NEW-PROMPT. Save."
1090-
;; does it make sense to make a "agent-shell-queue--with-save" macro and then inline all of this?
10911110
(when-let* ((pair (agent-shell-queue--item-by-id id)))
1092-
(setf (agent-shell-queue-item-args (cdr pair)) new-prompt)
1111+
(setf (agent-shell-queue-item-args (cdr pair)) (agent-shell-queue--clean-args new-prompt))
10931112
(agent-shell-queue--save)))
10941113

10951114
(defun agent-shell-queue-set-background-task (id flag)
@@ -1233,7 +1252,8 @@ insertion so response capture can find the reply."
12331252

12341253
(agent-shell-queue-register-executor
12351254
(symbol-name 'agent-shell-queue--default-executor)
1236-
#'agent-shell-queue--default-executor)
1255+
#'agent-shell-queue--default-executor
1256+
nil)
12371257

12381258
(defun agent-shell-queue-send-item (id)
12391259
"Send the item with ID to its target buffer, marking it as running.
@@ -1325,36 +1345,50 @@ all collapsed blocks are folded: only the prose between them."
13251345
(> (prop-match-beginning end-marker) start-pos))
13261346
(prop-match-beginning end-marker)
13271347
(point-max)))
1328-
;; The echoed prompt has field=input; model response
1329-
;; starts where field transitions away from input.
1348+
;; If start-pos is still inside the echoed field=input
1349+
;; region, skip forward to where field changes (the
1350+
;; model response). If shell-maker already advanced
1351+
;; past field=input before the position was recorded
1352+
;; (the common case), use start-pos directly — a
1353+
;; next-single-property-change call here would return
1354+
;; the field change at the very end of the response,
1355+
;; making response-start equal to end-pos and
1356+
;; collecting nothing.
13301357
(response-start
1331-
(or (next-single-property-change start-pos 'field nil end-pos)
1332-
start-pos))
1333-
;; Walk forward collecting plain-text segments (no
1334-
;; agent-shell-ui-state), skipping collapsed blocks.
1358+
(if (eq (get-text-property start-pos 'field) 'input)
1359+
(or (next-single-property-change start-pos 'field nil end-pos)
1360+
start-pos)
1361+
start-pos))
1362+
;; Walk forward, keeping only the last visible segment.
1363+
;; Thinking blocks, tool calls, and intermediate agent
1364+
;; messages are overwritten each iteration; only the
1365+
;; final non-empty visible block is retained.
13351366
(pos response-start)
1336-
(segments nil))
1367+
(last-seg nil))
13371368
(while (< pos end-pos)
13381369
(let* ((state (get-text-property pos 'agent-shell-ui-state))
13391370
(block-end (or (next-single-property-change
13401371
pos 'agent-shell-ui-state nil end-pos)
13411372
end-pos)))
13421373
(if (and state (text-property-any pos block-end 'invisible t))
1343-
;; Block has hidden body (collapsed tool call, thinking, etc.) — skip.
1374+
;; Hidden body (collapsed tool call, thinking, etc.) — skip.
13441375
(setq pos block-end)
1345-
;; Visible content (plain text, agent message, expanded block) — collect.
1376+
;; Visible content — overwrite; we want only the last one.
13461377
(let ((seg (string-trim
13471378
(buffer-substring-no-properties pos block-end))))
13481379
(when (not (string-empty-p seg))
1349-
(push seg segments))
1380+
(setq last-seg seg))
13501381
(setq pos block-end)))))
1351-
(when segments
1352-
(string-join (nreverse segments) "\n\n")))))))
1353-
(when (and text (not (string-empty-p text)))
1354-
(setf (agent-shell-queue-item-response (cdr pair))
1355-
(if (> (length text) 8192)
1356-
(concat (substring text 0 8192) "\n\n…[truncated]")
1357-
text)))))))
1382+
last-seg)))))
1383+
(if (and text (not (string-empty-p text)))
1384+
(let ((stored (if (> (length text) 8192)
1385+
(concat (substring text 0 8192) "\n\n…[truncated]")
1386+
text)))
1387+
(setf (agent-shell-queue-item-response (cdr pair)) stored)
1388+
(message "agent-shell-queue: captured response for %s (%d chars%s)"
1389+
id (length text)
1390+
(if (> (length text) 8192) ", truncated" "")))
1391+
(message "agent-shell-queue: no response captured for %s" id))))))
13581392

13591393
(defun agent-shell-queue--mark-running-done (buf-name)
13601394
"Mark any running items for BUF-NAME as done, recording completion time.

0 commit comments

Comments
 (0)