Liking cljdoc? Tell your friends :D

com.blockether.vis.internal.foundation.acp

ACP — the Agent Client Protocol (Zed's editor↔agent protocol, also spoken by Neovim/JetBrains plug-ins) served BY vis.

MCP points OUT of the agent (agent → tools). ACP points IN (editor → agent), so here vis is the SERVER: an editor spawns vis acp, speaks newline-framed JSON-RPC 2.0 over stdio, and drives real vis sessions.

The protocol layer is deliberately TRANSPORT-FREE and BACKEND-FREE:

  • handle-line! takes one framed line and returns the response map. Every adversarial case (bad JSON, batch array, non-object message, wrong jsonrpc, missing/duplicate id, unknown method, wrong param types, calls before initialize) is answered with a well-formed JSON-RPC error instead of a thrown stack trace or a desynchronized stream.

  • connection takes a :backend map of functions (:new-session/:load-session/:prompt/:cancel). gateway-backend is the live one — every ACP session IS an ordinary gateway session, so the SAME turn can be watched from the TUI, the web surface, and the phone Companion while the editor drives it. Nobody else's ACP agent does that, because everyone else's is a private per-editor subprocess.

Two transports ship:

  • serve! — stdio, the transport the ACP spec requires. Full duplex, so session/request_permission and fs/* work.
  • routes-contributionPOST /v1/acp on the regular gateway daemon, one JSON-RPC message per request, every message the agent produced coming back in messages. HALF-DUPLEX by construction (the agent cannot block on an answer that would need a second request), so client-bound calls fail fast instead of deadlocking, and the permission hook stays out of the way.

Encoding is total: json-safe renders keywords, non-finite doubles and unknown objects rather than letting write-json-str throw halfway through a frame and poison the stream — the same discipline gateway/wire.clj applies.

ACP — the Agent Client Protocol (Zed's editor↔agent protocol, also spoken by
Neovim/JetBrains plug-ins) served BY vis.

MCP points OUT of the agent (agent → tools). ACP points IN (editor → agent),
so here vis is the SERVER: an editor spawns `vis acp`, speaks newline-framed
JSON-RPC 2.0 over stdio, and drives real vis sessions.

The protocol layer is deliberately TRANSPORT-FREE and BACKEND-FREE:

  * [[handle-line!]] takes one framed line and returns the response map. Every
    adversarial case (bad JSON, batch array, non-object message, wrong
    `jsonrpc`, missing/duplicate id, unknown method, wrong param types, calls
    before `initialize`) is answered with a well-formed JSON-RPC error instead
    of a thrown stack trace or a desynchronized stream.

  * [[connection]] takes a `:backend` map of functions
    (`:new-session`/`:load-session`/`:prompt`/`:cancel`). [[gateway-backend]]
    is the live one — every ACP session IS an ordinary gateway session, so the
    SAME turn can be watched from the TUI, the web surface, and the phone
    Companion while the editor drives it. Nobody else's ACP agent does that,
    because everyone else's is a private per-editor subprocess.

Two transports ship:

  * [[serve!]] — stdio, the transport the ACP spec requires. Full duplex, so
    `session/request_permission` and `fs/*` work.
  * [[routes-contribution]] — `POST /v1/acp` on the regular gateway daemon,
    one JSON-RPC message per request, every message the agent produced coming
    back in `messages`. HALF-DUPLEX by construction (the agent cannot block on
    an answer that would need a second request), so client-bound calls fail
    fast instead of deadlocking, and the permission hook stays out of the way.

Encoding is total: [[json-safe]] renders keywords, non-finite doubles and
unknown objects rather than letting `write-json-str` throw halfway through a
frame and poison the stream — the same discipline `gateway/wire.clj` applies.
raw docstring

*client-call-timeout-ms*clj

How long the agent waits for an editor to answer fs/* or session/request_permission before giving up. A hung editor must never hang the agent forever.

How long the agent waits for an editor to answer `fs/*` or
`session/request_permission` before giving up. A hung editor must never hang
the agent forever.
sourceraw docstring

*max-mirror-bytes*clj

Files larger than this are not pushed into an editor buffer.

Files larger than this are not pushed into an editor buffer.
sourceraw docstring

*mirror-ops*clj

Tool ops whose result is mirrored into the editor's buffer with fs/write_text_file, so the editor shows the change without a disk reload.

Tool ops whose result is mirrored into the editor's buffer with
`fs/write_text_file`, so the editor shows the change without a disk reload.
sourceraw docstring

*permission-ops*clj

Tool ops routed through session/request_permission while an ACP editor owns the session.

Tool ops routed through `session/request_permission` while an ACP editor owns
the session.
sourceraw docstring

agent-infoclj

ACP Implementation for the initialize response — what a client shows the user and puts in bug reports. Version is the vis/VERSION build resource, "dev" when running from source.

ACP `Implementation` for the initialize response — what a client shows the
user and puts in bug reports. Version is the `vis/VERSION` build resource,
"dev" when running from source.
sourceraw docstring

arg-pathsclj

(arg-paths x)

Every :path/"path" value anywhere inside a tool's arguments.

The walk is ITERATIVE on purpose. Arguments are untrusted JSON that an editor, a model, or an extension can nest arbitrarily deep, and this runs inside the permission/mirror hook: a recursive walk answers a deep value with a StackOverflowError, which is an Error no handler catches — it kills the turn instead of degrading the way json-safe does.

Every `:path`/`"path"` value anywhere inside a tool's arguments.

The walk is ITERATIVE on purpose. Arguments are untrusted JSON that an editor,
a model, or an extension can nest arbitrarily deep, and this runs inside the
permission/mirror hook: a recursive walk answers a deep value with a
`StackOverflowError`, which is an `Error` no handler catches — it kills the
turn instead of degrading the way [[json-safe]] does.
sourceraw docstring

around-hookclj

(around-hook env op-kw args next-fn)

:around op-hook. Outside an ACP session this is a pass-through. Inside one, a mutating op is approved by the editor first (allow always remembered per session+op) and its result is mirrored into the editor's buffers.

`:around` op-hook. Outside an ACP session this is a pass-through. Inside one,
a mutating op is approved by the editor first (`allow always` remembered per
session+op) and its result is mirrored into the editor's buffers.
sourceraw docstring

call!clj

(call! conn method params & [{:keys [timeout-ms]}])

Send a client-bound REQUEST and block for the answer. Returns {:result r} or {:error e} — never throws, so a hostile/absent editor cannot break a tool.

Send a client-bound REQUEST and block for the answer. Returns `{:result r}` or
`{:error e}` — never throws, so a hostile/absent editor cannot break a tool.
sourceraw docstring

client-supports?clj

(client-supports? conn & ks)

True when the client advertised the nested capability, e.g. (client-supports? conn "fs" "writeTextFile").

True when the client advertised the nested capability, e.g.
`(client-supports? conn "fs" "writeTextFile")`.
sourceraw docstring

connectionclj

(connection {:keys [out-fn backend half-duplex?]})

A live ACP connection. :out-fn is called with ONE encoded line per outgoing message and must be safe to call from several threads. :half-duplex? marks a transport that cannot carry agent→client REQUESTS (the HTTP route).

A live ACP connection. `:out-fn` is called with ONE encoded line per outgoing
message and must be safe to call from several threads. `:half-duplex?` marks a
transport that cannot carry agent→client REQUESTS (the HTTP route).
sourceraw docstring

connection-forclj

(connection-for sid)
source

content-block->textclj

(content-block->text b)

Flatten ONE ACP content block to the text vis puts in a turn request. Unknown or malformed blocks flatten to nil rather than exploding.

Flatten ONE ACP content block to the text vis puts in a turn request. Unknown
or malformed blocks flatten to nil rather than exploding.
sourceraw docstring

decodeclj

(decode line)

Parse ONE framed line. Returns {:msg m}, or {:error e} with the JSON-RPC error object to answer with (id null).

Parse ONE framed line. Returns `{:msg m}`, or `{:error e}` with the JSON-RPC
error object to answer with (id `null`).
sourceraw docstring

default-permission-optionsclj

source

echo-backendclj

(echo-backend & [f])

A dependency-free backend used by tests and by --self-test: it never starts a daemon and answers every prompt from f (default: echo the prompt text).

:load-session answers nil for a session it never created, which is the backend contract for "no such session".

A dependency-free backend used by tests and by `--self-test`: it never starts a
daemon and answers every prompt from `f` (default: echo the prompt text).

`:load-session` answers `nil` for a session it never created, which is the
backend contract for "no such session".
sourceraw docstring

encodeclj

(encode msg)

One newline-free JSON line for msg.

One newline-free JSON line for `msg`.
sourceraw docstring

error-codesclj

JSON-RPC 2.0 codes plus ACP's own reserves.

ACP hands out meanings inside JSON-RPC's implementation-defined range: -32000 is Authentication required, -32002 is Resource not found, -32800 is Request cancelled. :not-initialized is ours alone, so it sits on -32001, which the spec leaves free — it used to squat on -32002 and told any client that special-cases the standard codes that a FILE was missing.

JSON-RPC 2.0 codes plus ACP's own reserves.

ACP hands out meanings inside JSON-RPC's implementation-defined range: -32000
is `Authentication required`, -32002 is `Resource not found`, -32800 is
`Request cancelled`. `:not-initialized` is ours alone, so it sits on -32001,
which the spec leaves free — it used to squat on -32002 and told any client
that special-cases the standard codes that a FILE was missing.
sourceraw docstring

event->updateclj

(event->update event)

Translate ONE gateway SSE event into the update payload of an ACP session/update, or nil when the event has no ACP meaning.

Translate ONE gateway SSE event into the `update` payload of an ACP
`session/update`, or nil when the event has no ACP meaning.
sourceraw docstring

gateway-backendclj

(gateway-backend)

The live backend: every ACP session is an ORDINARY vis gateway session, so the editor, the TUI, the web surface, and the phone all watch the same turns.

The live backend: every ACP session is an ORDINARY vis gateway session, so the
editor, the TUI, the web surface, and the phone all watch the same turns.
sourceraw docstring

handle-line!clj

(handle-line! conn line)

Process ONE framed message. Emits the response through the connection's out-fn and returns it, or nil for a notification / a client answer.

This never throws: malformed input becomes a JSON-RPC error message.

Process ONE framed message. Emits the response through the connection's
`out-fn` and returns it, or nil for a notification / a client answer.

This never throws: malformed input becomes a JSON-RPC error message.
sourceraw docstring

handlersclj

ACP method → handler (fn [conn params]).

ACP method → handler `(fn [conn params])`.
sourceraw docstring

json-safeclj

(json-safe x)
(json-safe x depth)

Total JSON projection of x: keywords/symbols become strings, map keys become strings, NaN/±Infinity become nil, anything unknown becomes its str, and anything nested past [[max-json-depth]] or longer than [[max-json-items]] becomes a marker string. ACP is one message per LINE, so a value that cannot be encoded must not be able to throw halfway through a write and desynchronize the framing.

Total JSON projection of `x`: keywords/symbols become strings, map keys become
strings, NaN/±Infinity become nil, anything unknown becomes its `str`, and
anything nested past [[max-json-depth]] or longer than [[max-json-items]]
becomes a marker string. ACP is one
message per LINE, so a value that cannot be encoded must not be able to throw
halfway through a write and desynchronize the framing.
sourceraw docstring

notify!clj

(notify! conn method params)

Send a client-bound NOTIFICATION (no id, no reply).

Send a client-bound NOTIFICATION (no id, no reply).
sourceraw docstring

op-hooksclj

source

prompt->textclj

(prompt->text blocks)

The prompt array of an ACP session/prompt as one request string.

The prompt array of an ACP `session/prompt` as one request string.
sourceraw docstring

protocol-versionclj

Newest ACP protocol version this agent implements.

Newest ACP protocol version this agent implements.
sourceraw docstring

read-text-file!clj

(read-text-file! conn session-id path & [{:keys [line limit]}])

The editor's CURRENT buffer for path — unsaved edits included — when the client advertises fs.readTextFile; the bytes on disk otherwise.

The editor's CURRENT buffer for `path` — unsaved edits included — when the
client advertises `fs.readTextFile`; the bytes on disk otherwise.
sourceraw docstring

register-connection!clj

(register-connection! sid conn)
source

request-permission!clj

(request-permission! conn session-id tool-call & [options])

Ask the editor to approve tool-call. Returns :allow-once, :allow-always, :reject-once, :reject-always, or :cancelled.

FAILS CLOSED: a timeout, a transport error, or an option id we never offered is a rejection, never an approval.

Ask the editor to approve `tool-call`. Returns `:allow-once`, `:allow-always`,
`:reject-once`, `:reject-always`, or `:cancelled`.

FAILS CLOSED: a timeout, a transport error, or an option id we never offered
is a rejection, never an approval.
sourceraw docstring

routes-contributionclj

(routes-contribution)
source

serve!clj

(serve! & [{:keys [in out backend]}])

Run the ACP agent loop over :in/:out (default stdin/stdout) until EOF. Lines are handled IN ORDER, except session/prompt, which runs on its own virtual thread so session/cancel lands while the turn is still streaming. Returns the closed connection.

Run the ACP agent loop over `:in`/`:out` (default stdin/stdout) until EOF.
Lines are handled IN ORDER, except `session/prompt`, which runs on its own
virtual thread so `session/cancel` lands while the turn is still streaming.
Returns the closed connection.
sourceraw docstring

slash-specsclj

source

supported-protocol-versionsclj

Versions handle-line! will echo back as negotiated.

ONLY v1, deliberately. v0 was listed here too, but nothing in this namespace ever branched on it: we answered protocolVersion 0 and then spoke v1 shapes at the client anyway. Claiming a version we do not implement is worse than refusing it — a v0-only client now reads 1 out of the initialize response and disconnects on its own terms, per the spec's instruction to the client.

Versions [[handle-line!]] will echo back as negotiated.

ONLY v1, deliberately. v0 was listed here too, but nothing in this namespace
ever branched on it: we answered `protocolVersion 0` and then spoke v1 shapes
at the client anyway. Claiming a version we do not implement is worse than
refusing it — a v0-only client now reads `1` out of the initialize response
and disconnects on its own terms, per the spec's instruction to the client.
sourceraw docstring

tool-kindclj

(tool-kind tool)

Map a vis tool name onto an ACP tool-call kind.

Map a vis tool name onto an ACP tool-call kind.
sourceraw docstring

unregister-connection!clj

(unregister-connection! sid)
source

vis-extensionclj

source

write-text-file!clj

(write-text-file! conn session-id path content)

Push content into the editor's buffer for path. Returns true when the editor took it, false when the client has no fs.writeTextFile.

Push `content` into the editor's buffer for `path`. Returns true when the
editor took it, false when the client has no `fs.writeTextFile`.
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