diff --git a/literate/package.org b/literate/package.org index 1291ee5..d16639c 100644 --- a/literate/package.org +++ b/literate/package.org @@ -64,6 +64,7 @@ flowchart TD #:context-get-system-logs #:context-resolve-path #:context-get-skill-telemetry + #:harness-track-telemetry #:context-assemble-global-awareness ;; --- Reactive Signal Pipeline --- @@ -149,6 +150,21 @@ The harness maintains a thread-safe circular log buffer to provide context for d (defvar *telemetry-lock* (bt:make-lock "harness-telemetry-lock")) #+end_src +** Telemetry Implementation +The system tracks the performance and reliability of individual skills. This logic is currently preserved in the package layer for future expansion into a dedicated telemetry skill. + +#+begin_src lisp :tangle ../src/package.lisp +(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))))) +#+end_src + ** Cognitive Tool Registry The Tool Registry allows the agent to interact with the physical world. Every tool must define a guard (for security) and a body (for execution). diff --git a/literate/perceive.org b/literate/perceive.org index 9262ce4..cae49f3 100644 --- a/literate/perceive.org +++ b/literate/perceive.org @@ -5,43 +5,78 @@ * Stage 1: Perceive (perceive.lisp) ** Architectural Intent: Sensory Ingestion -The Perceive stage is responsible for data normalization and sensory intake. It takes raw stimuli (from TCP sockets, Signal, or Heartbeats) and updates the global Memory graph. +The Perceive stage is the "sensory cortex" of the Org-Agent. It takes raw stimuli from the outside world (keyboard events, chat messages, heartbeats, or system interrupts) and normalizes them into internal **Signals**. + +** Async Sensor Routing +To prevent blocking the main pipeline, certain sensors (like user commands or chat messages) are processed asynchronously in their own threads. #+begin_src lisp :tangle ../src/perceive.lisp (in-package :org-agent) -(defun harness-track-telemetry (skill-name duration status) - "Updates performance metrics for a specific skill." - (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 *async-sensors* '(:chat-message :delegation :user-command) + "List of sensors that should be processed asynchronously to avoid blocking gateways.") +#+end_src +** Foveal Focus State +The system tracks the user's current point of interaction to provide context to the reasoning engine. + +#+begin_src lisp :tangle ../src/perceive.lisp +(defvar *foveal-focus-id* nil + "The Org ID of the node the user is currently interacting with.") +#+end_src + +** Stimulus Injection +The entry point for raw messages. It determines if the signal should be processed synchronously or asynchronously. + +#+begin_src lisp :tangle ../src/perceive.lisp (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)) - (async-p (or (getf payload :async-p) (member sensor '(:chat-message :delegation :user-command))))) + (async-p (or (getf payload :async-p) (member sensor *async-sensors*)))) (when stream (setf (getf raw-message :reply-stream) stream)) - (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 "org-agent-async-task") - (restart-case (handler-bind ((error (lambda (c) (harness-log "SYSTEM ERROR: ~a" c) (invoke-restart 'skip-event)))) (process-signal raw-message)) + (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 "org-agent-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.~%")))))) +#+end_src +** The Perceive Gate +The initial stage of the metabolic loop. It logs the signal, performs selective memory snapshots, and updates the Memory graph based on incoming AST updates. + +#+begin_src lisp :tangle ../src/perceive.lisp (defun perceive-gate (signal) "Initial processing: Normalizes raw stimuli and updates memory." (let* ((payload (getf signal :payload)) (type (getf signal :type)) (sensor (getf payload :sensor))) (harness-log "GATE [Perceive]: ~a (~a)" type (or sensor "no-sensor")) - (snapshot-memory) + (cond ((eq type :EVENT) (case sensor - (:buffer-update (let ((ast (getf payload :ast))) (when ast (ingest-ast ast)))) - (:point-update (let ((element (getf payload :element))) (when element (ingest-ast element)))) - (:interrupt (bt:with-lock-held (*interrupt-lock*) (setf *interrupt-flag* t))))) + (: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)) #+end_src diff --git a/skills/org-skill-engineering-standards.org b/skills/org-skill-engineering-standards.org index facb1a7..5d732cb 100644 --- a/skills/org-skill-engineering-standards.org +++ b/skills/org-skill-engineering-standards.org @@ -36,6 +36,9 @@ You are strictly forbidden from considering a task complete without updating `gt ** 8. Configuration Externalization (Environment-Driven) Source code MUST be strictly free of hardcoded configuration values (e.g., ports, rhythms, timeouts). All such values must be extracted to the environment (via `.env`) and documented in `.env.example`. This ensures portability and security. +** 9. Literate-Only Modification (The Tangle Mandate) +You are strictly forbidden from modifying generated source code files (e.g., `.lisp`, `.py`, `.el`) directly. All changes MUST be made within the corresponding Literate Org file and then tangled to the source. Direct modification of source code is only permitted with explicit user authorization. + * Phase B: Blueprint (PROTOCOL) :PROPERTIES: :STATUS: SIGNED diff --git a/src/package.lisp b/src/package.lisp index 977c7fb..91129b9 100644 --- a/src/package.lisp +++ b/src/package.lisp @@ -43,6 +43,7 @@ #:context-get-system-logs #:context-resolve-path #:context-get-skill-telemetry + #:harness-track-telemetry #:context-assemble-global-awareness ;; --- Reactive Signal Pipeline --- @@ -113,6 +114,16 @@ (defvar *skill-telemetry* (make-hash-table :test 'equal)) (defvar *telemetry-lock* (bt:make-lock "harness-telemetry-lock")) +(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)) (defstruct cognitive-tool diff --git a/src/perceive.lisp b/src/perceive.lisp index bebf208..a6ef592 100644 --- a/src/perceive.lisp +++ b/src/perceive.lisp @@ -1,21 +1,26 @@ (in-package :org-agent) -(defun harness-track-telemetry (skill-name duration status) - "Updates performance metrics for a specific skill." - (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 *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)) - (async-p (or (getf payload :async-p) (member sensor '(:chat-message :delegation :user-command))))) + (async-p (or (getf payload :async-p) (member sensor *async-sensors*)))) (when stream (setf (getf raw-message :reply-stream) stream)) - (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 "org-agent-async-task") - (restart-case (handler-bind ((error (lambda (c) (harness-log "SYSTEM ERROR: ~a" c) (invoke-restart 'skip-event)))) (process-signal raw-message)) + (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 "org-agent-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) @@ -24,13 +29,25 @@ (type (getf signal :type)) (sensor (getf payload :sensor))) (harness-log "GATE [Perceive]: ~a (~a)" type (or sensor "no-sensor")) - (snapshot-memory) + (cond ((eq type :EVENT) (case sensor - (:buffer-update (let ((ast (getf payload :ast))) (when ast (ingest-ast ast)))) - (:point-update (let ((element (getf payload :element))) (when element (ingest-ast element)))) - (:interrupt (bt:with-lock-held (*interrupt-lock*) (setf *interrupt-flag* t))))) + (: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))