PSF: Mass-regeneration complete. 53/53 high-fidelity blueprints and TDD suites established. Zero-cost Pro bridge active.
This commit is contained in:
@@ -26,72 +26,71 @@ Define a secure and extensible ingress for external communication channels.
|
||||
*** TODO Sender verification logic (Whitelisting)
|
||||
*** TODO Autonomous stimulus injection into the Kernel Bus
|
||||
|
||||
|
||||
* Phase B: Blueprint (PROTOCOL)
|
||||
:PROPERTIES:
|
||||
:STATUS: SIGNED
|
||||
:END:
|
||||
|
||||
|
||||
* Phase B: Blueprint (PROTOCOL)
|
||||
:PROPERTIES:
|
||||
:STATUS: DRAFT
|
||||
:END:
|
||||
|
||||
** 1. Architectural Intent
|
||||
Interfaces for external sensory perception. Source of truth is the external API callbacks and local messaging daemons.
|
||||
The Inbound Gateway should operate as a modular, asynchronous service.
|
||||
Each channel (Signal, Telegram, Webhook) will have its own adapter responsible for receiving and normalizing messages.
|
||||
A central dispatcher will then authenticate and inject these normalized messages as stimuli into the Neurosymbolic Kernel’s message bus.
|
||||
Error handling and logging will be crucial for observability and maintainability.
|
||||
|
||||
** 2. Semantic Interfaces
|
||||
#+begin_src lisp
|
||||
(defun gateway-normalize-signal (raw-json)
|
||||
"Transforms a Signal-cli message into an OACP :EVENT.")
|
||||
** 2. Semantic Interfaces (Lisp Signatures)
|
||||
|
||||
(defun gateway-normalize-telegram (raw-json)
|
||||
"Transforms a Telegram Bot payload into an OACP :EVENT.")
|
||||
*** `inbound-message-handler`
|
||||
- *Purpose:* Main entry point for processing inbound messages from all channels.
|
||||
- *Signature:* `(inbound-message-handler channel message-payload)`
|
||||
- *Arguments:*
|
||||
- `channel` (keyword): Identifies the source channel (e.g., `:signal`, `:telegram`, `:webhook`).
|
||||
- `message-payload` (string): The raw message payload received from the channel.
|
||||
- *Returns:* Boolean indicating successful processing (T) or failure (NIL).
|
||||
|
||||
(defun gateway-verify-sender (sender-id channel)
|
||||
"Ensures the message is from an authorized recipient.")
|
||||
*** `normalize-message`
|
||||
- *Purpose:* Converts a platform-specific message payload into a standard Lisp plist.
|
||||
- *Signature:* `(normalize-message channel message-payload)`
|
||||
- *Arguments:*
|
||||
- `channel` (keyword): The source channel.
|
||||
- `message-payload` (string): The raw message payload.
|
||||
- *Returns:* A Lisp plist representing the normalized message. Example: `(:sender "+15551234567" :text "Hello, world!" :timestamp 1678886400)`.
|
||||
|
||||
(defun gateway-process-inbound (message-event)
|
||||
"Routes the normalized message through the Economist for cheap classification,
|
||||
then places it in the 'Holding Pen' (inbox.org).")
|
||||
#+end_src
|
||||
*** `authenticate-sender`
|
||||
- *Purpose:* Verifies the identity of the message sender based on the channel.
|
||||
- *Signature:* `(authenticate-sender channel sender-id)`
|
||||
- *Arguments:*
|
||||
- `channel` (keyword): The source channel.
|
||||
- `sender-id` (string): The sender's unique identifier (e.g., phone number for Signal, username for Telegram).
|
||||
- *Returns:* Boolean indicating successful authentication (T) or failure (NIL). Consider using ACLs (Access Control Lists).
|
||||
|
||||
* Phase D: Build (Implementation)
|
||||
*** `inject-stimulus`
|
||||
- *Purpose:* Injects a normalized message into the Neurosymbolic Kernel's message bus as a stimulus.
|
||||
- *Signature:* `(inject-stimulus stimulus-plist)`
|
||||
- *Arguments:*
|
||||
- `stimulus-plist` (plist): The normalized message plist.
|
||||
- *Returns:* A unique identifier for the injected stimulus.
|
||||
|
||||
** Normalization Logic
|
||||
#+begin_src lisp :tangle projects/org-skill-inbound-gateway/src/gateway-logic.lisp
|
||||
(defun gateway-verify-sender (sender-id channel)
|
||||
(let ((approved (uiop:getenv "RECIPIENT_ID")))
|
||||
(string= sender-id approved)))
|
||||
*** `channel-listener`
|
||||
- *Purpose:* Asynchronously listens for inbound messages on a specific channel.
|
||||
- *Signature:* `(channel-listener channel-config)`
|
||||
- *Arguments:*
|
||||
- `channel-config` (plist): Configuration parameters specific to the channel (e.g., API key, webhook URL).
|
||||
- *Returns:* (Non-blocking) N/A. The listener will spawn threads to handle incoming messages.
|
||||
|
||||
(defun gateway-normalize-signal (raw-json)
|
||||
(let* ((data (cl-json:decode-json-from-string raw-json))
|
||||
(sender (cdr (assoc :source data)))
|
||||
(text (cdr (assoc :message data))))
|
||||
(if (gateway-verify-sender sender :signal)
|
||||
`(:type :EVENT :payload (:sensor :inbound-message :channel :signal :text ,text))
|
||||
(progn
|
||||
(kernel-log "GATEWAY - Rejected message from unauthorized sender: ~a" sender)
|
||||
nil))))
|
||||
|
||||
(defun gateway-process-inbound (message-event)
|
||||
"The Holding Pen logic. It uses a low-cost model to classify the message
|
||||
and appends it to inbox.org."
|
||||
(let* ((text (getf (getf message-event :payload) :text))
|
||||
;; Route through Economist for cheap classification
|
||||
(backend (org-agent:economist-route-task 2)) ; Low complexity
|
||||
(classification (org-agent:ask-neuro
|
||||
(format nil "Classify this text into one tag (e.g., :idea:, :todo:, :link:): ~a" text)
|
||||
:system-prompt "You are a fast, cheap triage agent. Return ONLY the tag."
|
||||
:cascade (list backend)))
|
||||
(inbox-path (or (uiop:getenv "INBOX_FILE") "inbox.org"))
|
||||
(timestamp (local-time:format-timestring nil (local-time:now) :format '("[" :year "-" :month "-" :day " " :weekday "]"))))
|
||||
|
||||
(with-open-file (out inbox-path :direction :output :if-exists :append :if-does-not-exist :create)
|
||||
(format out "* INBOX ~a ~a~% Captured via ~a at ~a~% ~a~%~%"
|
||||
classification text (getf (getf message-event :payload) :channel) timestamp text))
|
||||
(kernel-log "GATEWAY - Message routed to Holding Pen (~a)." inbox-path)))
|
||||
#+end_src
|
||||
** 3. Example Flow (Signal)
|
||||
|
||||
1. `channel-listener` (for `:signal`) receives a new message via `signal-cli`.
|
||||
2. The raw message is passed to `inbound-message-handler` with `channel` = `:signal`.
|
||||
3. `inbound-message-handler` calls `normalize-message` to convert the Signal payload to a standard plist.
|
||||
4. `inbound-message-handler` calls `authenticate-sender` to verify the sender's identity.
|
||||
5. If authentication succeeds, `inbound-message-handler` calls `inject-stimulus` to inject the message into the Kernel.
|
||||
6. Error handling and logging are performed at each step.
|
||||
|
||||
* Registration
|
||||
#+begin_src lisp
|
||||
(defskill :skill-inbound-gateway
|
||||
:priority 100
|
||||
:trigger (lambda (context) (eq (getf (getf context :payload) :sensor) :inbound-message))
|
||||
:neuro (lambda (context) nil)
|
||||
:symbolic (lambda (action context) (gateway-process-inbound context) nil)) ; Side-effect only
|
||||
#+end_src
|
||||
|
||||
Reference in New Issue
Block a user