The agent control plane is the command-line surface for driving
fractal.engine.api from shell-accessible agents while a human can inspect the
state it leaves behind. It is intentionally not a consumer terminal product. It
is a process seam: explicit inputs, structured outputs, durable ids, and
readback commands that expose the engine's own projections.
Run it with:
clojure -M:cli <command> [options] [args]
The implementation lives in fractal.engine.cli and stays thin over
fractal.engine.api. It does not add product nouns, task templates, hidden
memory, or a second runtime model.
The primary consumer is an automation agent with shell access. The supervising human should be able to inspect command output, config files, durable session state, events, heads, lineage, and payload refs without depending on hidden process state.
The seam is:
--pretty and report;--edn.Human-friendly tools can be layered above this. The default contract remains structured output.
The Clojure API owns engine behavior:
make-configstart-session!run-turn!run-turn-async!resume-session!stop-session!close-session!compact-session!viewprogressevent-streamevents-sinceread-payloadsubscribe!CLI commands open or create a durable SDK session, perform one usage or inspection action, emit JSON or EDN, and close local resources. If a behavior is not expressible through the public API, it does not belong in the CLI until the API owns it.
Model-facing host functions are runtime functions available to the model under the selected harness and capability profile. They are not top-level CLI commands:
FINAL
lm
map-lm
rlm
map-rlm
attach-rlm
The CLI has two command families.
flowchart LR
Agent["Shell agent or script"] --> CLI["fractal CLI"]
CLI --> Usage["Usage commands"]
CLI --> Inspect["Inspection commands"]
Usage --> API["fractal.engine.api writes"]
Inspect --> Reads["API reads and payload hydration"]
API --> Store["Durable session store"]
Store --> Reads
Reads --> Output["JSON or EDN output"]
Usage commands create, resume, mutate, or close durable runtime state:
| Command | Purpose |
|---|---|
init | Create an EDN config reference file and store directory. |
config | Print resolved, redacted engine config. |
start | Create a durable session if absent, otherwise report the existing session. |
run | Start or resume a session, send one blocking turn, then close resources. |
turn | Resume an existing session, send one blocking turn, then close resources. |
resume | Verify that a durable session can reopen. |
stop | Request stop on a durable session. |
close | Reopen and immediately close resources. |
compact | Force session compaction through the engine API. |
wait | Reopen and report the latest session state; cross-process async waiting is not implemented. |
Inspection commands read durable state without making provider calls, except where the engine API being invoked explicitly does work such as compaction:
| Command | Purpose |
|---|---|
status | Compact counters and current session status. |
progress | API progress projection. |
view | Full folded engine view. |
events | Durable events since --since N. |
heads | All heads plus current-head id. |
head | One head selected with --head or a positional id. |
edges | Lineage edges for recursion and derived sessions. |
tree | Known session tree assembled from durable lineage. |
payload | Hydrate one payload ref supplied with --ref or a positional EDN ref. |
messages | Folded message projection. |
turns | Folded turn projection. |
steps | Folded step projection. |
evals | Folded eval projection. |
trace | Hydrated assistant code and observation messages for one turn, plus final value when present. |
check | Store sanity check for known sessions and payload refs. |
report | Human-observable compact summary of the latest session state. |
help prints the command list and common options.
Use config files for normal operation. CLI flags are overrides, not the primary configuration surface.
Flat config:
{:adapter :sdk
:provider :provider-key
:model "root-model-id"
:harness :rlm
:capability :default
:store :sqlite
:store/dir ".fractal/sessions/example"
:child-model "child-model-id"
:leaf-model "leaf-model-id"
:max-steps 8
:max-turns 4
:call-timeout-ms 90000
:max-fanout 4
:fanout-pool 2
:leaf-concurrency 2
:cache-ttl "5m"}
Profile config:
{:default-profile :fake
:profiles
{:fake
{:adapter :fake
:model "fake-model"
:harness :rlm
:capability :default
:store :sqlite
:store/dir ".fractal/sessions/fake"
:fake/respond [[:default "```clojure\n(FINAL {:ok true})\n```"]]}
:live
{:adapter :sdk
:provider :provider-key
:model "root-model-id"
:harness :rlm
:capability :default
:store :sqlite
:store/dir ".fractal/sessions/live"
:child-model "child-model-id"
:leaf-model "leaf-model-id"
:max-steps 8
:max-turns 4
:call-timeout-ms 90000
:max-fanout 4
:fanout-pool 2
:leaf-concurrency 2
:cache-ttl "5m"}}}
Select profiles and sessions explicitly:
clojure -M:cli config --config fractal.edn --profile live --pretty
clojure -M:cli run --config fractal.edn --profile live --session demo --message-file prompt.md
Provider credentials should come from the provider SDK's runtime environment or from local secret management outside the repository. Do not commit credential values, credential file paths, cloud project ids, or machine-specific setup.
JSON is the default because agents should not scrape prose:
clojure -M:cli status --config fractal.edn --session demo
Use --pretty for human-observed JSON:
clojure -M:cli report --config fractal.edn --session demo --pretty
Use --edn when exact Clojure-shaped values matter, especially payload refs
that will be passed back to payload:
clojure -M:cli turns --config fractal.edn --session demo --edn
clojure -M:cli payload --config fractal.edn --session demo --ref '{:fractal/ref :payload, ...}'
Successful commands emit one object with :ok true / "ok": true, the command
name, and command-specific fields. Errors emit one object with ok false,
error/message, error/class, and error/data when typed data is available.
JSON output stringifies EDN keyword keys and values. Namespaced keys are rendered
as strings such as "session/id" and "turn/final-value". EDN output preserves
keywords and payload refs exactly.
Create a reference config:
clojure -M:cli init --config fractal.edn --store-dir .fractal/sessions/demo --session demo
Run one blocking turn, creating the session if needed:
clojure -M:cli run --config fractal.edn --session demo --message-file prompt.md --pretty
Continue an existing session:
clojure -M:cli turn --config fractal.edn --session demo --message "continue from the current vars"
Force compaction and then inspect the summary:
clojure -M:cli compact --config fractal.edn --session demo
clojure -M:cli report --config fractal.edn --session demo --pretty
Stop and close:
clojure -M:cli stop --config fractal.edn --session demo
clojure -M:cli close --config fractal.edn --session demo
Read compact status:
clojure -M:cli status --config fractal.edn --session demo --pretty
Tail durable events from an event id:
clojure -M:cli events --config fractal.edn --session demo --since 42 --pretty
Inspect the current head:
head_id="$(clojure -M:cli heads --config fractal.edn --session demo | jq -r '.current-head')"
clojure -M:cli head --config fractal.edn --session demo --head "$head_id" --pretty
Read recursion lineage:
clojure -M:cli edges --config fractal.edn --session demo --pretty
clojure -M:cli tree --config fractal.edn --session demo --pretty
Hydrate a large final value:
clojure -M:cli turns --config fractal.edn --session demo --edn > turns.edn
# Extract a payload ref from the EDN projection, then:
clojure -M:cli payload --config fractal.edn --session demo --ref "$payload_ref" --pretty
Read the model-code / engine-observation trace for the latest turn:
clojure -M:cli trace --config fractal.edn --session demo --pretty
Run the local integrity check:
clojure -M:cli check --config fractal.edn --session demo --pretty
The CLI preserves the API's distinction between returned turn results and preflight or command failures.
Returned turn outcomes live under the result field for run and turn:
:final:error:timeout:budget-exceededCommand failures return ok false and a non-zero exit code. Common typed
failures include:
A completed turn with :status :error is different from a command that could
not open or address the session.
run-turn-async! exists in the API, but this CLI is process-per-command. The
current wait command reopens durable state and reports status; it does not
attach to a background turn owned by another CLI process.
Background turn ownership belongs in a separate process-control adapter if this repo grows one. The current CLI contract is explicit process-per-command usage: write commands open or resume a session, perform one action, return structured output, and release process-local resources.
The control plane should be safe to use in public logs and documentation.
Tracked files and committed reports must not include:
When paths are needed in examples, use relative ignored paths such as
.fractal/... or placeholders.
Before adding a public control-plane behavior, answer these questions:
fractal.engine.api call owns the behavior?TurnResult versus reported as a preflight error?If any answer is unclear, the behavior is not ready for the public control-plane surface.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |