Liking cljdoc? Tell your friends :D

com.blockether.vis.internal.gateway.state

Gateway session manager.

One process-global registry over the live session fleet: per-session ordered event log (monotonic :seq, ring-buffered), SSE subscriber fan-out, async turn submission with idempotency keys, cancellation, and turn/cost metrics.

The engine is reached ONLY through the same internal surfaces the TUI channel uses: loop/create!-send!-close! for the lifecycle, :hooks {:on-chunk ...} phased chunks for the live stream, ctx-loop/session-snapshot for the context. No engine state lives here - this namespace owns wire bookkeeping (events, turn records, subscribers), nothing else.

Gateway session manager.

One process-global registry over the live session fleet: per-session
ordered event log (monotonic `:seq`, ring-buffered), SSE subscriber
fan-out, async turn submission with idempotency keys, cancellation,
and turn/cost metrics.

The engine is reached ONLY through the same internal surfaces the
TUI channel uses: `loop/create!`-`send!`-`close!` for the
lifecycle, `:hooks {:on-chunk ...}` phased chunks for the live
stream, `ctx-loop/session-snapshot` for the context. No engine state
lives here - this namespace owns wire bookkeeping (events, turn
records, subscribers), nothing else.
raw docstring

abandon-draft!clj

(abandon-draft! sid workspace-id reason)

Permanently discard one active draft owned by sid's current repo. A parked draft may be removed directly. If it is the caller's current draft, first repoint the session to that draft's real trunk. Drafts from another repo or pinned to another session are rejected. Returns refreshed canonical workspace info.

Permanently discard one active draft owned by `sid`'s current repo. A parked
draft may be removed directly. If it is the caller's current draft, first
repoint the session to that draft's real trunk. Drafts from another repo or
pinned to another session are rejected. Returns refreshed canonical workspace
info.
sourceraw docstring

add-event-tap!clj

(add-event-tap! k f)

Register f ([sid event], canonical string-keyed event) under k, replacing any previous tap with that key. A tap that throws is swallowed — an observer must never break the appender.

Register `f` (`[sid event]`, canonical string-keyed event) under `k`, replacing
any previous tap with that key. A tap that throws is swallowed — an observer
must never break the appender.
sourceraw docstring

add-filesystem-root!clj

(add-filesystem-root! sid path)

Add path as an extra filesystem root for the session pinned to sid, then return the refreshed session-workspace-info. Runs SERVER-SIDE in the daemon so the draft backend-fork and DB write land where the session actually lives; every channel (web footer, TUI picker/footer) then reads the same roots back over the gateway. Channel-agnostic twin of set-session-model!.

Add `path` as an extra filesystem root for the session pinned to `sid`, then
return the refreshed `session-workspace-info`. Runs SERVER-SIDE in the daemon
so the draft backend-fork and DB write land where the session actually lives;
every channel (web footer, TUI picker/footer) then reads the same roots back
over the gateway. Channel-agnostic twin of `set-session-model!`.
sourceraw docstring

append-event!clj

(append-event! sid type payload)
(append-event! sid type payload {:keys [store?]})

Append one event for sid, fan it out to LOCAL subscribers, and publish it on the cross-process bus so watchers in OTHER processes stream it too.

The event is normalized to THE canonical wire shape (wire/canonical: snake_case STRING keys) BEFORE it is stored/fanned/published, so every consumer — in-process sink, replay ring, /poll, SSE, journal tail — reads the IDENTICAL string-keyed map.

Assigns the next monotonic "seq" atomically. :store? false events are fanned out live but kept OUT of the replay ring, so neither a cursor replay nor a /poll pull (both read the ring) re-delivers them - reserve it for genuinely ephemeral fan-out where SSE/poll divergence is acceptable (no current caller; :store? defaults true). A subscriber sink that throws is dropped - one dead SSE connection must never poison the appender or sibling subscribers.

Append one event for `sid`, fan it out to LOCAL subscribers, and publish
it on the cross-process bus so watchers in OTHER processes stream it too.

The event is normalized to THE canonical wire shape (`wire/canonical`:
snake_case STRING keys) BEFORE it is stored/fanned/published, so every
consumer — in-process sink, replay ring, `/poll`, SSE, journal tail —
reads the IDENTICAL string-keyed map.

Assigns the next monotonic `"seq"` atomically. `:store? false` events
are fanned out live but kept OUT of the replay ring, so neither a
cursor replay nor a `/poll` pull (both read the ring) re-delivers
them - reserve it for genuinely ephemeral fan-out where SSE/poll
divergence is acceptable (no current caller; `:store?` defaults true).
A subscriber sink that throws is dropped - one dead SSE connection
must never poison the appender or sibling subscribers.
sourceraw docstring

assign-project!clj

(assign-project! sid pid)

Assign a session to pid (nil clears / removes from project). Returns the refreshed soul.

Assign a session to `pid` (nil clears / removes from project). Returns the refreshed soul.
sourceraw docstring

attach-turn-sync!clj

(attach-turn-sync! sid tid {:keys [on-event]})

Attach to an ALREADY-submitted turn tid on sid and block until it reaches a terminal event, returning the same engine-shaped result as submit-turn-sync!.

Creates NO new turn: it drives in-process (TUI) rendering for a turn the gateway queued and then auto-drains, so a busy-time submission becomes a real gateway queued record instead of a client-side shadow queue. Optional :on-event fires for every replay/live event (canonical string-keyed) of tid.

Attach to an ALREADY-submitted turn `tid` on `sid` and block until it reaches a
terminal event, returning the same engine-shaped result as `submit-turn-sync!`.

Creates NO new turn: it drives in-process (TUI) rendering for a turn the gateway
queued and then auto-drains, so a busy-time submission becomes a real gateway
queued record instead of a client-side shadow queue. Optional `:on-event` fires
for every replay/live event (canonical string-keyed) of `tid`.
sourceraw docstring

attachment-bytesclj

(attachment-bytes {:keys [base64 storage-uri]})

Raw bytes for ONE attachment map (an iteration-attachments element): its inline :base64 decoded, else its external :storage-uri fetched through the storage rail. nil when neither resolves.

Raw bytes for ONE attachment map (an [[iteration-attachments]] element): its
inline `:base64` decoded, else its external `:storage-uri` fetched through the
storage rail. nil when neither resolves.
sourceraw docstring

bus-wiringclj

source

cancel-all-running!clj

(cancel-all-running!)

Fire the cancellation token of EVERY running turn across all sessions. Called on gateway shutdown to break in-flight provider loops BEFORE the shared HTTP executor is torn down — a looping turn would otherwise redispatch its next iteration into the dying pool and die with a RejectedExecutionException surfaced as a bogus "Provider unavailable". Best-effort; returns the number of turns signalled.

Fire the cancellation token of EVERY running turn across all sessions.
Called on gateway shutdown to break in-flight provider loops BEFORE the
shared HTTP executor is torn down — a looping turn would otherwise
redispatch its next iteration into the dying pool and die with a
RejectedExecutionException surfaced as a bogus "Provider unavailable".
Best-effort; returns the number of turns signalled.
sourceraw docstring

cancel-current-turn!clj

(cancel-current-turn! sid)

Tid-less twin of cancel-turn!: fire the cancellation token of WHATEVER turn currently holds sid's :current-turn slot. For clients that lost (or never learned) the turn id — an Esc that raced the turn.started late-bind, or a client-side cancel self-heal that dropped its :gateway-turn-id while the server turn kept running. Without this, that ghost turn keeps :current-turn and every next submit silently queues behind it. Returns {:status "cancelling" :turn_id tid} or {:error :no-running-turn}.

Tid-less twin of `cancel-turn!`: fire the cancellation token of WHATEVER
turn currently holds `sid`'s `:current-turn` slot. For clients that lost
(or never learned) the turn id — an Esc that raced the `turn.started`
late-bind, or a client-side cancel self-heal that dropped its
`:gateway-turn-id` while the server turn kept running. Without this, that
ghost turn keeps `:current-turn` and every next submit silently queues
behind it. Returns `{:status "cancelling" :turn_id tid}` or
`{:error :no-running-turn}`.
sourceraw docstring

cancel-turn!clj

(cancel-turn! sid tid)

Fire the cancellation token of a running turn. Returns {:status "cancelling"} or {:error ...}.

Fire the cancellation token of a running turn. Returns
`{:status "cancelling"}` or `{:error ...}`.
sourceraw docstring

change-root!clj

(change-root! sid path)

Repoint the session pinned to sid at path as its PRIMARY root, then return the refreshed session-workspace-info (whose :id is the newly pinned workspace). Server-side so the change lands in the daemon that runs the turns.

Repoint the session pinned to `sid` at `path` as its PRIMARY root, then return
the refreshed `session-workspace-info` (whose `:id` is the newly pinned
workspace). Server-side so the change lands in the daemon that runs the turns.
sourceraw docstring

close-session!clj

(close-session! sid)

DELETE a session: dispose the live environment, trash the session's draft clones (primary + auto-cloned filesystem roots — only DRAFTS have clones; a trunk workspace's roots are the user's real dirs and are never touched), then delete the session tree. Idempotent. NOTE: this is the DELETE path — merely quitting/closing a session (navigating away, no server call) keeps the draft intact so it can be resumed.

DELETE a session: dispose the live environment, trash the session's draft
clones (primary + auto-cloned filesystem roots — only DRAFTS have clones; a
trunk workspace's roots are the user's real dirs and are never touched),
then delete the session tree. Idempotent. NOTE: this is the DELETE path —
merely quitting/closing a session (navigating away, no server call) keeps
the draft intact so it can be resumed.
sourceraw docstring

context-snapshotclj

(context-snapshot sid)

The read-only ctx mirror the model sees as its bound session (ctx-loop/session-snapshot), for an existing session, ENRICHED for the USER with :session/archived (the GC'd/summarized entities that are no longer in the model's live ctx). Resolving the env through lp/env-for rehydrates an evicted session on demand. nil when the session does not exist.

The read-only ctx mirror the model sees as its bound `session`
(`ctx-loop/session-snapshot`), for an existing session, ENRICHED for
the USER with `:session/archived` (the GC'd/summarized entities that are
no longer in the model's live ctx). Resolving the env
through `lp/env-for` rehydrates an evicted session on demand.
nil when the session does not exist.
sourceraw docstring

create-draft!clj

(create-draft! sid label blank?)

Create and enter a named draft for sid in the daemon. If the session is already in a draft, park that draft first; creating from the picker is thus non-destructive and always forks the real repo trunk. blank? creates an empty draft lineage. Returns refreshed canonical workspace info.

Create and enter a named draft for `sid` in the daemon. If the session is
already in a draft, park that draft first; creating from the picker is thus
non-destructive and always forks the real repo trunk. `blank?` creates an
empty draft lineage. Returns refreshed canonical workspace info.
sourceraw docstring

create-project!clj

(create-project! opts)
source

create-session!clj

(create-session! {:keys [channel external-id workspace-id root] :as opts})

Create or adopt a gateway-managed session.

Ordinary default-workspace creates consume the gateway-owned warm pool and replenish it in the background. :workspace-id, :root, and :external-id require a purpose-built environment and bypass the pool.

Create or adopt a gateway-managed session.

Ordinary default-workspace creates consume the gateway-owned warm pool and
replenish it in the background. `:workspace-id`, `:root`, and `:external-id`
require a purpose-built environment and bypass the pool.
sourceraw docstring

current-seqclj

(current-seq sid)

Highest event :seq assigned for sid so far. Subscribing with this as the cursor yields a live-only stream (empty replay).

Highest event `:seq` assigned for `sid` so far. Subscribing with this
as the cursor yields a live-only stream (empty replay).
sourceraw docstring

delete-project!clj

(delete-project! pid)

Delete a project; its member sessions scatter back to project-less (never deleted).

Delete a project; its member sessions scatter back to project-less (never deleted).
sourceraw docstring

delete-queued-turn!clj

(delete-queued-turn! sid tid)

Remove a queued turn before it starts. Returns deleted status or an error.

Remove a queued turn before it starts. Returns deleted status or an error.
sourceraw docstring

discard-prewarmed!clj

(discard-prewarmed!)

Cancel warmups and delete every unused pooled session. Gateway shutdown owns this cleanup; channel shutdowns must not discard a pool shared by other clients.

Cancel warmups and delete every unused pooled session. Gateway shutdown owns
this cleanup; channel shutdowns must not discard a pool shared by other clients.
sourceraw docstring

drain-idle!clj

(drain-idle! sid)

Start the oldest queued turn for sid IF the session is idle (no turn in flight). No-op returning nil otherwise. Lets an attaching channel kick an orphaned backlog — submitted from another channel while this one was away — into motion the moment a client opens/resumes, instead of letting it sit forever.

The cancel provenance gate is NOT re-implemented here: drain-next-queued! owns it for every caller, so a backlog the user stopped with Esc can never be resurrected by a background attach (tab open, project switch) either.

Safe to call redundantly: drain-next-queued! guards on :current-turn.

Start the oldest queued turn for `sid` IF the session is idle (no turn in
flight). No-op returning nil otherwise. Lets an attaching channel kick an
orphaned backlog — submitted from another channel while this one was away —
into motion the moment a client opens/resumes, instead of letting it sit
forever.

The cancel provenance gate is NOT re-implemented here: `drain-next-queued!`
owns it for every caller, so a backlog the user stopped with Esc can never be
resurrected by a background attach (tab open, project switch) either.

Safe to call redundantly: `drain-next-queued!` guards on `:current-turn`.
sourceraw docstring

ensure-prewarmed!clj

(ensure-prewarmed! channel)

Asynchronously top up the gateway-owned warm-session pool for channel. Idempotent and race-safe: ready plus in-flight sessions never exceed the pool depth. Does nothing after gateway shutdown has stopped pool acceptance.

Asynchronously top up the gateway-owned warm-session pool for `channel`.
Idempotent and race-safe: ready plus in-flight sessions never exceed the pool
depth. Does nothing after gateway shutdown has stopped pool acceptance.
sourceraw docstring

ensure-project-for-root!clj

(ensure-project-for-root! root)
(ensure-project-for-root! owner-id root name)

Get-or-create the wire project bound to canonical workspace root.

Get-or-create the wire project bound to canonical workspace `root`.
sourceraw docstring

events-sinceclj

(events-since sid cursor)

Read-only peek at the replay ring: stored canonical (string-keyed) events with "seq" > cursor, oldest first. Lets a page renderer locate the running turn's turn.started seq so its SSE reconnect can replay the WHOLE in-flight turn instead of only what happens after connect.

Read-only peek at the replay ring: stored canonical (string-keyed) events
with `"seq"` > cursor, oldest first. Lets a page renderer locate the
running turn's `turn.started` seq so its SSE reconnect can replay the WHOLE
in-flight turn instead of only what happens after connect.
sourceraw docstring

get-projectclj

(get-project pid)
source

get-project-by-rootclj

(get-project-by-root root)
(get-project-by-root owner-id root)

Wire project bound to canonical workspace root for owner-id (default "local"), or nil.

Wire project bound to canonical workspace `root` for `owner-id` (default
"local"), or nil.
sourceraw docstring

get-turnclj

(get-turn sid tid)

Canonical (string-keyed) wire view of one turn record, or nil.

Canonical (string-keyed) wire view of one turn record, or nil.
sourceraw docstring

ingest-mirrored-event!clj

(ingest-mirrored-event! sid store? event)

Deliver a FOREIGN gateway event (produced in another process, arriving via the cross-process bus, already in the canonical string-keyed wire shape) into THIS process's registry so a TUI watcher streams a turn running elsewhere in real time.

The foreign event is RE-SEQUENCED onto this process's OWN monotonic "seq", never the producer's. Each process runs an independent seq counter, but the SSE wire treats "seq" as a single strictly-increasing per-connection cursor; adopting the producer's raw counter would let a watcher whose local seq is already past that value (e.g. it ran an earlier turn on this session) silently drop the entire foreign turn. Re-sequencing keeps THIS process's stream monotonic for its own subscribers regardless of the producer's counter — and is safe because only the producer persists the turn; the mirror is live-only.

Stored in the ring when store?; :current-turn mirrored so the session list lights up while the turn runs elsewhere. A running TURN ROW is materialized in :turns/:turn-order on turn.started (and marked terminal on turn.completed/turn.failed) so list-turns frames the mirrored turn exactly like a locally-started one — user bubble, running chip, correct live placement — instead of leaking bare deltas under the previous answer.

Ignores sessions this process has never touched (no local registry entry), so no state accrues for conversations nobody here is watching.

Deliver a FOREIGN gateway event (produced in another process, arriving via
the cross-process bus, already in the canonical string-keyed wire shape)
into THIS process's registry so a TUI watcher streams a
turn running elsewhere in real time.

The foreign event is RE-SEQUENCED onto this process's OWN monotonic `"seq"`,
never the producer's. Each process runs an independent seq counter, but the
SSE wire treats `"seq"` as a single strictly-increasing per-connection cursor;
adopting the producer's raw counter would let a watcher whose local seq is
already past that value (e.g. it ran an earlier turn on this session) silently
drop the entire foreign turn. Re-sequencing keeps THIS process's stream
monotonic for its own subscribers regardless of the producer's counter — and
is safe because only the producer persists the turn; the mirror is live-only.

Stored in the ring when `store?`; `:current-turn` mirrored so the session
list lights up while the turn runs elsewhere. A running TURN ROW is
materialized in `:turns`/`:turn-order` on `turn.started` (and marked terminal
on `turn.completed`/`turn.failed`) so `list-turns` frames the mirrored turn
exactly like a locally-started one — user bubble, running chip, correct live
placement — instead of leaking bare deltas under the previous answer.

Ignores sessions this process has never touched (no local registry entry), so
no state accrues for conversations nobody here is watching.
sourceraw docstring

iteration-attachmentsclj

(iteration-attachments iid)

Ordered OUTBOUND artifacts (matplotlib figures / produced images) a tool call persisted under iteration iid, in the db-list-iteration-attachments shape (each hydrated with inline :base64 or an external :storage-uri), or []. THE canonical, ordered list the attachment byte endpoint indexes AND the live iteration.completed descriptors mirror — so index N always names the same artifact live, on re-fetch, and across a restart. nil/unparsable id -> [].

Ordered OUTBOUND artifacts (matplotlib figures / produced images) a tool call
persisted under iteration `iid`, in the `db-list-iteration-attachments` shape
(each hydrated with inline `:base64` or an external `:storage-uri`), or `[]`.
THE canonical, ordered list the attachment byte endpoint indexes AND the live
`iteration.completed` descriptors mirror — so index N always names the same
artifact live, on re-fetch, and across a restart. nil/unparsable id -> `[]`.
sourceraw docstring

list-draftsclj

(list-drafts sid)

Active/stashed DRAFTS for the repo the session pinned to sid lives in, in THE canonical string-keyed wire shape [{"workspace_id" "label" "root" "repo_root" "fork_ms" "is_current"}], newest first — the parked drafts any channel can resume-draft!. The session's own current draft (when it is in one) rides "is_current" true. Server-side twin of the /draft list slash: ONE gateway fact every channel (web picker, TUI drafts view) reads instead of typing the slash. Never throws; returns [] when the session or its repo is unknown.

Active/stashed DRAFTS for the repo the session pinned to `sid` lives in, in
THE canonical string-keyed wire shape
`[{"workspace_id" "label" "root" "repo_root" "fork_ms" "is_current"}]`,
newest first — the parked drafts any channel can `resume-draft!`. The session's
own current draft (when it is in one) rides `"is_current" true`. Server-side
twin of the `/draft list` slash: ONE gateway fact every channel (web picker,
TUI drafts view) reads instead of typing the slash. Never throws; returns []
when the session or its repo is unknown.
sourceraw docstring

list-projectsclj

(list-projects)
(list-projects opts)

Wire projects for one owner view (see loop/projects) — projects are cross-channel. opts keys: :owner-id, :include-archived?.

Wire projects for one owner view (see loop/projects) — projects are
cross-channel. `opts` keys: :owner-id, :include-archived?.
sourceraw docstring

list-queued-turnsclj

(list-queued-turns sid)

ONLY the still-queued rows for sid, oldest-first — the exact slice a tray polls for, and nothing else.

A queued turn lives solely in the in-memory registry overlay: persistence never holds one (a row reaches the DB after it RUNS). So this reads the overlay and skips list-turns's whole-history DB hydration. That matters on the wire, not just in the server: a companion polling the backlog every 5s was pulling the session's ENTIRE turn history — 600KB of completed :content for a long session — just to learn the queue is empty.

ONLY the still-queued rows for `sid`, oldest-first — the exact slice a tray
polls for, and nothing else.

A queued turn lives solely in the in-memory registry overlay: persistence
never holds one (a row reaches the DB after it RUNS). So this reads the
overlay and skips [[list-turns]]'s whole-history DB hydration. That matters
on the wire, not just in the server: a companion polling the backlog every
5s was pulling the session's ENTIRE turn history — 600KB of completed
`:content` for a long session — just to learn the queue is empty.
sourceraw docstring

list-sessionsclj

(list-sessions)
(list-sessions channel)

Wire souls for every persisted session, each decorated with the bulk summary facts (turn_count, modified_at, lean workspace) so ONE GET /v1/sessions is enough to paint a session picker.

The gateway owns navigator ordering: live sessions first, followed by idle sessions in most-recently-active order. Clients must preserve this order.

CROSS-CHANNEL by default (channel = :all): a conversation started in one channel is visible in the others and vice-versa. Pass a specific channel keyword only when a caller genuinely needs a single-channel slice (e.g. resolving a chat by external-id).

Wire souls for every persisted session, each decorated with the bulk
summary facts (`turn_count`, `modified_at`, lean `workspace`) so ONE
`GET /v1/sessions` is enough to paint a session picker.

The gateway owns navigator ordering: live sessions first, followed by idle
sessions in most-recently-active order. Clients must preserve this order.

CROSS-CHANNEL by default (`channel` = `:all`): a conversation started
in one channel is visible in the others and vice-versa. Pass a specific
channel keyword only when a caller genuinely needs a single-channel
slice (e.g. resolving a chat by external-id).
sourceraw docstring

list-turnsclj

(list-turns sid)

Canonical (string-keyed) wire views of every turn for sid, OLDEST-first (chronological, chat order): persisted history hydrated from the engine DB (survives daemon restarts), plus only genuinely live gateway overlay rows (running/queued or terminal rows not yet visible in persistence). Ordering matches transcript so every consumer renders top-to-bottom directly — no channel re-reverses for display.

DEDUP: the gateway's tid is NOT the engine's persisted row id - the engine mints its own id inside send!. Once the durable row is visible, prefer it: it owns the iteration trace. Keeping the completed gateway row alongside the persisted row rendered the last request/response twice after refresh, with the transient duplicate missing the iterations disclosure.

Canonical (string-keyed) wire views of every turn for `sid`, OLDEST-first (chronological, chat order):
persisted history hydrated from the engine DB (survives daemon restarts), plus
only genuinely live gateway overlay rows (running/queued or terminal rows not
yet visible in persistence). Ordering matches `transcript` so every consumer
renders top-to-bottom directly — no channel re-reverses for display.

DEDUP: the gateway's `tid` is NOT the engine's persisted row id - the engine
mints its own id inside `send!`. Once the durable row is visible, prefer it:
it owns the iteration trace. Keeping the completed gateway row alongside the
persisted row rendered the last request/response twice after refresh, with the
transient duplicate missing the iterations disclosure.
sourceraw docstring

metrics-snapshotclj

(metrics-snapshot)

Global, per-session, concurrency, replay-buffer, and JVM gauges for /metrics.

Global, per-session, concurrency, replay-buffer, and JVM gauges for /metrics.
sourceraw docstring

queue-paused-infoclj

(queue-paused-info sid)

The live :queue-paused marker for sid ({:reason :held :fails :gen …}), or nil when the queue is running.

The live `:queue-paused` marker for `sid` (`{:reason :held :fails :gen …}`),
or nil when the queue is running.
sourceraw docstring

reconcile-orphaned-turns!clj

(reconcile-orphaned-turns!)

Mark turns left running by a dead process as interrupted.

Queued work is deliberately memory-only. Startup never reconstructs or resubmits messages from persisted user requests. Returns the persistence sweep result.

Mark turns left running by a dead process as interrupted.

Queued work is deliberately memory-only. Startup never reconstructs or
resubmits messages from persisted user requests. Returns the persistence
sweep result.
sourceraw docstring

reconcile-running-turns!clj

(reconcile-running-turns!)

Gateway facade for startup/client resume reconciliation of orphaned running turns.

Gateway facade for startup/client resume reconciliation of orphaned running turns.
sourceraw docstring

release-session!clj

(release-session! sid)

Release the live runtime for a session while keeping persisted data resumable.

This is the gateway facade for local clients that are merely closing a view (for example a TUI tab or process exit). Use close-session! for DELETE.

Background resources (background shell processes, managed REPLs) are STOPPED here: closing the view is the user walking away, and a bg child must not outlive that — the transcript stays resumable, the processes do not.

Release the live runtime for a session while keeping persisted data resumable.

This is the gateway facade for local clients that are merely closing a view
(for example a TUI tab or process exit). Use `close-session!` for DELETE.

Background resources (background `shell` processes, managed REPLs) are STOPPED here:
closing the view is the user walking away, and a bg child must not outlive
that — the transcript stays resumable, the processes do not.
sourceraw docstring

remove-event-tap!clj

(remove-event-tap! k)
source

remove-filesystem-root!clj

(remove-filesystem-root! sid path)

Remove path from the session's extra filesystem roots and return the refreshed session-workspace-info. Server-side twin of add-filesystem-root!.

Remove `path` from the session's extra filesystem roots and return the
refreshed `session-workspace-info`. Server-side twin of `add-filesystem-root!`.
sourceraw docstring

reorder-project-sessions!clj

(reorder-project-sessions! pid session-ids)

Atomically adopt loose named sessions into pid, then persist their manual order. Guests owned by another project are never stolen. Returns the member count applied.

Atomically adopt loose named sessions into `pid`, then persist their manual
order. Guests owned by another project are never stolen. Returns the member
count applied.
sourceraw docstring

resume-draft!clj

(resume-draft! sid workspace-id)

Switch the session pinned to sid INTO the stashed draft workspace-id, then return the refreshed session-workspace-info. The target is validated against the session's current repo BEFORE any current draft is stashed. When the session is currently in another draft it is then stashed non-destructively, so this is a true draft switch, not just an enter-from-trunk. Runs SERVER-SIDE in the daemon. Throws ex-info with a :type when workspace-id is not resumable (see workspace/resume!). Channel-agnostic twin of the /draft resume slash.

Switch the session pinned to `sid` INTO the stashed draft `workspace-id`, then
return the refreshed `session-workspace-info`. The target is validated against
the session's current repo BEFORE any current draft is stashed. When the session
is currently in another draft it is then stashed non-destructively, so this is a
true draft switch, not just an enter-from-trunk. Runs SERVER-SIDE in the daemon.
Throws `ex-info` with a `:type` when `workspace-id` is not resumable (see
`workspace/resume!`). Channel-agnostic twin of the `/draft resume` slash.
sourceraw docstring

resume-queue!clj

(resume-queue! sid {:keys [auto?]})

Clear a paused backlog and start its head. :auto? marks a breaker/backoff auto-resume; an explicit (user) resume ALSO resets the consecutive-failure counter so the breaker re-arms. No-op when the queue is not paused. Returns the started turn, or nil.

Clear a paused backlog and start its head. `:auto?` marks a breaker/backoff
auto-resume; an explicit (user) resume ALSO resets the consecutive-failure
counter so the breaker re-arms. No-op when the queue is not paused. Returns
the started turn, or nil.
sourceraw docstring

running-turn-countclj

(running-turn-count)

Number of live turns currently owned by this gateway process. Used by the daemon lifecycle gate: the server may only self-stop when this is zero AND the client refcount is zero.

Number of live turns currently owned by this gateway process. Used by the
daemon lifecycle gate: the server may only self-stop when this is zero AND
the client refcount is zero.
sourceraw docstring

running-turn-start-cursorclj

(running-turn-start-cursor sid)

For a live-only subscriber joining a session mid-turn: the cursor (one below the currently-running turn's turn.started seq) that replays the WHOLE in-flight turn — user bubble, thinking, forms, activity — instead of only the deltas that happen after connect. This is what lets a companion/web client that OPENS a session already driven from the TUI paint the same live 'Vis is running: …' bubble the originating channel shows. nil when no turn is running locally or its start seq wasn't recorded (a foreign turn is handled instead by subscribe!'s hydrate, which appends it above the live-only cursor).

For a live-only subscriber joining a session mid-turn: the cursor (one below
the currently-running turn's `turn.started` seq) that replays the WHOLE
in-flight turn — user bubble, thinking, forms, activity — instead of only the
deltas that happen after connect. This is what lets a companion/web client
that OPENS a session already driven from the TUI paint the same live 'Vis is
running: …' bubble the originating channel shows. nil when no turn is running
locally or its start seq wasn't recorded (a foreign turn is handled instead by
`subscribe!`'s hydrate, which appends it above the live-only cursor).
sourceraw docstring

search-session-idsclj

(search-session-ids query)
(search-session-ids channel query)

Soul-id STRINGS whose TRANSCRIPT (user request + assistant iteration text) matches query. The SERVER-side half of transcript search: clients match title/project locally over the already-loaded list and union these ids for the deep matches, so the 105MB of assistant text never crosses the wire. Blank query → [].

Soul-id STRINGS whose TRANSCRIPT (user request + assistant iteration text)
matches `query`. The SERVER-side half of transcript search: clients match
title/project locally over the already-loaded list and union these ids for
the deep matches, so the 105MB of assistant text never crosses the wire.
Blank query → [].
sourceraw docstring

search-session-matchesclj

(search-session-matches query)
(search-session-matches channel query)

Soul-id STRINGS whose TRANSCRIPT matches query, each TAGGED with WHERE it hit and carrying up to a handful of MATCH SNIPPETS: [{:session_id str :is_in_request bool :is_in_reply bool :request_snippet str :reply_snippet str :hits [{:side "request"|"reply" :snippet str :at ms}]}] (wire-shaped: snake_case string-ish keys, is_<foo> flags). Same SERVER-side deep search as search-session-ids — the assistant text never crosses the wire, only these snippet windows. :is_in_request = the user's own request matched; :is_in_reply = assistant reply text matched. Blank query → [].

Soul-id STRINGS whose TRANSCRIPT matches `query`, each TAGGED with WHERE it hit
and carrying up to a handful of MATCH SNIPPETS:
`[{:session_id str :is_in_request bool :is_in_reply bool
   :request_snippet str :reply_snippet str
   :hits [{:side "request"|"reply" :snippet str :at ms}]}]`
(wire-shaped: snake_case string-ish keys, `is_<foo>` flags). Same SERVER-side
deep search as `search-session-ids` — the assistant text never crosses the wire,
only these snippet windows. `:is_in_request` = the user's own request matched;
`:is_in_reply` = assistant reply text matched. Blank query → [].
sourceraw docstring

session-modelclj

(session-model sid)

The session's persisted model preference as {:provider :model} (DB-backed shared store), or nil for the router default.

The session's persisted model preference as `{:provider :model}`
(DB-backed shared store), or nil for the router default.
sourceraw docstring

session-model-cachedclj

(session-model-cached sid)

Cached variant of session-model for hot render paths. Still part of the gateway facade: callers do not reach into the session-model store directly.

Cached variant of `session-model` for hot render paths. Still part of the
gateway facade: callers do not reach into the session-model store directly.
sourceraw docstring

session-workspace-infoclj

(session-workspace-info sid)

Workspace state for a channel surface (the web footer AND the TUI directory picker), in THE canonical string-keyed wire shape: {"id" "draft?" "root" "repo_root" "label" "fork_ms" "filesystem_roots" "git"} for the session pinned to sid (soul id), or nil. "id" is the workspace-id every filesystem-root mutation (add/remove-filesystem-root!) needs — WITHOUT it the TUI picker treats the session as read-only and C-a silently no-ops. "filesystem_roots" is the the PUBLIC element shape [{"dir" "isolated" "draft_dir"}]. Lets the footer announce that the session — and its extra roots — are isolated drafts. Resolves soul → latest state → workspace; never throws.

Workspace state for a channel surface (the web footer AND the TUI
directory picker), in THE canonical string-keyed wire shape:
`{"id" "draft?" "root" "repo_root" "label" "fork_ms"
"filesystem_roots" "git"}` for the session pinned to `sid` (soul id), or
nil. `"id"` is the workspace-id every filesystem-root mutation
(`add/remove-filesystem-root!`) needs — WITHOUT it the TUI picker treats the
session as read-only and C-a silently no-ops. `"filesystem_roots"` is the
the PUBLIC element shape `[{"dir" "isolated" "draft_dir"}]`. Lets the footer announce
that the session — and its extra roots — are isolated drafts. Resolves
soul → latest state → workspace; never throws.
sourceraw docstring

set-session-model!clj

(set-session-model! sid provider model)

Set (or clear, with blank model) the per-session PROVIDER + MODEL preference. Every turn submitted for sid routes through it (the engine reads it at turn start; router-for-model hoists the model, an unknown name degrades to the default order). Channel-agnostic: web + TUI + embedded callers all set it here, persisted in the DB and shared across channels.

Set (or clear, with blank model) the per-session PROVIDER + MODEL
preference. Every turn submitted for `sid` routes through it (the engine
reads it at turn start; `router-for-model` hoists the model, an unknown
name degrades to the default order). Channel-agnostic: web + TUI + embedded
callers all set it here, persisted in the DB and shared across channels.
sourceraw docstring

set-title!clj

(set-title! sid title)
source

soulclj

(soul sid)

Canonical (string-keyed) wire soul for one session: persisted record + live gateway status. Running sessions include their request, start timestamp, and the gateway clock sampled in the same response so remote channels can derive one elapsed baseline without trusting their device wall clock.

Canonical (string-keyed) wire soul for one session: persisted record + live
gateway status. Running sessions include their request, start timestamp, and
the gateway clock sampled in the same response so remote channels can derive
one elapsed baseline without trusting their device wall clock.
sourceraw docstring

start-prewarming!clj

(start-prewarming! channels)

Start gateway-owned warm pools for every supplied channel. This is the sole lifecycle entry point; channels never manage pools directly.

Start gateway-owned warm pools for every supplied channel.
This is the sole lifecycle entry point; channels never manage pools directly.
sourceraw docstring

stash-draft!clj

(stash-draft! sid)

Park the session's current draft — leave the draft row :active and its clone on disk, repoint the session back to trunk — then return the refreshed session-workspace-info. The non-destructive twin of abandoning; a no-op that returns trunk info when the session is already on trunk. Runs SERVER-SIDE in the daemon that owns the DB. Channel-agnostic twin of the /draft stash slash.

Park the session's current draft — leave the draft row `:active` and its clone
on disk, repoint the session back to trunk — then return the refreshed
`session-workspace-info`. The non-destructive twin of abandoning; a no-op that
returns trunk info when the session is already on trunk. Runs SERVER-SIDE in
the daemon that owns the DB. Channel-agnostic twin of the `/draft stash` slash.
sourceraw docstring

submit-turn!clj

(submit-turn! sid
              {:keys [request messages idempotency-key model reasoning-default
                      cancel-token extra-body turn-features workspace
                      engine-opts attachments display-request]})

Submit one turn for sid. Async: starts immediately when idle, otherwise queues.

Returns {:turn record} (plus :idempotent? true on an idempotency replay) or {:error :session-not-found | :invalid-request, ...}. One engine turn still runs per session; busy submissions become visible queued records.

Submit one turn for `sid`. Async: starts immediately when idle, otherwise queues.

Returns `{:turn record}` (plus `:idempotent? true` on an idempotency
replay) or `{:error :session-not-found | :invalid-request, ...}`. One engine
turn still runs per session; busy submissions become visible queued records.
sourceraw docstring

submit-turn-sync!clj

(submit-turn-sync! sid {:keys [on-event] :as opts})

Submit one turn through the gateway and block until that turn reaches a terminal event.

Accepts the same request keys as submit-turn!; optional :on-event is called for every replay/live event (canonical string-keyed) for the submitted turn. Returns an engine-shaped result map for in-process clients (CLI/TUI) that need a blocking call without bypassing the canonical gateway machinery.

Submit one turn through the gateway and block until that turn reaches a terminal event.

Accepts the same request keys as `submit-turn!`; optional `:on-event` is called
for every replay/live event (canonical string-keyed) for the submitted turn.
Returns an engine-shaped result map for in-process clients (CLI/TUI)
that need a blocking call without bypassing the canonical gateway machinery.
sourceraw docstring

subscribe!clj

(subscribe! sid sub-id sink cursor)

Register an SSE sink and return the replay vector (canonical string-keyed events with "seq" > cursor) ATOMICALLY with the registration, so no event can fall between replay and live fan-out. The sink must be NON-BLOCKING (fan-out runs on the appending turn thread); the caller dedups via a seq guard, since a live event may land in both the replay and the sink (see server.clj).

Before capturing replay, HYDRATE any turn currently running in a sibling process from the cross-process journal (bus/hydrate!) — but only when this process isn't already tracking a live turn (:current-turn unset), so an already-mirrored turn isn't re-delivered to existing subscribers. This materializes the running turn's row + ring HERE, so a watcher joining a turn in flight elsewhere replays it from turn.started (user bubble + running frame) instead of catching only the bare deltas after connect.

Register an SSE sink and return the replay vector (canonical string-keyed
events with `"seq"` > `cursor`) ATOMICALLY with the registration, so no
event can fall between replay and live fan-out. The sink must be
NON-BLOCKING (fan-out runs on the appending turn thread); the caller
dedups via a seq guard, since a live event may land in both the replay
and the sink (see server.clj).

Before capturing replay, HYDRATE any turn currently running in a sibling
process from the cross-process journal (`bus/hydrate!`) — but only when this
process isn't already tracking a live turn (`:current-turn` unset), so an
already-mirrored turn isn't re-delivered to existing subscribers. This
materializes the running turn's row + ring HERE, so a watcher joining a turn
in flight elsewhere replays it from `turn.started` (user bubble + running
frame) instead of catching only the bare deltas after connect.
sourceraw docstring

title-listenerclj

source

transcriptclj

(transcript sid)

Rich persisted transcript rows for sid in THE canonical wire shape (wire/canonical): turns oldest-first, each carrying its persisted iteration rows under :iterations. Canonicalizing AT THE SOURCE makes the HTTP hop an identity — an in-process reader and a remote gateway client (TUI / web / mobile) see the SAME maps, so there is exactly ONE transcript shape and a channel can never again be written against a shape only one transport sees.

Rich persisted transcript rows for `sid` in THE canonical wire shape
(`wire/canonical`): turns oldest-first, each carrying its persisted iteration
rows under `:iterations`. Canonicalizing AT THE SOURCE makes the HTTP hop an
identity — an in-process reader and a remote gateway client (TUI / web /
mobile) see the SAME maps, so there is exactly ONE transcript shape and a
channel can never again be written against a shape only one transport sees.
sourceraw docstring

transcript-pageclj

(transcript-page sid {:keys [limit offset]})

A WINDOW of sid's transcript, hydrated LAZILY — the whole point is that only the rows in the window pay for iteration/attachment hydration, which is where a big session's cost lives (a 247-turn session: ~26 ms to list, ~750 ms to hydrate all of it, ~50 ms to hydrate the newest 30).

The cursor is an INDEX into the oldest-first list, NOT :position — positions are neither unique nor monotonic in practice (one real 247-turn session has 172 distinct positions), so they cannot page anything. New turns only ever append, so an offset counted from the OLDEST row is stable while paging backwards.

A windowed request is ALSO capped in bytes (TRANSCRIPT_PAGE_MAX_BYTES): the newest rows are hydrated first and the oldest ones fall out of the page, so :offset can come back HIGHER than the one asked for. Clients must page from the RETURNED :offset, never from their own arithmetic.

opts: :limit window size (nil = every row, unbudgeted — the TUI's whole-transcript read), :offset 0-based start in the oldest-first list (nil = the NEWEST :limit rows).

Returns {:turns <oldest-first window> :total <turn count> :offset <window start> :has-more <older rows exist>}.

A WINDOW of `sid`'s transcript, hydrated LAZILY — the whole point is that only
the rows in the window pay for iteration/attachment hydration, which is where
a big session's cost lives (a 247-turn session: ~26 ms to list, ~750 ms to
hydrate all of it, ~50 ms to hydrate the newest 30).

The cursor is an INDEX into the oldest-first list, NOT `:position` — positions
are neither unique nor monotonic in practice (one real 247-turn session has
172 distinct positions), so they cannot page anything. New turns only ever
append, so an offset counted from the OLDEST row is stable while paging
backwards.

A windowed request is ALSO capped in bytes (`TRANSCRIPT_PAGE_MAX_BYTES`): the
newest rows are hydrated first and the oldest ones fall out of the page, so
`:offset` can come back HIGHER than the one asked for. Clients must page from
the RETURNED `:offset`, never from their own arithmetic.

`opts`: `:limit` window size (nil = every row, unbudgeted — the TUI's
whole-transcript read), `:offset` 0-based start in the oldest-first list
(nil = the NEWEST `:limit` rows).

Returns `{:turns <oldest-first window> :total <turn count> :offset <window
start> :has-more <older rows exist>}`.
sourceraw docstring

turn-traceclj

(turn-trace tid)

THE canonical wire trace of ONE persisted turn: its iteration rows (each with hydrated :attachments re-read from the attachment store) through wire/canonical, same as transcript — canonicalizing AT THE SOURCE keeps the HTTP hop an identity, so an in-process client and a remote client (TUI / mobile) render from the SAME maps. Returns a (possibly empty) vector for a valid turn id, nil for an unparsable id or a read failure — callers use nil to fall back / retry.

THE canonical wire trace of ONE persisted turn: its iteration rows (each
with hydrated `:attachments` re-read from the attachment store) through
`wire/canonical`, same as [[transcript]] — canonicalizing AT THE SOURCE
 keeps the HTTP hop an identity, so an in-process client and a remote
client (TUI / mobile) render from the SAME maps. Returns a (possibly empty)
vector for a valid turn id, nil for an unparsable id or a read failure —
callers use nil to fall back / retry.
sourceraw docstring

unsubscribe!clj

(unsubscribe! sid sub-id)
source

update-project!clj

(update-project! pid opts)
source

update-queued-turn!clj

(update-queued-turn! sid tid request)

Replace the prompt text for a queued turn. Returns the updated turn or an error.

The row's presentation is re-derived from the NEW text: image chips are re-resolved and the stale :display_request (which described the text the submitter authored BEFORE this edit) is dropped, so an edited row never keeps painting the old prompt.

Replace the prompt text for a queued turn. Returns the updated turn or an error.

The row's presentation is re-derived from the NEW text: image chips are
re-resolved and the stale `:display_request` (which described the text the
submitter authored BEFORE this edit) is dropped, so an edited row never
keeps painting the old prompt.
sourceraw docstring

warm-db!clj

(warm-db!)

Force the persistence backend + shared connection on the CALLER's thread. The gateway runs this on its single-threaded boot path so the heavyweight backend namespace never lazy-loads under request concurrency (see require-backend-ns! in internal/persistance.clj).

Force the persistence backend + shared connection on the CALLER's
thread. The gateway runs this on its single-threaded boot path so
the heavyweight backend namespace never lazy-loads under request
concurrency (see require-backend-ns! in internal/persistance.clj).
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