Liking cljdoc? Tell your friends :D

com.blockether.vis.internal.loop


answer-markdownclj

(answer-markdown answer)

Extract the raw Markdown source from a final-answer value.

Canonical shapes:

  • {:answer string} -> the string (the plain prose answer)
  • {:vis/answer-mode :needs-input :answer/text string} -> :answer/text
  • [:ir {…} …] canonical IR AST -> rendered to flat Markdown. loop.clj hands back an IR AST (NOT a Markdown map) for the provider-error / fatal-iteration fallbacks. The LIVE wire carries it as :answer_ir, but session_turn_state.answer_markdown is the only persisted answer channel — so without rendering it here a failed turn persisted a NULL answer and reopened as a bare "(error)" with the real message lost.

Returns nil for anything else.

Extract the raw Markdown source from a final-answer value.

Canonical shapes:
- `{:answer string}`           -> the string (the plain prose answer)
- `{:vis/answer-mode :needs-input :answer/text string}` -> `:answer/text`
- `[:ir {…} …]` canonical IR AST -> rendered to flat Markdown. loop.clj
  hands back an IR AST (NOT a Markdown map) for the provider-error /
  fatal-iteration fallbacks. The LIVE wire carries it as `:answer_ir`,
  but `session_turn_state.answer_markdown` is the only persisted answer
  channel — so without rendering it here a failed turn persisted a NULL
  answer and reopened as a bare "(error)" with the real message lost.

Returns nil for anything else.
sourceraw docstring

ask-code!clj

(ask-code! opts)

One-shot routed svar/ask-code! against the global router. Plain-text completion + Markdown-code-block extraction — returns the svar map {:blocks :raw :reasoning :tokens :cost :duration-ms :assistant-message :provider-state}. :blocks is a vec of {:lang :source} (one entry per Markdown code block); concatenate yourself with svar.internal.codes/concat-sources if you need a single string. ask! (JSON-spec) is gone; every Vis caller uses ask-code!.

One-shot routed `svar/ask-code!` against the global router.
Plain-text completion + Markdown-code-block extraction — returns the
svar map `{:blocks :raw :reasoning :tokens :cost :duration-ms
:assistant-message :provider-state}`. `:blocks` is a vec of
`{:lang :source}` (one entry per Markdown code block); concatenate
yourself with `svar.internal.codes/concat-sources` if you need a
single string. `ask!` (JSON-spec) is gone; every Vis caller uses
`ask-code!`.
sourceraw docstring

auto-archive-hot-symbols!clj

(auto-archive-hot-symbols! _environment)

Deprecated NOOP. Cross-turn def survival was removed when the definition_* sidecar tables were dropped; the Python sandbox starts fresh each turn, so there is nothing to archive.

Deprecated NOOP. Cross-turn def survival was removed when the
`definition_*` sidecar tables were dropped; the Python sandbox starts
fresh each turn, so there is nothing to archive.
sourceraw docstring

by-channelclj

(by-channel channel)
source

by-idclj

(by-id id)

Return the session record (UUID :id) or nil.

Return the session record (UUID `:id`) or nil.
sourceraw docstring

cacheclj

In-process env cache.

Keyed by java.util.UUID session-soul-id. Under the 1:1 session ↔ workspace invariant this key is isomorphic to (:workspace/id env) — one cache entry = one session = one workspace = one Python sandbox lineage. Lookups normalize incoming strings to UUID via cache-key so string-id callers keep working alongside the UUID key.

In-process env cache.

Keyed by `java.util.UUID` session-soul-id. Under the 1:1 session ↔
workspace invariant this key is isomorphic to `(:workspace/id env)`
— one cache entry = one session = one workspace = one Python sandbox
lineage. Lookups normalize incoming strings to UUID via `cache-key`
so string-id callers keep working alongside the UUID key.
sourceraw docstring

cache-env!clj

(cache-env! session-id env)

Insert env into the cache under session-id (UUID, or string normalized via cache-key). Returns {:id <UUID> :environment env}.

Insert `env` into the cache under `session-id` (UUID, or string
normalized via `cache-key`). Returns `{:id <UUID> :environment env}`.
sourceraw docstring

child-forced-togglesclj

Toggles a sub_loop child has ON by DEFAULT, whatever the global state — so a dispatched agent can always run shell commands and reach the harness skills/agents (the user's rule: sub-agents get both compat layers enabled). Soft keyword coupling only (no load dependency on those extensions), the same convention as the posix shim's tool-name coupling. Bound around the child turn; binding-conveyance carries it into parallel futures.

Toggles a `sub_loop` child has ON by DEFAULT, whatever the global state — so
a dispatched agent can always run shell commands and reach the harness
skills/agents (the user's rule: sub-agents get both compat layers enabled).
Soft keyword coupling only (no load dependency on those extensions), the same
convention as the posix shim's tool-name coupling. Bound around the child
turn; binding-conveyance carries it into `parallel` futures.
sourceraw docstring

child-workspace!clj

(child-workspace! db-info parent-ws)

Spawn the child's workspace. An isolation backend available for the parent's root → a CoW clone of the parent's workspace (workspace/create! {:from parent-ws}): isolated writes, and workspace/apply! later lands the since-fork diff back into the parent root. Else (Windows / non-POSIX, or no backend) → a trunk row at the parent's root (create-trunk-at!): SHARED files, no clone (safety = disjoint :files). Returns the workspace row.

Spawn the child's workspace. An isolation backend available for the parent's
root → a CoW clone of the parent's workspace (`workspace/create! {:from
parent-ws}`): isolated writes, and `workspace/apply!` later lands the
since-fork diff back into the parent root. Else (Windows / non-POSIX, or no
backend) → a trunk row at the parent's root (`create-trunk-at!`): SHARED
files, no clone (safety = disjoint `:files`). Returns the workspace row.
sourceraw docstring

close!clj

(close! id)
source

close-all!clj

(close-all!)
source

create!clj

(create! channel)
(create! channel {:keys [title external-id workspace-id]})

Create a brand-new session.

Opts (all optional): :title display title :external-id channel-specific external id :workspace-id pre-spawned workspace to pin the new session to. When omitted, a trunk workspace is auto-minted in create-environment.

Create a brand-new session.

Opts (all optional):
  :title         display title
  :external-id   channel-specific external id
  :workspace-id  pre-spawned workspace to pin the new session to.
                 When omitted, a trunk workspace is auto-minted in
                 create-environment.
sourceraw docstring

create-environmentclj

(create-environment router
                    {:keys [db session channel external-id title workspace-id
                            child]})

Creates a vis environment (component) for session lifecycle and querying.

The environment holds:

  • Python sandbox context with custom bindings + bindings cache
  • DB connection (or shared-mem datasource)
  • Router (LLM provider config)
  • Extension registry atom

Params: router - Required. Result of llm/make-router. opts - Map with :db and optional :session, :channel, :external-id, :title.

:db accepted forms: nil - no DB (sandbox-only execution) :memory - ephemeral in-process SQLite DB path string - persistent SQLite DB at path {:path p} - persistent SQLite DB at path {:datasource ds} - caller-owned DataSource (not closed on dispose)

Returns the vis environment map.

Creates a vis environment (component) for session lifecycle and
querying.

The environment holds:
  - Python sandbox context with custom bindings + bindings cache
  - DB connection (or shared-mem datasource)
  - Router (LLM provider config)
  - Extension registry atom

Params:
  `router` - Required. Result of `llm/make-router`.
  `opts`   - Map with `:db` and optional `:session`,
              `:channel`, `:external-id`, `:title`.

  `:db` accepted forms:
    nil               - no DB (sandbox-only execution)
    :memory           - ephemeral in-process SQLite DB
    path string       - persistent SQLite DB at path
    {:path p}         - persistent SQLite DB at path
    {:datasource ds}  - caller-owned DataSource (not closed on dispose)

Returns the vis environment map.
sourceraw docstring

custom-bindingsclj

(custom-bindings env)

Current custom sandbox bindings {sym -> value}.

Current custom sandbox bindings {sym -> value}.
sourceraw docstring

db-infoclj

(db-info)

Return the process-wide shared DB connection bound to (config/resolve-db-spec). Thin wrapper over persistance.core/db-shared-connection! that fills in the default db-spec so frontend callers stay clear of config resolution.

Return the process-wide shared DB connection bound to
`(config/resolve-db-spec)`. Thin wrapper over
`persistance.core/db-shared-connection!` that fills in the default db-spec
so frontend callers stay clear of config resolution.
sourceraw docstring

db-sweep-orphaned-running-turns!clj

(db-sweep-orphaned-running-turns!)
(db-sweep-orphaned-running-turns! db)

Mark every :running turn as :interrupted. Run at process start to clean up turns that crashed or were killed mid-write so the next turn's handover digest renders the right outcome instead of guessing. Returns the number of turns swept.

Mark every `:running` turn as `:interrupted`. Run at process start
to clean up turns that crashed or were killed mid-write so the next
turn's handover digest renders the right outcome instead of guessing.
Returns the number of turns swept.
sourceraw docstring

delete!clj

(delete! id)
source

dispose-environment!clj

(dispose-environment! environment)

Disposes a vis environment and releases resources. For persistent DBs (created with :path), data is preserved. For disposable DBs, all data is deleted.

A sub_loop CHILD env BORROWS the parent's DB connection (:owns-db? false) — disposing the child must NOT close it, or the parent loses its DB mid-turn.

Disposes a vis environment and releases resources. For persistent DBs
(created with `:path`), data is preserved. For disposable DBs, all
data is deleted.

A sub_loop CHILD env BORROWS the parent's DB connection (`:owns-db?` false) —
disposing the child must NOT close it, or the parent loses its DB mid-turn.
sourceraw docstring

env-forclj

(env-for id)
source

final-answer-gate-errorclj

(final-answer-gate-error environment iteration blocks)
(final-answer-gate-error environment iteration blocks answer-value)
(final-answer-gate-error environment
                         iteration
                         blocks
                         answer-value
                         active-extensions)
(final-answer-gate-error environment
                         iteration
                         blocks
                         answer-value
                         active-extensions
                         extra-ctx)

Dispatch :turn.answer/validate extension hooks against the candidate answer. Returns nil when every hook accepts, otherwise a single string surfaced as the rejected answer's validation error.

A final answer is plain prose with no tool calls, so it inherently never shares an iteration with extension/tool calls: the model uses one iteration to observe tool output, then a later iteration replies with the answer. Extensions that need an additional veto (e.g. user-facing safety / format gates) still get their :turn.answer/validate hook fired here.

active-extensions is passed by the turn loop so activation is computed once per turn; direct callers may omit it and provide :extensions on the environment.

Dispatch `:turn.answer/validate` extension hooks against the
candidate answer. Returns nil when every hook accepts,
otherwise a single string surfaced as the rejected answer's
validation error.

A final answer is plain prose with no tool calls, so it inherently
never shares an iteration with extension/tool calls: the model uses
one iteration to observe tool output, then a later iteration replies
with the answer. Extensions that need an additional veto (e.g.
user-facing safety / format gates) still get their
`:turn.answer/validate` hook fired here.

`active-extensions` is passed by the turn loop so activation is
computed once per turn; direct callers may omit it and provide
`:extensions` on the environment.
sourceraw docstring

for-telegram-chat!clj

(for-telegram-chat! chat-id)
source

get-localsclj

(get-locals _environment)

User-defined sandbox vars surface. Live-vars introspection is cosmetic-off for the Python engine (the agent uses its own Python scope + stdlib), so this returns an empty map. Kept as a stable seam for the trailer/renderer callers.

User-defined sandbox vars surface. Live-vars introspection is cosmetic-off
for the Python engine (the agent uses its own Python scope + stdlib), so this
returns an empty map. Kept as a stable seam for the trailer/renderer callers.
sourceraw docstring

get-routerclj

(get-router)

Get or create the shared LLM router.

Honors :router opts from ~/.vis/config.edn (:rate-limit, :network, :budget, ...). Without that block svar's built-in defaults apply. See config/router-opts for the supported keys.

Get or create the shared LLM router.

Honors `:router` opts from `~/.vis/config.edn` (`:rate-limit`,
`:network`, `:budget`, ...). Without that block svar's built-in
defaults apply. See `config/router-opts` for the supported keys.
sourceraw docstring

handle-iteration-exception!clj

(handle-iteration-exception! e ctx)

Error path for the main-loop try/catch around run-iteration. Infrastructure failures are terminal for the turn; model/format/code failures still return {::iteration-error ...} for RLM self-correction.

Error path for the main-loop try/catch around `run-iteration`.
Infrastructure failures are terminal for the turn; model/format/code
failures still return `{::iteration-error ...}` for RLM self-correction.
sourceraw docstring

install-extension!clj

(install-extension! environment ext)

Register a validated extension into environment (per-env registration, distinct from the global-registry register-extension! defined earlier in this file).

Checks :ext/requires - if the extension declares dependencies, all listed extension namespaces must already be registered. Throws on missing dependencies.

If an extension with the same :ext/name is already registered, it is replaced (not duplicated). Enables hot-swap via reload-extension! (removed for GraalVM native-image compatibility).

Returns environment for chaining.

Register a validated extension into `environment` (per-env registration,
distinct from the global-registry `register-extension!` defined earlier
in this file).

Checks `:ext/requires` - if the extension declares dependencies, all
listed extension namespaces must already be registered. Throws on
missing dependencies.

If an extension with the same `:ext/name` is already registered,
it is replaced (not duplicated). Enables hot-swap via
`reload-extension!` (removed for GraalVM native-image compatibility).

Returns `environment` for chaining.
sourceraw docstring

iteration-loopclj

(iteration-loop environment
                user-request
                {:keys [system-prompt session-turn-id max-context-tokens hooks
                        cancel-atom cancel-token reasoning-default routing
                        extra-body turn-features allow-copilot-claude-deep?
                        workspace-overrides]})

The core iteration loop. Runs assemble -> ask LLM -> execute -> persist until the model emits :answer or the user cancels.

The core iteration loop. Runs assemble -> ask LLM -> execute -> persist
until the model emits `:answer` or the user cancels.
sourceraw docstring

llm-text!clj

(llm-text! {:keys [messages system prompt reasoning temperature routing]
            :as opts})

Fast helper LLM call for extensions.

Uses svar routing (:routing {:optimize :cost}) instead of Vis-side model name heuristics. The call still goes through svar/ask-code! because Vis no longer uses the retired ask! structured-output path; :lang "text", :reasoning :off, and :code-tail-pointer? true make the return a plain text string under :text. Callers may pass either :messages or :system + :prompt.

Fast helper LLM call for extensions.

Uses svar routing (`:routing {:optimize :cost}`) instead of Vis-side model
name heuristics. The call still goes through `svar/ask-code!` because Vis no
longer uses the retired `ask!` structured-output path; `:lang "text"`,
`:reasoning :off`, and `:code-tail-pointer? true` make the return a plain
text string under :text. Callers may pass either :messages or :system +
:prompt.
sourceraw docstring

log-stage!clj

(log-stage! stage iteration data)
source

markdown-answer?clj

(markdown-answer? v)

True for the canonical final-answer VALUE: {:answer string}. The answer is the plain prose the model replies with; answer-fn wraps that string into this {:answer string} shape. The only other accepted value is the needs-input-answer? map.

True for the canonical final-answer VALUE: `{:answer string}`.
The answer is the plain prose the model replies with; `answer-fn` wraps that
string into this `{:answer string}` shape. The only other accepted value is
the `needs-input-answer?` map.
sourceraw docstring

model-routing-statusclj

(model-routing-status displayed-provider displayed-model)
(model-routing-status router displayed-provider displayed-model)

Live routing health for the model a channel is DISPLAYING (displayed-provider

  • displayed-model — the per-session pick or the config default the picker shows).

svar opens a circuit breaker on a provider after repeated transient failures (5xx / 'Overloaded' 529 / dropped streams) and routes turns to the next AVAILABLE provider so work keeps flowing. The displayed model is computed from config ORDER and is NOT breaker-aware, so during an outage the picker says opus while turns actually land on zai. This reconciles the two: when the displayed provider's breaker is open/half-open, it reports what svar is actually serving so the channel can surface ⚠ <displayed> overloaded — routing to <serving>.

Returns nil when the displayed provider is healthy, else {:overloaded-provider <kw> :overloaded-model <str> :serving-provider <kw> :serving-model <str>}. serving-* is nil if every provider is down.

Live routing health for the model a channel is DISPLAYING (`displayed-provider`
+ `displayed-model` — the per-session pick or the config default the picker
shows).

svar opens a circuit breaker on a provider after repeated transient failures
(5xx / 'Overloaded' 529 / dropped streams) and routes turns to the next
AVAILABLE provider so work keeps flowing. The displayed model is computed
from config ORDER and is NOT breaker-aware, so during an outage the picker
says `opus` while turns actually land on `zai`. This reconciles the two: when
the displayed provider's breaker is open/half-open, it reports what svar is
actually serving so the channel can surface
`⚠ <displayed> overloaded — routing to <serving>`.

Returns nil when the displayed provider is healthy, else
`{:overloaded-provider <kw> :overloaded-model <str>
  :serving-provider <kw> :serving-model <str>}`. `serving-*` is nil if every
provider is down.
sourceraw docstring

needs-input-answer?clj

(needs-input-answer? v)

True for explicit clarification/needs-input answer payloads.

Foundation exposes this through (needs-input ...); the loop keeps the predicate data-shaped instead of depending on foundation namespaces so the core runtime has no extension cycle.

True for explicit clarification/needs-input answer payloads.

Foundation exposes this through `(needs-input ...)`; the loop
keeps the predicate data-shaped instead of depending on foundation
namespaces so the core runtime has no extension cycle.
sourceraw docstring

normalize-reasoning-levelclj

(normalize-reasoning-level v)
source

parallel-sub-loops!clj

(parallel-sub-loops! parent-env specs)

Run several sub-loop!s CONCURRENTLY on Clojure futures, bounded by MAX-PARALLEL-SUBLOOPS (a Semaphore), and return their results as a vector in INPUT ORDER. specs is a seq of {:prompt :subctx :models} maps (the model passes a list of dicts; keys arrive keyword-snake at the GraalPy boundary).

All children share the parent's ONE db-info + depth-cap; the fast rift clone / merge-back steps serialize on workspace-mutation-lock while the expensive child LLM turns overlap. A child that throws does NOT sink the batch — its slot becomes a {:status "failed" :error …} result so the coordinator can see the failure and merge the rest. The sandbox denies Python threads, so concurrency lives Clojure-side on the shared GraalVM Engine (forks are safe mid-eval).

Run several `sub-loop!`s CONCURRENTLY on Clojure futures, bounded by
`MAX-PARALLEL-SUBLOOPS` (a Semaphore), and return their results as a vector in
INPUT ORDER. `specs` is a seq of `{:prompt :subctx :models}` maps (the model
passes a list of dicts; keys arrive keyword-snake at the GraalPy boundary).

All children share the parent's ONE db-info + depth-cap; the fast rift clone /
merge-back steps serialize on `workspace-mutation-lock` while the expensive
child LLM turns overlap. A child that throws does NOT sink the batch — its slot
becomes a `{:status "failed" :error …}` result so the coordinator can see the
failure and merge the rest. The sandbox denies Python threads, so concurrency
lives Clojure-side on the shared GraalVM Engine (forks are safe mid-eval).
sourceraw docstring

rebuild-router!clj

(rebuild-router! config)

Rebuild the router from the given config. Used when provider settings change.

Forwards :router opts so live config edits (e.g. tuning :same-provider-delays-ms) take effect on the next set-provider! without restarting the JVM.

Rebuild the router from the given config. Used when provider settings change.

Forwards `:router` opts so live config edits (e.g. tuning
`:same-provider-delays-ms`) take effect on the next `set-provider!`
without restarting the JVM.
sourceraw docstring

refresh-cached-routers!clj

(refresh-cached-routers! router)

Reseat :router on every cached env's environment map.

create-environment snapshots the router into (:router env) at construction time, and the iteration loop calls (svar/ask-code! (:router environment) ...) - not the global router-atom. So when a frontend changes provider config and rebuilds the global router, every long-lived env in the cache (TUI keeps one for the whole session) keeps talking to the previous model until disposed.

Call this immediately after rebuild-router! so the next send! on any cached session picks up the new router.

Reseat `:router` on every cached env's environment map.

`create-environment` snapshots the router into
`(:router env)` at construction time, and the iteration loop calls
`(svar/ask-code! (:router environment) ...)` - not the global
`router-atom`. So when a frontend changes provider
config and rebuilds the global router, every long-lived env in the
cache (TUI keeps one for the whole session) keeps talking to the
*previous* model until disposed.

Call this immediately after `rebuild-router!` so the
next `send!` on any cached session picks up the new router.
sourceraw docstring

resolve-effective-modelclj

(resolve-effective-model router)
(resolve-effective-model router _routing-overrides)

Best-effort root model descriptor from router config.

The returned map carries :name (model id, e.g. "gpt-4o") AND :provider (provider id keyword, e.g. :openai) so every caller can persist BOTH alongside the model. Earlier versions returned just the model map and the provider id was silently dropped on the way to the DB - leaving the meta layer with no way to render provider/model.

Best-effort root model descriptor from router config.

The returned map carries `:name` (model id, e.g. "gpt-4o") AND
`:provider` (provider id keyword, e.g. `:openai`) so every caller
can persist BOTH alongside the model. Earlier versions returned
just the model map and the provider id was silently dropped on
the way to the DB - leaving the meta layer with no way to render
`provider/model`.
sourceraw docstring

retry-sub-loop!clj

(retry-sub-loop! parent-env spec n)

DECORATOR (not a composite): re-run the SAME spec until its child SUCCEEDS, up to n total attempts (default 2). Returns the first successful result, else the last failure — stamped with the :attempts made. (Contrast selector-sub-loops!, which tries DIFFERENT alternatives.)

DECORATOR (not a composite): re-run the SAME `spec` until its child SUCCEEDS,
up to `n` total attempts (default 2). Returns the first successful result,
else the last failure — stamped with the `:attempts` made. (Contrast
`selector-sub-loops!`, which tries DIFFERENT alternatives.)
sourceraw docstring

router-for-modelclj

(router-for-model router prefs)

Return a router variant whose provider/model ORDER reflects a model PREFERENCE, so svar's router picks + falls back accordingly — WE don't pick one model, we express the preference and let the inner router decide (no svar change: it already routes by the router's order). prefs is a model name OR an ORDERED coll of names; matching models are hoisted to the front in preference order (within each provider AND across providers), and the rest of the router follows UNCHANGED as fallback. Blank/unknown prefs → the router as-is (child inherits the parent's order). Coordinator: sub_loop(prompt, subctx, {"models": ["haiku", "sonnet"]}) (or a single "model") — try the cheap one first, fall back.

Return a router variant whose provider/model ORDER reflects a model PREFERENCE,
so svar's router picks + falls back accordingly — WE don't pick one model, we
express the preference and let the inner router decide (no svar change: it
already routes by the router's order). `prefs` is a model name OR an ORDERED
coll of names; matching models are hoisted to the front in preference order
(within each provider AND across providers), and the rest of the router follows
UNCHANGED as fallback. Blank/unknown prefs → the router as-is (child inherits the
parent's order). Coordinator: `sub_loop(prompt, subctx, {"models": ["haiku",
"sonnet"]})` (or a single `"model"`) — try the cheap one first, fall back.
sourceraw docstring

router-initialized?clj

(router-initialized?)

True once the shared router has been built (via get-router/rebuild-router!). Lets a frontend defer the FIRST build to lazy first-use instead of forcing it at startup — so OAuth token fetches (Copilot/Codex) never run at TUI boot.

True once the shared router has been built (via `get-router`/`rebuild-router!`).
Lets a frontend defer the FIRST build to lazy first-use instead of forcing it
at startup — so OAuth token fetches (Copilot/Codex) never run at TUI boot.
sourceraw docstring

run-iterationclj

(run-iteration environment
               messages
               &
               [{:keys [routing iteration reasoning-level resolved-model
                        on-chunk extra-body llm-headers active-extensions
                        answer-validation-context]}])

Runs a single RLM iteration: ask! -> check final -> execute code. Returns map with :thinking :blocks :final-result :api-usage etc.

Runs a single RLM iteration: ask! -> check final -> execute code.
Returns map with :thinking :blocks :final-result :api-usage etc.
sourceraw docstring

run-turn!clj

(run-turn! env user-request loop-opts)

Store turn -> iteration-loop -> update turn -> return result.

Derives :prior-outcome (one of :complete, :cancelled, :error) from the loop result and persists it on the session_turn_state row. The next turn's <system_state> digest reads it.

BEFORE the LLM round-trip, every turn is passed through slash/dispatch. When the user-message resolves to a registered slash, the turn is fully handled by a synthetic iteration (tag :user-slash) and the LLM is never called. The transcript still shows the user message + the slash envelope.

A slash NO extension claims (:reason :unknown) gets one more chance as a PROMPT TEMPLATE (.vis/prompts/*.md, ~/.vis/prompts, provider-contributed templates like /skill:<name>): when a template matches, the expanded text runs as a NORMAL LLM turn. Registered slashes always win over templates.

Store turn -> iteration-loop -> update turn -> return result.

Derives `:prior-outcome` (one of `:complete`, `:cancelled`, `:error`)
from the loop result and
persists it on the `session_turn_state` row. The next turn's
`<system_state>` digest reads it.

BEFORE the LLM round-trip, every turn is passed through
`slash/dispatch`. When the user-message resolves
to a registered slash, the turn is fully handled by a synthetic
iteration (`tag :user-slash`) and the LLM is never called. The
transcript still shows the user message + the slash envelope.

A slash NO extension claims (`:reason :unknown`) gets one more
chance as a PROMPT TEMPLATE (`.vis/prompts/*.md`, `~/.vis/prompts`,
provider-contributed templates like `/skill:<name>`): when a
template matches, the expanded text runs as a NORMAL LLM turn.
Registered slashes always win over templates.
sourceraw docstring

selector-sub-loops!clj

(selector-sub-loops! parent-env specs)

:selector composite (a.k.a. fallback) — try specs IN ORDER until one child SUCCEEDS, then STOP. Serial. Returns the vector of results tried, in order: the failed alternatives followed by the first success (the last result), or — if every alternative failed — all of them (all failures). Mirrors the BT selector: any-succeed. (Unlike retry, the alternatives are DIFFERENT specs.)

`:selector` composite (a.k.a. fallback) — try `specs` IN ORDER until one
child SUCCEEDS, then STOP. Serial. Returns the vector of results tried, in
order: the failed alternatives followed by the first success (the last
result), or — if every alternative failed — all of them (all failures).
Mirrors the BT selector: any-succeed. (Unlike `retry`, the alternatives are
DIFFERENT specs.)
sourceraw docstring

send!clj

(send! id messages)
(send! id messages opts)
source

sequence-sub-loops!clj

(sequence-sub-loops! parent-env specs)

:sequence composite — run specs IN ORDER, each only after the prior SUCCEEDS, SHORT-CIRCUITING on the first failure. Serial by nature (each child may depend on the last). Returns the vector of results ACTUALLY RUN, in order: all of them when every child succeeded, or up to and INCLUDING the first failure when it stopped early (that last result carries the failure). Mirrors the BT sequence: all-succeed, fail-fast.

`:sequence` composite — run `specs` IN ORDER, each only after the prior
SUCCEEDS, SHORT-CIRCUITING on the first failure. Serial by nature (each child
may depend on the last). Returns the vector of results ACTUALLY RUN, in order:
all of them when every child succeeded, or up to and INCLUDING the first
failure when it stopped early (that last result carries the failure). Mirrors
the BT sequence: all-succeed, fail-fast.
sourceraw docstring

set-provider!clj

(set-provider! provider)

Set the single active provider config. Persists to disk, updates in-memory state, rebuilds the global router, and reseats cached session envs. provider is a svar-native provider map {:id :base-url :api-key :models [...]}. Replaces an existing provider with the same :id or appends a new entry.

Set the single active provider config. Persists to disk, updates
in-memory state, rebuilds the global router, and reseats cached
session envs. `provider` is a svar-native provider map
`{:id :base-url :api-key :models [...]}`. Replaces an existing
provider with the same `:id` or appends a new entry.
sourceraw docstring

set-title!clj

(set-title! id title)

Host-driven title change. Resolves the live env (if any) so the in-memory atom + listener fan-out stay in sync; falls back to a plain DB write when no env is live for this session (e.g. vis sessions rename ops).

Host-driven title change. Resolves the live env (if any) so the
in-memory atom + listener fan-out stay in sync; falls back to a
plain DB write when no env is live for this session (e.g.
`vis sessions` rename ops).
sourceraw docstring

sub-loop!clj

(sub-loop! parent-env {:keys [prompt subctx models system-prompt]})

Run a CHILD agentic loop for prompt over subctx (the model-supplied focused slice; see subctx->seed-ctx). Forks a child session env (own ctx-atom seeded from subctx, own forked Context on the shared Engine, own workspace per the parent root's isolation backend, reusing the parent's SINGLE DB connection + depth-cap), optionally on a cheaper PROPOSED model preference list models (router-for-model — always a vector, svar falls back). Runs run-turn!, merges the child's workspace diff back (rift path), then ALWAYS tears the child down — env disposed, rift clone trashed (both via guard, failures logged) so nothing leaks across parallel/retry. Returns: {:task_id <focus> :status <string> :answer :changed_files} Throws :vis/subloop-depth-exceeded past MAX-SUBLOOP-DEPTH.

Run a CHILD agentic loop for `prompt` over `subctx` (the model-supplied focused
slice; see `subctx->seed-ctx`). Forks a child session env (own ctx-atom seeded
from subctx, own forked Context on the shared Engine, own workspace per
the parent root's isolation backend, reusing the parent's SINGLE DB
connection + depth-cap),
optionally on a cheaper PROPOSED model preference list `models`
(`router-for-model` — always a vector, svar falls back). Runs `run-turn!`,
merges the child's workspace diff back (rift path), then ALWAYS tears the child
down — env disposed, rift clone trashed (both via `guard`, failures logged) so
nothing leaks across `parallel`/`retry`. Returns:
  {:task_id <focus> :status <string> :answer :changed_files}
Throws `:vis/subloop-depth-exceeded` past `MAX-SUBLOOP-DEPTH`.
sourceraw docstring

subctx->seed-ctxclj

(subctx->seed-ctx _subctx)

Seed ctx for a sub_loop child's ctx-atom from the model-supplied subctx. Child contexts start from an empty engine ctx. PURE. Kept as the named seed site.

Seed ctx for a sub_loop child's ctx-atom from the model-supplied `subctx`.
Child contexts start from an empty engine ctx. PURE. Kept as the named seed site.
sourceraw docstring

sync-active-extension-symbols!clj

(sync-active-extension-symbols! environment)
(sync-active-extension-symbols! environment active-extensions)

Make the Python sandbox's callable globals match active extension state.

install-extension! keeps every extension row in :extensions, but only active extensions contribute callable symbols. Called after per-env installation and again at turn start so :ext/activation-fn changes become real tool availability, not just prompt visibility.

The Python sandbox is FLAT globals (no namespaces/aliases/macros): active extensions putMember their symbols straight into the top scope; deactivated extensions have theirs removed (putMember nil). Symbol names are snake-ified by env/set-python-binding!.

Make the Python sandbox's callable globals match active extension state.

`install-extension!` keeps every extension row in `:extensions`, but only
active extensions contribute callable symbols. Called after per-env
installation and again at turn start so `:ext/activation-fn` changes become
real tool availability, not just prompt visibility.

The Python sandbox is FLAT globals (no namespaces/aliases/macros): active
extensions putMember their symbols straight into the top scope; deactivated
extensions have theirs removed (putMember nil). Symbol names are snake-ified
by env/set-python-binding!.
sourceraw docstring

turn!clj

(turn! environment messages)
(turn! environment messages opts)

Runs one session turn on an RLM environment using iterative LLM code evaluation.

Params: environment - RLM environment from create-environment. messages - Vector of message maps. Always a vector, e.g.: [(svar/user <prompt-text>)] [(svar/user <prompt-text> (svar/image <b64> <mime-type>))] opts - Map, optional:

  • :spec - Output spec for structured answers.
  • :model - Override config's default model.
  • :max-context-tokens - Token budget for context.
  • :debug? - Enable verbose debug logging (default: false). Logs iteration details, code evaluation, LLM responses at :info level with :rlm-phase context.
  • :reasoning-default - Optional base reasoning effort for reasoning-capable models. Accepts :low/:medium/:high or low/medium/high strings. Adaptive escalation still applies.
  • :extra-body - Optional provider-specific request-body params merged into the upstream LLM call after auto max_tokens + reasoning translation.

Returns: Map with:

  • :trace - Vector of iteration trace entries, each containing: {:iteration N :response <llm-response-text> :blocks [{:id 0 :code <code-str> :result <value> :error nil :envelope {:started-at-ms 10 :finished-at-ms 15 ...}} ...]}
  • :iteration-count - Number of iterations used.
  • :duration-ms - Turn duration in milliseconds.
  • :tokens - Token usage map {:input N :output N :total N}.
  • :cost - Cost map {:input-cost N :output-cost N :total-cost N}.
  • :confidence - Confidence level (:high/:medium/:low) from final iteration.
  • :reasoning - String summary of how the answer was derived (from LLM's FINAL call).
  • :status - Only present on failure (:error or :cancelled).
Runs one session turn on an RLM environment using iterative LLM code evaluation.

 Params:
 `environment` - RLM environment from create-environment.
 `messages` - Vector of message maps. Always a vector, e.g.:
              [(svar/user <prompt-text>)]
              [(svar/user <prompt-text> (svar/image <b64> <mime-type>))]
`opts` - Map, optional:
  - :spec - Output spec for structured answers.
  - :model - Override config's default model.
   - :max-context-tokens - Token budget for context.
   - :debug? - Enable verbose debug logging (default: false). Logs iteration details,
     code evaluation, LLM responses at :info level with :rlm-phase context.
   - :reasoning-default - Optional base reasoning effort for reasoning-capable models.
     Accepts :low/:medium/:high or low/medium/high strings. Adaptive escalation still applies.
   - :extra-body - Optional provider-specific request-body params merged into the
     upstream LLM call after auto max_tokens + reasoning translation.

 Returns:
Map with:
   - :trace - Vector of iteration trace entries, each containing:
       {:iteration N
        :response <llm-response-text>
        :blocks [{:id 0 :code <code-str> :result <value> :error nil
                  :envelope {:started-at-ms 10 :finished-at-ms 15 ...}}
                    ...]}
  - :iteration-count - Number of iterations used.
  - :duration-ms - Turn duration in milliseconds.
  - :tokens - Token usage map {:input N :output N :total N}.
  - :cost - Cost map {:input-cost N :output-cost N :total-cost N}.
  - :confidence - Confidence level (:high/:medium/:low) from final iteration.
   - :reasoning - String summary of how the answer was derived (from LLM's FINAL call).
   - :status - Only present on failure (`:error` or `:cancelled`).
sourceraw docstring

validate-iteration-blocks!clj

(validate-iteration-blocks! blocks)

Fail fast if a stored/evaluated block lost mandatory envelope. Tool-result envelopes enforce their nested info separately; this spec enforces the outer block-level eval envelope for every executed block.

Fail fast if a stored/evaluated block lost mandatory envelope.
Tool-result envelopes enforce their nested info separately;
this spec enforces the outer block-level eval envelope for every
executed block.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close