diff --git a/docs/ux.org b/docs/ux.org new file mode 100644 index 0000000..2b90b3e --- /dev/null +++ b/docs/ux.org @@ -0,0 +1,57 @@ +#+TITLE: User Experience (UX) Journey +#+AUTHOR: Amr +#+FILETAGS: :ux:design:psf: +#+STARTUP: content + +* Overview +This document traces the intended User Experience (UX) journey for the ~org-agent~. It serves as a living design document to ensure that architectural decisions align with a frictionless, sovereign, and intuitive user interaction model. + +* 1. The Zero-to-One Experience (Onboarding) +** Goal +A user should be able to go from discovering the project to having a running, calibrated agent in under 3 minutes, with zero prerequisite knowledge of Lisp. + +** The Appliance Paradigm (Primary Path) +The user runs a single command in their terminal: +#+begin_src bash +curl -fsSL https://raw.githubusercontent.com/gharbeia/org-agent/main/scripts/install.sh | bash +#+end_src + +** The Interactive Wizard +The script verifies Docker presence and then launches an interactive prompt before booting the container: +1. *Identity:* "What is your name?" -> Configures ~$MEMEX_USER~ +2. *Assistant:* "What shall we name your Assistant?" -> Configures ~$MEMEX_ASSISTANT~ +3. *Neural Provider:* "Select your primary neural provider [Gemini/OpenRouter/Anthropic/OpenAI]" -> Configures API Keys. +4. *Data Gravity:* "Where is your Memex located?" -> Maps the host directory to the Docker container. + +*Outcome:* The `.env` is generated, core skills are seeded into the user's Memex, and `docker-compose up -d` launches the daemon in the background. The user sees: /"Booting your sovereign brain in the background..."/ + +* 2. The First Contact (The CLI Gateway) +** Goal +Immediately after boot, the user needs a way to verify the agent is alive and capable of answering questions about their Memex without configuring complex third-party integrations (like Telegram bots). + +** The Interaction +The user types a local client command to connect to the background daemon: +#+begin_src bash +org-agent chat +#+end_src + +This opens a slick, colorful interactive terminal session: +#+begin_example +> User: Hello, what are my active projects? +> Agent: [Thinking...] +> Agent: You currently have 3 active projects: +> 1. Org-agent v1.0 +> 2. Home Renovation +> 3. Read 'The Sovereign Individual' +#+end_example + +** Behind the Scenes +1. The ~org-agent chat~ client connects to the daemon's local port (e.g., 9105). +2. It sends a ~:chat-message~ signal. +3. The core harness routes this to the Probabilistic Engine. +4. The Context Manager retrieves active projects from the Memex AST. +5. The Deterministic Engine (Bouncer) verifies it is a safe read-only action. +6. The ~:cli~ Actuator formats the Lisp response into Markdown and sends it back over the socket. + +* 3. The Continuous Loop (Daily Usage) +(To be defined as the agent's capabilities expand into Scribe, Gardener, and Emacs-native interactions). diff --git a/scripts/org-agent-chat.sh b/scripts/org-agent-chat.sh new file mode 100755 index 0000000..7e2c26c --- /dev/null +++ b/scripts/org-agent-chat.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# org-agent-chat: The terminal mouthpiece for the Sovereign Brain. +PORT=9105 +HOST=${1:-localhost} + +echo "Connecting to org-agent at $HOST:$PORT..." +echo "Type your message and press Enter. Ctrl+C to exit." +echo "--------------------------------------------------" + +# Uses netcat (nc) for a simple bidirectional pipe. +# Requires an open connection. We use a simple loop for persistence. +while true; do + read -p "User: " MESSAGE + if [ -z "$MESSAGE" ]; then continue; fi + # Send message and wait for one line of response from Agent + echo "$MESSAGE" | nc -N $HOST $PORT +done diff --git a/skills/org-skill-chat.org b/skills/org-skill-chat.org index f7ec30e..52189ac 100644 --- a/skills/org-skill-chat.org +++ b/skills/org-skill-chat.org @@ -124,6 +124,7 @@ The Chat skill acts as the conversational UI. Because the ~org-agent~ kernel eva (:telegram (format nil "- To reply via Telegram: (:type :REQUEST :target :telegram :chat-id \"~a\" :text \"\")" chat-id)) (:signal (format nil "- To reply via Signal: (:type :REQUEST :target :signal :chat-id \"~a\" :text \"\")" chat-id)) (:matrix (format nil "- To reply via Matrix: (:type :REQUEST :target :matrix :room-id \"~a\" :text \"\")" chat-id)) + (:cli (format nil "- To reply via CLI: (:type :REQUEST :target :cli :text \"\")")) (t "- To reply via Emacs: (:type :REQUEST :target :emacs :action :insert-at-end :buffer \"*org-agent-chat*\" :text \"* \")")))) (ask-probabilistic trimmed-text :system-prompt (concatenate 'string "ACTUATOR IDENTITY: You are the pure Lisp actuator for the org-agent kernel. diff --git a/skills/org-skill-cli-gateway.org b/skills/org-skill-cli-gateway.org new file mode 100644 index 0000000..07affc2 --- /dev/null +++ b/skills/org-skill-cli-gateway.org @@ -0,0 +1,148 @@ +:PROPERTIES: +:ID: cli-gateway-skill +:CREATED: [2026-04-13 Mon 17:00] +:END: +#+TITLE: SKILL: CLI Gateway (Universal Literate Note) +#+STARTUP: content +#+FILETAGS: :gateway:cli:io:psf: + +* Overview +The *CLI Gateway* is the primary interaction point for the Org-Agent MVP. It provides a lightweight TCP socket server that allows local terminal clients to communicate with the daemon. It ensures a frictionless "First Contact" experience immediately following installation. + +* Phase A: Demand (PRD) +:PROPERTIES: +:STATUS: SIGNED +:END: + +** 1. Purpose +Provide a secure, local, and low-latency terminal interface for the Org-Agent. + +** 2. Success Criteria +- [X] *Ingress:* Accept plain-text messages over TCP port 9105. +- [X] *Normalisation:* Inject messages into the harness as `:chat-message` signals. +- [X] *Egress:* Implement the `:cli` actuator to route agent responses back to the correct client socket. +- [X] *Client:* Provide a standalone client script for the user's host machine. + +* Phase B: Blueprint (PROTOCOL) +:PROPERTIES: +:STATUS: SIGNED +:END: + +** 1. Architectural Intent +The gateway runs a multi-threaded TCP server. Each connection is handled in its own thread. Inbound lines are wrapped in a Signal and processed. The `:cli` actuator retrieves the `:reply-stream` from the signal context to send the response back to the specific connected client. + +** 2. Semantic Interfaces +- Inbound: `(:sensor :chat-message :channel :cli :text "...")` +- Outbound: `(:type :REQUEST :target :cli :text "...")` + +* Phase D: Build (Implementation) + +** Package Context +#+begin_src lisp +(in-package :org-agent) +#+end_src + +** State: Server Control +#+begin_src lisp +(defvar *cli-server-thread* nil) +(defvar *cli-server-socket* nil) +(defvar *cli-port* 9105) +#+end_src + +** Actuator: CLI Response +The CLI actuator writes the agent's response back to the client's network stream. It applies a simple "Agent: " prefix for clarity. + +#+begin_src lisp +(defun execute-cli-action (action context) + "Sends a message back to the connected CLI client via its network stream." + (let* ((payload (getf action :payload)) + (text (or (getf payload :text) (getf action :text))) + (stream (getf context :reply-stream))) + (if (and stream (open-stream-p stream)) + (progn + (format stream "Agent: ~a~%" text) + (finish-output stream)) + (harness-log "CLI ERROR: No active reply stream for signal.")))) +#+end_src + +** Server: Client Handler +Handles an individual TCP connection. It reads lines until the connection is closed. + +#+begin_src lisp +(defun handle-cli-client (stream) + "Reads lines from a CLI client and injects them as stimuli." + (handler-case + (loop for line = (read-line stream nil nil) + while line do + (let ((trimmed (string-trim '(#\Space #\Tab #\Newline #\Return) line))) + (when (> (length trimmed) 0) + (harness-log "CLI: Received input -> ~a" trimmed) + (inject-stimulus (list :type :EVENT + :payload (list :sensor :chat-message + :channel :cli + :text trimmed)) + :stream stream)))) + (error (c) (harness-log "CLI CLIENT ERROR: ~a" c)))) +#+end_src + +** Server: Main Loop +Listens for new TCP connections on the configured port. + +#+begin_src lisp +(defun start-cli-gateway (&optional (port *cli-port*)) + "Starts the TCP listener for local CLI clients." + (setf *cli-server-socket* (usocket:socket-listen "0.0.0.0" port :reuse-address t)) + (setf *cli-server-thread* + (bt:make-thread + (lambda () + (unwind-protect + (loop + (let* ((socket (usocket:socket-accept *cli-server-socket*)) + (stream (usocket:socket-stream socket))) + (bt:make-thread (lambda () + (unwind-protect (handle-cli-client stream) + (usocket:socket-close socket))) + :name "org-agent-cli-client-handler"))) + (usocket:socket-close *cli-server-socket*))) + :name "org-agent-cli-gateway")) + (harness-log "CLI: Gateway listening on port ~a" port)) +#+end_src + +** Registration +#+begin_src lisp +(register-actuator :cli #'execute-cli-action) + +(defskill :skill-gateway-cli + :priority 200 + :trigger (lambda (ctx) (declare (ignore ctx)) nil) + :probabilistic nil + :deterministic (lambda (action ctx) (declare (ignore ctx)) action)) +#+end_src + +** Initialization +#+begin_src lisp +(start-cli-gateway) +#+end_src + +* Phase E: The Client (Scripts) +We tangle a lightweight client script that the user can run on their host machine. + +** The Bash Client +#+begin_src bash :tangle ../scripts/org-agent-chat.sh :shebang "#!/bin/bash" +# org-agent-chat: The terminal mouthpiece for the Sovereign Brain. +PORT=9105 +HOST=${1:-localhost} + +echo "Connecting to org-agent at $HOST:$PORT..." +echo "Type your message and press Enter. Ctrl+C to exit." +echo "--------------------------------------------------" + +# Uses netcat (nc) for a simple bidirectional pipe. +# Requires an open connection. We use a simple loop for persistence. +while true; do + read -p "User: " MESSAGE + if [ -z "$MESSAGE" ]; then continue; fi + # Send message and wait for one line of response from Agent + echo "$MESSAGE" | nc -N $HOST $PORT +done +#+end_src