Liking cljdoc? Tell your friends :D

toolnexus.agents.home

Agent home — the persona surface (SPEC.md §7E).

A persona is an identity that lives in FILES, keeps durable MEMORY it can edit, and runs on a HEARTBEAT so it can act unprompted. All three ride seams that already ship: the composed soul is just a system prompt, the memory tool is a plain Tool, and the heartbeat is post + wake on the §7D runtime's INJECTABLE CLOCK. This layer adds no runtime behavior — it is a directory convention plus composition, not new machinery.

(def ava (home/from-dir "./personas/ava"))

;; one-shot, or as a tool in someone else's toolkit
(def rt (rt/create-runtime {:registry {(:name ava) ava} :llm llm}))
(rt/run-agent rt (:name ava) "what is on my plate?")
(rt/agent-tool rt (:name ava))

;; …or give it its own clock
(def started (home/start-agent ava {:llm llm} {:every-ms 1800000
                                               :on-beat println}))
((:stop started))

Everything here is also usable against a plain client with no runtime at all — a composed soul is a :system-prompt, and the memory tool is a tool like any other.

Agent home — the persona surface (SPEC.md §7E).

A persona is an identity that lives in FILES, keeps durable MEMORY it can edit,
and runs on a HEARTBEAT so it can act unprompted. All three ride seams that
already ship: the composed soul is just a system prompt, the memory tool is a
plain `Tool`, and the heartbeat is `post` + `wake` on the §7D runtime's
INJECTABLE CLOCK. This layer adds no runtime behavior — it is a directory
convention plus composition, not new machinery.

    (def ava (home/from-dir "./personas/ava"))

    ;; one-shot, or as a tool in someone else's toolkit
    (def rt (rt/create-runtime {:registry {(:name ava) ava} :llm llm}))
    (rt/run-agent rt (:name ava) "what is on my plate?")
    (rt/agent-tool rt (:name ava))

    ;; …or give it its own clock
    (def started (home/start-agent ava {:llm llm} {:every-ms 1800000
                                                   :on-beat println}))
    ((:stop started))

Everything here is also usable against a plain client with no runtime at all —
a composed soul is a `:system-prompt`, and the memory tool is a tool like any
other.
raw docstring

bootstrap-orderclj/s

Bootstrap discovery order (§7E): identity first, durable memory last. Each present file is injected into the soul as a ## <filename> section, in exactly this order. The order is part of the cross-port contract — a persona whose MEMORY.md outranked its SOUL.md would behave differently in seven languages.

Bootstrap discovery order (§7E): identity first, durable memory last. Each
present file is injected into the soul as a `## <filename>` section, in exactly
this order. The order is part of the cross-port contract — a persona whose
MEMORY.md outranked its SOUL.md would behave differently in seven languages.
sourceraw docstring

compose-soulclj/s

(compose-soul dir)

Compose the bootstrap files present in dir into one soul string — the frozen snapshot injected as the system prompt for a whole run.

Returns {:soul "…" :found ["SOUL.md" …]}. Only present files appear, always in bootstrap-order, each as a ## <filename> section with its body trimmed. Composition happens once, at session start: the soul is fixed for the run, which is what keeps a long-lived persona cache-stable.

Compose the bootstrap files present in `dir` into one soul string — the frozen
snapshot injected as the system prompt for a whole run.

Returns `{:soul "…" :found ["SOUL.md" …]}`. Only present files appear, always
in `bootstrap-order`, each as a `## <filename>` section with its body trimmed.
Composition happens once, at session start: the soul is fixed for the run, which
is what keeps a long-lived persona cache-stable.
sourceraw docstring

from-dirclj/s

(from-dir dir)
(from-dir dir {:keys [does model tools memory] :as opts})

Build a persona AgentDef from a home directory (§7E).

Discovers the bootstrap files in dir, composes them into a FROZEN soul snapshot (once, here — the soul is fixed for the whole run, which is what keeps a long-lived persona cache-stable), and wires a memory tool over the same directory unless :memory false asks for a read-only persona.

Options:

:name the agent name; default the directory's last segment :does the routing description a delegating model sees :model default "inherit" — the runtime's own :llm model :tools extra tools, placed BEFORE the memory tool :memory false omits the memory tool

The return value is a plain AgentDef map, which is this port's spelling of §7D's Level-1 agent: put it in a runtime's :registry and both directions of the axiom work on it unchanged — (rt/run-agent rt name prompt) is .run, and (rt/agent-tool rt name) is .asTool. There is no Agent object to learn, because a def IS the agent here.

Build a persona AgentDef from a home directory (§7E).

Discovers the bootstrap files in `dir`, composes them into a FROZEN soul
snapshot (once, here — the soul is fixed for the whole run, which is what keeps
a long-lived persona cache-stable), and wires a `memory` tool over the same
directory unless `:memory false` asks for a read-only persona.

Options:

  :name    the agent name; default the directory's last segment
  :does    the routing description a delegating model sees
  :model   default `"inherit"` — the runtime's own `:llm` model
  :tools   extra tools, placed BEFORE the memory tool
  :memory  `false` omits the memory tool

The return value is a plain AgentDef map, which is this port's spelling of
§7D's Level-1 agent: put it in a runtime's `:registry` and both directions of
the axiom work on it unchanged — `(rt/run-agent rt name prompt)` is `.run`, and
`(rt/agent-tool rt name)` is `.asTool`. There is no Agent object to learn,
because a def IS the agent here.
sourceraw docstring

heartbeat-okclj/s

The silent-no-op sentinel: a heartbeat reply containing this surfaces nothing.

The silent-no-op sentinel: a heartbeat reply containing this surfaces nothing.
sourceraw docstring

heartbeat-promptclj/s

The prompt a heartbeat wakes the persona with. It contains HEARTBEAT_OK so the model knows the silent-reply contract, and the word "Heartbeat" so a HEARTBEAT.md that keys off it can recognise the trigger.

The prompt a heartbeat wakes the persona with. It contains `HEARTBEAT_OK` so
the model knows the silent-reply contract, and the word "Heartbeat" so a
HEARTBEAT.md that keys off it can recognise the trigger.
sourceraw docstring

max-file-bytesclj/s

Per-file bootstrap cap (§7E), measured in BYTES (2 MiB), not characters. A larger file is injected truncated; the file on disk is untouched.

Per-file bootstrap cap (§7E), measured in BYTES (2 MiB), not characters. A
larger file is injected truncated; the file on disk is untouched.
sourceraw docstring

memory-toolclj/s

(memory-tool dir)

The memory builtin (§7E) — file-backed and OPT-IN. Not one of the default §4A builtins: it exists only when a home directory is wired.

One tool, three actions over MEMORY.md (the agent's own notes) and USER.md (its model of the user):

add append an entry replace swap an existing substring for with remove delete an existing substring

Every action writes to DISK. It does NOT touch the live session's system prompt: under the frozen-snapshot rule the edit loads at the START of the next session, which is what keeps a long-lived persona cache-stable — and the tool's own description says so, because the model is the one that has to know. A replace/remove whose substring is absent is a loud isError, never a silent no-op.

The `memory` builtin (§7E) — file-backed and OPT-IN. Not one of the default
§4A builtins: it exists only when a home directory is wired.

One tool, three actions over `MEMORY.md` (the agent's own notes) and `USER.md`
(its model of the user):

  add      append an entry
  replace  swap an existing substring for `with`
  remove   delete an existing substring

Every action writes to DISK. It does NOT touch the live session's system
prompt: under the frozen-snapshot rule the edit loads at the START of the next
session, which is what keeps a long-lived persona cache-stable — and the tool's
own description says so, because the model is the one that has to know. A
`replace`/`remove` whose substring is absent is a loud `isError`, never a
silent no-op.
sourceraw docstring

memory-tool-descriptionclj/s

source

memory-tool-input-schemaclj/s

source

start-agentclj/s

(start-agent agent-def run-opts {:keys [every-ms on-beat]})

Give a persona its own clock (§7E).

On each :every-ms interval the persona posts a tick to its OWN inbox — the unsolicited rail, where timer ticks COALESCE, so a beat slower than the turn it starts can never pile up — and, WHEN IDLE, wakes it with heartbeat-prompt. A reply containing heartbeat-ok is SILENT: only a substantive reply is collected and handed to :on-beat. Silence is the default, which is the whole point — a persona that reported every beat would be a cron job with a bill.

Every timer goes through the RUNTIME'S INJECTABLE CLOCK, never a sleep: pass {:clock (rt/virtual-clock)} in run-opts and a fixture drives the beats with ((:advance! clock) ms), deterministically.

run-opts is anything rt/create-runtime takes except :registry, which is derived from agent-def (plus any registry the caller supplies, so a persona with a :team still resolves its team-mates).

Returns:

:runtime the live runtime — the host's seam for INBOUND channels. §7E is explicit that channels are the host's job: deliver an external event by calling rt/post / rt/wake on :handle :handle the persona's handle id :beats an atom holding the substantive beats so far (HEARTBEAT_OK excluded) :stop (fn []) — cancel the heartbeat and close the tree gracefully

Throws only if the persona cannot be spawned at all; a spawn failure is a configuration error at the root, which is the one place §7D permits a throw.

Give a persona its own clock (§7E).

On each `:every-ms` interval the persona `post`s a tick to its OWN inbox — the
unsolicited rail, where timer ticks COALESCE, so a beat slower than the turn it
starts can never pile up — and, WHEN IDLE, `wake`s it with `heartbeat-prompt`.
A reply containing `heartbeat-ok` is SILENT: only a substantive reply is
collected and handed to `:on-beat`. Silence is the default, which is the whole
point — a persona that reported every beat would be a cron job with a bill.

Every timer goes through the RUNTIME'S INJECTABLE CLOCK, never a sleep: pass
`{:clock (rt/virtual-clock)}` in `run-opts` and a fixture drives the beats with
`((:advance! clock) ms)`, deterministically.

`run-opts` is anything `rt/create-runtime` takes except `:registry`, which is
derived from `agent-def` (plus any registry the caller supplies, so a persona
with a `:team` still resolves its team-mates).

Returns:

  :runtime  the live runtime — the host's seam for INBOUND channels. §7E is
            explicit that channels are the host's job: deliver an external
            event by calling `rt/post` / `rt/wake` on `:handle`
  :handle   the persona's handle id
  :beats    an atom holding the substantive beats so far (HEARTBEAT_OK
            excluded)
  :stop     `(fn [])` — cancel the heartbeat and close the tree gracefully

Throws only if the persona cannot be spawned at all; a spawn failure is a
configuration error at the root, which is the one place §7D permits a throw.
sourceraw docstring

truncation-noticeclj/s

Notice appended to a bootstrap file truncated at max-file-bytes.

Notice appended to a bootstrap file truncated at `max-file-bytes`.
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