:PROPERTIES: :ID: 59ac52fb-2310-4a9d-8e4e-0263af15181c :CREATED: [2026-03-30 Mon 21:16] :EDITED: [2026-04-07 Tue 13:42] :END: #+TITLE: SKILL: Emacs Bridge Agent (Universal Literate Note) #+STARTUP: content #+FILETAGS: :bridge:emacs:io:system:psf: * Overview The *Emacs Bridge Agent* is the primary sensory and motor interface to Emacs. It abstracts TCP socket management, allowing the core kernel to interact with buffers as native data structures. * Phase A: Demand (PRD) :PROPERTIES: :STATUS: FROZEN :END: ** 1. Purpose Define the transport layer for OpenCortex Communication Protocol (Harness Communication). ** 2. User Needs - *Isolation:* Kernel remains transport-agnostic. - *Persistence:* Multi-client server support for simultaneous sessions. - *Dispatch:* Reliable routing of actions to actuators and sensors to the kernel. ** 3. Success Criteria *** TODO Socket Listener Initialization *** TODO Multi-client Connection Handling *** TODO Harness Communication Message Framing Verification * Phase B: Blueprint (PROTOCOL) :PROPERTIES: :STATUS: SIGNED :END: ** 1. Architectural Intent Interfaces for TCP I/O and protocol framing. Source of truth is the Harness Communication specification. ** 2. Semantic Interfaces #+begin_src lisp (defun start-emacs-server (&key (port 9105)) "Starts the Harness Communication listener.") (defun broadcast-to-emacs (action-plist) "Sends a framed message to all connected clients.") #+end_src * Phase D: Build (Implementation) ** TCP Sensory Layer #+begin_src lisp :tangle ../projects/org-skill-emacs-bridge/src/bridge-logic.lisp (defun handle-emacs-client (stream) ;; Logic for parsing length-prefixed Harness Communication messages (format nil "Handling client on stream: ~a" stream)) #+end_src ** Outbound Actuation #+begin_src lisp :tangle ../projects/org-skill-emacs-bridge/src/bridge-logic.lisp (defun stream-to-emacs (stream action-plist) "Streams a chunk of data to a specific Emacs client over Harness Communication using framing." (let* ((type (or (getf action-plist :type) :request)) (payload (getf action-plist :payload)) ;; Ensure Emacs always receives a :payload drawer (envelope (if (and (getf action-plist :type) payload) action-plist (let ((clean-payload (copy-list action-plist))) (remf clean-payload :type) (remf clean-payload :id) (list :type type :id (or (getf action-plist :id) (get-universal-time)) :payload clean-payload)))) (msg (prin1-to-string envelope)) (len (length msg)) (framed (format nil "~6,'0x~a" len msg))) (handler-case (progn (write-string framed stream) (finish-output stream)) (error (c) (kernel-log "BRIDGE - Lost client: ~a" stream) (opencortex:unregister-emacs-client stream))))) (defun broadcast-to-emacs (action-plist context) "Sends a framed message back to the client that sent the stimulus, or all clients if async." (let ((stream (getf context :reply-stream))) (if stream (stream-to-emacs stream action-plist) (progn (kernel-log "BRIDGE - Async broadcast to all clients...") (bt:with-lock-held (opencortex:*clients-lock*) (dolist (s opencortex:*emacs-clients*) (stream-to-emacs s action-plist))))))) #+end_src * Registration #+begin_src lisp (opencortex:register-actuator :emacs #'broadcast-to-emacs) (defskill :skill-emacs-bridge :priority 100 :trigger (lambda (context) nil) :probabilistic (lambda (context) nil) :deterministic (lambda (action context) action)) #+end_src * Emacs Bridge Test Suite #+begin_src elisp :tangle (expand-file-name "tests/org-agent-test.el" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) ;;; opencortex-test.el --- Tests for the opencortex Emacs stub (require 'ert) (require 'cl-lib) (require 'opencortex "/home/amr/.openclaw/workspace/memex/5_projects/opencortex/src/opencortex.el") (ert-deftest test-opencortex-framing () "Verify that opencortex-send correctly frames a plist." (let ((captured-framed nil)) (cl-letf (((symbol-function 'process-send-string) (lambda (proc string) (setq captured-framed string))) ((symbol-function 'process-live-p) (lambda (proc) t)) (opencortex--process t)) (opencortex-send '(:type :EVENT :id 1)) (should (string= "000014(:type :EVENT :id 1)" captured-framed))))) (ert-deftest test-opencortex-parsing () "Verify that the filter correctly parses communication protocol framed messages." (let ((mock-buffer (generate-new-buffer " *opencortex-test*")) (received-plist nil)) (cl-letf (((symbol-function 'opencortex--handle-message) (lambda (proc plist) (setq received-plist plist)))) (with-current-buffer mock-buffer (insert "000014(:type :EVENT :id 1)") (opencortex--process-buffer mock-buffer) (should (equal '(:type :EVENT :id 1) received-plist)) (should (= (buffer-size) 0)))))) (ert-deftest test-opencortex-actuator-message () "Verify that the :message actuator works." (let ((opencortex--process nil) (captured-response nil)) (cl-letf (((symbol-function 'opencortex-send) (lambda (plist) (setq captured-response plist)))) (opencortex--execute-request nil 101 '(:action :message :text "Hello from Daemon")) ;; Check that we sent a success response back (should (eq :RESPONSE (plist-get captured-response :type))) (should (eq :success (plist-get (plist-get captured-response :payload) :status)))))) (ert-deftest test-opencortex-run-command () "Verify that opencortex-run-command sends the correct event." (let ((captured-framed nil)) (cl-letf (((symbol-function 'process-send-string) (lambda (proc string) (setq captured-framed string))) ((symbol-function 'process-live-p) (lambda (proc) t)) (opencortex--process t)) (opencortex-run-command :test-cmd) (should (string-match-p ":sensor :user-command" captured-framed)) (should (string-match-p ":command :test-cmd" captured-framed))))) (ert-deftest test-opencortex-ast-cleaning () "Verify that opencortex--clean-element produces a pure plist." (let* ((org-text "* Hello\nWorld") (ast (with-temp-buffer (org-mode) (insert org-text) (org-element-parse-buffer))) (cleaned (opencortex--clean-element ast))) (should (plist-get cleaned :type)) (should (eq 'org-data (plist-get cleaned :type))) ;; Check that children exist (should (plist-get (car (plist-get cleaned :contents)) :type)) ;; Check that we didn't leak buffer objects (should-not (plist-get (plist-get cleaned :properties) :buffer)))) (ert-deftest test-opencortex-actuator-eval () "Verify that the :eval actuator can execute elisp." (let ((opencortex--process nil) (captured-response nil)) (cl-letf (((symbol-function 'opencortex-send) (lambda (plist) (setq captured-response plist)))) (opencortex--execute-request nil 102 '(:action :eval :code "(+ 1 2)")) (should (equal "3" (plist-get (plist-get captured-response :payload) :result)))))) #+end_src