- Changed all 50 org file :tangle targets from ../lisp/ to ~/.local/share/passepartout/lisp/ (XDG data dir) - Removed 49 generated .lisp files from project lisp/ directory - Removed tests/system-integration-tests.lisp (generated) - Removed lisp/*.fasl (compiled, stale) - Updated core-manifest.org to tangle .asd to XDG root - Remapped quicklisp symlink: local-projects/passepartout → XDG TUI fixes in channel-tui-main.org: - Removed with-raw-terminal (stty raw breaks fd 0 reads in this SBCL) - Use cat subprocess + pipe for keyboard input (via :input :interactive) - Blocking read-char on pipe with with-timeout 0.1s for daemon processing - Key events queued via drain-queue alongside daemon messages - Full dialog key routing (Escape, Up/Down, Enter, filters, Backspace) - SIGWINCH resize handling - Post-handshake backend-size re-query - Daemon version in status bar (was v0.5.0 hardcoded) - Handshake version stored in state, no add-msg - :daemon-version and :size-queried in state plist - view-status uses draw-rect for background - Test section gated with #+passepartout-tests
127 lines
4.3 KiB
Org Mode
127 lines
4.3 KiB
Org Mode
#+TITLE: Symbolic Identity — Agent Self-Concept
|
|
#+FILETAGS: :skill:identity:
|
|
#+PROPERTY: header-args:lisp :tangle /home/user/.local/share/passepartout/lisp/symbolic-identity.lisp
|
|
|
|
* Overview
|
|
Load `~/memex/IDENTITY.org` into the agent's self-concept at daemon
|
|
startup. The identity text is injected into the system prompt's
|
|
`IDENTITY` section, between assistant name and reflection feedback.
|
|
|
|
The file is user-editable and survives restarts. If the file is
|
|
missing or empty, identity is silently `""` (no-op).
|
|
|
|
* Contract
|
|
|
|
1. `(load-identity-file &optional path)`:
|
|
Reads IDENTITY.org from `path` (default `~/memex/IDENTITY.org`).
|
|
Sets `*agent-identity*` to the file content string.
|
|
Returns the content string, or NIL if file missing/unreadable.
|
|
2. `(agent-identity)`:
|
|
Returns the cached identity string (`*agent-identity*`), or `""` if
|
|
identity has not been loaded.
|
|
3. `*agent-identity*`:
|
|
Special variable holding the loaded identity text (string).
|
|
|
|
#+begin_src lisp
|
|
(in-package :passepartout)
|
|
|
|
(defvar *agent-identity* ""
|
|
"Identity text loaded from ~/memex/IDENTITY.org at startup.
|
|
|
|
This variable holds the contents of the user's identity file.
|
|
Loaded by `load-identity-file` at daemon/skill initialization,
|
|
called from `agent-identity` for system prompt injection.
|
|
|
|
The file is user-editable and persists across restarts.
|
|
If the file is missing or empty, this variable remains \"\".")
|
|
|
|
(defun load-identity-file (&optional (path nil path-p))
|
|
"Load agent identity from an org file.
|
|
|
|
Reads the identity text file and caches it in
|
|
`*agent-identity*`. If PATH is not provided, defaults to
|
|
`~/memex/IDENTITY.org`.
|
|
|
|
Returns the file content string on success, or NIL if the file
|
|
does not exist or cannot be read."
|
|
(let* ((file-path (if path-p
|
|
(uiop:ensure-pathname path :ensure-absolute t)
|
|
(merge-pathnames "memex/IDENTITY.org"
|
|
(user-homedir-pathname)))))
|
|
(when (uiop:file-exists-p file-path)
|
|
(handler-case
|
|
(let ((content (uiop:read-file-string file-path)))
|
|
(setf *agent-identity* content)
|
|
content)
|
|
(error () nil)))))
|
|
|
|
(defun agent-identity ()
|
|
"Return the currently loaded agent identity string."
|
|
(or *agent-identity* ""))
|
|
|
|
;; Auto-load identity at skill init
|
|
(load-identity-file)
|
|
|
|
#+end_src
|
|
|
|
* Test Squad
|
|
** Test Package
|
|
#+begin_src lisp
|
|
(defpackage :passepartout-identity-tests
|
|
(:use :common-lisp :fiveam :passepartout)
|
|
(:export :identity-suite))
|
|
#+end_src
|
|
|
|
** Test Suite
|
|
#+begin_src lisp
|
|
(in-package :passepartout-identity-tests)
|
|
|
|
(def-suite identity-suite
|
|
:description "Agent identity loading and caching")
|
|
(in-suite identity-suite)
|
|
|
|
(test test-load-identity-file-returns-content
|
|
"Contract 1: load-identity-file reads an existing file, returns content."
|
|
(let* ((path "/tmp/memex-test-identity.org")
|
|
(content "### Personality
|
|
- Friendly
|
|
- Concise"))
|
|
(with-open-file (f path :direction :output :if-exists :supersede)
|
|
(write-string content f))
|
|
(unwind-protect
|
|
(let ((result (passepartout::load-identity-file path)))
|
|
(is (stringp result))
|
|
(is (search "Friendly" result))
|
|
(is (search "Concise" result)))
|
|
(ignore-errors (delete-file path)))))
|
|
|
|
(test test-load-identity-file-missing-nil
|
|
"Contract 1: nil when file does not exist."
|
|
(let ((result (passepartout::load-identity-file
|
|
"/tmp/memex-nonexistent-xxxx.org")))
|
|
(is (null result))))
|
|
|
|
(test test-agent-identity-cached
|
|
"Contract 2+3: agent-identity returns cached value after load."
|
|
(let* ((path "/tmp/memex-test-identity2.org")
|
|
(content "### Preferences
|
|
- Use shell cautiously"))
|
|
(with-open-file (f path :direction :output :if-exists :supersede)
|
|
(write-string content f))
|
|
(unwind-protect
|
|
(progn
|
|
(passepartout::load-identity-file path)
|
|
(let ((id (passepartout::agent-identity)))
|
|
(is (search "shell cautiously" id))))
|
|
(ignore-errors (delete-file path)))))
|
|
|
|
(test test-agent-identity-empty-default
|
|
"Contract 2: returns empty string when nothing was loaded."
|
|
(let ((prev passepartout::*agent-identity*))
|
|
(unwind-protect
|
|
(progn
|
|
(setf passepartout::*agent-identity* nil)
|
|
(is (string= "" (passepartout::agent-identity))))
|
|
(setf passepartout::*agent-identity* prev))))
|
|
#+end_src
|