The vaelii.host.* vars this repo reads, and the one namespace in vaelii.agents.*
that reaches for them. vaelii.host.* is the engine's tooling tier, private and free
to change, so a breaking change upstream is an edit here. Each var is the engine's
own under the name the callers use.
The daemon's op tables (vaelii.host.serve): ops, kbless-ops, feed-ops and
write-ops.
vaelii.agents.tools generates the model's tool schemas from the read subset of
ops and dispatches tool calls back through it, so a model's reads are the daemon's
reads with no second copy.
vaelii.agents.mcp.* (the MCP server) reaches further than vaelii.agents.tools does:
its write verbs (assert, unassert) have to reconstruct, ahead of a write, every
predicate a sentence's assert would actually store — rule surfaces, ist nesting,
except/exceptWhen peeling, forall desugaring, storage-context resolution — work
vaelii.core does not expose a single call for. The vars below are that reach, one
per engine fn the server calls outside vaelii.core:
require-token!, api-token and public-bind? (vaelii.host.guard);
register-cache (vaelii.impl.caches); read-forms and load-entries!
(vaelii.impl.io.text); context? and literals (vaelii.impl.naming);
ist-parts and storage-context (vaelii.impl.assert-entry);
soft-priority-surface?, cardinality-surface?, minimize-surface?,
split-exceptWhen, inner-rule and expand-rule (vaelii.impl.rules);
free-vars, conjunction?, peel-exception-strength, forall?,
desugar-forall-literal, defn-sentence?, rule-functor and
sentex-handle (vaelii.impl.sentex); substitute and unify
(vaelii.impl.resolution). None of this is vaelii.host.* or vaelii.impl.*
surface this repo would propose keeping stable on its own account — every var here
is a candidate for a vaelii.core read a write-verb author would otherwise have to
duplicate (see this repo's README, "Reaching into the engine", and docs/mcp.md,
"Engine reach", which proposes the vaelii.core entry point).
The `vaelii.host.*` vars this repo reads, and the one namespace in `vaelii.agents.*` that reaches for them. `vaelii.host.*` is the engine's tooling tier, private and free to change, so a breaking change upstream is an edit here. Each var is the engine's own under the name the callers use. The daemon's op tables (`vaelii.host.serve`): [[ops]], [[kbless-ops]], [[feed-ops]] and [[write-ops]]. `vaelii.agents.tools` generates the model's tool schemas from the read subset of [[ops]] and dispatches tool calls back through it, so a model's reads are the daemon's reads with no second copy. `vaelii.agents.mcp.*` (the MCP server) reaches further than `vaelii.agents.tools` does: its write verbs (`assert`, `unassert`) have to reconstruct, ahead of a write, every predicate a sentence's assert would actually store — rule surfaces, `ist` nesting, `except`/`exceptWhen` peeling, `forall` desugaring, storage-context resolution — work `vaelii.core` does not expose a single call for. The vars below are that reach, one per engine fn the server calls outside `vaelii.core`: [[require-token!]], [[api-token]] and [[public-bind?]] (`vaelii.host.guard`); [[register-cache]] (`vaelii.impl.caches`); [[read-forms]] and [[load-entries!]] (`vaelii.impl.io.text`); [[context?]] and [[literals]] (`vaelii.impl.naming`); [[ist-parts]] and [[storage-context]] (`vaelii.impl.assert-entry`); [[soft-priority-surface?]], [[cardinality-surface?]], [[minimize-surface?]], [[split-exceptWhen]], [[inner-rule]] and [[expand-rule]] (`vaelii.impl.rules`); [[free-vars]], [[conjunction?]], [[peel-exception-strength]], [[forall?]], [[desugar-forall-literal]], [[defn-sentence?]], [[rule-functor]] and [[sentex-handle]] (`vaelii.impl.sentex`); [[substitute]] and [[unify]] (`vaelii.impl.resolution`). None of this is `vaelii.host.*` or `vaelii.impl.*` surface this repo would propose keeping stable on its own account — every var here is a candidate for a `vaelii.core` read a write-verb author would otherwise have to duplicate (see this repo's README, "Reaching into the engine", and docs/mcp.md, "Engine reach", which proposes the `vaelii.core` entry point).
The Anthropic Messages API backend, over java.net.http and cheshire. Beyond the
neutral request keys it reads :thinking-display and :fallbacks. See docs/llm.md
§ The Messages API backend.
The Anthropic Messages API backend, over `java.net.http` and `cheshire`. Beyond the neutral request keys it reads `:thinking-display` and `:fallbacks`. See docs/llm.md § The Messages API backend.
The part of an HTTP provider's transport that is not its wire format: the connect and
read deadlines, the typed send, the not-JSON refusal, the bounded excerpt. Each fn
takes an endpoint
{:label :slug}: :label names the far end in a message, :slug in a thread name.
See docs/llm.md § What both HTTP backends share.
The part of an HTTP provider's transport that is not its wire format: the connect and
read deadlines, the typed send, the not-JSON refusal, the bounded excerpt. Each fn
takes an endpoint
`{:label :slug}`: `:label` names the far end in a message, `:slug` in a thread name.
See docs/llm.md § What both HTTP backends share.The Ollama chat API backend, over java.net.http and cheshire, plus the host
probes. Beyond the neutral request keys it reads :temperature and :keep-alive.
See docs/llm.md § The local backend.
The Ollama chat API backend, over `java.net.http` and `cheshire`, plus the host probes. Beyond the neutral request keys it reads `:temperature` and `:keep-alive`. See docs/llm.md § The local backend.
A yes/no question put to a model several times across a pool of Ollama hosts, and the vote over the answers. See docs/llm.md § The vote pool.
A yes/no question put to a model several times across a pool of Ollama hosts, and the vote over the answers. See docs/llm.md § The vote pool.
The Provider extension point: two methods over a provider-neutral request and
response map, neither naming HTTP, JSON or a vendor field. See docs/llm.md § Providers.
Request:
{:model "claude-opus-5-5" ; provider-resolved when absent
:system [{:text "…" :cache? true} …] ; blocks, in order
:messages [{:role "user"|"assistant" :content <string | [block …]}]
:tools [<tool schema> …] ; vaelii.agents.tools/schemas
:format {…} ; a JSON schema decoding is constrained to
:max-tokens 8192
:effort "low"|"medium"|"high"|"xhigh"|"max"
:thinking-display "summarized" ; Anthropic only
:num-ctx 8192} ; context length, tokens; local providers only
:cache? on a system block asks for a cache breakpoint after it. This list is the
whole of what a provider reads, so a key any path sends belongs here. A provider
ignores a key it cannot express: :cache? on Ollama, :num-ctx on Anthropic.
Response:
{:stop-reason "end_turn"|"tool_use"|"refusal"|"max_tokens"|…
:stop-details {…} ; populated only on a refusal
:content [{:type :text :text "…"}
{:type :tool-use :id "…" :name "…" :input {…}}
{:type :thinking :text "…"}]
:model "…"
:usage {…}}
A refusal is a successful answer with empty or partial :content: a caller reads
:stop-reason first, and a provider never fabricates a text block. Assistant content
is echoed back verbatim in the next request's :messages, so a provider may carry its
own vendor blocks through :content.
The `Provider` extension point: two methods over a provider-neutral request and
response map, neither naming HTTP, JSON or a vendor field. See docs/llm.md § Providers.
Request:
{:model "claude-opus-5-5" ; provider-resolved when absent
:system [{:text "…" :cache? true} …] ; blocks, in order
:messages [{:role "user"|"assistant" :content <string | [block …]}]
:tools [<tool schema> …] ; vaelii.agents.tools/schemas
:format {…} ; a JSON schema decoding is constrained to
:max-tokens 8192
:effort "low"|"medium"|"high"|"xhigh"|"max"
:thinking-display "summarized" ; Anthropic only
:num-ctx 8192} ; context length, tokens; local providers only
`:cache?` on a system block asks for a cache breakpoint after it. This list is the
whole of what a provider reads, so a key any path sends belongs here. A provider
ignores a key it cannot express: `:cache?` on Ollama, `:num-ctx` on Anthropic.
Response:
{:stop-reason "end_turn"|"tool_use"|"refusal"|"max_tokens"|…
:stop-details {…} ; populated only on a refusal
:content [{:type :text :text "…"}
{:type :tool-use :id "…" :name "…" :input {…}}
{:type :thinking :text "…"}]
:model "…"
:usage {…}}
A refusal is a successful answer with empty or partial `:content`: a caller reads
`:stop-reason` first, and a provider never fabricates a text block. Assistant content
is echoed back verbatim in the next request's `:messages`, so a provider may carry its
own vendor blocks through `:content`.Which backend a turn runs against: :stub (the default), :ollama or :anthropic,
resolved lazily, with an unreachable backend falling back to the stub. Every fn here
that can build a stub reads that stub's own opts (vaelii.agents.llm.stub/provider)
from opts's :stub, and hands a backend opts without it. See docs/llm.md
§ Choosing one.
Which backend a turn runs against: `:stub` (the default), `:ollama` or `:anthropic`, resolved lazily, with an unreachable backend falling back to the stub. Every fn here that can build a stub reads that stub's own opts (`vaelii.agents.llm.stub/provider`) from `opts`'s `:stub`, and hands a backend `opts` without it. See docs/llm.md § Choosing one.
The default provider: deterministic, offline, no credential, and scripted. See docs/llm.md § The stub is the default.
A :script entry is a full response map, or one of these shorthands:
"some text" a plain text answer
{:tool "kb_sentexes_matching" :input {…}} a tool-use turn (:id, :prose optional)
A caller with answer shapes of its own passes :expand, which sees each map entry
first.
The default provider: deterministic, offline, no credential, and scripted. See
docs/llm.md § The stub is the default.
A `:script` entry is a full response map, or one of these shorthands:
"some text" a plain text answer
{:tool "kb_sentexes_matching" :input {…}} a tool-use turn (`:id`, `:prose` optional)
A caller with answer shapes of its own passes `:expand`, which sees each map entry
first.Four-valued epistemic status for ground assertions.
Queries both a sentence and its explicit negation (not S) and returns a verdict map with :verdict, :for, :against, and justification evidence.
Two query modes:
ask? (ground facts + prover registry, no rule expansion)query (rules fire, bounded)Callers who want rule expansion MUST pass {:max-depth N} explicitly. A silent default depth would turn derivable facts into false :unknown.
Four-valued epistemic status for ground assertions.
Queries both a sentence and its explicit negation (not S) and returns
a verdict map with :verdict, :for, :against, and justification evidence.
Two query modes:
- No opts: uses `ask?` (ground facts + prover registry, no rule expansion)
- With opts containing :max-depth: uses `query` (rules fire, bounded)
Callers who want rule expansion MUST pass {:max-depth N} explicitly.
A silent default depth would turn derivable facts into false :unknown.Pure v1 audit-record construction, validation, normalization, and hashing.
This namespace performs no I/O and does not invent journal metadata. The
durable writer (vaelii.agents.mcp.dispatcher's JournalAuditSink) adds
record identity, sequence, and server time at the commit point.
Pure v1 audit-record construction, validation, normalization, and hashing. This namespace performs no I/O and does not invent journal metadata. The durable writer (`vaelii.agents.mcp.dispatcher`'s `JournalAuditSink`) adds record identity, sequence, and server time at the commit point.
Server configuration reader.
A config file maps bearer-token fingerprints to stable principal IDs and fork IDs. Raw tokens never appear in the config — only their SHA-256 hex digest.
EDN format:
{:state-dir "path/to/authority" :forks-dir "path/to/forks" :principals {"alice" {:token-sha256 "abcdef..." :fork-id "alice" :lifecycle :durable} "bob" {:token-sha256 "012345..." :fork-id "bob" :lifecycle :durable}}}
All paths are resolved relative to the config file's parent directory. Unknown top-level keys are rejected (closed schema). :state-dir is ALWAYS required — even all-leased configs need durable authority.
Server configuration reader.
A config file maps bearer-token fingerprints to stable principal IDs and
fork IDs. Raw tokens never appear in the config — only their SHA-256 hex
digest.
EDN format:
{:state-dir "path/to/authority"
:forks-dir "path/to/forks"
:principals
{"alice" {:token-sha256 "abcdef..."
:fork-id "alice"
:lifecycle :durable}
"bob" {:token-sha256 "012345..."
:fork-id "bob"
:lifecycle :durable}}}
All paths are resolved relative to the config file's parent directory.
Unknown top-level keys are rejected (closed schema).
:state-dir is ALWAYS required — even all-leased configs need durable authority.Transport-neutral MCP dispatch with A1 audit integration and effect fencing.
Audit lifecycle per request:
Fencing: the generation counter in the session registry increments on revocation or lease retirement. A permit acquired at admission is checked before the mutation commits. If the generation changed, the mutation is refused even though the session was valid at admission time.
Transport-neutral MCP dispatch with A1 audit integration and effect fencing. Audit lifecycle per request: 1. Health-check the A1 sink → refuse if unhealthy 2. Mint operation_id 3. Record durable Attempt → refuse if fails 4. Acquire generation permit from session registry 5. Check permit at mutation boundary (inside verb execution) 6. Execute verb 7. Record durable Completion → error handling per effect certainty 8. Return response Fencing: the generation counter in the session registry increments on revocation or lease retirement. A permit acquired at admission is checked before the mutation commits. If the generation changed, the mutation is refused even though the session was valid at admission time.
Streamable HTTP transport for the Vaelii MCP server.
Implements MCP Streamable HTTP (POST/DELETE/GET on /mcp) and the POST /op raw-operation route. Service handle owns Jetty + lease sweeper; stop is idempotent.
Streamable HTTP transport for the Vaelii MCP server. Implements MCP Streamable HTTP (POST/DELETE/GET on /mcp) and the POST /op raw-operation route. Service handle owns Jetty + lease sweeper; stop is idempotent.
Small helpers over vaelii.core that vaelii.agents.mcp.overlay needs and
vaelii.core does not itself provide.
Small helpers over `vaelii.core` that `vaelii.agents.mcp.overlay` needs and `vaelii.core` does not itself provide.
An overlay knowledge loader: a directory's excepts.edn and *.clj files
into the base KB at server start, each file into the context contexts.edn
names for it. Kept out of vaelii.agents.mcp.server, which calls
load-overlay! once, so the server file stays a near copy of the shape a
non-overlay deployment would use.
An overlay knowledge loader: a directory's `excepts.edn` and `*.clj` files into the base KB at server start, each file into the context `contexts.edn` names for it. Kept out of `vaelii.agents.mcp.server`, which calls `load-overlay!` once, so the server file stays a near copy of the shape a non-overlay deployment would use.
Production entry point for the Vaelii MCP server.
A4: The production startup path MUST NOT construct a RecordingAuditSink or any in-memory test sink. The A1 adapter is mandatory.
Production entry point for the Vaelii MCP server. A4: The production startup path MUST NOT construct a RecordingAuditSink or any in-memory test sink. The A1 adapter is mandatory.
Session registry with durable, linearizable security authority.
ALL mutable state lives in ONE atom: sessions, revoked principals, fork KBs, fork metadata, and a monotonic generation counter.
Authority changes (revocation, restoration, provisioning, lease retirement) are commit-before-ack: the durable store is written BEFORE in-memory state becomes less restrictive.
The generation counter increments on every revocation, restoration, or lease retirement. Dispatchers acquire a permit (generation snapshot) at admission; the mutation boundary checks the permit is still current.
Three lifetimes: connection ≠ session ≠ fork
Session registry with durable, linearizable security authority. ALL mutable state lives in ONE atom: sessions, revoked principals, fork KBs, fork metadata, and a monotonic generation counter. Authority changes (revocation, restoration, provisioning, lease retirement) are commit-before-ack: the durable store is written BEFORE in-memory state becomes less restrictive. The generation counter increments on every revocation, restoration, or lease retirement. Dispatchers acquire a permit (generation snapshot) at admission; the mutation boundary checks the permit is still current. Three lifetimes: connection ≠ session ≠ fork
MCP JSON-RPC 2.0 transport over stdio.
Reads newline-delimited JSON-RPC messages from stdin, dispatches them through the transport-neutral dispatcher, and writes JSON-RPC responses to stdout. Stderr is reserved for server-side logging.
This transport owns:
This transport does NOT own:
MCP JSON-RPC 2.0 transport over stdio. Reads newline-delimited JSON-RPC messages from stdin, dispatches them through the transport-neutral dispatcher, and writes JSON-RPC responses to stdout. Stderr is reserved for server-side logging. This transport owns: - Reading bytes from stdin, writing bytes to stdout - Parsing/serializing JSON - Authentication (token extraction from initialize or env var) This transport does NOT own: - MCP method routing (dispatcher's job) - Session/verb semantics (dispatcher's job) - HTTP concepts (Streamable HTTP adapter is separate)
The six MCP verb tools — assert, unassert, unassert-handle, query,
argue, matching — the write-and-read surface an MCP client reaches a fork KB through.
Each verb is a map → map function: JSON-shaped input (string keys, EDN values
carried as strings) → a Clojure result map the transport serializes to JSON.
Errors are maps with :mcp-error true, never thrown — the transport layer
decides how to encode them (a JSON-RPC error, an MCP error content block, etc.).
Fork injection. Every verb receives the fork KB through the dynamic var
*mcp-kb*, which the MCP server binds per request. The verbs never see the
base KB directly; the fork's overlay isolates their writes.
Strength cap. An MCP client may assert at :default only — never
:monotonic. The cap is enforced here, before the call reaches vaelii.core,
so a client cannot promote a belief past the fork's own defeat semantics.
EDN coercion. Sentence, goal, and context arguments arrive as JSON strings
holding EDN s-expressions. Parsed with clojure.edn/read-string (never
read-string) — EDN has no reader-eval, so a client's output cannot execute
code.
Wire format. Results are Clojure maps serialized to JSON by the transport. Handles are longs. Bindings are maps of string keys to EDN-string values.
The six MCP verb tools — `assert`, `unassert`, `unassert-handle`, `query`, `argue`, `matching` — the write-and-read surface an MCP client reaches a fork KB through. Each verb is a map → map function: JSON-shaped input (string keys, EDN values carried as strings) → a Clojure result map the transport serializes to JSON. Errors are maps with `:mcp-error true`, never thrown — the transport layer decides how to encode them (a JSON-RPC error, an MCP error content block, etc.). **Fork injection.** Every verb receives the fork KB through the dynamic var `*mcp-kb*`, which the MCP server binds per request. The verbs never see the base KB directly; the fork's overlay isolates their writes. **Strength cap.** An MCP client may assert at `:default` only — never `:monotonic`. The cap is enforced here, before the call reaches `vaelii.core`, so a client cannot promote a belief past the fork's own defeat semantics. **EDN coercion.** Sentence, goal, and context arguments arrive as JSON strings holding EDN s-expressions. Parsed with `clojure.edn/read-string` (never `read-string`) — EDN has no reader-eval, so a client's output cannot execute code. **Wire format.** Results are Clojure maps serialized to JSON by the transport. Handles are longs. Bindings are maps of string keys to EDN-string values.
The model's read tools, generated from the read subset of the engine daemon's op
table (vaelii.agents.engine/ops) and dispatched back through it. No tool
writes. See docs/llm.md § Tools.
The model's read tools, generated from the read subset of the engine daemon's op table (`vaelii.agents.engine/ops`) and dispatched back through it. No tool writes. See docs/llm.md § Tools.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |