Lambda Symbolics

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:

Common Lisp
(: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:

Variable Default Meaning
AUTOLITH_MANAGEMENT_REPL off Enable the endpoint.
AUTOLITH_MANAGEMENT_REPL_TRANSPORT unix, or tcp on Windows unix or tcp.
AUTOLITH_MANAGEMENT_REPL_UNIX_SOCKET ${XDG_STATE_HOME:-~/.local/state}/autolith/management/repl.sock Unix socket pathname.
AUTOLITH_MANAGEMENT_REPL_TCP_ADDRESS 127.0.0.1 IPv4 loopback listener address.
AUTOLITH_MANAGEMENT_REPL_TCP_PORT 4141 TCP listener port.
AUTOLITH_MANAGEMENT_REPL_TOKEN_FILE ${XDG_CONFIG_HOME:-~/.config}/autolith/management-repl.token Mode-0600 token file.
AUTOLITH_MANAGEMENT_REPL_TIMEOUT 10 Evaluation deadline in seconds.
AUTOLITH_MANAGEMENT_REPL_MAX_FRAME 1048576 Maximum wire-frame octets.
AUTOLITH_MANAGEMENT_REPL_MAX_SOURCE 262144 Maximum UTF-8 source octets.
AUTOLITH_MANAGEMENT_REPL_MAX_OUTPUT 262144 Maximum captured output and value text.
AUTOLITH_MANAGEMENT_REPL_QUEUE_CAPACITY 8 Maximum queued evaluations.
AUTOLITH_MANAGEMENT_REPL_MAX_CLIENTS 8 Maximum accepted clients, including authentication.
AUTOLITH_MANAGEMENT_REPL_AUTH_TIMEOUT 10 Absolute 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:

Common Lisp
(: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:

Common Lisp
(: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:

Common Lisp
(: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:

Common Lisp
(: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:

Common Lisp
(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:

Common Lisp
(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:

Example
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:

Common Lisp
(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:

Shell
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.

Shell
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:

Common Lisp
(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

Common Lisp
(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:

Common Lisp
(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:

Common Lisp
(: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:

JSON
{"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:

Common Lisp
(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:

JSON
{"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:

Common Lisp
(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:

Common Lisp
(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:

Common Lisp
(: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:

Common Lisp
(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:

Common Lisp
(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:

JSON
{"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:

Common Lisp
: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.