Skip to content

Latest commit

 

History

History
2076 lines (1685 loc) · 101 KB

guide.org

File metadata and controls

2076 lines (1685 loc) · 101 KB

New User Guide

Intro

Autolith is a terminal agent running in a live Common Lisp image. It works on Linux x86-64 and macOS.

You can launch autolith by typing its name into your favorite terminal:

autolith

If you haven’t authenticated already, you can check out the README for how to do that.

Autolith’s prompt window can also evuluate lisp forms directly:

(help)
(resource.read :uri "workspace:.")
(prompt (read-file "review-notes.org"))

This can be frequently helpful since you can use the exact same tools the clanker can, and the same machinery is exposed to you.

Goals and verified missions

Use (goal "OBJECTIVE") for lightweight autonomous work. Use (goal "pause"), (goal "resume") or (goal "clear") to control it. For externally verifiable work, install explicit acceptance policy with (mission "start PLIST"), or evaluate:

(application-mission-start
 *active-application*
 '(:objective "Deliver the accepted build"
   :turn-limit 40 :token-limit 100000 :wall-seconds 3600
   :criteria ((:id "build" :description "The build passes" :gates ("build"))
              (:id "review" :description "The user accepts the result"))
   :gates ((:id "build" :kind :command :command "make check"
            :inputs ("Makefile" "src/main.lisp") :deterministic-p t
            :attempt-limit 3 :timeout-seconds 300))))

Use (mission "run") to begin, (mission "status") to inspect the complete durable state, and (mission "verify") to request harness verification. The model can also call mission.status, mission.verify, mission.stop and mission.invalidate. Only the primary agent may mutate mission policy through these model operations. Configure acceptance criteria yourself; the model cannot weaken them or supply user acceptance.

Each criterion has a unique :id, a :description and optional ordered gate IDs in :gates. Criteria without gates require your explicit evidence:

(mission "accept (\"review\" \"I inspected the artifact and accept the result\")")
(mission "resume")
(mission "verify")

The model’s completion marker requests verification. Passing a gate proves only the criteria you explicitly associated with it. Verification requires all gates and all criteria. Inspect the acceptance mapping before beginning work.

The turn allowance counts provider inference calls across the primary agent, children, compaction and recursive inference, including mapped and nested frames. Reported billable tokens use the provider API’s usage normalization and exclude known prompt-cache reads. Admissions and settlements are serialized within a mission; provider requests execute concurrently. Each admission reserves a turn and an output ceiling from the unspent, unreserved tokens. The default ceiling divides those tokens among the remaining turns; an explicit provider output cap replaces that division. Reviewer requests reserve from both allowances. Settlement releases the ceiling and charges reported input and output usage, so a request can exhaust the token allowance. Missing usage is recorded as unknown and blocks further inference. Install a new explicit budget to proceed after unknown usage.

The wall-clock deadline includes gate execution, delegated tools, pauses and time away. Work runs under cl-jobpond supervision. Use (goal "pause") and (mission "resume") without resetting any mission budget; use (mission "cancel REASON") to terminate it. Terminal states are verified, exhausted, cancelled and failed. A blocked mission retains the evidence needed to resolve its blocker and resume. Interrupted inference restores as blocked when its usage cannot be recovered. Gate attempts, fingerprints and evidence restore with the conversation and survive compaction independently of model summaries.

Gates run in order, stopping at the first failure. Command gates use the ordinary shell authorization and sandbox boundary. Artifact gates require :path and optionally an expected :sha256. Lisp gates require a :predicate string and run an assertion in the existing isolated Lisp worker. Every gate supports :timeout-seconds and a separate :attempt-limit, defaulting to 60 seconds and three executions. Each gate retains bounded evidence.

For deterministic gates, declare all relevant file paths in :inputs and set :deterministic-p t. File contents and missing-file states form the fingerprint; directory paths are rejected. Unchanged passing gates supply current proof, and unchanged failed gates are withheld while the model works on their relevant inputs. For remote state, omit deterministic policy or invalidate a particular gate explicitly:

(mission "invalidate (\"build\" \"The remote build configuration changed\")")

Invalidation permits another execution without replenishing its attempt budget. Before acceptance, every gate fingerprint is checked again under the workspace mutation lock. Changes from a later gate invalidate earlier passing evidence. Collect terminal results and outstanding inference usage before verifying.

Independent review checkpoints

Attach a reviewer to an active mission with mission-review.configure, or evaluate:

(application-mission-review-configure
 *active-application*
 '(:id "release-review" :phase :final-acceptance
   :inputs ("src/main.lisp" "tests/main-tests.lisp")
   :context "Review the changed behavior and test coverage. Report concrete findings."
   :turn-limit 4 :token-limit 16000 :run-limit 3))

Declare the complete relevant file set. The reviewer receives an independent snapshot and background, returns contracted findings, and has no tools or spawning permission. Reviewer call and token allowances are cumulative across changed snapshots and also count against the mission allowances. :run-limit bounds the number of distinct snapshots reviewed.

Use mission-review.trigger with id to review a checkpoint and mission-review.inspect to read its durable findings, decisions and usage. Unchanged snapshots reuse completed, failed or interrupted runs. Resolve or reject each finding through mission-review.decide with id, finding, decision (resolve or reject) and an evidence-bearing reason. Only the primary agent or local user may configure, trigger and adjudicate reviews.

Final verification runs configured final-acceptance checkpoints. Pending findings and failed or interrupted reviews block acceptance. For before-integration and after-edit-set checkpoints, trigger the named review explicitly at the chosen boundary. Reviewer findings are advisory; mission gates and user acceptance supply harness evidence.

Scheduled mission continuations

Attach explicit wakeups to the current mission with mission-schedule.add. Supply an immutable id, continuation content, and a time policy: at is an absolute Common Lisp universal time, interval is a positive number of seconds, and event names an external event instead of a time policy. For example:

(application-mission-schedule-add
 *active-application* :id "build-followup"
 :content "Check the pending build and continue the mission."
 :at (+ (get-universal-time) 300) :missed-policy :latest)

For recurring wakeups, choose missed-policy latest (the default), all or skip to select the newest missed occurrence, every missed occurrence or none. Use mission-schedule.event with an event name and stable external event id; repeated delivery of that ID is deduplicated. The local daemon listener hosts time wakeups. Due work enters the ordinary primary command queue as a persisted (mission-wakeup "TICKET") command and executes as a mission continuation under the existing turn, token and wall-clock budgets.

Only the primary agent may use the mission-schedule tools. Each wakeup retains its exact mission identity and version, checked at admission and execution; replaced or inactive mission work is cancelled. Use mission-schedule.inspect to read schedules, occurrence IDs and claim tokens, and mission-schedule.cancel with the schedule id to cancel future and pending work.

Interrupted admissions and executions restore as uncertain outcomes. Inspect the external effects before calling mission-schedule.resolve with the readable occurrence ID, current token and action completed, failed or retry. Retry is an explicit decision and is refused while the occurrence is executing. Cancellation retains claim and duplicate history.

Editor sessions over ACP

Configure your ACP-compatible editor to launch:

autolith acp

Authenticate the selected provider with autolith auth before starting the editor session. ACP uses UTF-8 JSON-RPC on stdin/stdout; diagnostics go to stderr. The normal launch loads durable mutations and user initialization. For AUI, use autolith --permissions auto acp. Use --pristine when recovering from a damaged mutable image.

New sessions use the editor’s absolute working directory and a durable Autolith conversation ID. Load that ID to replay messages, visible reasoning, tool calls, and results. Text prompts, resource links, and embedded text resources are supported. File and process operations use the native Autolith tools.

The default ask mode requests command and external-tool approval in the editor. An approval for the session covers the exact command and working directory, or external tool identity and arguments. Select auto, sandboxed, or full-access through ACP session modes; use --permissions auto|sandbox|full on the command line. Changing modes clears session approvals.

Client-supplied stdio and HTTP MCP servers are merged with native registrations for that session. Literal environment values and headers are ephemeral and redacted from server output. Cancel a prompt to stop its provider, tools, and owned jobs; close the session or disconnect to release its resources and lease. One primary prompt, setting mutation, tool invocation, or session setup runs at a time in each ACP process. Catalog reads and owned-job controls can run alongside it.

Typed client extensions

Request _autolith/capabilities to discover installed methods and schema version. Include sessionId in every other extension request. Responses contain outcome (ok, condition, busy, unsupported, or cancelled), with value on success and a bounded report on failure. Unknown measurements are JSON null.

Method suffixValue or action
stateSession, turn, model, permission, usage, plan and health snapshot
settings, models, operationsTyped catalog items
set-settingSet name to value; return the updated setting row
invokeInvoke operation with JSON arguments; return presented text
steer, queueDurably admit primary text; return accepted and delivery
conversationsPage durable conversations using limit and cursor; optionally filter cwd
jobs, jobPage owned jobs or inspect one jobId
job-transcriptRead jobId with offset, limit and output (text or structured)
job-send, job-cancelSend text to or cancel an owned jobId
resourceRead bounded workspace: lines or shell-log: bytes from uri

Prefix these suffixes with _autolith/. Catalog operations expose admission and availability; tools use their native decoders and ACP approval callbacks. Terminal-only operations return an explicit unsupported outcome. Workspace resource windows use startLine and lineCount and include the observed revision. Shell-log windows use byteOffset and byteCount and require session ownership.

Invoke ordinary commands with arguments: {"input": "..."}, using native command arguments without the command name. text is an alias for input. Commands such as skills, hurry-up and papercuts return their presented text. Operations with terminalWithoutArguments require an argument to avoid a terminal picker. During an active turn, native inspection and execution policies apply; boundary-applied commands return delivery: "scheduled" and present their results as ACP updates.

steer delivers text at the next safe boundary, including submissions made while an approval is pending. Late steering is promoted ahead of ordinary queued follow-ups. queue appends a follow-up for the next available primary turn. Both accept up to 262144 characters and return delivery: "steering" or "queued". Display accepted input separately until its durable user_message_chunk arrives. Cancellation pauses pending input; a new submission resumes it. Accepted input survives session close and reload. ACP has no per-item withdrawal operation.

For structured child transcripts, value.updates contains ordinary ACP SessionUpdate bodies, with messages, reasoning, tool calls and result content. Offsets count updates from zero; the default limit is 100 and the maximum is 200. The typed response envelope is bounded to 262144 UTF-8 bytes. A single update over that bound returns a condition instead of advancing the cursor. nextOffset is JSON null when complete is true, meaning the current snapshot is exhausted. For a running child, poll again from total to read later updates. Text output uses character offsets and retains its existing numeric end cursor.

Treat _autolith/event state and catalog notifications as invalidation hints. Refetch the affected snapshot after a sequence gap. Events with kind turn are ordered lifecycle boundaries: data contains state (running or idle), an integer epoch, source (prompt, queue, completion, or invoke), and a terminal outcome (end-turn, cancelled, or condition; null while running). Match the epoch when handling approvals, cancellation and transcript boundaries.

Automatic job continuations stream through ordinary ACP updates. Cancellation or an inference failure pauses continuation; submit an explicit prompt to resume.

Emacs and sly

Autolith’s image can be your Common Lisp development image. emacs/autolith.el in the source tree sets up one frame with three windows: your code, a sly REPL connected to Autolith’s own image, and an agent-shell conversation with the same Autolith. What you define in the REPL lives beside the agent, which can inspect it and advise.

The sly REPL spans the bottom quarter of the whole frame. Above it, edit code on the left and converse in the right third.

Install sly and agent-shell, load the file, and run M-x autolith-ide from the buffer you are editing:

(load "/path/to/autolith/emacs/autolith.el")

Optional Sly contribs need their Lisp dependencies in Autolith’s image. For sly-stepper, install agnostic-lizard in a persistent ASDF source registry visible to the Autolith process before Slynk starts.

autolith-ide starts the Emacs server if needed and launches autolith acp through agent-shell with three environment variables:

  • AUTOLITH_SLYNK_DIRECTORY is your sly’s own slynk/ directory, so the server speaks exactly your client’s protocol. Autolith loads that slynk.asd by path and compiles it into the ASDF cache on first use.
  • AUTOLITH_SLYNK_PORT_FILE receives the loopback port once Slynk listens, or error: REASON when it cannot start. autolith-ide waits for it, up to autolith-slynk-timeout seconds, and connects sly.
  • AUTOLITH_EMACS_SERVER is this Emacs’s server socket and enables the emacs: resources and emacs.* tools.

M-x autolith-agent-shell starts only the conversation, with Emacs access and without Slynk. The same variables, or the :slynk-directory, :slynk-port-file and :emacs-server-socket settings, work outside Emacs. In the terminal, (slynk) starts Slynk and shows the port for M-x sly-connect, and (slynk "stop") closes it.

The agent reaches your definitions with lisp.describe, lisp.source and lisp.apropos using target self, or with self.eval. While Slynk serves, a context note tells it so. Your Emacs appears to the agent only while the configured server socket exists:

  • emacs:current, read with resource.read, lists authorized visible windows with point, the active region and open file buffers, each by its emacs:buffer/NAME URI. Outside-root files and non-file buffers require full-access approval; denied buffers are omitted from this snapshot.
  • emacs:buffer/NAME is a live buffer’s text, unsaved edits included, with the buffer name percent-encoded. resource.edit applies line operations to the buffer itself: the change stays unsaved and undoable, and is refused as stale when you edited the buffer since the agent read it. A buffer visiting a file outside the workspace and source roots asks the same permission as that path.
  • emacs.visit shows a file position in a file window of the selected frame.
  • emacs.eval evaluates Emacs Lisp. It asks command authorization for emacs.eval -- FORM and runs only with full access.

Slynk listens on 127.0.0.1 only. Like ordinary sly it has no authentication beyond the optional ~/.sly-secret, so anyone who can reach loopback on this machine can evaluate in the image. Your code shares the agent’s heap: a fatal error or (sb-ext:exit) ends the session, which recovers like any crash. Slynk’s debugger and output hooks stay off for Autolith’s own threads, so your REPL sees only your evaluations. A checkpoint stops Slynk, closing sly connections, and serves the same port again; reconnect with M-x autolith-ide-reconnect. A checkpoint waits for in-flight sly evaluations and fails if they exceed the shutdown timeout.

Fullscreen terminal

Autolith uses the ordinary inline terminal presentation by default. Opt into the application-owned fullscreen viewport for a session with:

autolith --fullscreen

Save the preference for future launches at the Autolith prompt:

(preferences-set-fullscreen (application-configuration *active-application*) t)

The fullscreen viewport keeps the transcript scrollable and pins the composer to the bottom. Use PageUp and PageDown for page movement, Ctrl-PageUp and Ctrl-PageDown to jump between the user and assistant messages while skipping the reasoning and tool activity in between, Ctrl-Home and Ctrl-End for the oldest and newest transcript positions, and the mouse wheel for incremental scrolling. The --fullscreen command-line option enables it for one launch without changing the saved preference. Pass nil to preferences-set-fullscreen to return future launches to inline mode. Because the viewport owns the mouse, hold Shift while dragging to select text with the terminal emulator. Fullscreen history lives in the application rather than the terminal emulator’s native scrollback.

For input with matching saved memories, a dim @ N relevant memories exist line appears below your message. Its count is the number offered to the model for that request. Each input gets one notice across tool calls and provider retries.

Input recall includes every user message, command, and Lisp submission in the conversation. Automatic messages and assistant output are excluded. On first use, the complete input-only sidecar is built from the conversation records; later submissions are appended to it. Resuming reads this sidecar instead of replaying archived transcript chunks.

Clicking a web address in the fullscreen transcript opens it in the default browser, and every fenced code block ends with a ⧉ copy label that copies the block’s source. Copies go to the host clipboard through wl-clipboard, xclip or xsel, pbcopy, or the Windows clipboard, and the terminal also receives an OSC 52 request, so a terminal reached over SSH or a localgroup relay copies on your own machine. Web addresses in every mode are emitted as OSC 8 hyperlinks, which terminals open with their usual modifier-click.

Fullscreen startup presents a brief Lisp-machine boot screen with a centered tip and a rock mascot that adapts to short viewports, then the session banner and transcript above the composer. Mouse wheel scrolling also works through a controlling local-session attachment.

The boot screen then waits for Space or Enter before opening the listener, rotating the startup tip every ten seconds while it waits. Ctrl-C on the waiting boot screen halts the session. Turn the wait off for future launches with (setf (config :boot-screen-linger-p) nil) or for one launch with AUTOLITH_BOOT_LINGER=off, and change the rotation with (setf (config :boot-screen-tip-seconds) 30) or AUTOLITH_BOOT_TIP_SECONDS. The animation takes (config :boot-screen-seconds) in total, 3.5 by default and also set by AUTOLITH_BOOT_DURATION, shared evenly by its phases. Skip the boot screen entirely with (setf (config :boot-screen-p) nil) or AUTOLITH_BOOT_SCREEN=off; (settings) lists all four.

Almighty theme

autolith --almighty

forces the fullscreen viewport and presents the Almighty Lisp theme from almightylisp.com: near-white text on the site’s blue, yellow for everything emphasized, cyan for code and strings, and an ALMIGHTY LITH boot mark. Terminals that advertise true color through COLORTERM get the exact colors; others get the nearest indexed or basic ones. The theme sets the terminal’s default colors only while the viewport is active and restores them on exit. Save it for future launches with (setf (config :terminal-theme) :almighty); :autolith restores the default presentation. Detached sessions carry the theme across handoff.

Install, update, and uninstall

Nix

Nix is the simplest way to install Autolith:

nix run github:luciusmagn/autolith

The first launch builds Lisp images Autolith needs to launch. These images live under ${XDG_DATA_HOME:-~/.local/share}/autolith/nix/images/}.

Linux binary release

It is possible to install autolith on Linux x86_64 via a shell installer:

curl -fsSL https://sh.lambda-symbolics.com/autolith | sh

The installer verifies the release archive’s SHA-256 before extraction. The checksum must identify that exact archive; malformed or missing checksums abort installation. The initial shell script is fetched over HTTPS.

Read the installer before piping it to a shell, or use Nix if that tradeoff is unwelcome. Linux binary releases require OpenSSL and Bubblewrap. Bubblewrap provides the filesystem and network isolation used for sandboxed command execution. The installer names the missing dependency instead of installing a release that cannot offer sandboxed or safe automatic execution. The launcher uses the installation layout, rather than an environment marker alone, to distinguish binary, Nix, and source starts.

Autolith automatically checks for updates at most once every 20 hours. To install the latest binary release and exit without starting a session:

autolith update

The command exits successfully when updated or already current, and returns a nonzero status if checking or installing fails. autolith --update is an equivalent update-and-exit alias. Run autolith afterward to start the new release. Both spellings accept --help or -h; extra arguments exit with status 64 before checking for updates. Use your flake or Nix profile to update Nix installations; update the checkout and run ./script/bootstrap for source installations. These installations explain their update mechanism without building or starting Lisp images.

To update from a session and restart it with its original command arguments:

(update)

Release archives are built and checked by the installer host. GitHub publishes release metadata after the host checksum is available. See Release service.

Source checkout

For a fresh macOS machine:

brew install sbcl rust cmake pkg-config openssl@3
curl -o /tmp/quicklisp.lisp https://beta.quicklisp.org/quicklisp.lisp
sbcl --no-sysinit --no-userinit --load /tmp/quicklisp.lisp \
  --eval '(quicklisp-quickstart:install)' --quit

On Linux, also install Bubblewrap with your distribution’s package manager. Autolith uses its bwrap executable to isolate the host filesystem and network for sandboxed commands and the sandboxed decisions made by automatic permission mode. Nix installations include it. Bubblewrap is not a macOS or BSD dependency. Without platform sandbox support, the sandbox choice is unavailable; explicit once, always, and full approvals run with the user’s full privileges.

With Quicklisp in ~/quicklisp, bootstrap and check the checkout:

./script/bootstrap
./script/check

The check runs FiveAM tests in isolated SBCL processes, defaulting to the available logical CPU count (one worker if detection fails), and verifies the pristine recovery image. Each run first saves an active image of the checked source, so cases that start a fresh Autolith boot that core instead of loading the system. List or select test suites and individual cases with:

./script/check --list
./script/check --suite workspace-resource --suite fixtures
./script/check --test test-workspace-file-resources --jobs 1
./script/check --jobs 8 --timeout 900

Selected runs skip recovery probes. Selectors are exact, case-insensitive names and can be repeated; unknown names fail. --jobs controls process concurrency, and --timeout sets the deadline per worker in seconds (default 600). Results include per-case timings and the slowest cases. AGENTS.md documents test registration, reusable fixtures, and interactive Lisp entry points.

To put a checkout into PATH:

mkdir -p ~/.local/bin
ln -sfn "$PWD/bin/autolith" ~/.local/bin/autolith

This is assuming you have .local/bin/ in PATH.

Windows

For the Windows x86-64 development artifact, extract the archive into a writable folder and run .\bin\autolith.cmd from PowerShell. Use Windows 10 version 1903 or newer. The bundled SBCL executable has a UTF-8 process manifest for Unicode paths; system-wide locale changes and 8.3 filenames are unnecessary. The first launch builds per-user cores under .autolith-images beside the release.

To build the source checkout, install Git for Windows, Rust with the MSVC toolchain and Cargo, and Quicklisp under %USERPROFILE%\quicklisp. Put clang on PATH for the ColorLisp syntax library. An SBCL 2.6.6 or newer on PATH is used when present; otherwise the bootstrap downloads the official SBCL Windows binary pinned in sbcl-windows-releases.sha256, verifies its SHA-256, and unpacks it below the data root without registering it with Windows. OpenSSL 3 comes from Git for Windows, from AUTOLITH_OPENSSL_DIRECTORY, or from a native\openssl directory beside the sources.

From PowerShell, bootstrap and check the checkout:

script\bootstrap.ps1
script\check.ps1

script\check.ps1 takes the same selectors as ./script/check. Checks that need FIFOs, forked children, pseudo-terminals, POSIX file modes, or the POSIX shell are recorded as skipped. To package a Windows archive, install the Windows SDK (including mt.exe) and run script\package-windows-release.ps1 -Tag VERSION. The POSIX release-server scripts are not built on Windows.

Start a session with bin\autolith.cmd or bin\autolith.ps1; both take the arguments bin/autolith takes. Windows Terminal is the primary console; legacy consoles without virtual-terminal input are refused with a message.

Windows sandboxed commands use the bundled AppContainer helper. They can write in the workspace and a private temporary directory, read system applications and explicit tool directories, and have no network access. Repository metadata is protected. Bootstrap builds the helper with Clang; release archives include it.

Windows supports supervised detached sessions, client-first starts, and exact-heap restart checkpoints. It withholds fork-based checkpoint saving and the unix management REPL transport.

Uninstall

To remove Autolith and keep your data:

autolith uninstall

The launcher lists what it will remove, asks for confirmation, and exits without starting a session; --yes skips the prompt, which is required when no terminal is attached. It removes the packaged installation and its command link, managed SBCL runtimes, the active and recovery images, retained generation cores, saved worker images, Nix image trees, release markers, crash capsules, launcher pointers, session sockets and leases, provider caches, and the cache root.

User data stays in place: conversations, inference traces, memories, papercuts, agendas, plans, pending input, private image commits, the mutation journal and mutation history, permissions, preferences, credentials, and the configuration root. Stop running sessions first. A source checkout stays as well; delete it yourself. Nix installations also need the package removed from the flake or profile. On Windows the launcher prints the folders to delete, because the runtime in use lives inside them.

Talk to Autolith

Prose, Lisp, and commands

Ordinary text is a prompt to the primary agent. It is equivalent to:

(prompt :to 'autolith "Review this change")

autolith is reserved, case-insensitively, for the primary agent.

As a shortcut, if you want to submit something to the main agent, you don’t need to include the :to parameter:

(prompt "Shut yourself down homie")

prompt can also compute text or attach local images:

(prompt (read-file "request.org"))
(prompt :images "/tmp/diagram.png" "Review this diagram")
(prompt :to 'test-review "Run focused tests and report failures.")

If you message a sub-agent, it can respond to you in the main conversation thread if it wants to.

Start a message with ( to evaluate it as Common Lisp in the active image:

(help)
(resource.read :uri "workspace:.")
(eval-now (setf *print-pretty* nil))

Use (help) for a short overview. Choose a topic or look up one command:

(help :conversations)
(help :workspace)
(help 'resume)
(help "lisp.eval")

Keyword topics open sections. Quoted symbols and strings open command or tool help, including usage and parameter descriptions. Browse tools by namespace with (help :lisp), list all commands with (help :commands), or request the full reference with (help :all). Help uses the terminal Markdown renderer and Lisp syntax highlighting.

Press Tab after =(help = to browse topics and registered operations. Command options also appear after a command name and a space, for example =(ste =.

Type @ in prose or inside a Lisp string to fuzzy-search workspace files through fff. Accepting a match inserts the path and drops the @. Directories keep a trailing slash so you can keep completing. A completed file stays in the draft.

The clanker sees both your source input and its evaluated result apart from any other effects it may have.

Start input with ? followed by whitespace to evaluate one form asynchronously in the active image. Use this for a blocking operation or a bare variable:

? *package*
? (progn (sleep 10) (values :finished 42))

The editor displays ? in place of the normal prompt. Submit with Enter, then continue using the session while the form runs. ?? and ?text are chat; spaces, tabs, and line breaks after ? select asynchronous Lisp. Continue an incomplete form on another line as with ordinary Lisp input.

Inspect or cancel the returned exec:N job with job.get, job.wait, or job.cancel. Standard output, error output, and Lisp diagnostic streams are captured with bounded retention and rate-limited, sanitized terminal display. On completion, receive all returned values or the error, together with captured output. Asynchronous evaluation uses noninteractive error handling and EOF input rather than a restart picker.

The submitted form and its eventual result are retained in the originating session, including across a session switch. The model receives these messages at a safe request boundary; submitting a form does not start a model turn.

When local Lisp or an interactive command signals a serious condition, the restart debugger keeps the signaling stack and its live restarts available. Use Ask Autolith why this failed to run an independent diagnosis while the failed operation remains suspended. Diagnosis can offer up to three explicitly selected recoveries that invoke a live restart, run repair source first, retry the whole operation, return replacement values when supported, or abort the operation. Retry does not roll back effects completed before the failure.

The debugger shows the condition type and report, followed by available details such as the offending value, expected type, operation, filename, and reader location. These fields come from the condition itself and are also included in model diagnosis.

Escape during diagnosis cancels only the diagnosis and returns to the live restarts. Escape at the ordinary restart picker aborts the failed operation. During an active provider turn, Escape cancels the turn and continues queued steering in submission order, oldest first. Ctrl-C cancels and pauses queued work until you submit input; a second interrupt within the force-exit window exits the application.

Parking and editing queued input

Run (vault-store) to move queued work and unconsumed steering into the recovery input vault immediately, including during an active turn. Active work continues. Use (vault-contents) to read the parked entries in restoration order and setf to replace the whole list or one zero-based entry:

(vault-store)
(vault-contents)
(setf (vault-contents 0) '(:message "Revised task"))
(setf (vault-contents) '((:message "First task") (:lisp "(+ 1 2)")))
(vault-restore)

Entries are (:message INPUT), (:command TEXT), or (:lisp SOURCE). Direct vault reads and top-level setf forms containing only vault-contents places execute during an active turn. Returned lists are detached snapshots; publish edits with setf. Use (setf (vault-contents) nil) to clear parked input. Run (vault) for a summary or (vault-restore) to prepend parked work to the live queue.

Managed resources

resource.read observes durable state and returns an opaque revision. resource.edit applies a resource-specific operation only against that exact observation.

For multi-file semantic changes, Autolith prepares a cl-resources change set against retained file revisions and validates every member before publication. Each file is published atomically. On a later failure, Autolith restores earlier members; conflicting external updates require explicit recovery rather than replacement. File moves preserve source permissions captured during staging.

Papercuts use current-workspace resource URIs:

  • papercut:current lists active reports and accepts papercut-report.
  • papercut:id/<percent-encoded-stable-id> reads one active report and accepts papercut-close with a complete resolution.

The existing papercut.report tool and (papercuts), (papercut "ID"), and (papercut-close "ID") commands remain convenient direct interfaces.

Conversation history uses read-only resource URIs:

  • conversation:current is the current conversation’s complete durable history, including the records compaction removed from the model context. A plain read returns the newest records; start-sequence and record-count (default 20, maximum 200) page by durable record sequence. A window holds at most 7,000 characters and shortens long records, so read a record alone to see all of it.
  • With query, the read lists the newest records containing every whitespace-separated term, each with its sequence and an excerpt. ASCII letters match in either case and other characters match exactly. max-results defaults to 20 and is capped at 50. Messages, assistant replies, reasoning summaries, tool call arguments and outputs, web searches, and compaction summaries are searched.
  • conversation:id/<id> reads or searches another conversation by its identifier. Searches never span several conversations.

Each model report is acknowledged durably in the conversation, and a report that repeats an active papercut of the workspace is not recorded again: a title matching word for word, or a title and body whose words largely overlap a report from the last day, answers with that existing report instead. A conversation re-filing within an hour of its own report is matched more loosely, since rewritten repeats share fewer words.

Exporting and importing data

Use the same portable archive from the command line or an active session:

autolith data export all.sexp --all
autolith data export project.sexp --workspace /home/al/code/project
autolith data import project.sexp --workspace /home/al/src/project
(data.export "all.sexp" :all)
(data.export "project.sexp" :workspace "/home/al/code/project")
(data.import "project.sexp" :workspace "/home/al/src/project")

Export defaults to all workspaces and global data. A workspace export contains its conversations, memories, agendas, plans, papercuts, and associated session assets. The archive contains Autolith data rather than the repository files; clone or copy the repository separately. Credentials, configuration, executable image state, and caches are excluded.

Import preserves stable identifiers and merges into local data. Identical data is skipped; a conflicting identity aborts the import before publication. For a single-workspace archive, --workspace DIRECTORY or :workspace "DIRECTORY" sets its new home directory. Without that option, workspace locations are preserved. Relative paths are resolved against the current working directory.

Export creates a new private archive and refuses to overwrite an existing file. The CLI exits without starting a session: success is 0, invalid arguments are 64, and transfer failures are 1. Both command groups provide --help.

Session titles and resume

A session gets an immediate local title from its first prompt or image. After the third actual user turn, Autolith may ask the active model for one concise replacement and persist it with the conversation. The local title does not depend on provider generation.

Provider-generated refreshes are enabled by default. Use (titles), (titles "on") or (titles "off") to inspect or change the durable global preference.

(resume) displays titles in its picker while an explicit (resume "ID") continues to address the stable conversation identifier. The terminal status bar and local session status display the title when one is available and otherwise show the conversation ID.

A resumed conversation runs in a fresh process, so background jobs, asynchronous shell runs, and Lisp worker state from before the restart no longer exist. The first user turn after a resume carries a request-local notice telling the model so, which keeps it from polling or waiting on dead jobs. Only durable child task results stay readable through job.get.

Inspect a saved conversation without resuming or locking it with:

autolith replay ID
autolith replay ID turn 12
autolith replay ID date 2026-08-28
autolith replay ID time 2026-08-28T10:30:00
autolith replay ID sequence 1200

On an interactive terminal, replay behaves like a small debugger over durable conversation events. next and previous step records; turn, date, time, and sequence jump to a location; raw shows the selected replay form; and quit exits. Replay reads complete persisted forms without acquiring the conversation lease or repairing, appending, or resuming the conversation.

Local sessions

Every running Autolith session publishes a private authenticated local endpoint. Its session ID is the active conversation ID, shared with resume and stable across detach, restart and checkpoint quiescence. Selecting another conversation with new, resume or fork moves the endpoint to that conversation ID while attached terminals continue using the same connection. Inspect endpoints with:

autolith localgroup status
autolith localgroup status --sexp

idle means that the process is at input with no active work of any kind.

Control a sessions with the following commands:

autolith localgroup tell   SESSION-ID "message"
autolith localgroup pause  SESSION-ID
autolith localgroup attach SESSION-ID
autolith localgroup attach SESSION-ID --read-only
autolith localgroup attach SESSION-ID --take-over
autolith localgroup detach SESSION-ID
autolith localgroup kill   SESSION-ID

Use a full SESSION-ID or a prefix that matches one session. Prefixes accept IDs with or without the display hyphen, for example b, b-6, or b6 for b-6uouHJ. Full IDs take priority over prefix matches. If a prefix matches more than one session, the error lists the matching IDs; use a longer prefix. Current IDs are case-sensitive. Legacy hexadecimal IDs accept either case.

tell uses input to tell something to a session and resumes a paused session. pause cancels active work and holds queued primary work. kill requests graceful shutdown.

Detaching hands a session to a supervised process group that outlives the terminal. Windows has no process groups or detached sessions, so sessions there always run directly in the terminal that started them: (detach) and client-first starts report that the handoff is withheld, while the loopback endpoint and the localgroup commands work as elsewhere.

Autolith stores configuration, durable data, process state, and replaceable caches below ${XDG_CONFIG_HOME:-~/.config}/autolith/, ${XDG_DATA_HOME:-~/.local/share}/autolith/, ${XDG_STATE_HOME:-~/.local/state}/autolith/, and ${XDG_CACHE_HOME:-~/.cache}/autolith/. XDG base-directory values must be nonempty absolute paths; invalid values use these defaults. Newly created Autolith roots have mode 0700.

On Windows the same four roots are, unless an absolute XDG variable overrides one, %APPDATA%\autolith\, %LOCALAPPDATA%\autolith\data\, %LOCALAPPDATA%\autolith\state\, and %LOCALAPPDATA%\autolith\cache\. Private files and directories carry an access control list granting only the owner and SYSTEM, which is what mode 0600 and 0700 mean there; a file Autolith publishes read-only withholds write access through that list and keeps the read-only attribute clear, so it can still be replaced and deleted.

Conversation leases, Localgroup endpoint records, and recovery state live under ${XDG_STATE_HOME:-~/.local/state}/autolith/ rather than XDG_RUNTIME_DIR. They must survive logout so detached sessions, handoffs, and crash recovery can continue across login sessions. Autolith uses authenticated loopback TCP rather than filesystem sockets for its inference and Localgroup protocols, and cleans stale Localgroup records itself.

Configure Autolith

Autolith loads the optional user file ${XDG_CONFIG_HOME:-~/.config}/autolith/init.lisp after tracked code, a selected private image commit, and any site or trusted directory configuration. It reloads on retained generation reconnect.

The file is ordinary Common Lisp in package AUTOLITH, with your full privileges. It is not copied into private replay or the pristine recovery image.

Packagers may supply one separate, read-only configuration tree with --site-config-root DIRECTORY or AUTOLITH_SITE_CONFIG_ROOT. The directory must already exist and be absolute. It may contain init.lisp, mcp.sexp, skills/, and agents/. Site configuration loads before user configuration, so it provides defaults without replacing the user’s normal XDG tree. A site init.lisp has the same full privileges as user initialization and is not a security boundary.

Executable initialization loads in this order:

  1. site init.lisp
  2. trusted directory .autolith/init.lisp files, outermost to nearest
  3. the user XDG init.lisp

Missing files are ordinary. A present file that cannot be read or evaluated fails startup or reload with its exact pathname. directory-scopes.sexp is user-owned.

Settings

Every knob Autolith exposes is a named setting with a type, a description, a default, and, where the choices are finite, its options. (settings) opens the settings page: a picker listing each setting with its current value and where that value came from. Choosing an adjustable setting opens its options; choosing a read-only one explains why it cannot change in a running session. The page returns to the list after every change until you cancel.

The same operations work without the page. (settings "NAME") describes one setting, (settings "NAME" "VALUE") changes it, and (settings) called from a script, where no picker can open, prints every setting grouped by area. Values are written as they appear on the page: on and off for switches, option names for choices, and integers or paths as text. A change to a durable setting is saved at once for future sessions.

Settings live in one of four scopes. Durable settings, such as the model, reasoning effort, Fast mode, reasoning traces, compact view, timestamps, cache miss notices, Simple Technical English, generated titles, fullscreen, and the saved permission mode, persist in preferences.sexp under the state directory. Session settings, such as hurry-up mode, last until exit. Process settings, such as the immutable flag, the management endpoint, and the roots, come from the command line, the environment, or defaults at startup and stay fixed. Derived settings, such as the context window and provider endpoint, follow the model. The window uses a declared model value, else the catalog value, else the default.

A durable value is chosen at startup from the first source that supplies it: an explicit command-line choice, the setting’s environment variable, then the saved file. A saved model or effort that no registered provider serves is dropped rather than applied.

From Lisp, user initialization and replay scripts read a setting with (config :model), which uses the current configuration, or (config :model configuration) for an explicit one, and change it with (setf (config :reasoning-traces-p) t). Unknown setting names signal a configuration error. Create an independent configuration with configuration-copy.

The preferences file uses version 8: one flat record of setting names and values that keeps unknown keys. Before upgrading a preferences file from versions 1 through 7 to 0.54 or later, run a 0.53 release once to write version 8.

Language servers

Install your language server, then configure it in lsp.sexp under Autolith’s user configuration directory, alongside init.lisp. For example:

(:version 1
 :servers ((:name "clangd"
            :command "clangd"
            :arguments ("--background-index")
            :extensions (".c" ".h")
            :language-id "c"
            :root-markers ("compile_commands.json" ".git")
            :timeout-seconds 30)))

Configuration is declarative. Supply a unique :name, executable :command, nonempty :extensions list, and :language-id for each entry. Arguments are literal strings. Optional :initialization-options and :settings are JSON object strings; :disabled-p t disables an entry. The request timeout is an integer from 1 through 120 seconds, defaulting to 30.

Only configure commands you trust: language servers run with your privileges and inherited environment, outside the shell approval sandbox. Keep credentials out of this file. Executables must already be installed; commands are resolved on PATH. Repository-local LSP configuration is not loaded.

Autolith exposes the lsp tools only when lsp.sexp defines at least one enabled server; without it the registry omits them. A malformed lsp.sexp keeps the tools registered so the configuration error surfaces when a tool runs. When at least one server is enabled, each provider request includes a context note naming those servers.

Ask Autolith to use these tools:

  • lsp.status: inspect configuration and running project/server pairs.
  • lsp.query: request definition, references, hover, implementation, type-definition, document-symbols, or workspace-symbols for a workspace file. Position queries require one-based line and character coordinates, with columns measured in UTF-16 code units. For workspace symbols, supply query; the file selects the project and server.
  • lsp.diagnostics: synchronize a file and retrieve diagnostics.
  • lsp.restart: stop servers and reload the configuration after changing it.

Use lsp.semantic for revisioned transformations:

  • prepare-rename: inspect the symbol at path, line and character.
  • rename: propose a symbol rename with those coordinates and new-name.
  • code-actions: discover choices for a range, using line, character, end-line and end-character. Optionally filter only action kinds or supply diagnostics.
  • resolve: retrieve a complete action using its returned choice ID.
  • propose: retain an action’s edit as an applicable proposal using choice.
  • move: propose server preparatory edits followed by moving path to new-path. Both paths must identify regular files or a missing destination.
  • inspect: review a retained proposal ID.
  • apply: apply a retained proposal. Obtain explicit approval for annotations with needsConfirmation, then include their IDs in approved-annotations. Confirm each synthetic lsp.semantic-confirmation request through the ordinary command-permission prompt. Without a permission callback, confirmation is denied.

Proposals preserve ordered text, create, move and delete effects. Every affected file passes the ordinary resource authority checks before observation and again before application. Resource revisions cover file kind and content; synchronized document versions must also match. Request a fresh proposal after a conflicting edit or server restart. Moves preserve the source permissions captured during staging, including edited multi-hop moves. Publication is atomic per file with change-set rollback on failure. Proposals and action choices own their exact snapshots and are conversation-local. Retain up to 32 choices, 240,000 encoded characters per choice, 128 resources per choice, and 512 resources or 16 MiB of UTF-8 snapshots per conversation. Older choices expire when those budgets fill. Server commands are presented as data for separate explicit execution. Use local file:/// URIs; directories, service resources and URI authorities are unsupported.

Servers start lazily on file requests, including diagnostic requests after a successful resource.edit workspace edit. All enabled entries with matching filename suffixes are queried. The project root is the nearest ancestor with a configured root marker, bounded by the workspace; otherwise it is the workspace. Open files are refreshed from disk before queries, including shell edits.

Results contain native file URIs and zero-based UTF-16 ranges. Push and pull reports are retained independently and combined, removing duplicate items. Diagnostic state received identifies versioned reports, unversioned means a report’s snapshot cannot be confirmed, and pending means no report arrived. The sources field gives push and pull freshness separately.

Explicit diagnostics allow two seconds for push reports and use the configured request timeout for pull requests. Edit follow-ups have one shared one-second blocking deadline across lock acquisition, startup, synchronization, and all matching servers, with at most half a second for push settling. Failed startup also reaps its process. If automatic diagnostics time out, use lsp.diagnostics for the full server timeout. Reports retain at most 200 diagnostics per file and mark truncation. Files are limited to 2 MiB, with 64 open documents per server and 16 live project/server pairs per registry. Use lsp.restart to release them. Server shutdown occurs when the tool runtime is retired.

Active-image management endpoint

The management REPL is a separate opt-in subsystem for trusted programs that must evaluate Common Lisp in the running AUTOLITH image. It is image-daemon’s evaluation endpoint, whose eval-connect and eval-call form a complete Lisp client, and does not use the localgroup protocol, Swank, or Slynk. It is disabled unless AUTOLITH_MANAGEMENT_REPL is set to on.

Every connection authenticates with a fresh random nonce and HMAC-SHA-256. The client never sends the reusable token. Put a nonempty token in a regular file owned by the current uid with mode 0600. Autolith opens that file without following symbolic links only while checking a proof, wipes the token octets, and retains only the token-file pathname.

The following environment variables configure newly created applications:

VariableDefaultMeaning
AUTOLITH_MANAGEMENT_REPLoffEnable the endpoint.
AUTOLITH_MANAGEMENT_REPL_TRANSPORTunix, or tcp on Windowsunix or tcp.
AUTOLITH_MANAGEMENT_REPL_UNIX_SOCKET${XDG_STATE_HOME:-~/.local/state}/autolith/management/repl.sockUnix socket pathname.
AUTOLITH_MANAGEMENT_REPL_TCP_ADDRESS127.0.0.1IPv4 loopback listener address.
AUTOLITH_MANAGEMENT_REPL_TCP_PORT4141TCP listener port.
AUTOLITH_MANAGEMENT_REPL_TOKEN_FILE${XDG_CONFIG_HOME:-~/.config}/autolith/management-repl.tokenMode-0600 token file.
AUTOLITH_MANAGEMENT_REPL_TIMEOUT10Evaluation deadline in seconds.
AUTOLITH_MANAGEMENT_REPL_MAX_FRAME1048576Maximum wire-frame octets.
AUTOLITH_MANAGEMENT_REPL_MAX_SOURCE262144Maximum UTF-8 source octets.
AUTOLITH_MANAGEMENT_REPL_MAX_OUTPUT262144Maximum captured output and value text.
AUTOLITH_MANAGEMENT_REPL_QUEUE_CAPACITY8Maximum queued evaluations.
AUTOLITH_MANAGEMENT_REPL_MAX_CLIENTS8Maximum accepted clients, including authentication.
AUTOLITH_MANAGEMENT_REPL_AUTH_TIMEOUT10Absolute authentication deadline in seconds.

TCP accepts IPv4 loopback addresses only. This endpoint does not provide server TLS, and arbitrary Lisp results may contain secrets. Challenge-response prevents bearer-token replay, but an unauthenticated local process can relay a challenge and proof between trusted local clients. Treat loopback TCP clients as locally trusted. Unix transport creates a mode-0700 private directory and a mode-0600 socket. Startup refuses an active socket and any alien or non-socket path; it removes a socket only after proving that it is a stale current-user endpoint. Windows has no filesystem sockets, so unix is refused there with a message and tcp is the default.

Wire protocol

Each message is a four-octet unsigned big-endian length followed by exactly that many UTF-8 octets containing one readable S-expression. Length zero, oversized or truncated frames, invalid UTF-8, malformed syntax, and trailing forms are rejected. Readers use standard I/O syntax, a fresh standard readtable, and *read-eval* nil.

The server starts each connection with:

(:challenge :version 1 :algorithm :hmac-sha-256 :nonce HEX)

HEX encodes 32 random nonce octets as 64 lowercase hexadecimal characters. The client reads its token file, computes HMAC-SHA-256(token, nonce), and replies:

(:authenticate :proof HEX)

A valid proof receives (:authenticated :version 1). A proof is valid only for that connection’s nonce. Authentication failure closes the connection without a reusable credential or detailed oracle response.

After authentication, send one request at a time:

(:evaluate :source "(values (+ 20 22) *package*)")

The source string must contain exactly one form. A dedicated serial evaluator thread reads it in package AUTOLITH, applies the configured deadline, prevents interactive debugger entry, and captures standard, error, trace, query, debug, and terminal output in a bounded Gray stream. Successful responses have this shape:

(:evaluation-result
 :status :ok
 :values ("42" "#<PACKAGE \"AUTOLITH\">")
 :values-truncated-p nil
 :output ""
 :output-truncated-p nil)

:values contains one bounded readable string for every multiple value. Conditions return :status :condition, a bounded :condition-type and :report, captured output, and truncation flags. Deadline expiry returns :status :timeout. The client may submit further requests on the authenticated connection.

Evaluation deadlines use in-process interrupts and are best effort for trusted forms; a form that suppresses interrupts may outlive its deadline. Checkpointing stops the listener, clients, evaluator, and queue before saving the image and restarts the endpoint afterward. If an evaluator cannot quiesce within the documented two-second shutdown bound, checkpointing aborts with a management-repl-error whose operation is :quiesce rather than saving live management threads. Normal shutdown is idempotent and removes only the exact Unix socket created by that runtime.

Context

Autolith has a concept of ephemeral notes, which can be used to add temporary additional information to the context that will eventually disappear:

(define-context-contributor release-advice (request)
  "Add advice for release requests."
  (when (search "release" (or (request-context-latest-user-text request) "")
                :test #'char-equal)
    (make-context-contribution
     :identifier  "release-check"
     :instruction "Verify release artifacts before publishing."
     :lifetime    ':turn
     :priority    40)))
  • A contributor sees a read-only request snapshot
  • It can return none, one, or many contributions
  • Notes stack unless they deduplicate, supersede, or conflict
  • Priority only comes into play when the advice budget is full
  • You can use (context)

Note that these are provider-request-only, and don’t not count as a conversation turn, and do not get attached to your messages.

Register selective project or user advice as a declarative rule:

(register-context-rule
 '(:id "lisp-edits"
   :when (:paths ("src/") :languages ("common-lisp")
          :events ("tool-success"))
   :instruction "Check the changed declarations before committing."
   :priority 30 :lifetime :while-relevant))

Use :paths, :languages, :tools, :namespaces, :roles, :diagnostics and :events in :when. Fields are conjunctive; values within a field are alternatives. Paths match prefixes; other values match exactly. Add an absolute :workspace directory for project scope. Authorized tool operations and structured LSP severities supply bounded current-turn metadata. Asynchronous completions use their originating turn identity. Rules share the ordinary advice budget, priority, lifetime and deduplication policy. Inspect delivery with context-status; remove a rule with unregister-context-rule and its original ID.

The status line keeps a context meter visible while idle. It shows used tokens and the full model window. | marks the automatic compaction point. The ChatGPT subscription catalog currently reports a 272K-token window for the GPT-6.1, GPT-6, and GPT-5.6 models, which compact at about 218K tokens by default:

ctx [==========|..] 218K / 272K used

At narrow widths, the meter keeps the used and window counts and drops the bar.

Use (compact) to compact the current conversation immediately, regardless of the meter. It uses the same provider-native checkpoint and portable summary path as automatic compaction. During an active turn, the command waits in the idle queue. An empty conversation needs no provider request. Compaction adds a durable checkpoint without adding a user message or starting a model turn. The native checkpoint and the portable summary are two provider requests. Each is recorded with its usage as a numbered request of the turn, so the request count and the persisted usage cover compaction work instead of hiding it.

Before either request, Autolith captures a fixed history cutoff. The checkpoint includes unresolved calls with correlated actual or interrupted/unknown outputs, plus results and steering received during summarization. Running jobs and undelivered outcomes are recorded separately from the model-written summary. Ordinary requests include bounded authoritative job references and an omitted count; inspect job.continuity, job.* and their resources for full details. Provider-family changes use the portable handoff and exclude foreign private context.

Providers bill context served from their prompt cache at a fraction of the uncached input price, so a request that re-reads context the previous request had cached costs noticeably more. (cache-misses "on") enables a dim notice after any request whose cache reads fell below half of the previous prompt, and after any request whose cache reads stopped growing while the prompt grew and at least a sixteenth of the previous prompt, never less than 4096 tokens, was re-read. The notice gives the re-read and total prompt token counts and the likely cause: a model change, an idle gap past the roughly five-minute provider cache lifetime, rewritten earlier context, a resumed conversation whose cache had expired, a changed prompt prefix, or a stalled prefix, which means the request reached a provider backend without the newest cached context. When the provider attributes cache reads to request fields, the notice adds whether the instructions and tools were re-read or still cached, so a changed prefix is told apart from lost routing. The first request after compaction is expected to miss and stays quiet. Autolith carries no price table, so the notice reports tokens rather than currency. The setting is off by default and persists across restarts.

Providers

Register a OpenAI-compatible provider:

(register-openai-compatible-provider
 :name            "my-provider"
 :description     "My OpenAI-compatible provider"
 :endpoint        "https://api.example.com/v1/chat/completions"
 :models-endpoint "https://api.example.com/v1/models")

List every model ID known to Autolith without authenticating or starting a session:

autolith models | grep gpt

The command prints one sorted, deduplicated model ID per line from built-in and locally configured catalogs.

Inside a session, (auth) without a provider picks one interactively, and (auth "my-provider") selects one explicitly. Use autolith auth my-provider to authenticate during startup.

After a key is saved, static metadata or the last good cache keeps models available. (models) triggers discovery. You can also pass non-secret :headers and static :models for descriptions, windows, efforts, or providers that have no model-list endpoint.

Built-in ChatGPT subscriptions use browser OAuth with PKCE and a local callback on localhost:1455 or localhost:1457 by default. Use (auth "chatgpt" "device") or autolith auth chatgpt device to select the device-code flow instead. The browser authorization URL is always printed for manual use. Grok and Nous Research use browser device flows. Gemini uses Google installed-application OAuth with a local loopback callback. Anthropic, Fireworks, OpenCode, OpenRouter, Mistral, and user-registered OpenAI-compatible providers use API keys.

Authenticate a GitHub Copilot subscription with autolith auth copilot or (auth "copilot"). Approve the displayed device code at GitHub. Credentials are stored in the private copilot-auth.sexp under the state root. Tokens renew automatically before expiry.

Login discovers tool-capable picker models and copilot/auto, and attempts to enable unconfigured picker models. Models are named copilot/<model-id> locally to avoid collisions with direct vendor subscriptions. Successful Copilot login selects Auto and saves it as the default, unless the current Copilot model is still available. Use (models) to refresh the catalog, then (model "copilot/auto") to select Auto on an existing installation, or launch with --model copilot/auto. Normal startup reads the last successful catalog without a network request.

Use Auto with Copilot Student or Free plans, which do not offer manual model selection. Each request resolves an account-authorized concrete model and uses its Chat Completions, Responses, or Messages protocol with a short-lived session token. Auto keeps the previous concrete model while it is available. Hidden named models are not offered as manual choices. For other plans, select an available named model with (model). Disabled picker models are excluded.

For GitHub Enterprise Cloud with data residency, set AUTOLITH_COPILOT_DOMAIN to your TENANT.ghe.com hostname during both login and subsequent launches. Tokens are pinned to that host and streams use the subscription-specific endpoint returned by Copilot. Chat Completions, Responses, and Anthropic Messages reuse the existing transports. This integration uses GitHub’s internal Copilot endpoints, following pi and VS Code, so it may require updates when GitHub changes that service.

ChatGPT Codex Fast mode sends service_tier=priority when the active Codex model advertises Fast support. Fast responses count more heavily against plan usage; the exact rate is set by OpenAI per model and is not tracked here. The built-in GPT-6.1, GPT-6, and GPT-5.6 models support it. Use (fast) or (fast "status") to inspect it, and (fast "on") or (fast "off") to change the saved preference. The activity status line displays FAST while the active model uses Fast mode. Other providers and unknown Codex models keep the preference but use the standard path. Set AUTOLITH_CODEX_FAST_MODE=on or off to override the saved choice for one process; while it is set, (fast "on") and (fast "off") are unavailable.

A request that fails before the model has produced anything is retried up to six times with jittered delays, since the failed attempt cost only the wait. Once reasoning or output has started streaming, every reconnect bills a fresh generation of the same prompt, so at most two such retries are made before the request fails with a stream-abandoned error. Every failed attempt is recorded in the conversation next to its request number with the attempt count, the elapsed seconds, whether output had been received, and the provider’s identifiers, so retry storms can be audited after the fact.

ChatGPT Codex uses standard Responses requests and native Responses compaction. Normal requests enable provider parallel tool calls. Autolith executes independent calls concurrently, then records their results in provider wire order. Calls requiring a provider round trip, exclusive tools, and calls sharing one mutable runtime execute in separate ordered waves.

GPT-5.4 and later Codex models use native Responses tool search. Autolith sends stable namespace names and descriptions up front, marks each function defer_loading=true, and declares a client-executed tool_search. When the model searches, Autolith answers from its own registry over the namespaces that request advertised, scoring namespace names, tool names, and description words, and appends the tool_search_output as a durable provider item. The expansion replays intact in every later request, which keeps those tools loaded and the prompt prefix cacheable; only native compaction requests replay it empty. Other models and providers receive the complete eager tool array.

Authenticate Gemini with autolith auth gemini or (auth "gemini"). Autolith stores its Google OAuth credentials separately at gemini-auth.sexp under the state root. The provider uses the Gemini CLI Code Assist subscription service directly, including streaming text and thinking, tool calls, token usage, account onboarding, and managed companion projects. AUTOLITH_GEMINI_OAUTH_CLIENT_ID may replace the public installed-application client ID; AUTOLITH_GEMINI_OAUTH_CLIENT_SECRET supplies a client secret when a deployment requires one.

autolith auth nous

Nous models are discovered from the authenticated account. Run (models) after login to refresh the catalog. Model identifiers beginning with anthropic/ use the native Anthropic Messages route; the rest use OpenAI-compatible Chat Completions. Hermes model identifiers are omitted from the catalog because they are not reliable for agentic tool calling.

The Nous integration accepts three deployment overrides:

  • AUTOLITH_NOUS_PORTAL_URL selects the OAuth portal.
  • AUTOLITH_NOUS_INFERENCE_BASE_URL selects the /v1 inference base used for model discovery, Chat Completions, and Messages.
  • AUTOLITH_NOUS_PROVIDER_ENDPOINT overrides only the Chat Completions endpoint.

Autolith stores Nous OAuth state separately at nous-auth.sexp under its state root and serializes rotating refresh-token updates across processes sharing that root.

Mistral models are discovered from the authenticated account and filtered to models advertising Chat Completions support. MISTRAL_API_KEY takes precedence over Autolith’s private Mistral key. AUTOLITH_MISTRAL_PROVIDER_ENDPOINT and AUTOLITH_MISTRAL_MODELS_ENDPOINT override the chat and discovery endpoints independently.

OpenRouter models are discovered from the authenticated catalog and use openrouter/<vendor>/<model> identifiers locally. Discovery includes only models that emit text and advertise function tools, tool choice, and normalized reasoning, matching the request shape Autolith sends. OPENROUTER_API_KEY takes precedence over Autolith’s private OpenRouter key. Authenticate with autolith auth openrouter or (auth "openrouter"). AUTOLITH_OPENROUTER_PROVIDER_ENDPOINT and AUTOLITH_OPENROUTER_MODELS_ENDPOINT override the chat and discovery endpoints independently.

Set :openrouter-provider-routing to a JSON map of wire model names to OpenRouter provider preference objects. Omit the local openrouter/ prefix from model keys. Use * for models without their own entry. For example:

(setf (config :openrouter-provider-routing)
      "{\"deepseek/deepseek-v4.1-flash\":{\"order\":[\"baseten\",\"together\"],\"allow_fallbacks\":true}}")

You can also supply this JSON through AUTOLITH_OPENROUTER_PROVIDER_ROUTING. Supply an empty string to use OpenRouter’s normal routing. Provider preference fields are sent as supplied, including order, allow_fallbacks, only, ignore, sort, and max_price.

Commands

(define-application-command my-version-command
    (:name              "/my-version"
     :argument          "[LABEL]"
     :description       "show a labeled local integration version"
     :tip               "shows the version supplied by init.lisp."
     :busy-behavior     :inspect
     :terminal-behavior :shared
     :callable          t)
    (application &optional (label "integration"))
  (application-present application (format nil "~A 3" label))
  :continue)
  • Missing interactive arguments open the restart debugger. supply-arguments takes one Lisp form of replacement arguments. Excess slash arguments are rejected before dispatch. Programmatic calls keep ordinary Common Lisp arity rules.
  • Busy behavior is :inspect, :execute, :apply, :hold, or :cancel. During active work, :execute runs immediately, :apply applies its change at the turn’s next safe provider boundary, and :hold waits for the idle queue. Argument-free :inspect and :apply invocations only display state, so they run immediately. An unknown command reports its error immediately instead of being scheduled.
  • Terminal behavior is :shared, :exclusive, or :exclusive-without-arguments. It declares which invocations open a modal picker so active work keeps them off the immediate reader path. A modal picker requires exclusive terminal input when open. The responsive reader continues animating until then.
  • :static-options replaces :argument for one finite positional parameter. It derives the help hint and both slash and parenthesized completions.

Live definitions

Use self.redefine to install loaded Lisp definitions, new or replacing, including functions and methods outside the Autolith package. self.define names the same tool. Qualify the target symbol in the source, or supply the package argument to select its reader package. Use qualified symbols with self.set for foreign global bindings.

Installation, discard and replay temporarily unlock the reader and target packages, then restore their locks. New definition records include the target’s owning package separately from the reader package. Replay skips stale definitions whose ownership moved to another library. Reapply an old skipped definition to record an intentional replacement under its current owner.

Tracked source stays authoritative over private replay. Each published definition records the tracked source it shadowed, or that it introduced a new name. Startup replay skips a definition whose tracked definition changed, was removed, or appeared since publication, lists every skip in one startup notice grouped by reason, and leaves it out of the next self.commit. Commits published before this record replay their tracked overrides only while the image runs the source revision the lineage was published against. Reapply a skipped definition against the current source to restore it, or use (fix-skipped-definitions) to hand every skipped definition to the model with its persisted source, the tracked source it was written against, and the current tracked source. The model rebuilds each override on the current source with self.redefine, drops any the tracked source already covers, and publishes the result with self.commit. During an active turn, the command waits in the idle queue.

Refinement

Use self.refine to record trajectory evidence, choose the smallest useful target or none, and exercise existing memory, context, skill, role, RLM or mutation operations. Every transition names the proposal ID and its observed revision. Evaluation records actual task observations separately from owner compile/replay checks. Inspect proposals with (refine) and supply a local assessment before promotion:

(refine "(:assess \"PROPOSAL-ID\" 3 :passed \"Evidence from a separate task\")")

Promotion proceeds from session to project to global scope, with renewed evaluation at each scope and an owner-state guard against intervening changes. Recover an interrupted operation explicitly using recorded evidence; replay never repeats its effects automatically.

Executable Skills

Use skill.load with an exact catalog name to select prose instructions for the logical turn. To publish executable behavior, put a bounded data-only EXECUTABLE.sexp beside SKILL.md or SKILL.sexp. Declare the Skill and ASDF system versions, entrypoint, input/output contracts, capabilities and exact host tool names. An executable manifest has this shape:

(:skill-executable :format-version 1 :skill-version "1.0.0"
 :system "my-workflow" :system-version "1.0.0"
 :entrypoint ("MY-WORKFLOW" "RUN")
 :self-test ("MY-WORKFLOW" "SELF-TEST")
 :verify ("MY-WORKFLOW" "VERIFY")
 :capabilities ("workspace-read") :tools ("resource.read")
 :input (:type :object) :output (:type :object))

The invocation function accepts input &key context; self-test and verification functions accept &key context. Input and output use the native portable JSON representation, such as (:object ("status" "ready")). Install the declared ASDF system in the worker environment, or supply its authorized asd pathname. Call skill.invoke with name and contract-valid input:

{"name":"my-workflow","input":{},
 "asd":".autolith/skills/my-workflow/my-workflow.asd","async":true}

Use skill.verify with name to run the self-test; set entrypoint to verify for the verification function. Each operation requires ordinary explicit full-access code authorization and runs in a fresh disposable worker. Capabilities are declarations for admission; only declared exact host tools allowed by the originating agent policy enter the callback bridge. Source and sidecar identity and the complete manifest are rechecked before loading executable code; declared ASDF versions are checked during authorized system resolution.

Invocation output is contract-validated and retained with its executable identity as native execution evidence. Result publication is bounded to 1 MiB and read as one data-only form after stopping the worker. Use async: true for managed jobs; completion policy applies as described below. Inspect full results with job.get or deliberately await them with job.wait. Mission accounting and ordinary execution supervision apply to workflows and their callbacks.

Worker tool calls

Supply host-tools, an array of exact tool names, to lisp.eval or lisp.scratchpad-run for an explicitly authorized workflow request. Inside that request, call:

(autolith:worker-tool-call "resource.read" "{\"uri\":\"workspace:src/main.lisp\"}")
(autolith:worker-tool-context)

Read :success-p, :content, :code and :details from the returned result. The host dispatch retains the originating conversation, callbacks, capability policy, mission context and job lineage. Requests and results are bounded and correlated in the conversation audit. Sequential callbacks are supported; choose a different REPL for nested worker operations. Omit host-tools for ordinary worker execution.

For a Lisp-controlled workflow, branch on returned native results and make the next authorized call from the same evaluation. For example, pass these arguments to lisp.eval:

{"host-tools":["resource.read"],
 "forms":["(let ((result (autolith:worker-tool-call \"resource.read\" \"{\\\"uri\\\":\\\"workspace:README.org\\\"}\"))) (if (getf result :success-p) (getf result :content) (getf result :code)))"]}

Host calls pass through ordinary registry authorization with the captured mission admission, including its absence. An interrupted callback is recorded as unknown-outcome in the audit. Inspect effects before repeating a mutating call.

Structural code edits

Load the optional autolith/structural system and configure an ast-grep executable before creating the tool registry:

(asdf:load-system "autolith/structural")
(setf autolith:*structural-program* "/path/to/ast-grep")

Use structural.query with an observed workspace URI and revision. Use structural.rewrite to retain a complete preview, inspect the proposal, then publish with structural.apply. Executable and file access require ordinary command authorization. Proposals are conversation-owned, bounded and single-use; publication uses the same stale-revision checks as resource edits.

For an existing registry, call structural-register-tools explicitly with :program. Run the optional checks with CLASTED_AST_GREP set to the backend path and (asdf:test-system "autolith/structural/tests").

Debug adapter sessions

Load autolith/debug before creating a registry. Configure an adapter in user initialization, or pass an explicit adapter to debug.launch / debug.attach:

(asdf:load-system "autolith/debug")
(setf autolith:*debug-adapter-program* "/path/to/debug-adapter"
      autolith:*debug-adapter-arguments* nil)

For an existing registry, call debug-register-tools with :program and literal :arguments. Loading the system starts no adapter. Adapter execution, launch, attach and expression evaluation require exact full-access command approval. Authorize source and program paths through ordinary path permissions.

Use the returned session ID for debug.breakpoints, debug.continue, debug.pause, debug.step, debug.threads, debug.stack, debug.scopes, debug.variables and debug.evaluate. Inspect bounded queued events with debug.events, using count and wait-for as needed. Session IDs belong to the originating conversation. Use debug.cancel to interrupt an operation, and debug.terminate to close the session and reap its adapter. Timeouts and cancellation close the owned adapter. Run the optional checks with (asdf:test-system "autolith/debug/tests").

Child roles

Roles load from these directories. First match wins:

  • .autolith/agents/
  • ${XDG_CONFIG_HOME:-~/.config}/autolith/agents/
  • <site-config-root>/agents/
  • bundled roles

A higher-precedence malformed role is reported, not silently replaced.

A role is one bounded UTF-8 *.sexp property list:

  • required: :name, :description, :instructions
  • optional: :tools, :spawns, :models, :reasoning-effort, :output, :blocking-p

Roles cannot grant self.*, task.*, or yield.*. Grant job.* when a role needs to inspect, await, or cancel asynchronous work. Children can access only their own jobs and descendant jobs, not parent or sibling jobs. This is independent of permission to spawn another child.

  • :tools may be nil, :all, or canonical tool names and namespace patterns.
  • :spawns may be nil, :all, or role names. Task depth still applies.
  • :reasoning-effort names a supported effort. Omitting it or writing :auto selects medium, so routine delegated work does not inherit the parent’s effort. Children never inherit Codex Fast mode.
  • :output is the native schema DSL. There, nil is JSON false and :null is JSON null.

Example:

(:name             "reviewer"
 :description      "Review a change for regressions."
 :instructions     "Report concrete, evidence-backed findings."
 :tools            ("resource.read" "search.*")
 :spawns           nil
 :models           ("@parent")
 :reasoning-effort :high
 :blocking-p t)

task.agents shows effective roles. task.run starts one task or an independent batch. Nonblocking work detaches by default. blocking: true waits. A role with :blocking-p t always waits. Each child must finish with one yield.submit of success, failed, or aborted. Persisted child results must contain one data-only form with valid terminal status and field types. The default reading limits are 16 MiB, 128 nesting levels, and 262,144 tree nodes.

Completion-driven turns

Set completion-policy to continue (the default) or notify on asynchronous tools and task.run. A batch inherits its outer policy unless an item overrides it. The policy also applies after the synchronous grace period hands work off.

In a primary terminal session, continue independent work while jobs run. When only job results are pending, yield the current turn. Receive a bounded result summary, truthful outcome and artifact reference in the exact owning conversation. Idle completions within a one-second window share one automatic turn through the ordinary controller. Busy turns receive results at their next safe request boundary. With notify, receive results on the next ordinary turn instead.

An active goal or mission does not urge the model on while a job that will wake it is queued, running or undelivered. Its automatic continuations wait, the goal stays active, and the transcript shows ∙ continuing with job results when the wakeup arrives. The goal’s continuations resume after that turn.

Delivery receipts survive restart and conversation compaction. Notification reconstruction reads terminal artifacts without repeating job effects. Cancellation, paused or terminal missions, spent budgets, and conversation ownership constrain automatic turns. Inspect retained results with job.get, job.continuity or (tasks). Child-owned outcomes are inspection-only outside their exact conversation; completing a child does not reopen it or redirect descendant results into the primary context. ACP sessions also own a completion controller and stream automatic turns to the connected client. Interfaces without an input controller deliver results on subsequent ordinary turns. Use job.wait when deliberate waiting is appropriate.

Task inspection and continuity

Use (tasks) for a compact nonmodal inventory of owned live and durable work, including lineage, timing, reported usage, recent activity and artifact paths:

(tasks "get JOB-ID")
(tasks "transcript JOB-ID 0 4000")
(tasks "send JOB-ID Inspect the failing check before continuing.")
(tasks "cancel JOB-ID")

Transcript arguments are a character offset and window limit, defaulting to 0 and 4000; use the returned :next-offset for another window. get, send and cancel use the ordinary owned job operations and authorization.

Use job.continuity with action list or get to classify retained work as live detached, completed, reconstructible from a checkpoint, safe to restart, abandoned or needing a decision. List pages contain whole records with :total, :next-offset and :more-p; offset defaults to 0 and limit to 20 (maximum 100). Supply a visible job or durable execution id for other actions.

The primary owner may declare allow-restart with a safety rationale and authorize: true, or declare checkpoint with a compatible saved worker image, target repl and the same explicit authority. A safety rationale is an assertion about repeating external effects. Revival reattaches live work, starts the named REPL from its saved image, or dispatches a new child from a safe retained shared-workspace task specification:

(tasks "revive JOB-ID authorize")
(tasks "abandon EXECUTION-ID authorize")

Equivalent job.continuity actions are revive and abandon with id and authorize: true. The durable recovery decision precedes dispatch and links the old execution to the new execution. Failed or interrupted revival consumes its claim and records an uncertain outcome for explicit inspection. Resuming a conversation presents this inventory for a decision rather than replaying work.

Granted peer messages

The primary owner establishes directional communication with peer.grant. Supply an immutable grant id, sender and receiver as primary-visible job IDs or primary. For example, these arguments grant one child permission to send coordination data to the primary:

{"id":"build-report","sender":"CHILD-JOB-ID","receiver":"primary","context":true}

Each participating actor uses peer.discover to inspect its current grants and exact endpoint identities. Call peer.send with a stable message id, the receiver’s endpoint ID from discovery, and text of 1 to 8192 characters. The sender is the actual executing actor. peer.receive claims incoming work and returns a delivery token; use peer.ack with id and token after handling it. peer.wait waits for incoming work or a named message acknowledgment, with timeout bounded to five seconds. peer.inspect returns scoped metadata pages or the payload and recovery token for an exact message id.

With context: true on the grant, receive coordination data at the existing safe steering boundary. Acknowledgment follows durable persistence of that input. Peer text is coordination data, not additional tool or execution authority. Children need their ordinary role permission for each peer tool; grants do not expose parent or sibling job-control operations.

Cross-session communication requires paired explicit grants on both sessions. Set remote-session and direction (outgoing or incoming); name a remote child with both remote-job and remote-execution, or omit both for its primary. Transport uses authenticated local daemon discovery. peer.revoke durably revokes a grant. Endpoints include exact execution identity, so a replacement child does not inherit its predecessor’s grant.

Grants and messages restore with the session; interrupted deliveries become uncertain. Keep the same stable message ID when transport completion is unknown. Only the primary may call peer.resolve with id, current token and action acknowledge, retry or cancel; retry requires a current grant. Final session finish closes its mailbox, while transient daemon handoff preserves delivery state.

Engineering task artifacts

Select the bundled engineering role or give a native role :output :engineering. Its version-1 contract requires schemaVersion, baseline (kind, workspace, revision), change (kind, reference), paths, checks, evidence, issues and integrationNotes. symbols is optional. Each check names the command, status (passed, failed, skipped or not-run) and summary, with optional evidence references. Use baseline kind unknown when no revision is available and change kind none when no change was produced.

Extend the common contract in a native role:

:output (:type :engineering
         :properties (("review" (:type :string)))
         :required ("review"))

Common properties cannot be overridden. The usual structured yield.submit and durable result transport validate and retain these values. Checks and evidence are child-reported observations; mission gates establish harness acceptance. A failed or aborted child follows the ordinary error-only terminal contract.

Isolated modifying tasks

Supply an isolation object in a flat task.run call or in each selected batch item. Omit it for the ordinary shared workspace. The object accepts baseline (a commit-resolving revision, default HEAD), dirtyPolicy (reject by default, or explicit ignore), and artifactKind (patch by default, or commit-range). With ignore, use only the committed baseline; staged, unstaged and untracked source edits are excluded. Git isolation is unavailable for non-Git workspaces.

Sandboxed commands in an isolated child may write its owned checkout and the repository’s Git administrative directories, including a linked worktree’s common Git directory. Integration requires authorization for the target workspace.

The child edits and tests in its detached checkout. Before it starts, persist the source repository, exact resolved baseline, isolated path and conversation-qualified owner. Stage new files before yielding. A patch includes committed and uncommitted tracked changes; a commit range requires a clean checkout and ancestor baseline. Terminal results, including aborted tasks, identify the secured engineering artifact. Extraction failures retain the checkout and report :worktree-error; a successful child without a complete artifact is reported as failed.

Use task.worktree with an explicit action:

  • inspect returns provenance, touched paths and the durable artifact pathname; extract explicitly retries extraction after repairing an incomplete checkout. Supply a visible terminal job id or its durable execution ID.
  • check verifies patch applicability without mutation. apply applies the patch to the clean target’s index and working tree; commits cherry-picks the recorded commit range. Supply id and an explicit target repository.
  • conflicts inspects unmerged paths; abort explicitly aborts an interrupted cherry-pick. Supply the target repository.
  • cleanup removes only the task’s owned checkout. Dirty cleanup requires discardDirty: true. Secured artifacts are independent of checkout removal.
  • discover lists this conversation’s active and absent owned records. Supply source to recover interrupted registry publication from Git markers. cleanup-orphan uses a discovery worktree id and explicit source, including after task-result publication failed. Unrelated worktrees are excluded.

Git lifecycle and integration commands use ordinary command authorization and execution policy. Target paths use ordinary tool-path authorization. Isolation is an accidental-overlap boundary, not hostile-code confinement. The parent chooses integration order and resolves conflicts deliberately.

Primary shell and Lisp executions and child agents appear as live rows with spinners and elapsed time. The command and agent strips share a viewport bound, and provider activity remains visible below them. shell.run accepts an optional short :description for its row label; otherwise it derives one from the command. Use POSIX shell syntax for commands on POSIX hosts and PowerShell syntax on Windows. Both Windows authorization modes use native PowerShell; MSYS/Git Bash requires a shared object namespace that AppContainer does not permit. Windows sandboxed commands execute serially while temporary filesystem ACLs are installed. Their writable temporary directory is removed after completion or cancellation. Add existing read-only tool directories through *win32-sandbox-read-roots* for tools installed outside system application directories. Windows scopes must be local directories without reparse points or hard-linked files. Unsupported scopes or an unavailable helper fail instead of running without isolation.

The default pool admits eight concurrent children. AUTOLITH_TASK_MAX_CONCURRENCY raises that up to 32. Child jobs have no deadline unless AUTOLITH_TASK_MAX_RUNTIME_MS is set positive.

Retained command output

Use shell.run for synchronous commands, detached commands and command mission gates. Each authorized invocation receives private execution-owned raw captures before launch. Results include exit, timeout and cancellation status, retained and observed byte counts, capture completeness, and stable shell-log: references. The inline preview samples the beginning and end, with most space reserved for trailing diagnostics. Set separate-output: true to capture stdout and stderr independently; the preview allocates more space to stderr.

Read a reference with resource.read using zero-based byte-offset and byte-count (default and maximum 4096 bytes). Use query for a literal UTF-8 search, optionally max-results and byte-offset. Continue with the returned next offset. Each search scans at most 1 MiB and returns at most 20 excerpts. Text rendering replaces invalid UTF-8; the retained files contain the original bytes. Access follows the owning conversation and ordinary task-descendant inspection authority.

References are included in job inspection, durable tool-result metadata and completion notices. Reopen them after restart or compaction without executing the command again. Missing, interrupted and pruned captures return diagnostics.

Defaults are 64 MiB per stream, 1 GiB of reserved capture capacity and 256 retained artifacts. Eligible completed captures become pruneable after seven days or under capacity pressure, on the next allocation. Active captures and undelivered completion evidence are protected. Adjust *shell-log-capture-byte-limit*, *shell-log-retention-byte-limit*, *shell-log-retention-count-limit* and *shell-log-retention-age* in trusted configuration. Captures exceeding their limit are explicitly incomplete. Session export collects task metadata independently of local log storage.

Non-interactive jobs

run-job executes one caller-supplied child role without an interactive terminal. It uses normal project, user, and bundled task-agent discovery, provider and MCP configuration, role tool and spawn policy, task traces, usage accounting, and native structured-output contracts.

autolith --permissions auto run-job \
  --input /path/to/job.sexp \
  --output /path/to/result.sexp

The input is exactly one bounded data-only S-expression:

(:autolith-job
 :version 1
 :id "job-identifier"
 :role "project-role"
 :prompt "Perform the assigned work."
 :input (:caller-owned "opaque data")
 :output-contract (:type :object
                   :properties (("answer" (:type :string)))
                   :required ("answer")
                   :additional-properties nil)
 :timeout-seconds 900)

The supplied output contract applies only to that run and does not alter the role definition. The terminal artifact is atomically installed at --output as (:autolith-job-result :version 1 ...). Structured objects and arrays use :OBJECT and :ARRAY tags, so false, null, and empty arrays remain distinct. Failures use stable categories and statuses :failed, :cancelled, or :timed-out. Successful jobs use :succeeded and include the contracted result. Trace and usage fields are included when available.

Headless execution never opens an approval picker. auto classifies shell commands and denies decisions that would require approval. sandbox permits only sandboxed commands. full permits full command execution. External tool calls whose configured policy requires approval are denied unless full access was selected. The role allowlist remains the child capability boundary.

Live S-expression events

Add --events sexp to observe a run on stdout. Capture diagnostics separately on stderr and read the authoritative terminal result from --output:

autolith --permissions auto run-job \
  --input /path/to/job.sexp --output /path/to/result.sexp \
  --events sexp >events.sexp 2>diagnostics.log

Read consecutive top-level data forms, rather than splitting physical lines. Strings may contain newlines. Each version-one record has this shape:

(:autolith-event :version 1
 :run-id "a-generated-run-uuid" :seq 1 :time "2026-10-08T09:00:00Z"
 :kind :run-started :data nil)

The event kinds are :run-started, :job-started, :tool-started, :progress, :tool-finished, :job-finished, :usage, :warning, :progress-omitted, and :run-finished. Ignore unknown versioned kinds. Sequence numbers increase strictly within one run. Job and execution identifiers are opaque execution UUIDs; provider call identifiers are pseudonymous within that run. Tool names come from the registered tool catalog. Observe only the requested root and its owned descendants, excluding other roots in the same conversation. Event data contains allowlisted statuses and numeric counters, not prompts, tool arguments, tool output, or provider payloads. Interactive input is EOF, and diagnostic/query streams go to stderr in the initiating thread and its workers.

Use run-job-event-read-stream to read one validated record at a time, returning NIL at EOF. It uses the bounded sexp-config data grammar with *read-eval* NIL. Only approved keywords, strings, NIL/T, bounded nonnegative integers, and finite proper lists are accepted. The maximum record size is 16 KiB of UTF-8; character streams supplied to the library must already use UTF-8. Binary streams receive UTF-8 octets.

Root :job-finished and final :run-finished records follow atomic result publication. Published terminal job events include a :result-uri resource reference; descendant references identify execution-qualified artifacts. The final data includes :status and :published-p; successful publication also includes :output-path. An output write failure reports :failed, :published-p nil, and :category :result-publication-failed, with a nonzero exit status. Inspect the result artifact for contracted data and full failure details.

Delivery is best effort through one bounded queue and a single writer. Progress may be omitted under backpressure, with :progress-omitted reporting its count. Lifecycle overflow is reported with :warning and :code :queue-full; final delivery has a reserved slot. A slow or disconnected consumer may miss the terminal record; execution and result publication continue. There is no replay or reconnect cursor. For a new invocation, expect a new run UUID and sequence beginning at one.

From Lisp, call run-job-run with :events :sexp and :event-output STREAM. Without the events option, use the existing artifact-only interface.

Generations and recovery

(checkpoint) requires a clean Git worktree and a passing ./script/check. On POSIX it saves a retained image without stopping the active session. On Windows it saves the exact live heap, restarts through the stable launcher, and resumes the active conversation after the saved image passes its probe.

  • (generations) lists retained images.
  • (rollback ID) selects a compatible image and exits through recovery.

Inspect or boot retained generations from the CLI:

autolith --recovery --list
autolith --recovery --generation GENERATION-ID

Recovery starts the separately built pristine core, not the damaged active core. If that image is missing or invalid, it falls back to source.

Automatic recovery boots a private clean checkout of current committed source with --pristine. It does not load retained generations or private mutation replay. An intentional (rollback ID) still boots the selected generation. Explicit generation selection may use an older revision.

To start a rescue session directly, use:

autolith resume --pristine --permissions full

--pristine forces tracked source instead of a saved active core and skips the selected private image commit, mutation journal reconstruction, and executable user initialization for this process. It implies --immutable so ordinary self-mutation tools cannot replace the selected private commit. It does not erase any mutation or conversation data. Ordinary startup remains unchanged.

A fatal active failure publishes a private crash capsule and restores the terminal. Recovery restores the conversation without duplicating scrollback. With a valid capsule, it queues one read-only diagnosis turn before ordinary input.

That turn can inspect:

  • bounded crash context
  • workspace and tracked source
  • indexed source
  • active state

It has no shell, MCP, hosted provider, image, write, mutation, checkpoint, or rollback tools. It asks before any repair. A manually started recovery image does not queue diagnosis.

Saved cores require their recorded SBCL version, OS build, and architecture. Source remains authoritative. To reconstruct without a core, check out the manifest revision, run ./script/bootstrap, load autolith, then load the manifest’s ordered reconstruct.lisp in package AUTOLITH:

(asdf:load-asd (truename "autolith.asd"))
(asdf:load-system :autolith)
(load #P"/path/from/generation/manifest/reconstruct.lisp")