Files
passepartout/skills/org-skill-tool-permissions.org
Amr Gharbeia dfe318425f Add v0.2.0 features: vector search + tool permissions
- Local vector search: Ollama embeddings + semantic search
  - get-embedding with caching
  - cosine-similarity computation
  - semantic-search cognitive tool
  - :semantic-search tool for LLM queries

- Tool permission tiers: security gating for cognitive tools
  - Three tiers: :allow, :deny, :ask
  - Gate in execute-tool-action before tool runs
  - Defaults: :deny for shell/delete-file, :ask for eval/write-file
  - :tool-permissions cognitive tool for management

- Embedding provider support: Ollama AND llama.cpp
  - EMBEDDING_PROVIDER env var
  - EMBEDDING_MODEL env var
  - LLAMA_HOST for llama.cpp server

- .env.example: Add embedding config variables
- Fix parse-message in communication.lisp

- Update ASDF: add test files, tool-permissions skill

All 60 tests pass (6 suites x 100%)
2026-04-23 13:43:50 -04:00

5.2 KiB

SKILL: Tool Permission Tiers

Overview

This skill implements tool permission tiers for security - controlling which cognitive tools can execute without user interaction.

Also provides vector embeddings via Ollama or llama.cpp.

The Three Tiers

Tier Behavior Use Case
:allow Executes immediately Trusted, safe tools
:deny Blocks before execution Dangerous tools
:ask Prompts user, pauses execution Sensitive tools

Embedding Providers

  • EMBEDDING_PROVIDER environment: "ollama" or "llama.cpp"
  • OLLAMA_HOST / LLAMA_HOST for the API endpoint
  • EMBEDDING_MODEL model name

Implementation

Tool permissions and embedding generation via multiple providers.

(in-package :opencortex)

(defvar *tool-permissions* (make-hash-table :test 'equal)
  "Hash table mapping tool names to :allow/:deny/:ask.")

(defun get-tool-permission (tool-name)
  (let ((key (string-downcase (string tool-name))))
    (or (gethash key *tool-permissions*) :allow)))

(defun set-tool-permission (tool-name tier)
  (setf (gethash (string-downcase (string tool-name)) *tool-permissions*) tier)
  (harness-log "TOOL PERMISSION: Set ~a = ~a" tool-name tier))

(defun check-tool-permission-gate (tool-name context)
  (declare (ignore context))
  (let ((perm (get-tool-permission tool-name)))
    (case perm
      (:allow :allow)
      (:deny :deny)
      (:ask (list :ask tool-name context))
      (t :allow))))

(def-cognitive-tool :get-embedding
  "Generates vector embeddings via Ollama or llama.cpp API."
  ((:text :type :string :description "Text to embed."))
  :body (lambda (args)
          (let* ((text (getf args :text))
                 (provider (or (uiop:getenv "EMBEDDING_PROVIDER") "ollama"))
                 (model (or (uiop:getenv "EMBEDDING_MODEL") "nomic-embed-text"))
                 (embedding nil))
            (cond
              ((string= provider "ollama")
               (let* ((host (or (uiop:getenv "OLLAMA_HOST") "localhost:11434"))
                      (url (format nil "http://~a/api/embeddings" host))
                      (body (cl-json:encode-json-to-string `((model . ,model) (prompt . ,text)))))
                 (handler-case
                     (let* ((response (dex:post url :headers '(("Content-Type" . "application/json")) :content body :connect-timeout 5 :read-timeout 30))
                            (json (cl-json:decode-json-from-string response))
                            (vec (cdr (assoc :embedding json))))
                       (when vec (setf embedding vec)))
                   (error (c) (harness-log "EMBEDDING: Ollama failed: ~a" c)))))
              ((string= provider "llama.cpp")
               (let* ((host (or (uiop:getenv "LLAMA_HOST") "localhost:8080"))
                      (url (format nil "http://~a/v1/embeddings" host))
                      (body (cl-json:encode-json-to-string `((model . ,model) (input . ,text)))))
                 (handler-case
                     (let* ((response (dex:post url :headers '(("Content-Type" . "application/json")) :content body :connect-timeout 5 :read-timeout 30))
                            (json (cl-json:decode-json-from-string response))
                            (data (cdr (assoc :data json)))
                            (vec (when data (cdr (assoc :embedding (car data))))))
                       (when vec (setf embedding vec)))
                   (error (c) (harness-log "EMBEDDING: llama.cpp failed: ~a" c))))))
            (if embedding
                (list :status :success :vector embedding)
                (list :status :error :message "Embedding generation failed")))))

(def-cognitive-tool :tool-permissions
  "View or set tool permission tiers."
  ((:tool :type :string :description "Tool name")
   (:action :type :keyword :description "Action: :get, :set, :list" :default :get)
   (:tier :type :keyword :description "For :set: :allow/:deny/:ask"))
  :body (lambda (args)
          (let ((tool (getf args :tool))
                (action (getf args :action :get))
                (tier (getf args :tier)))
            (case action
              (:get (list :status :success :tool tool :permission (get-tool-permission tool)))
              (:set (progn (set-tool-permission tool tier)
                        (list :status :success :message (format nil "Set ~a = ~a" tool tier))))
              (:list (let ((r nil))
                       (maphash (lambda (k v) (push (list :tool k :permission v) r)) *tool-permissions*)
                       (list :status :success :tools r)))
              (t (list :status :error :message "Invalid action"))))))

;; Defaults
(set-tool-permission :shell :deny)
(set-tool-permission :delete-file :deny)
(set-tool-permission :eval :ask)
(set-tool-permission :write-file :ask)
(harness-log "TOOL PERMISSIONS: Initialized")

(defskill :skill-tool-permissions
  :priority 600
  :trigger (lambda (c) (declare (ignore c)) nil)
  :deterministic (lambda (a c)
    (let ((tool (getf (getf a :payload) :tool)))
      (when tool (check-tool-permission-gate tool c)))))