feat: HITL — continuation-based human-in-the-loop
Some checks failed
Deploy (Gitea) / deploy (push) Failing after 3s

- dispatcher-check: add :level :approval-required to network/high-impact returns
- cognitive-verify: distinguish approval-required from hard rejection; pass
  approval requests through to act gate instead of returning early
- loop-gate-reason: don't retry approval requests; pass them as approved-action
  with :status :requires-approval
- loop-gate-act: detect approval-required, create Flight Plan, dispatch HITL
  message to user's client, don't execute original action
- loop-gate-perceive: handle re-injected approved signals from
  dispatcher-approvals-process; set :approved-action on signal
- dispatcher-approvals-process: fix function name (stimulus-inject) and wrap
  action in proper signal envelope with :sensor :approval-required
- Fix: list-objects-with-attribute → memory-objects-by-attribute
- Fix: org-id-new → org-id-generate
- Fix: inject-stimulus → stimulus-inject (correct function name)

Flow:
1. LLM proposes high-risk action → dispatcher returns approval-required
2. cognitive-verify collects approval request → passes to reason as :requires-approval
3. loop-gate-act creates Flight Plan → dispatches HITL message to client → exits
4. Human approves in Emacs → heartbeat re-injects with :approved t
5. Re-injected signal flows through pipeline → dispatcher passes through
6. Action executed normally
This commit is contained in:
2026-05-03 13:00:19 -04:00
parent 231c3bb445
commit 5e7b1cee33
11 changed files with 209 additions and 108 deletions

View File

@@ -51,20 +51,29 @@
type (or sensor "no-sensor") (getf meta :source))
(cond ((eq type :EVENT)
(case sensor
(:buffer-update
(let ((ast (getf payload :ast)))
(when ast
(snapshot-memory)
(ingest-ast ast))))
(:point-update
(let ((element (getf payload :element)))
(when element
(snapshot-memory)
(setf *loop-focus-id* (getf element :id))
(ingest-ast element))))
(:interrupt
(setf *loop-interrupt* t))))
(case sensor
(:buffer-update
(let ((ast (getf payload :ast)))
(when ast
(snapshot-memory)
(ingest-ast ast))))
(:point-update
(let ((element (getf payload :element)))
(when element
(snapshot-memory)
(setf *loop-focus-id* (getf element :id))
(ingest-ast element))))
(:interrupt
(setf *loop-interrupt* t))
;; HITL: re-injected approved action from dispatcher-approvals-process
(:approval-required
(when (getf payload :approved)
(log-message "GATE [Perceive]: Approved Flight Plan re-injected")
(setf (getf signal :approved t))
(setf (getf signal :approved-action) (getf payload :action))))
;; Default sensor: pass through without requiring user-input processing
(otherwise
(log-message "GATE [Perceive]: Unknown sensor ~a, passing through" sensor))))
((eq type :RESPONSE)
(log-message "GATE [Perceive]: Act Result -> ~a" (getf payload :status))))