DOCS: Systematic overhaul of Literate source (Granularity & Technical Reasoning)
Some checks failed
Deploy-Agent-V15-Stdin / JOB-V15-STDIN (push) Failing after 2s
Some checks failed
Deploy-Agent-V15-Stdin / JOB-V15-STDIN (push) Failing after 2s
This commit is contained in:
@@ -1,44 +1,65 @@
|
||||
:PROPERTIES:
|
||||
:ID: tui-client-spec
|
||||
:CREATED: [2026-04-17 Fri 11:00]
|
||||
:END:
|
||||
#+TITLE: OpenCortex TUI Client (Standalone)
|
||||
#+STARTUP: content
|
||||
#+TITLE: OpenCortex TUI Client (tui-client.lisp)
|
||||
#+AUTHOR: Amr
|
||||
#+FILETAGS: :tui:ux:client:
|
||||
#+STARTUP: content
|
||||
|
||||
* Overview
|
||||
The OpenCortex TUI Client is a standalone Common Lisp application built on **Croatoan**. It provides a real-time, multi-window interface for interacting with the OpenCortex daemon.
|
||||
* OpenCortex TUI Client (tui-client.lisp)
|
||||
|
||||
* Implementation
|
||||
** Architectural Intent: High-Fidelity Interaction
|
||||
The TUI Client is a standalone consumer of the OpenCortex protocol. It uses the ~croatoan~ (ncurses) library to provide a split-pane, interactive terminal experience.
|
||||
|
||||
*** Design Requirements
|
||||
1. **Concurrency:** The client must listen for incoming protocol events (heartbeats, status updates, thoughts) in a background thread to prevent the UI from freezing.
|
||||
2. **Buffer Safety:** User input must be captured in a thread-safe buffer and framed correctly before being sent to the daemon.
|
||||
3. **Transparency:** The status bar must provide real-time feedback on the state of background workers (Scribe and Gardener).
|
||||
|
||||
** Package Context
|
||||
#+begin_src lisp :tangle ../src/tui-client.lisp
|
||||
(in-package :cl-user)
|
||||
(defpackage :opencortex.tui
|
||||
(:use :cl :croatoan)
|
||||
(:export :main))
|
||||
(defpackage :opencortex.tui (:use :cl :croatoan) (:export :main))
|
||||
(in-package :opencortex.tui)
|
||||
#+end_src
|
||||
|
||||
* UI State Management
|
||||
|
||||
** Networking and Streams
|
||||
#+begin_src lisp :tangle ../src/tui-client.lisp
|
||||
(defvar *daemon-host* "127.0.0.1")
|
||||
(defvar *daemon-port* 9105)
|
||||
(defvar *socket* nil)
|
||||
(defvar *stream* nil)
|
||||
(defvar *chat-history* (list))
|
||||
(defvar *status-text* "Connecting...")
|
||||
(defvar *input-buffer* (make-array 0 :element-type 'char :fill-pointer 0 :adjustable t))
|
||||
#+end_src
|
||||
|
||||
** Terminal Buffers
|
||||
#+begin_src lisp :tangle ../src/tui-client.lisp
|
||||
(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 *queue-lock* (bt:make-lock))
|
||||
(defvar *incoming-msgs* nil)
|
||||
(defvar *status-text* "Connecting...")
|
||||
#+end_src
|
||||
|
||||
** Thread-Safe Message Queue
|
||||
We use a simple locked queue to move messages from the background listener thread to the foreground rendering loop.
|
||||
|
||||
#+begin_src lisp :tangle ../src/tui-client.lisp
|
||||
(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 *incoming-msgs*)))
|
||||
(bt:with-lock-held (*queue-lock*) (push msg *msg-queue*)))
|
||||
|
||||
(defun dequeue-msgs ()
|
||||
(bt:with-lock-held (*queue-lock*)
|
||||
(let ((msgs (nreverse *incoming-msgs*)))
|
||||
(setf *incoming-msgs* nil)
|
||||
msgs)))
|
||||
(bt:with-lock-held (*queue-lock*) (let ((m (reverse *msg-queue*))) (setf *msg-queue* nil) m)))
|
||||
#+end_src
|
||||
|
||||
* Protocol Integration
|
||||
|
||||
** Keyword Sanitization (clean-keywords)
|
||||
Clients often receive data with inconsistent keyword casing. This helper ensures all incoming keys are normalized for easier processing.
|
||||
|
||||
#+begin_src lisp :tangle ../src/tui-client.lisp
|
||||
(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
|
||||
@@ -46,7 +67,12 @@ The OpenCortex TUI Client is a standalone Common Lisp application built on **Cro
|
||||
(push v clean))
|
||||
(nreverse clean))
|
||||
msg))
|
||||
#+end_src
|
||||
|
||||
** Payload Extraction (format-payload)
|
||||
The core "intelligence" of the TUI display. It recursively searches a protocol payload for the most relevant human-readable content.
|
||||
|
||||
#+begin_src lisp :tangle ../src/tui-client.lisp
|
||||
(defun format-payload (payload)
|
||||
"Extracts human-readable text from a protocol payload, handling nested tool calls."
|
||||
(let* ((action (getf payload :ACTION))
|
||||
@@ -67,7 +93,12 @@ The OpenCortex TUI Client is a standalone Common Lisp application built on **Cro
|
||||
(format nil "CALL [~a] (ARGS: ~s)" tool args))))
|
||||
(result (format nil "RESULT: ~a" result))
|
||||
(t (format nil "~s" payload)))))
|
||||
#+end_src
|
||||
|
||||
** Background Listener (listen-thread)
|
||||
Runs as a separate thread. It continuously reads framed messages from the daemon and enqueues them for the UI.
|
||||
|
||||
#+begin_src lisp :tangle ../src/tui-client.lisp
|
||||
(defun listen-thread ()
|
||||
(loop while *is-running* do
|
||||
(handler-case
|
||||
@@ -97,8 +128,16 @@ The OpenCortex TUI Client is a standalone Common Lisp application built on **Cro
|
||||
(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)))
|
||||
#+end_src
|
||||
|
||||
* Main Interaction Loop
|
||||
|
||||
** TUI Entry Point (main)
|
||||
Initializes the ncurses screen, sets up the window layout, and handles user keyboard input.
|
||||
|
||||
#+begin_src lisp :tangle ../src/tui-client.lisp
|
||||
(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)))
|
||||
@@ -118,11 +157,12 @@ The OpenCortex TUI Client is a standalone Common Lisp application built on **Cro
|
||||
(setf (input-blocking input-win) nil)
|
||||
|
||||
(loop while *is-running* do
|
||||
;; 1. Handle incoming messages
|
||||
;; 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)
|
||||
@@ -132,7 +172,7 @@ The OpenCortex TUI Client is a standalone Common Lisp application built on **Cro
|
||||
(incf line-num)))
|
||||
(refresh chat-win)))
|
||||
|
||||
;; 2. Render Status Bar ONLY if changed
|
||||
;; 2. Render Status Bar
|
||||
(unless (equal *status-text* last-status)
|
||||
(clear status-win)
|
||||
(add-string status-win *status-text* :attributes '(:reverse))
|
||||
@@ -148,9 +188,7 @@ The OpenCortex TUI Client is a standalone Common Lisp application built on **Cro
|
||||
(let ((cmd (coerce *input-buffer* 'string)))
|
||||
(setf (fill-pointer *input-buffer*) 0)
|
||||
(when (> (length cmd) 0)
|
||||
;; Local Echo
|
||||
(enqueue-msg (concatenate 'string "> " cmd))
|
||||
;; Send to Brain
|
||||
;; 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)))))
|
||||
|
||||
Reference in New Issue
Block a user