Files
passepartout/harness/act.org
Amr Gharbeia 60f2c152e0
Some checks failed
Deploy-Agent-V15-Stdin / JOB-V15-STDIN (push) Failing after 2s
REORG: Apply semantic directory structure and documentation cleanup
2026-04-21 19:07:32 -04:00

8.6 KiB

Stage 3: Act (act.lisp)

Stage 3: Act (act.lisp)

Architectural Intent: Actuation

The Act stage performs the final side-effects of the reasoning engine. It routes approved actions to their registered physical actuators (CLI, Shell, Emacs, etc.) and handles the execution of internal system tools.

Actuator Configuration

The core harness can be configured via environment variables to operate silently or target different default outputs.

(in-package :opencortex)

(defvar *default-actuator* :cli)
(defvar *silent-actuators* '(:cli :system-message :emacs))

(defun initialize-actuators ()
  "Loads actuator routing defaults from environment variables and registers core harness actuators."
  (let ((def (uiop:getenv "DEFAULT_ACTUATOR"))
        (silent (uiop:getenv "SILENT_ACTUATORS")))
    (when def
      (setf *default-actuator* (intern (string-upcase def) "KEYWORD")))
    (when silent
      (setf *silent-actuators*
            (mapcar (lambda (s) (intern (string-upcase (string-trim '(#\Space) s)) "KEYWORD"))
                    (str:split "," silent)))))
  
  ;; Register core harness actuators
  (register-actuator :system #'execute-system-action)
  (register-actuator :tool #'execute-tool-action)
  (register-actuator :tui (lambda (action context)
                            (let* ((meta (getf context :meta))
                                   (stream (getf meta :reply-stream)))
                              (when (and stream (open-stream-p stream))
                                (format stream "~a" (frame-message action))
                                (finish-output stream))))))

Dispatching Actions

The `dispatch-action` function is the primary router. It identifies the target actuator and executes the requested side-effects.

(defun dispatch-action (action context)
  (let ((payload (proto-get action :payload)))
    (when (eq (proto-get payload :sensor) :heartbeat)
      (return-from dispatch-action nil)))
  "Routes an approved action to its registered physical actuator."
  (when (and action (listp action))
    (let* ((meta (proto-get context :meta))
           (source (proto-get meta :source))
           (raw-target (or (ignore-errors (getf action :TARGET))
                           (ignore-errors (getf action :target))
                           source
                           *default-actuator*))
           (target (intern (string-upcase (string raw-target)) :keyword))
           (actuator-fn (gethash target *actuator-registry*)))
      ;; Ensure outbound action has meta if context had it
      (when (and meta (null (getf action :meta)))
        (setf (getf action :meta) meta))
      (if actuator-fn 
          (funcall actuator-fn action context) 
          (harness-log "ACT ERROR: No actuator for ~s (from ~s)" target raw-target)))))

Internal System Actions

The `:system` actuator handles internal harness commands like code evaluation and dynamic skill loading.

(defun execute-system-action (action context)
  "Processes internal harness commands. (ACTUATOR)"
  (declare (ignore context))
  (let* ((payload (ignore-errors (getf action :payload))) 
         (cmd (ignore-errors (getf payload :action))))
    (case cmd
      (:eval (let ((code (getf payload :code)))
               (eval (read-from-string code))))
      (:create-skill (let* ((filename (getf payload :filename)) (content (getf payload :content))
                            (skills-dir (merge-pathnames "skills/" (asdf:system-source-directory :opencortex))) 
                            (full-path (merge-pathnames filename skills-dir)))
                       (with-open-file (out full-path :direction :output :if-exists :supersede) (write-string content out))
                       (load-skill-from-org full-path)))
      (:message (harness-log "ACT [System]: ~a" (getf payload :text)))
      (t (harness-log "ACT ERROR [System]: Unknown command ~s" cmd)))))

Cognitive Tool Actuation

The `:tool` actuator handles the execution of registered cognitive tools.

(defun format-tool-result (tool-name result)
  "Intelligently formats a tool result for user display."
  (if (listp result)
      (let ((status (getf result :status))
            (content (getf result :content))
            (msg (getf result :message)))
        (cond ((and (eq status :success) content) (format nil "~a" content))
              ((and (eq status :error) msg) (format nil "ERROR [~a]: ~a" tool-name msg))
              (t (format nil "TOOL [~a] RESULT: ~s" tool-name result))))
      (format nil "TOOL [~a] RESULT: ~a" tool-name result)))

(defun execute-tool-action (action context)
  "Executes a registered cognitive tool. (ACTUATOR)"
  (let* ((payload (getf action :payload))
         (tool-name (getf payload :tool))
         (tool-args (getf payload :args))
         (depth (getf context :depth 0))
         (meta (getf context :meta))
         (source (getf meta :source))
         (tool (gethash (string-downcase (string tool-name)) *cognitive-tools*)))
    (if tool
        (handler-case
            (let* ((clean-args (if (and (listp tool-args) (listp (car tool-args))) (car tool-args) tool-args))
                   (result (funcall (cognitive-tool-body tool) clean-args)))
              (let ((feedback (list :TYPE :EVENT :DEPTH (1+ depth) :META meta
                                    :PAYLOAD (list :SENSOR :tool-output :RESULT result :TOOL tool-name))))
                ;; If we have a source, send a status message with the result, formatted for humans
                (when source
                   (dispatch-action (list :TYPE :REQUEST :TARGET source 
                                          :PAYLOAD (list :ACTION :MESSAGE :TEXT (format-tool-result tool-name result))) 
                                    context))
                feedback))
          (error (c)
            (list :TYPE :EVENT :DEPTH (1+ depth) :META meta
                  :PAYLOAD (list :SENSOR :tool-error :tool tool-name :message (format nil "~a" c)))))
        (list :TYPE :EVENT :DEPTH (1+ depth) :META meta
              :PAYLOAD (list :SENSOR :tool-error :message "Tool not found")))))

The Act Gate

The final stage of the metabolic loop. It performs a "last-mile" safety check before dispatching the action to the registered actuator.

(defun act-gate (signal)
  "Final Stage: Actuation and feedback generation."
  (let* ((approved (getf signal :approved-action))
         (type (getf signal :type))
         (meta (getf signal :meta))
         (source (getf meta :source))
         (feedback nil)
         ;; context must keep internal objects for actuators to function
         (context signal))
    
    ;; 1. Last-Mile Safety Check (The Bouncer & Deterministic Gates)
    (when approved
      (let* ((original-type (getf approved :type))
             (verified (deterministic-verify approved signal)))
        (if (and (listp verified) 
                 (member (getf verified :type) '(:LOG :EVENT :log :event))
                 (not (member original-type '(:LOG :EVENT :log :event))))
            (progn
              (harness-log "ACT BLOCKED: Action failed last-mile deterministic check.")
              (setf (getf signal :approved-action) nil)
              (setf approved nil)
              (setf feedback verified))
            (progn
              (setf (getf signal :approved-action) verified)
              (setf approved verified)))))

    ;; 2. Actuation Logic
    (case type
      (:REQUEST (dispatch-action signal context))
      (:LOG (dispatch-action signal context))
      (:EVENT 
       (if approved
           (let* ((target (getf approved :target))
                  (result (dispatch-action approved context)))
             ;; If the actuator returns a signal (like :tool-output), it becomes the feedback.
             ;; Otherwise, generate tool-output feedback for non-silent actuators.
             (cond ((and (listp result) (member (getf result :type) '(:EVENT :LOG)))
                    (setf feedback result))
                   ((and result (not (member target *silent-actuators*)))
                    (setf feedback (list :type :EVENT :depth (1+ (getf signal :depth 0)) :meta meta
                                         :payload (list :sensor :tool-output :result result :tool approved))))))
           ;; If no approved action but we have a source, this might be a raw event/log stimulus.
           (when source
             (dispatch-action signal context)))))
    
    (setf (getf signal :status) :acted)
    feedback))