RELEASE: Finalize Semantic Restructuring v0.1.0
Some checks failed
Deploy-Agent-V15-Stdin / JOB-V15-STDIN (push) Failing after 3s

- Folders: literate->harness, src->library, system->environment, scripts->interfaces.
- Synchronized all :tangle paths and system definitions.
- Hardened .gitignore for binary and log artifacts.
- Consolidated all documentation into docs/.
This commit is contained in:
2026-04-21 12:41:50 -04:00
parent dd3873cd5e
commit 94a8a0ab0b
53 changed files with 475 additions and 760 deletions

151
library/act.lisp Normal file
View File

@@ -0,0 +1,151 @@
(in-package :opencortex)
(defvar *default-actuator* :cli
"The fallback actuator used if a signal has no source or target metadata.")
(defvar *silent-actuators* '(:cli :system-message :emacs)
"List of actuators whose feedback should not re-enter the Reasoning stage.")
(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))))))
(defun dispatch-action (action context)
"Routes an approved action to its registered physical actuator."
(let ((payload (proto-get action :payload)))
;; Optimization: Heartbeats are system events, not actions.
(when (eq (proto-get payload :sensor) :heartbeat)
(return-from dispatch-action nil)))
(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*)))
;; Propagation: Ensure outbound action inherits metadata
(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)))))
(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)))))
(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 and generates feedback signals. (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))))
;; UI Propagation: Send distilled text result back to the source client
(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")))))
(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)))
(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))))))
;; Fallback: route generic stimuli back to their origin
(when source
(dispatch-action signal context)))))
(setf (getf signal :status) :acted)
feedback))

View File

@@ -0,0 +1,39 @@
(in-package :opencortex)
(defun validate-communication-protocol-schema (msg)
"Strict structural validation for incoming communication protocol messages."
(unless (listp msg)
(error "Communication Protocol Schema Error: Message must be a property list (got ~s)" (type-of msg)))
(let ((type (let ((raw (proto-get msg :type))) (if (keywordp raw) (intern (string-upcase (string raw)) :keyword) raw))))
(unless (member type '(:REQUEST :EVENT :RESPONSE :LOG :STATUS :CHAT))
(progn (harness-log "REJECTED MSG: ~s" msg) (error "Communication Protocol Schema Error: Invalid message type '~a'" type)))
(case type
(:REQUEST
(unless (proto-get msg :target)
(error "Communication Protocol Schema Error: REQUEST missing mandatory :target"))
(unless (proto-get msg :payload)
(error "Communication Protocol Schema Error: REQUEST missing mandatory :payload")))
(:EVENT
(let ((payload (proto-get msg :payload)))
(unless (and payload (listp payload))
(error "Communication Protocol Schema Error: EVENT missing or invalid :payload"))
(unless (or (proto-get payload :action) (proto-get payload :sensor))
(error "Communication Protocol Schema Error: EVENT payload must contain :action or :sensor"))))
(:RESPONSE
(unless (proto-get msg :payload)
(error "Communication Protocol Schema Error: RESPONSE missing mandatory :payload"))))
t))
(defskill :skill-communication-protocol-validator
:priority 95
:trigger (lambda (ctx) (member (getf (getf ctx :payload) :sensor) '(:protocol-received)))
:probabilistic nil
:deterministic (lambda (action ctx)
(declare (ignore ctx))
(validate-communication-protocol-schema action)
action))

View File

@@ -0,0 +1,46 @@
(in-package :opencortex)
(defun sanitize-protocol-message (msg)
"Recursively strips non-serializable objects (streams, sockets) from a protocol plist."
(if (and msg (listp msg))
(let ((clean nil))
(loop for (k v) on msg by #'cddr
do (unless (member k '(:reply-stream :socket :stream))
(push k clean)
(push (if (listp v) (sanitize-protocol-message v) v) clean)))
(nreverse clean))
msg))
(defun frame-message (msg)
"Serializes a message plist and prefixes it with a 6-character hex length."
(let* ((sanitized (sanitize-protocol-message msg))
(payload (let ((*print-pretty* nil) (*read-eval* nil)) (format nil "~s" sanitized)))
(len (length payload)))
(format nil "~6,'0x~a" len payload)))
(defun read-framed-message (stream)
"Reads a hex-prefixed message from a stream. Returns the parsed Lisp plist or :EOF."
(handler-case
(let ((len-buf (make-string 6)))
;; 1. Read the length prefix
(let ((count (read-sequence len-buf stream)))
(if (< count 6)
:eof
(let ((len (ignore-errors (parse-integer len-buf :radix 16))))
(if (and len (> len 0))
;; 2. Read exactly 'len' bytes
(let ((payload-buf (make-string len)))
(read-sequence payload-buf stream)
(let ((*read-eval* nil))
(read-from-string payload-buf)))
:error)))))
(error (c)
(harness-log "PROTOCOL ERROR: ~a" c)
:error)))
(defun make-hello-message (version)
"Constructs the standard HELLO handshake message."
(list :TYPE :EVENT
:PAYLOAD (list :ACTION :handshake
:VERSION version
:CAPABILITIES '(:AUTH :SWANK :ORG-AST))))

41
library/context.lisp Normal file
View File

@@ -0,0 +1,41 @@
(in-package :opencortex)
(defun context-get-active-projects ()
"Retrieves a list of project headlines currently marked as NEXT or in progress."
(let ((all-projects (list-objects-with-attribute :CATEGORY "Project")))
(loop for p in all-projects
collect (list :id (org-object-id p)
:title (getf (org-object-attributes p) :TITLE)))))
(defun context-get-recent-completed-tasks (&optional (limit 5))
"Retrieves the last N tasks marked as DONE from the memory history."
(let ((all-completed (list-objects-with-attribute :TODO "DONE")))
(subseq (sort all-completed #'> :key #'org-object-version)
0 (min limit (length all-completed)))))
(defun context-list-all-skills ()
"Returns a list of registered skills and their documentation."
(let ((results nil))
(maphash (lambda (id skill)
(push (list :id id :name (skill-name skill)) results))
*skills-registry*)
results))
(defun context-get-system-logs ()
"Retrieves the in-memory circular log buffer."
(bt:with-lock-held (*logs-lock*)
(format nil "~{~a~%~}" (reverse *system-logs*))))
(defun context-assemble-global-awareness ()
"Assembles the full context block for a neural request."
(let ((projects (context-get-active-projects))
(time (multiple-value-bind (s m h d mo y) (get-decoded-time) (format nil "~a-~a-~a ~a:~a:~a" y mo d h m s))))
(format nil "CURRENT_TIME: ~a. ACTIVE_PROJECTS: ~s. FOVEAL_FOCUS: ~a"
time
projects
(or *foveal-focus-id* "None"))))
(defun context-query-store (query &key (limit 5))
"Placeholder for semantic/vector search over the Memex."
(declare (ignore query limit))
nil)

92
library/loop.lisp Normal file
View File

@@ -0,0 +1,92 @@
(in-package :opencortex)
(defvar *interrupt-flag* nil
"Thread-safe signal to halt the metabolic pipeline and daemon.")
(defvar *interrupt-lock* (bt:make-lock "harness-interrupt-lock")
"Protects the interrupt flag from concurrent access.")
(defvar *heartbeat-thread* nil
"Reference to the background thread driving autonomous reflection.")
(defun process-signal (signal)
"The entry point to the Metabolic Pipeline: Perceive -> Reason -> Act."
(let ((current-signal signal))
(loop while current-signal do
(let ((depth (getf current-signal :depth 0))
(meta (getf current-signal :meta)))
;; Safety: Prevent infinite cognitive recursion.
(when (> depth 10) (harness-log "METABOLISM ERROR: Max depth reached.") (return nil))
;; Check for graceful shutdown.
(when (bt:with-lock-held (*interrupt-lock*) *interrupt-flag*)
(harness-log "METABOLISM: Interrupted.")
(bt:with-lock-held (*interrupt-lock*) (setf *interrupt-flag* nil))
(return nil))
(handler-case
(progn
;; Stage 1: Ingest and Normalize
(setf current-signal (perceive-gate current-signal))
;; Stage 2: Cogitate and Verify
(setf current-signal (reason-gate current-signal))
;; Stage 3: Actuate and Generate Feedback
(let ((feedback (act-gate current-signal)))
(if feedback
(progn
;; Inheritance: Metadata must persist across recursive cycles.
(unless (getf feedback :meta) (setf (getf feedback :meta) meta))
(setf current-signal feedback))
(setf current-signal nil))))
(error (c)
(let ((sensor (ignore-errors (getf (getf current-signal :payload) :sensor))))
(harness-log "METABOLISM CRASH [~a]: ~a" (or sensor :unknown) c)
;; Resilience: Only rollback on critical system errors.
(unless (member sensor '(:loop-error :tool-error :syntax-error))
(harness-log "CRITICAL ERROR: Initiating Micro-Rollback.")
(rollback-memory 0))
;; If recursion is shallow, attempt to notify the user of the error.
(if (or (> depth 2) (member sensor '(:loop-error :tool-error)))
(setf current-signal nil)
(setf current-signal (list :type :EVENT :depth (1+ depth) :meta meta
:payload (list :sensor :loop-error :message (format nil "~a" c) :depth depth)))))))))))
(defun start-heartbeat ()
"Starts the background heartbeat thread. Interval is loaded from HEARTBEAT_INTERVAL (default: 60s)."
(let ((interval (or (ignore-errors (parse-integer (uiop:getenv "HEARTBEAT_INTERVAL"))) 60)))
(setf *heartbeat-thread*
(bt:make-thread
(lambda ()
(loop
(sleep interval)
;; Note: inject-stimulus is synchronous for heartbeats to prevent task accumulation.
(inject-stimulus (list :type :EVENT :payload (list :sensor :heartbeat :unix-time (get-universal-time))))))
:name "opencortex-heartbeat"))))
(defun main ()
"Primary entry point for the OpenCortex daemon."
;; 1. Environment Hydration
(let* ((home (uiop:getenv "HOME"))
(env-file (uiop:merge-pathnames* ".local/share/opencortex/.env" (uiop:ensure-directory-pathname home))))
(when (uiop:file-exists-p env-file) (cl-dotenv:load-env env-file)))
;; 2. System Bootstrap
(initialize-actuators)
(initialize-all-skills)
;; 3. Wake up the heart.
(start-heartbeat)
;; 4. OS Signal Handling (SBCL specific)
#+sbcl
(sb-sys:enable-interrupt sb-unix:sigint
(lambda (sig code scp)
(declare (ignore sig code scp))
(harness-log "SHUTDOWN: SIGINT received. Exiting...")
(uiop:quit 0)))
;; 5. Primary Idle Loop
(let ((sleep-interval (or (ignore-errors (parse-integer (uiop:getenv "DAEMON_SLEEP_INTERVAL"))) 3600)))
(loop
(when (bt:with-lock-held (*interrupt-lock*) *interrupt-flag*) (return))
(sleep sleep-interval))))

81
library/memory.lisp Normal file
View File

@@ -0,0 +1,81 @@
(in-package :opencortex)
(defvar *memory* (make-hash-table :test 'equal)
"The primary in-memory graph of all Org-mode entities, keyed by their unique ID.")
(defvar *history-store* (make-array 0 :fill-pointer 0 :adjustable t)
"A versioned log of the memory state, allowing for temporal traversal and rollback.")
(defstruct org-object
"The fundamental unit of knowledge in the OpenCortex."
id
type
attributes
parent-id
children
version
last-sync
vector
content
hash)
(defun compute-merkle-hash (id type attributes content child-hashes)
"Computes a SHA-256 Merkle hash for a node based on its core properties and children's hashes."
(let* ((alist (loop for (k v) on attributes by #'cddr collect (cons k v)))
(sorted-alist (sort alist #'string< :key (lambda (x) (format nil "~a" (car x)))))
(attr-string (format nil "~s" sorted-alist))
(children-string (format nil "~{~a~}" child-hashes))
(raw-data (format nil "~a|~a|~a|~a|~a" id type attr-string (or content "") children-string)))
(ironclad:byte-array-to-hex-string
(ironclad:digest-sequence :sha256 (ironclad:ascii-string-to-byte-array raw-data)))))
(defun ingest-ast (ast &optional parent-id)
"Recursively parses an Org AST into the Lisp Memory registry."
(let* ((type (getf ast :type))
(properties (getf ast :properties))
(id (or (getf properties :ID) (uuid:make-v4-uuid)))
(content (getf ast :content))
(children (getf ast :contents))
(child-ids nil))
;; Recursively ingest children and collect their IDs
(dolist (child children)
(let ((child-obj (ingest-ast child id)))
(when child-obj (push (org-object-id child-obj) child-ids))))
(let ((obj (make-org-object :id id
:type type
:attributes properties
:parent-id parent-id
:children (nreverse child-ids)
:content content
:version (get-universal-time))))
(setf (gethash id *memory*) obj)
obj)))
(defun lookup-object (id)
"Retrieves an object from memory by its ID."
(gethash id *memory*))
(defun list-objects-with-attribute (key value)
"Returns a list of objects that possess the specified attribute pair."
(let ((results nil))
(maphash (lambda (id obj)
(declare (ignore id))
(when (equal (getf (org-object-attributes obj) key) value)
(push obj results)))
*memory*)
results))
(defun snapshot-memory ()
"Creates a deep copy of the memory hash table and pushes it to the history store."
(let ((new-snap (make-hash-table :test 'equal)))
(maphash (lambda (k v) (setf (gethash k new-snap) (copy-org-object v))) *memory*)
(vector-push-extend new-snap *history-store*)))
(defun rollback-memory (&optional (steps 1))
"Restores the memory to a previous snapshot state."
(let ((index (- (length *history-store*) steps 1)))
(when (>= index 0)
(setf *memory* (aref *history-store* index))
(harness-log "IMMUNE SYSTEM: Memory rolled back ~a steps." steps))))

424
library/opencortex.el Normal file
View File

@@ -0,0 +1,424 @@
;;; opencortex.el --- Probabilistic-Deterministic Lisp Machine Kernel for Org-mode -*- lexical-binding: t; -*-
;; Copyright (C) 2026 Amr
;;
;; Author: Amr
;; Version: 0.1.0
;; Package-Requires: ((emacs "27.1"))
;; Keywords: convenience, org
;; URL: https://github.com/amr/opencortex
;;; Commentary:
;; opencortex provides a Probabilistic-Deterministic Lisp Machine interface for Emacs.
;; It acts as the sensor/actuator array, communicating with a persistent
;; Common Lisp daemon over a high-speed communication protocol socket.
;;; Code:
(require 'json)
(require 'cl-lib)
(require 'org-id)
(require 'org-element)
(defgroup opencortex nil
"Emacs interface for the opencortex Common Lisp daemon."
:group 'org)
(defcustom opencortex-port 9105
"The port the opencortex daemon is listening on."
:type 'integer
:group 'opencortex)
(defcustom opencortex-host "127.0.0.1"
"The host the opencortex daemon is running on."
:type 'string
:group 'opencortex)
(defcustom opencortex-executable-path "opencortex-server"
"Path to the compiled opencortex-server binary.
If nil, Emacs will not attempt to start the daemon automatically and
will assume you have started it manually (e.g., via SBCL)."
:type '(choice (string :tag "Path to executable")
(const :tag "Manual daemon management" nil))
:group 'opencortex)
(defvar opencortex--network-process nil
"The network process connected to the daemon.")
(defvar opencortex--daemon-process nil
"The spawned daemon child process.")
(defun opencortex--start-daemon ()
"Start the daemon binary if not already running."
(when (and opencortex-executable-path
(not (process-live-p opencortex--daemon-process)))
(message "opencortex: Starting daemon (%s)..." opencortex-executable-path)
(setq opencortex--daemon-process
(make-process
:name "opencortex-daemon"
:buffer "*opencortex-daemon*"
:command (list opencortex-executable-path (number-to-string opencortex-port))
:connection-type 'pipe))
;; Give it a moment to bind to the port
(sleep-for 1.0)))
(defun opencortex-connect ()
"Connect to the opencortex daemon, starting it if necessary."
(interactive)
(when opencortex--network-process
(delete-process opencortex--network-process))
(opencortex--start-daemon)
(condition-case err
(progn
(setq opencortex--network-process
(make-network-process
:name "opencortex"
:buffer "*opencortex*"
:family 'ipv4
:host opencortex-host
:service opencortex-port
:filter #'opencortex--filter
:sentinel #'opencortex--sentinel))
(message "opencortex: Connected to daemon."))
(error
(message "opencortex: Failed to connect to daemon at %s:%s. Ensure it is running. Error: %s"
opencortex-host opencortex-port (error-message-string err)))))
(defun opencortex-disconnect ()
"Disconnect from the opencortex daemon."
(interactive)
(when opencortex--network-process
(delete-process opencortex--network-process)
(setq opencortex--network-process nil)
(message "opencortex: Disconnected from network."))
(when opencortex--daemon-process
(delete-process opencortex--daemon-process)
(setq opencortex--daemon-process nil)
(message "opencortex: Killed daemon process.")))
(defun opencortex--filter (proc string)
"Handle incoming communication protocol messages from the daemon via PROC with STRING."
(let ((buf (process-buffer proc)))
(when (buffer-live-p buf)
(with-current-buffer buf
(goto-char (point-max))
(insert string)
(opencortex--process-buffer buf proc)))))
(defun opencortex--process-buffer (buffer &optional proc)
"Process the communication protocol message BUFFER, optionally using PROC."
(with-current-buffer buffer
(goto-char (point-min))
(while (>= (buffer-size) 6)
(let* ((len-str (buffer-substring (point-min) (+ (point-min) 6)))
(msg-len (string-to-number len-str 16)))
(if (>= (buffer-size) (+ 6 msg-len))
(let* ((msg-start (+ (point-min) 6))
(msg-end (+ msg-start msg-len))
(msg-str (buffer-substring msg-start msg-end))
(plist (car (read-from-string msg-str))))
(delete-region (point-min) msg-end)
(opencortex--handle-message proc plist))
;; Message incomplete, stop loop
(goto-char (point-max))
(setq msg-len 1000000)))))) ; Break loop
(defun opencortex--plist-get (plist prop)
"Case-insensitive keyword lookup for communication protocol compatibility."
(or (plist-get plist prop)
(plist-get plist (intern (upcase (symbol-name prop))))
(plist-get plist (intern (downcase (symbol-name prop))))))
(defun opencortex--handle-message (proc plist)
"Route and execute incoming communication protocol messages from PROC using PLIST."
(let ((type (opencortex--plist-get plist :type))
(id (opencortex--plist-get plist :id))
(payload (or (opencortex--plist-get plist :payload) plist)))
(cond
((member type '(:request :REQUEST))
(opencortex--execute-request proc id payload))
((member type '(:response :RESPONSE))
(message "opencortex: Received response for ID %s" id))
((member type '(:log :LOG))
(let ((text (opencortex--plist-get payload :text))
(meta (opencortex--plist-get plist :meta)))
(opencortex--insert-to-history (concat "[reasoning" (if meta (format " (%s)" (opencortex--plist-get meta :source)) "") "] " text "\n") 'opencortex-system-face)))
(t (message "opencortex: Received unknown message type %s" type)))))
(defun opencortex--execute-request (proc id payload)
"Execute an actuator request from the daemon via PROC with ID and PAYLOAD."
(let ((action (opencortex--plist-get payload :action)))
(cond
((member action '(:eval :EVAL))
(let ((code (opencortex--plist-get payload :code)))
(condition-case err
(let ((result (eval (read code))))
(opencortex-send
`(:type :RESPONSE :id ,id :payload (:status :success :result ,(format "%s" result)))))
(error
(opencortex-send
`(:type :RESPONSE :id ,id :payload (:status :error :message ,(error-message-string err))))))))
((member action '(:message :MESSAGE))
(message "opencortex [DAEMON]: %s" (opencortex--plist-get payload :text))
(opencortex-send `(:type :RESPONSE :id ,id :payload (:status :success))))
((member action '(:insert-at-end :INSERT-AT-END))
(let ((text (opencortex--plist-get payload :text)))
(opencortex--insert-to-history (concat "\nAGENT: " text "\n\n"))
(opencortex-send `(:type :RESPONSE :id ,id :payload (:status :success)))))
((member action '(:refactor-subtree :REFACTOR-SUBTREE))
(let ((target-id (opencortex--plist-get payload :target-id))
(properties (opencortex--plist-get payload :properties)))
(condition-case err
(save-excursion
(when target-id (org-id-goto target-id))
(dolist (prop properties)
(org-set-property (car prop) (cdr prop)))
(opencortex-send `(:type :RESPONSE :id ,id :payload (:status :success))))
(error
(opencortex-send
`(:type :RESPONSE :id ,id :payload (:status :error :message ,(error-message-string err))))))))
(t
(message "opencortex: Unknown action %s" action)
(opencortex-send `(:type :RESPONSE :id ,id :payload (:status :unsupported)))))))
(defun opencortex--sentinel (proc event)
"Handle network process PROC lifecycle EVENT."
(when (string-match "finished" event)
(setq opencortex--network-process nil)
(message "opencortex: Connection lost.")))
(defun opencortex-send (plist)
"Send a Lisp PLIST to the daemon using communication protocol framing."
(let* ((msg (prin1-to-string plist))
(len (length msg))
(framed (format "%06x%s" len msg)))
(if (and opencortex--network-process (process-live-p opencortex--network-process))
(process-send-string opencortex--network-process framed)
(message "opencortex (offline): %s" framed))))
(defun opencortex--buffer-to-sexp ()
"Transform the current Org buffer into a pure Lisp AST (plist)."
(opencortex--clean-element (org-element-parse-buffer)))
(defun opencortex--clean-element (element)
"Recursively transform an Org ELEMENT into a pure Lisp plist."
(cond
((listp element)
(let* ((type (car element))
(props (nth 1 element))
(children (nthcdr 2 element))
(cleaned-props nil))
;; Filter and transform properties
(cl-loop for (key val) on props by 'cddr do
(unless (member key '(:standard-properties :parent :buffer))
(let ((json-val (cond
((stringp val) val)
((numberp val) val)
((booleanp val) val)
(t (format "%s" val)))))
(setq cleaned-props (plist-put cleaned-props key json-val)))))
;; Explicitly capture TODO state
(let ((todo (org-element-property :todo-keyword element)))
(when todo
(setq cleaned-props (plist-put cleaned-props :TODO-STATE (format "%s" todo)))))
(list :type type
:properties cleaned-props
:contents (mapcar #'opencortex--clean-element children))))
((stringp element) element)
(t (format "%s" element))))
;;; Sensors
(defun opencortex-notify-save ()
"Sensor: Notify daemon with full Semantic Perception (AST) when saved."
(when (and opencortex--network-process (derived-mode-p 'org-mode))
(opencortex-send
`(:type :EVENT
:payload (:sensor :buffer-update
:file ,(buffer-file-name)
:state :saved
:ast ,(opencortex--buffer-to-sexp))))))
(defun opencortex-notify-point ()
"Sensor: Notify daemon of the element currently at point (Incremental Perception).
This is much faster than parsing the entire buffer and allows for real-time
responsiveness to the user's cursor position."
(when (and opencortex--network-process (derived-mode-p 'org-mode))
(let ((element (org-element-at-point)))
(opencortex-send
`(:type :EVENT
:payload (:sensor :point-update
:file ,(buffer-file-name)
:element ,(opencortex--clean-element element)))))))
;;; Interaction Commands
(defun opencortex-set-model-cascade (cascade-string)
"Set the ordered list of LLM providers to use as fallbacks.
CASCADE-STRING should be a comma-separated list of keywords,
e.g., ':gemini,:openai,:ollama'."
(interactive "sEnter model cascade (e.g. :gemini,:openai): ")
(unless opencortex--network-process
(opencortex-connect))
(let ((cascade (mapcar #'intern (split-string cascade-string ","))))
(opencortex-send
`(:type :REQUEST
:id ,(truncate (float-time))
:target :system
:payload (:action :set-cascade :cascade ,cascade)))
(message "opencortex: Requesting model cascade update to %s" cascade)))
(defgroup opencortex-faces nil
"Faces for the opencortex chat interface."
:group 'opencortex)
(defface opencortex-user-face
'((((class color) (background dark)) :foreground "LightSkyBlue" :weight bold)
(((class color) (background light)) :foreground "blue" :weight bold)
(t :weight bold :underline t))
"Face for user messages in chat history."
:group 'opencortex-faces)
(defface opencortex-system-face
'((t :slant italic :foreground "gray50"))
"Face for system and reasoning logs."
:group 'opencortex-faces)
(defun opencortex-chat ()
"Modern chat interface for the opencortex kernel.
Opens a history buffer and a dedicated input area."
(interactive)
(let ((chat-buf (get-buffer-create "*opencortex-chat*"))
(input-buf (get-buffer-create "*opencortex-input*")))
;; History Buffer Setup
(with-current-buffer chat-buf
(unless (eq major-mode 'special-mode)
(special-mode)
(let ((inhibit-read-only t))
(erase-buffer)
(insert "--- opencortex History ---\n\n"))))
;; Input Buffer Setup
(with-current-buffer input-buf
(unless (eq major-mode 'org-mode)
(org-mode)
(local-set-key (kbd "C-c C-c") #'opencortex-chat-send)
(local-set-key (kbd "C-c C-k") #'opencortex-interrupt))
(let ((inhibit-read-only t))
(delete-region (point-min) (point-max))
(insert "# Type your message and press C-c C-c to send.\n")))
;; Layout: Chat History (Top), Input Area (Bottom)
(delete-other-windows)
(switch-to-buffer chat-buf)
(let ((win (split-window-below -6))) ; 6 lines for input
(set-window-buffer win input-buf)
(select-window win))))
(defun opencortex-interrupt ()
"Interrupt the opencortex reasoning loop."
(interactive)
(unless opencortex--network-process
(opencortex-connect))
(opencortex-send
`(:type :EVENT
:payload (:sensor :interrupt)))
(message "opencortex: Interrupt signal sent."))
(defun opencortex--insert-to-history (text &optional face)
"Insert TEXT into the chat history buffer with optional FACE and scroll."
(let ((buf (get-buffer-create "*opencortex-chat*")))
(with-current-buffer buf
(let ((inhibit-read-only t))
(save-excursion
(goto-char (point-max))
(insert (if face (propertize text 'face face) text)))
;; Force scroll in all windows showing this buffer
(walk-windows
(lambda (w)
(when (eq (window-buffer w) buf)
(set-window-point w (point-max))))
nil t)))))
(defun opencortex-chat-send ()
"Send the current chat buffer content to the agent."
(interactive)
(unless opencortex--network-process
(opencortex-connect))
(let* ((text (buffer-substring-no-properties (point-min) (point-max)))
(clean-text (string-trim (replace-regexp-in-string "^#.*\n" "" text))))
(when (> (length clean-text) 0)
;; Append to history with styling
(opencortex--insert-to-history (concat "YOU: " clean-text "\n\n") 'opencortex-user-face)
;; Clear input buffer
(let ((inhibit-read-only t))
(delete-region (point-min) (point-max))
(insert "# Type your message and press C-c C-c to send.\n"))
;; Send to daemon
(opencortex-send
`(:type :EVENT
:meta (:source :emacs)
:payload (:sensor :user-input
:text ,clean-text)))
(message "opencortex: Message sent."))))
(defun opencortex-auth-google (code)
"Submit the Google OAuth authorization CODE to the daemon."
(interactive "sEnter Google Authorization Code: ")
(unless opencortex--network-process
(opencortex-connect))
(opencortex-send
`(:type :REQUEST
:id ,(truncate (float-time))
:target :system
:payload (:action :auth-google-code :code ,code)))
(message "opencortex: Authorization code sent to daemon."))
(defun opencortex-organize-subtree ()
...
"Command: Ask the agent to organize the current Org subtree."
(interactive)
(opencortex-run-command :organize-subtree))
(defun opencortex-summarize-buffer ()
"Command: Ask the agent to summarize the current buffer."
(interactive)
(opencortex-run-command :summarize-buffer))
(defun opencortex-run-command (command-type)
"Generic runner for high-level COMMAND-TYPE."
(unless opencortex--network-process
(opencortex-connect))
(let ((ast (opencortex--buffer-to-sexp)))
(opencortex-send
`(:type :EVENT
:payload (:sensor :user-command
:command ,command-type
:file ,(buffer-file-name)
:ast ,ast)))
(message "opencortex: Requesting '%s'..." command-type)))
;;;###autoload
(define-minor-mode opencortex-mode
"Global minor mode for the opencortex Probabilistic-Deterministic kernel.
When enabled, this mode starts the Lisp daemon (if configured)
and establishes the network connection to enable proactive
Org-mode sensing."
:global t
:group 'opencortex
(if opencortex-mode
(progn
(add-hook 'after-save-hook #'opencortex-notify-save)
(add-hook 'post-command-hook #'opencortex-notify-point)
(add-hook 'kill-emacs-hook #'opencortex-disconnect)
(opencortex-connect))
(remove-hook 'after-save-hook #'opencortex-notify-save)
(remove-hook 'post-command-hook #'opencortex-notify-point)
(remove-hook 'kill-emacs-hook #'opencortex-disconnect)
(opencortex-disconnect)))
(provide 'opencortex)
;;; opencortex.el ends here

189
library/package.lisp Normal file
View File

@@ -0,0 +1,189 @@
(defpackage :opencortex
(:use :cl)
(:export
;; --- Communication Protocol ---
#:frame-message
#:read-framed-message
#:PROTO-GET
#:LIST-OBJECTS-WITH-ATTRIBUTE
#:COSINE-SIMILARITY
#:VAULT-MASK-STRING
#:*VAULT-MEMORY*
#:parse-message
#:make-hello-message
#:validate-communication-protocol-schema
;; --- Daemon Lifecycle ---
#:start-daemon
#:stop-daemon
#:harness-log
#:main
;; --- Memory (CLOSOS) ---
#:ingest-ast
#:lookup-object
#:list-objects-by-type
#:org-id-new
#:*memory*
#:*history-store*
#:org-object
#:make-org-object
#:org-object-id
#:org-object-type
#:org-object-attributes
#:org-object-parent-id
#:org-object-children
#:org-object-version
#:org-object-last-sync
#:org-object-vector
#:org-object-content
#:org-object-hash
#:snapshot-memory
#:rollback-memory
;; --- Context API (Peripheral Vision) ---
#:context-query-store
#:context-get-active-projects
#:context-get-recent-completed-tasks
#:context-list-all-skills
#:context-get-skill-source
#:context-get-system-logs
#:context-resolve-path
#:context-get-skill-telemetry
#:harness-track-telemetry
#:context-assemble-global-awareness
;; --- Reactive Signal Pipeline ---
#:process-signal
#:perceive-gate
#:probabilistic-gate
#:consensus-gate
#:act-gate
#:reason-gate
#:perceive-gate
#:dispatch-gate
#:inject-stimulus
#:initialize-actuators
#:dispatch-action
#:register-actuator
;; --- Skill Engine ---
#:load-skill-from-org
#:initialize-all-skills
#:load-skill-with-timeout
#:topological-sort-skills
#:validate-lisp-syntax
#:defskill
#:*skills-registry*
#:skill
#:skill-name
#:skill-priority
#:skill-dependencies
#:skill-trigger-fn
#:skill-probabilistic-prompt
#:skill-deterministic-fn
;; --- Tool Registry ---
#:def-cognitive-tool
#:*cognitive-tools*
#:cognitive-tool
#:cognitive-tool-name
#:cognitive-tool-description
#:cognitive-tool-parameters
#:cognitive-tool-guard
#:cognitive-tool-body
;; --- Emacs Client Registry ---
#:*emacs-clients*
#:*clients-lock*
#:register-emacs-client
#:unregister-emacs-client
;; --- Probabilistic Engine ---
#:ask-probabilistic
#:register-probabilistic-backend
#:distill-prompt
#:*provider-cascade*
;; --- Security Vault ---
#:vault-get-secret
#:vault-set-secret
;; --- Deterministic Logic ---
#:list-objects-with-attribute
#:deterministic-verify
;; --- AST Helpers ---
#:find-headline-missing-id))
(in-package :opencortex)
(defvar *system-logs* nil
"Thread-safe list of the most recent system messages.")
(defvar *logs-lock* (bt:make-lock "harness-logs-lock")
"Protects the circular log buffer from race conditions during concurrent skill execution.")
(defvar *max-log-history* 100
"The maximum number of entries to preserve in the in-memory log buffer.")
(defvar *skills-registry* (make-hash-table :test 'equal)
"Global registry of all loaded skills, keyed by their unique identifier.")
(defvar *skill-telemetry* (make-hash-table :test 'equal)
"Stores execution duration and failure counts for every registered skill.")
(defvar *telemetry-lock* (bt:make-lock "harness-telemetry-lock")
"Protects the telemetry store from concurrent updates.")
(defun proto-get (plist key)
"Robustly retrieves a value from a plist, checking both uppercase and lowercase keyword versions."
(let* ((s (string key))
(up (intern (string-upcase s) :keyword))
(dn (intern (string-downcase s) :keyword)))
(or (getf plist up) (getf plist dn))))
(defun harness-track-telemetry (skill-name duration status)
"Updates performance metrics for a specific skill. Status should be :success or :rejected."
(when skill-name
(bt:with-lock-held (*telemetry-lock*)
(let ((entry (or (gethash skill-name *skill-telemetry*) (list :executions 0 :total-time 0 :failures 0))))
(incf (getf entry :executions))
(incf (getf entry :total-time) duration)
(when (eq status :rejected) (incf (getf entry :failures)))
(setf (gethash skill-name *skill-telemetry*) entry)))))
(defvar *cognitive-tools* (make-hash-table :test 'equal)
"The active set of physical capabilities available to the agent.")
(defstruct cognitive-tool
"Represents a physical or virtual capability with explicit documentation and security guards."
name
description
parameters
guard
body)
(defmacro def-cognitive-tool (name description parameters &key guard body)
"Registers a new cognitive tool.
NAME: Keyword identifier.
DESCRIPTION: Human-readable intent (used in LLM prompts).
PARAMETERS: List of property lists defining arguments.
GUARD: (context -> boolean) function to prevent unsafe calls.
BODY: The actual Lisp execution logic."
`(setf (gethash (string-downcase (string ',name)) *cognitive-tools*)
(make-cognitive-tool :name (string-downcase (string ',name))
:description ,description
:parameters ',parameters
:guard ,guard
:body ,body)))
(defun harness-log (msg &rest args)
"Centralized logging for the harness. Writes to STDOUT and the thread-safe circular buffer."
(let ((formatted-msg (apply #'format nil msg args)))
(bt:with-lock-held (*logs-lock*)
(push formatted-msg *system-logs*)
(when (> (length *system-logs*) *max-log-history*)
(setq *system-logs* (subseq *system-logs* 0 *max-log-history*))))
(format t "~a~%" formatted-msg)
(finish-output)))

60
library/perceive.lisp Normal file
View File

@@ -0,0 +1,60 @@
(in-package :opencortex)
(defvar *async-sensors* '(:chat-message :delegation :user-command)
"List of sensors that should be processed asynchronously to avoid blocking gateways.")
(defvar *foveal-focus-id* nil
"The Org ID of the node the user is currently interacting with.")
(defun inject-stimulus (raw-message &key stream (depth 0))
"Enqueues a raw message into the reactive signal pipeline."
(let* ((payload (getf raw-message :payload))
(sensor (getf payload :sensor))
(meta (getf raw-message :meta))
(async-p (or (getf payload :async-p) (member sensor *async-sensors*))))
;; Ensure META exists and contains the stream if provided
(unless meta (setf meta (list :SOURCE :SYSTEM :SESSION-ID "internal")))
(when stream (setf (getf meta :reply-stream) stream))
(setf (getf raw-message :meta) meta)
(if async-p
(bt:make-thread
(lambda ()
(restart-case (handler-bind ((error (lambda (c) (harness-log "ASYNC ERROR: ~a" c) (invoke-restart 'skip-event))))
(process-signal raw-message))
(skip-event () nil)))
:name "opencortex-async-task")
(restart-case (handler-bind ((error (lambda (c) (harness-log "SYSTEM ERROR: ~a" c) (invoke-restart 'skip-event))))
(process-signal raw-message))
(skip-event () (harness-log "SYSTEM RECOVERY: Stimulus dropped.~%"))))))
(defun perceive-gate (signal)
"Initial processing: Normalizes raw stimuli and updates memory."
(let* ((payload (getf signal :payload))
(type (getf signal :type))
(meta (getf signal :meta))
(sensor (getf payload :sensor)))
(harness-log "GATE [Perceive]: ~a (~a) [Source: ~s]" 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 *foveal-focus-id* (ignore-errors (getf element :id)))
(ingest-ast element))))
(:interrupt
(bt:with-lock-held (*interrupt-lock*) (setf *interrupt-flag* t)))))
((eq type :RESPONSE)
(harness-log "GATE [Perceive]: Act Result -> ~a" (getf payload :status))))
(setf (getf signal :status) :perceived)
(setf (getf signal :foveal-focus) *foveal-focus-id*)
signal))

25
library/policy.lisp Normal file
View File

@@ -0,0 +1,25 @@
(in-package :opencortex)
(defun policy-check-autonomy (action context)
"Ensures the action does not violate the Autonomy invariant."
(declare (ignore context))
;; Implementation placeholder: currently permits all actions.
;; Future: Scan for non-autonomous domain names or proprietary API endpoints.
action)
(defun policy-deterministic-gate (action context)
"The main policy gate. Sub-calls engineering standards if available."
(let ((current-action (policy-check-autonomy action context)))
(when current-action
(let ((eng-pkg (find-package :opencortex.skills.org-skill-engineering-standards)))
(when eng-pkg
(let ((eng-gate (find-symbol "ENGINEERING-STANDARDS-GATE" eng-pkg)))
(when (and eng-gate (fboundp eng-gate))
(setf current-action (funcall (symbol-function eng-gate) current-action context)))))))
current-action))
(defskill :skill-policy
:priority 100
:trigger (lambda (ctx) t)
:probabilistic nil
:deterministic #'policy-deterministic-gate)

123
library/probabilistic.lisp Normal file
View File

@@ -0,0 +1,123 @@
(in-package :opencortex)
(defvar *probabilistic-backends* (make-hash-table :test 'equal))
(defvar *provider-cascade* nil)
(defun register-probabilistic-backend (name fn) (setf (gethash name *probabilistic-backends*) fn))
(defvar *model-selector-fn* nil "A function called with (provider context) to return a model ID.")
(defvar *consensus-enabled-p* nil "If T, ask-probabilistic queries all backends in parallel.")
(defun ask-probabilistic (prompt &key (system-prompt "You are the Probabilistic engine of a Probabilistic-Deterministic Lisp Machine.") (cascade nil) (context nil))
"Dispatches a neural request through the provider cascade or parallel consensus."
(let ((backends (cond
((and cascade (listp cascade)) cascade)
((functionp cascade) (funcall cascade context))
(t *provider-cascade*))))
(if *consensus-enabled-p*
;; PARALLEL CONSENSUS MODE
(let ((results nil)
(threads nil)
(lock (bt:make-lock)))
(dolist (backend backends)
(let ((backend-fn (gethash backend *probabilistic-backends*)))
(when backend-fn
(push (bt:make-thread
(lambda ()
(harness-log "PROBABILISTIC [Consensus]: Querying backend ~a..." backend)
(let* ((model (when *model-selector-fn* (funcall *model-selector-fn* backend context)))
(result (ignore-errors
(if model
(funcall backend-fn prompt system-prompt :model model)
(funcall backend-fn prompt system-prompt)))))
(bt:with-lock-held (lock)
(push result results)))))
threads))))
;; Wait for all threads with a timeout (e.g., 30s)
(let ((start-time (get-universal-time)))
(loop while (and (< (length results) (length threads))
(< (- (get-universal-time) start-time) 30))
do (sleep 0.1)))
;; Return the list of raw results (filtering out nils or errors)
(let ((valid-results (remove-if-not #'stringp results)))
(if valid-results
(format nil "~{~a~^|CONSENSUS-SEP|~}" valid-results)
"(:type :LOG :payload (:text \"Neural Consensus Failure\"))")))
;; SEQUENTIAL CASCADE MODE
(or (dolist (backend backends)
(let ((backend-fn (gethash backend *probabilistic-backends*)))
(when backend-fn
(harness-log "PROBABILISTIC: Attempting backend ~a..." backend)
(let* ((model (when *model-selector-fn* (funcall *model-selector-fn* backend context)))
(result (if model
(funcall backend-fn prompt system-prompt :model model)
(funcall backend-fn prompt system-prompt))))
(unless (or (null result)
(and (stringp result) (search ":LOG" result) (or (search "Failure" result) (search "missing" result))))
(return result))))))
"(:type :LOG :payload (:text \"Neural Cascade Failure\"))"))))
(defun think (context)
"Invokes the neural Probabilistic engine to propose a Lisp action based on context."
(let ((active-skill (find-triggered-skill context))
(tool-belt (generate-tool-belt-prompt))
(global-context (context-assemble-global-awareness)))
(if active-skill
(progn
(harness-log "PROBABILISTIC: Engaging skill '~a'~%" (skill-name active-skill))
(let* ((prompt-generator (skill-probabilistic-prompt active-skill))
(raw-prompt (when prompt-generator (funcall prompt-generator context)))
(full-system-prompt (concatenate 'string
"ACTUATOR IDENTITY: You are the pure Lisp actuator for the opencortex kernel.
MANDATE: Output EXACTLY ONE Common Lisp property list starting with (:type :REQUEST).
ZERO CONVERSATION: Do not explain. Do not say 'Okay'. Do not use markdown blocks.
STRICT RULE: Do not output multiple lists. Do not chain multiple requests.
DO NOT embed tool calls inside text strings.
"
global-context
"
"
tool-belt
"
IMPORTANT: To reply to the user, you MUST use:
(:type :REQUEST :target :emacs :action :insert-at-end :buffer \"*opencortex-chat*\" :text \"* <Response Text>\")
To call a tool, you MUST use:
(:type :REQUEST :target :tool :action :call :tool \"<name>\" :args (:arg1 \"val\"))
")))
(if (and raw-prompt (> (length raw-prompt) 1))
(let* ((thought (ask-probabilistic raw-prompt :system-prompt full-system-prompt :context context))
(raw-thoughts (cl-ppcre:split (cl-ppcre:quote-meta-chars "|CONSENSUS-SEP|") thought))
(suggestions nil))
(dolist (raw-thought raw-thoughts)
(harness-log "PROBABILISTIC RAW: ~a~%" raw-thought)
(let* ((cleaned-thought
(let ((match (cl-ppcre:scan-to-strings "(?s)```(?:lisp)?\\n?(.*?)\\n?```" raw-thought)))
(if match
(let ((regs (nth-value 1 (cl-ppcre:scan-to-strings "(?s)```(?:lisp)?\\n?(.*?)\\n?```" raw-thought))))
(if (and regs (> (length regs) 0)) (elt regs 0) raw-thought))
(string-trim '(#\Space #\Newline #\Tab) raw-thought))))
(suggestion (handler-case (read-from-string cleaned-thought)
(error (c)
;; EMIT ASYNCHRONOUS REPAIR STIMULUS
(list :type :EVENT :payload
(list :sensor :syntax-error
:code cleaned-thought
:error (format nil "~a" c)))))))
(harness-log "PROBABILISTIC Suggestion: ~a~%" cleaned-thought)
(when (and suggestion (listp suggestion))
(push suggestion suggestions))))
(if (and *consensus-enabled-p* suggestions)
(nreverse suggestions)
(first (nreverse suggestions))))
'(:type :LOG :payload (:text "Skill triggered (Deterministic only)")))))
nil)))
(defun distill-prompt (full-prompt successful-output)
(let ((system-instr "You are a Meta-Cognitive Prompt Architect. DISTILL into template."))
(ask-probabilistic (format nil "PROMPT: ~a~%RESULT: ~a" full-prompt successful-output) :system-prompt system-instr)))

124
library/reason.lisp Normal file
View File

@@ -0,0 +1,124 @@
(in-package :opencortex)
(defvar *probabilistic-backends* (make-hash-table :test 'equal)
"A global mapping of provider identifiers (keywords) to their respective execution functions.")
(defvar *provider-cascade* nil
"An ordered list of providers to attempt if the primary one fails.")
(defvar *model-selector-fn* nil
"A hook for dynamic model selection based on context complexity.")
(defvar *consensus-enabled-p* nil
"Flag to enable parallel multi-model voting (not implemented in MVP).")
(defun register-probabilistic-backend (name fn)
"Registers a neural provider with its calling function."
(setf (gethash name *probabilistic-backends*) fn))
(defun probabilistic-call (prompt &key (system-prompt "You are the Probabilistic engine.") (cascade nil) (context nil))
"Dispatches a neural request through the provider cascade. Returns a Lisp plist or a failure log."
(let ((backends (or cascade *provider-cascade*)))
(or (dolist (backend backends)
(let ((backend-fn (gethash backend *probabilistic-backends*)))
(when backend-fn
(harness-log "PROBABILISTIC: Attempting backend ~a..." backend)
(let* ((model (when *model-selector-fn* (funcall *model-selector-fn* backend context)))
(result (if model
(funcall backend-fn prompt system-prompt :model model)
(funcall backend-fn prompt system-prompt))))
(cond ((and (listp result) (eq (getf result :status) :success))
(return (getf result :content)))
((stringp result) (return result))
(t (harness-log "PROBABILISTIC: Backend ~a failed: ~a" backend (getf result :message))))))))
(list :type :LOG :payload (list :text "Neural Cascade Failure: All providers exhausted.")))))
(defun strip-markdown (text)
"Strips common markdown code block markers from text to ensure valid S-expression parsing."
(if (and text (stringp text))
(let ((cleaned text))
(setf cleaned (cl-ppcre:regex-replace-all "^```[a-z]*\\n" cleaned ""))
(setf cleaned (cl-ppcre:regex-replace-all "\\n```$" cleaned ""))
(setf cleaned (cl-ppcre:regex-replace-all "```" cleaned ""))
(string-trim '(#\Space #\Newline #\Tab) cleaned))
text))
(defun think (context)
"Generates a Lisp action proposal based on current context."
(let* ((active-skill (find-triggered-skill context))
(tool-belt (generate-tool-belt-prompt))
(global-context (context-assemble-global-awareness))
(system-logs (context-get-system-logs))
(assistant-name (or (uiop:getenv "MEMEX_ASSISTANT") "Agent")))
(let* ((prompt-generator (when active-skill (skill-probabilistic-prompt active-skill)))
(raw-prompt (if prompt-generator
(funcall prompt-generator context)
(let ((p (proto-get (proto-get context :payload) :text)))
(if (and p (stringp p)) p "Maintain metabolic stasis."))))
(system-prompt (format nil "IDENTITY: ~a. MANDATE: Respond with ONE Lisp plist. ~a ~a RECENT_LOGS: ~a
IMPORTANT: To reply to the user, you MUST use:
(:TYPE :REQUEST :PAYLOAD (:ACTION :MESSAGE :TEXT \"<Response Text>\"))
To call a tool, you MUST use:
(:TYPE :REQUEST :TARGET :TOOL :ACTION :CALL :TOOL \"<name>\" :ARGS (:arg1 \"val\"))
PROVIDER RULE: Always use the default cascade provider unless a specific model or capability is required for the task."
assistant-name global-context tool-belt system-logs)))
(let* ((thought (probabilistic-call raw-prompt :system-prompt system-prompt :context context))
(cleaned (strip-markdown thought))
(meta (proto-get context :meta))
(source (proto-get meta :source)))
(if (and cleaned (stringp cleaned))
(let ((*read-eval* nil))
(if (and (> (length cleaned) 0) (char= (char cleaned 0) #\())
(handler-case
(let ((parsed (read-from-string cleaned)))
(let ((type (proto-get parsed :TYPE))
(target (or (proto-get parsed :TARGET) (proto-get parsed :target))))
(cond ((member type '(:REQUEST :EVENT :STATUS :RESPONSE))
(unless (proto-get parsed :target) (setf (getf parsed :target) (or source :CLI)))
parsed)
;; Handle raw plists or lists of plists that look like tool calls or data
((or (eq target :TOOL) (eq target :tool) (getf parsed :TOOL) (getf parsed :tool)
(and (listp parsed) (listp (car parsed)) (keywordp (caar parsed))))
(list :TYPE :REQUEST :TARGET :TOOL :PAYLOAD parsed))
(t (list :TYPE :REQUEST :TARGET (or source :CLI) :PAYLOAD (list :ACTION :MESSAGE :TEXT cleaned))))))
(error (c) (list :TYPE :REQUEST :TARGET (or source :CLI) :PAYLOAD (list :ACTION :MESSAGE :TEXT cleaned))))
(list :TYPE :REQUEST :TARGET (or source :CLI) :PAYLOAD (list :ACTION :MESSAGE :TEXT cleaned))))
thought)))))
(defun deterministic-verify (proposed-action context)
"Iterates through all skill deterministic-gates sorted by priority. Ensures absolute safety of the neural proposal."
(let ((current-action proposed-action)
(skills nil))
(maphash (lambda (name skill) (declare (ignore name)) (when (skill-deterministic-fn skill) (push skill skills))) *skills-registry*)
(setf skills (sort skills #'> :key #'skill-priority))
(dolist (skill skills)
(let ((trigger (skill-trigger-fn skill))
(gate (skill-deterministic-fn skill)))
(when (or (null trigger) (ignore-errors (funcall trigger context)))
(let ((next-action (funcall gate current-action context)))
(let ((original-type (proto-get current-action :type)))
(when (and (listp next-action)
(member (proto-get next-action :type) '(:LOG :EVENT :log :event))
(or (not (member original-type '(:LOG :EVENT :log :event)))
(not (eq next-action current-action))))
(harness-log "DETERMINISTIC: Intercepted by skill '~a'" (skill-name skill))
(return-from deterministic-verify next-action)))
(setf current-action next-action)))))
current-action))
(defun reason-gate (signal)
"Unified Stage: Combines Probabilistic proposals and Deterministic verification."
(let* ((type (proto-get signal :type))
(payload (proto-get signal :payload))
(sensor (proto-get payload :sensor)))
;; Optimization: Only reason about user input or chat messages.
(unless (and (eq type :EVENT) (member sensor '(:user-input :chat-message)))
(return-from reason-gate signal))
(let ((candidate (think signal)))
(if candidate
(setf (getf signal :approved-action) (deterministic-verify candidate signal))
(setf (getf signal :approved-action) nil))
(setf (getf signal :status) :reasoned)
signal)))

79
library/skills.lisp Normal file
View File

@@ -0,0 +1,79 @@
(in-package :opencortex)
(defstruct skill
"Represents a hot-reloadable module of intelligence or actuation."
name
priority
dependencies
trigger-fn
probabilistic-prompt
deterministic-fn)
(defmacro defskill (name &key (priority 0) dependencies trigger probabilistic deterministic)
"Registers a new skill into the global harness registry."
`(setf (gethash (string-downcase (string ',name)) *skills-registry*)
(make-skill :name (string-downcase (string ',name))
:priority ,priority
:dependencies ,dependencies
:trigger-fn ,trigger
:probabilistic-prompt ,probabilistic
:deterministic-fn ,deterministic)))
(defun validate-lisp-syntax (file-path)
"Parses a Lisp file without evaluation to verify syntactic integrity."
(handler-case
(with-open-file (stream file-path)
(loop for form = (read stream nil :eof)
until (eq form :eof))
t)
(error (c)
(harness-log "SYNTAX ERROR in ~a: ~a" file-path c)
nil)))
(defun load-skill-from-org (org-file-path)
"Tangles and loads a single Org-mode skill file."
(let* ((filename (file-name-nondirectory (namestring org-file-path)))
(skill-id (pathname-name org-file-path))
(lisp-file (merge-pathnames (concatenate 'string "library/gen/" skill-id ".lisp")
(asdf:system-source-directory :opencortex))))
(ensure-directories-exist lisp-file)
(harness-log "LOADER: Loading ~a..." skill-id)
;; 1. Tangle the Org file into Lisp
(uiop:run-program (list "emacs" "--batch" "--eval" "(require 'org)"
"--eval" (format nil "(org-babel-tangle-file \"~a\")" org-file-path))
:output t)
;; 2. Verify and Load
(if (validate-lisp-syntax lisp-file)
(progn
(handler-case (load lisp-file)
(error (c) (harness-log "LOADER ERROR in skill '~a': ~a" skill-id c)))
t)
nil)))
(defun topological-sort-skills (skills)
"Calculates the correct loading order based on #+DEPENDS_ON metadata."
;; Placeholder: Currently sorts by priority as a proxy for dependencies.
(sort skills #'> :key #'skill-priority))
(defun initialize-all-skills ()
"Discovers and loads all Org files in the SKILLS_DIR."
(let* ((skills-dir (uiop:getenv "SKILLS_DIR"))
(files (when (and skills-dir (uiop:directory-exists-p skills-dir))
(uiop:directory-files skills-dir "*.org"))))
(dolist (f files)
(load-skill-from-org f))
(harness-log "LOADER: Boot Complete. [Ready: ~a] [Failed: 0]" (hash-table-count *skills-registry*))))
(defun find-triggered-skill (context)
"Iterates through the registry and returns the first skill whose trigger returns true."
(let ((skills nil))
(maphash (lambda (name skill) (declare (ignore name)) (push skill skills)) *skills-registry*)
(setf skills (sort skills #'> :key #'skill-priority))
(dolist (s skills)
(let ((trigger (skill-trigger-fn s)))
(when (and trigger (funcall trigger context))
(return-from find-triggered-skill s))))
nil))

157
library/tui-client.lisp Normal file
View File

@@ -0,0 +1,157 @@
(in-package :cl-user)
(defpackage :opencortex.tui (:use :cl :croatoan) (:export :main))
(in-package :opencortex.tui)
(defvar *daemon-host* "127.0.0.1")
(defvar *daemon-port* 9105)
(defvar *socket* nil)
(defvar *stream* nil)
(defvar *chat-history* nil "A list of strings representing the scrollback buffer.")
(defvar *input-buffer* (make-array 0 :element-type 'character :fill-pointer 0 :adjustable t))
(defvar *is-running* t)
(defvar *status-text* "Connecting...")
(defvar *msg-queue* nil)
(defvar *queue-lock* (bt:make-lock "tui-msg-lock"))
(defun enqueue-msg (msg)
(bt:with-lock-held (*queue-lock*) (push msg *msg-queue*)))
(defun dequeue-msgs ()
(bt:with-lock-held (*queue-lock*) (let ((m (reverse *msg-queue*))) (setf *msg-queue* nil) m)))
(defun clean-keywords (msg)
"Ensures all keys in a plist are uppercase keywords."
(if (listp msg)
(let ((clean nil))
(loop for (k v) on msg by #'cddr
do (push (intern (string k) :keyword) clean)
(push v clean))
(nreverse clean))
msg))
(defun format-payload (payload)
"Extracts human-readable text from a protocol payload, handling nested tool calls."
(let* ((action (getf payload :ACTION))
(text (getf payload :TEXT))
(msg (getf payload :MESSAGE))
(tool (getf payload :TOOL))
(prompt (getf payload :PROMPT))
(args (getf payload :ARGS))
(result (getf payload :RESULT)))
(cond (text text)
(msg msg)
((eq action :MESSAGE) (getf payload :TEXT))
((and tool prompt) (format nil "THOUGHT [~a]: ~a" tool prompt))
((and tool args)
(let ((inner-prompt (or (getf args :PROMPT) (getf args :TEXT))))
(if inner-prompt
(format nil "THOUGHT [~a]: ~a" tool inner-prompt)
(format nil "CALL [~a] (ARGS: ~s)" tool args))))
(result (format nil "RESULT: ~a" result))
(t (format nil "~s" payload)))))
(defun listen-thread ()
(loop while *is-running* do
(handler-case
(when (and *stream* (open-stream-p *stream*))
(let ((raw-msg (opencortex:read-framed-message *stream*)))
(unless (member raw-msg '(:eof :error))
(let* ((msg (clean-keywords raw-msg))
(type (or (getf msg :TYPE) (getf msg :type)))
(payload (or (getf msg :PAYLOAD) (getf msg :payload))))
(cond ((and (listp msg) (eq type :EVENT))
(let ((action (or (getf payload :ACTION) (getf payload :action)))
(text (or (getf payload :TEXT) (getf payload :text) (getf payload :MESSAGE) (getf payload :message))))
(cond ((eq action :handshake) (setf *status-text* "Ready"))
(text (enqueue-msg (format nil "SYSTEM: ~a" text))))))
((and (listp msg) (eq type :STATUS))
(setf *status-text* (format nil "[Scribe: ~a] [Gardener: ~a]"
(or (getf msg :SCRIBE) (getf msg :scribe))
(or (getf msg :GARDENER) (getf msg :gardener)))))
((and (listp msg) (member type '(:REQUEST :RESPONSE :LOG)))
(let ((formatted (format-payload payload)))
(when formatted (enqueue-msg formatted))))
((and (listp msg) (eq type :EVENT) (eq (getf payload :SENSOR) :TOOL-OUTPUT))
(let ((formatted (format-payload payload)))
(when formatted (enqueue-msg formatted))))
(t (harness-log "TUI: Ignored unknown type ~a" type)))))
(when (eq raw-msg :eof) (setf *is-running* nil))
(when (eq raw-msg :error) (setf *status-text* "Protocol Error"))))
(error (c) (setf *status-text* (format nil "Net Error: ~a" c)) (setf *is-running* nil)))
(sleep 0.05)))
(defun main ()
"Primary entry point for the standalone TUI client."
(handler-case
(setf *socket* (usocket:socket-connect *daemon-host* *daemon-port*))
(error (e) (format t "Error connecting: ~a~%" e) (return-from main)))
(setf *stream* (usocket:socket-stream *socket*))
(bt:make-thread #'listen-thread :name "tui-listener")
(unwind-protect
(with-screen (scr :input-echoing nil :input-blocking nil :enable-colors t :cursor-visible t)
(let* ((h (height scr))
(w (width scr))
(chat-win (make-instance 'window :height (- h 2) :width w :position (list 0 0)))
(status-win (make-instance 'window :height 1 :width w :position (list (- h 2) 0)))
(input-win (make-instance 'window :height 1 :width w :position (list (- h 1) 0)))
(last-status nil))
(setf (function-keys-enabled-p input-win) t)
(setf (input-blocking input-win) nil)
(loop while *is-running* do
;; 1. Handle incoming messages from the queue
(let ((new-msgs (dequeue-msgs)))
(when new-msgs
(dolist (msg new-msgs)
(push msg *chat-history*)
;; Maintenance: Cap scrollback to prevent memory bloat
(setf *chat-history* (subseq *chat-history* 0 (min (length *chat-history*) 500))))
(clear chat-win)
(let ((line-num 0))
(dolist (m (reverse (subseq *chat-history* 0 (min (length *chat-history*) (- h 3)))))
(add-string chat-win m :y line-num :x 0)
(incf line-num)))
(refresh chat-win)))
;; 2. Render Status Bar
(unless (equal *status-text* last-status)
(clear status-win)
(add-string status-win *status-text* :attributes '(:reverse))
(refresh status-win)
(setf last-status *status-text*))
;; 3. Handle Keyboard Input
(let* ((event (get-wide-event input-win))
(ch (and event (typep event 'event) (event-key event))))
(when ch
(cond
((or (eq ch #\Newline) (eq ch #\Return))
(let ((cmd (coerce *input-buffer* 'string)))
(setf (fill-pointer *input-buffer*) 0)
(when (> (length cmd) 0)
;; Frame and dispatch the message
(let ((framed (opencortex:frame-message (list :TYPE :EVENT
:META (list :SOURCE :tui :SESSION-ID "default")
:PAYLOAD (list :SENSOR :user-input :TEXT cmd)))))
(format *stream* "~a" framed)
(finish-output *stream*)))
(when (string= cmd "/exit") (setf *is-running* nil))))
((or (eq ch :backspace) (eq ch #\Backspace) (eq ch #\Rubout) (eq ch #\Del))
(when (> (length *input-buffer*) 0)
(decf (fill-pointer *input-buffer*))))
((characterp ch)
(vector-push-extend ch *input-buffer*))))
(clear input-win)
(add-string input-win (concatenate 'string "> " (coerce *input-buffer* 'string)))
(move input-win 0 (+ 2 (length *input-buffer*)))
(refresh input-win))
(sleep 0.02))))
(setf *is-running* nil)
(when *socket* (usocket:socket-close *socket*))))