- Implement outbound OACP bridge by passing streams through cognitive loop. - Robustify 'think' and 'dispatch-action' with salvage logic and case-insensitivity. - Fix skill loading crashes due to undefined functions in skeletal skills. - Update org-agent.el to cleanly manage 'Thinking...' status state.
52 lines
3.3 KiB
Common Lisp
52 lines
3.3 KiB
Common Lisp
(in-package :org-agent)
|
|
|
|
(defvar *skills-registry* (make-hash-table :test 'equal))
|
|
|
|
(defstruct skill name priority dependencies trigger-fn neuro-prompt symbolic-fn)
|
|
|
|
(defmacro defskill (name &key priority dependencies trigger neuro symbolic)
|
|
`(setf (gethash ,(string-downcase (string name)) *skills-registry*)
|
|
(make-skill :name ,(string-downcase (string name)) :priority (or ,priority 10) :dependencies ,dependencies
|
|
:trigger-fn ,trigger :neuro-prompt ,neuro :symbolic-fn ,symbolic)))
|
|
|
|
(defun find-triggered-skill (context)
|
|
(let ((triggered nil))
|
|
(maphash (lambda (name skill) (declare (ignore name)) (when (ignore-errors (funcall (skill-trigger-fn skill) context)) (push skill triggered))) *skills-registry*)
|
|
(first (sort triggered #'> :key #'skill-priority))))
|
|
|
|
(defun resolve-skill-dependencies (skill-name)
|
|
(let ((resolved nil) (seen nil))
|
|
(labels ((visit (name) (unless (member name seen :test #'equal) (push name seen)
|
|
(let ((skill (gethash (string-downcase (string name)) *skills-registry*)))
|
|
(when skill (dolist (dep (skill-dependencies skill)) (visit dep))))
|
|
(push name resolved))))
|
|
(visit skill-name) (nreverse resolved))))
|
|
|
|
(defun load-skill-from-org (filepath)
|
|
(when (uiop:file-exists-p filepath)
|
|
(let* ((content (uiop:read-file-string filepath)) (lines (uiop:split-string content :separator '(#\Newline)))
|
|
(in-lisp-block nil) (lisp-code "") (dependencies nil) (skill-base-name (pathname-name filepath))
|
|
(pkg-name (intern (string-upcase (format nil "ORG-AGENT.SKILLS.~a" skill-base-name)) :keyword)))
|
|
(dolist (line lines)
|
|
(let ((clean-line (string-trim '(#\Space #\Tab #\Return) line)))
|
|
(when (uiop:string-prefix-p "#+DEPENDS_ON:" (string-upcase clean-line))
|
|
(setf dependencies (mapcar (lambda (s) (string-trim "[] " s)) (uiop:split-string (subseq clean-line 13) :separator '(#\Space)))))))
|
|
(dolist (line lines)
|
|
(let ((clean-line (string-trim '(#\Space #\Tab #\Return) line)))
|
|
(cond ((uiop:string-prefix-p "#+begin_src lisp" (string-downcase clean-line)) (setf in-lisp-block t))
|
|
((uiop:string-prefix-p "#+end_src" (string-downcase clean-line)) (setf in-lisp-block nil))
|
|
(in-lisp-block (setf lisp-code (concatenate 'string lisp-code line (string #\Newline)))))))
|
|
(when (> (length lisp-code) 0)
|
|
(kernel-log "KERNEL: Jailing skill '~a' in package ~a" skill-base-name pkg-name)
|
|
(unless (find-package pkg-name)
|
|
(let ((new-pkg (make-package pkg-name :use '(:cl))))
|
|
(do-external-symbols (sym (find-package :org-agent)) (shadowing-import sym new-pkg))))
|
|
(let ((*read-eval* nil) (*package* (find-package pkg-name)))
|
|
(handler-case (eval (read-from-string (format nil "(progn ~a)" lisp-code)))
|
|
(error (c) (kernel-log "READER ERROR in skill '~a': ~a~%" skill-base-name c))))))))
|
|
|
|
(defun validate-lisp-syntax (code-string)
|
|
(handler-case (let ((*read-eval* nil)) (with-input-from-string (stream (format nil "(progn ~a)" code-string))
|
|
(loop for form = (read stream nil :eof) until (eq form :eof)) (values t nil)))
|
|
(error (c) (values nil (format nil "~a" c)))))
|