Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Extension Spec

Auto-Discovery

Extensions are auto-discovered from META-INF/vis/extensions.edn on the classpath. See Overview — Auto-Discovery for the full convention.

extension — build and validate

(ext/extension spec) → validated extension map
KeyRequiredDefaultDescription
:ext/namespaceFully qualified symbol, e.g. 'com.blockether.vis.ext.editing, 'com.acme.ext.git
:ext/docExtension-level description
:ext/groupTop-level prompt group, e.g. "knowledge"
:ext/subgroupsame as :ext/groupFiner-grained grouping within the group
:ext/activation-fn(constantly true)(fn [env] → bool) — when falsy, all symbols are unbound and nudge-fn is skipped
:ext/promptString or (fn [env] → string) — LLM-facing docs in system prompt
:ext/nudge-fn(fn [ctx] → string|nil) — per-iteration nudge composer (see Nudge System)
:ext/requires[]Vector of extension namespace symbols that must be registered first, e.g. ['com.blockether.vis.ext.editing]
:ext/versionSemver version string, e.g. "1.0.0", "0.3.1-SNAPSHOT"
:ext/authorAuthor name or org, e.g. "Blockether"
:ext/licenseSPDX license identifier, e.g. "MIT", "Apache-2.0", "Apache-2.0"
:ext/symbolsVector of symbol entries (from symbol / value)
:ext/classes{}{fq-symbol → Class} — Java classes exposed in sandbox
:ext/imports{}{short-symbol → fq-symbol} — short-name imports
:ext/ns-alias{:ns 'vis.ext.fs :alias 'fs}required. Creates a dedicated SCI namespace with alias. Symbols are bound only into this namespace, never into sandbox directly. The alias is auto-required in the sandbox. The LLM must use (fs/read-file ...) — bare (read-file ...) does not resolve.

symbol — function binding

(ext/symbol sym-name f opts) → validated fn symbol entry
OptRequiredDefaultDescription
:docOne-liner shown in the sandbox var’s docstring
:arglistsArgument signatures, e.g. '([query] [query opts])
:examplesderived from :arglistsUsage examples injected into system prompt
:before-fn(fn [env f args] → map) — pre-call hook
:after-fn(fn [env f args result] → map) — post-call hook
:on-error-fn(fn [err env f args] → map) — error handler

value — constant binding

(ext/value sym-name val opts) → validated value symbol entry
OptRequiredDescription
:docOne-liner description

wrap-extension — bind into SCI

(ext/wrap-extension ext env) → {sym → fn-or-value}

Wraps every function symbol through invoke-symbol-wrapper (before → fn → after, with on-error recovery). Value symbols are returned as {sym → value}. Each wrapped fn closes over the extension, symbol entry, and environment.

validate! — standalone validation

(ext/validate! ext) → normalized ext (or throws)

Normalizes :ext/prompt (string → fn) then checks the spec. Called internally by extension; safe to call standalone.

Note: :ext/prompt accepts string or fn?. Both extension and validate! normalize strings to (constantly s) before validation.

Full Example

(ns com.blockether.vis.ext.documents
  (:require [c.b.vis.loop.runtime.conversation.environment.extension :as ext]))

(defn- search-fn [query] ...)
(defn- search-with-opts [query opts] ...)

(def search-sym
  (ext/symbol 'search search-fn
    {:doc        "Full-text search across ingested documents."
     :arglists  '([query] [query opts])
     :examples  ["(docs/search \"neural\")"
                 "(docs/search \"attention\" {:limit 5})"]
     :before-fn (fn [env f args]
                  {:args (update args 0 str/lower-case)})
     :after-fn  (fn [env f args result]
                  {:result (take 10 result)})}))

(def max-results-sym
  (ext/value 'max-results 50
    {:doc "Maximum number of search results returned."}))

(def docs-ext
  (ext/extension
    {:ext/namespace     'com.blockether.vis.ext.documents
     :ext/doc           "Document search and retrieval"
     :ext/version       "1.0.0"
     :ext/author        "Blockether"
     :ext/license       "Apache-2.0"
     :ext/group         "knowledge"
     :ext/subgroup      "documents"
     :ext/ns-alias      {:ns 'vis.ext.docs :alias 'docs}
     :ext/requires      ['com.blockether.vis.ext.editing]
     :ext/prompt        "Document search (use docs/ prefix):
- (docs/search query) — full-text search across documents
- docs/max-results — max results constant (default 50)"
     :ext/activation-fn (fn [env] (seq (list-docs (:db-info env))))
     :ext/nudge-fn      (fn [{:keys [environment iteration prev-expressions]}]
                          (when (and (> iteration 5)
                                    (some :error prev-expressions))
                            "[system_nudge] Document searches are failing."))
     :ext/symbols       [search-sym max-results-sym]
     :ext/classes       {'java.time.LocalDate java.time.LocalDate}
     :ext/imports       {'LocalDate 'java.time.LocalDate}}))

;; Self-register at load time
(ext/register-global! docs-ext)

The LLM sees in the system prompt:

[namespace: docs → vis.ext.docs]
Document search (use docs/ prefix):
- (docs/search query) — full-text search across documents
- docs/max-results — max results constant (default 50)

And calls (docs/search "neural") from :code blocks. Bare (search "neural") does not resolve.