5 Reference
Complete, neutral description of every module, binding, parameter, and struct exposed by Stone. Organized by module. Every binding’s documentation is co-located with its module, so require-ing a module is the way to discover the vocabulary it adds.
5.1 stone/edge
| (require stone/edge) | package: Stone |
Composition primitives. Every binding in this module produces or consumes a ashlar-meta. See Edge Primitives for the conceptual model.
struct
(struct ashlar-meta ( fn produces-all queries children lens name schema validate-walk middleware rebuilder) #:transparent) fn : procedure? produces-all : (listof symbol?) queries : (listof symbol?) children : (listof ashlar-meta?) lens : (or/c procedure? #f) name : symbol? schema : (or/c hash? #f) validate-walk : (or/c procedure? #f) middleware : list? rebuilder : (or/c procedure? #f)
produces-all — every node type this ashlar or its subtree can produce.
queries — node types read from the DAG that no earlier sibling produces.
children — child ashlars; '() for leaves.
lens — lens attached to the ashlar (set by ashlar-match when its extractor is a lens).
name — name used in logs and validation messages.
schema — JSON schema for structured outputs.
validate-walk — walk rule used by the validator’s driver.
middleware — middleware list when the ashlar is an agent ashlar.
rebuilder — closure that can rebuild this ashlar with substituted children or middleware; used by ashlar-with-tool-stub.
procedure
(make-ashlar fn [ #:produces produces #:queries queries #:name name #:children children #:lens lens #:schema schema]) → ashlar-meta? fn : procedure? produces : (or/c symbol? #f) = #f queries : (listof symbol?) = '() name : (or/c symbol? #f) = #f children : (listof ashlar-meta?) = '() lens : (or/c procedure? #f) = #f schema : (or/c hash? #f) = #f
syntax
(~> ashlar ...)
procedure
(run-pipeline ashlar dag) →
node? dag? ashlar : ashlar-meta? dag : dag?
syntax
(ashlar-loop body #:until predicate #:max max-iterations)
predicate = (any-expr) max-iterations = exact-nonnegative-integer?
To wrap a node-shaped predicate that only inspects the latest head, see on-latest.
syntax
(ashlar-match extractor maybe-name [val branch] ...)
maybe-name =
| #:name name
extractor is either a lens? or a procedure.
A lens? is applied to the latest head’s content (the value of (node-content (dag-latest-head work-dag)) at the lens’s path).
A procedure is called with the work DAG and must return a value matching one of the val keys.
The branch whose val equals the extractor’s return value runs against the work DAG. If no branch matches, a 'match-failed failure node is appended.
See also Edge Primitives for the conceptual model and on-latest for wrapping a node-shaped extractor.
procedure
(on-latest pred) → procedure?
pred : procedure?
procedure
(ashlar-map extractor body [#:name name]) → ashlar-meta?
extractor : procedure? body : ashlar-meta? name : (or/c symbol? #f) = #f
procedure
(ashlar-parallel [#:name name] lane ...) → ashlar-meta?
name : (or/c symbol? #f) = #f lane : ashlar-meta?
procedure
(ashlar-reduce ashlar [#:name name]) → ashlar-meta?
ashlar : ashlar-meta? name : (or/c symbol? #f) = #f
procedure
(make-ask-human channels #:format-fn format-fn #:name name #:produces produces [ #:queries queries]) → ashlar-meta? channels : ask-human-channel? format-fn : (dag? . -> . string?) name : symbol? produces : symbol? queries : (listof symbol?) = '()
procedure
(make-scoped-ashlar body #:walk walk [ #:children children #:produces produces #:queries queries #:name name]) → ashlar-meta? body : procedure? walk : procedure? children : (listof ashlar-meta?) = '() produces : (or/c symbol? #f) = #f queries : (listof symbol?) = '() name : (or/c symbol? #f) = #f
procedure
(ashlar-produces s) → (or/c symbol? #f)
s : any/c
procedure
(ashlar-queries s) → (listof symbol?)
s : any/c
procedure
(ashlar-produces-all s) → (listof symbol?)
s : any/c
5.2 stone/dag
| (require stone/dag) | package: Stone |
Content-addressed, append-only typed DAG. See The DAG as Pipeline State for the conceptual model.
5.2.1 Nodes
struct
(struct node (id parents content meta ts type) #:transparent) id : string? parents : (listof string?) content : any/c meta : hash? ts : real? type : symbol?
procedure
(make-typed-node parents type content [meta]) → node?
parents : (listof string?) type : symbol? content : any/c meta : hash? = (hash)
procedure
(typed-node d type content) → node?
d : dag? type : symbol? content : any/c
procedure
(make-failure-node parents kind reason [meta]) → node?
parents : (listof string?) kind : symbol? reason : string? meta : hash? = (hash)
procedure
(failure-node? v) → boolean?
v : any/c
5.2.2 DAGs
struct
(struct dag (nodes heads root parent label) #:transparent) nodes : (hash/c string? node?) heads : (listof string?) root : (or/c string? #f) parent : (or/c dag? #f) label : symbol?
procedure
(dag-append d n) → dag?
d : dag? n : node?
procedure
(dag-latest-head d) → (or/c node? #f)
d : dag?
procedure
(dag-failed? d) → boolean?
d : dag?
procedure
(dag-nearest-ancestor d type) → (or/c node? #f)
d : dag? type : symbol?
procedure
(dag-collect-until d #:type collect-type #:until sentinel-type) → (listof node?) d : dag? collect-type : symbol? sentinel-type : symbol?
Returns nodes oldest-first (the one closest to the sentinel comes first). Returns '() when no sentinel-type ancestor is reachable on the first-parent line — there is no enclosing scope, so there is nothing to collect within.
Same first-parent discipline as dag-nearest-ancestor: deterministic under loops and fan-outs, and siblings on other lanes are not visited.
procedure
(dag-query-all d type [#:scope scope]) → (listof node?)
d : dag? type : symbol? scope : (or/c 'conversation #f) = #f
procedure
(dag-select d nid) → (listof node?)
d : dag? nid : string?
procedure
(dag-select-window d nid n) → (listof node?)
d : dag? nid : string? n : exact-nonnegative-integer?
5.3 stone/messages
| (require stone/messages) | package: Stone |
A single turn in an LLM conversation. Self-contained: no link to a pipeline DAG. Used as the element type of the 'conversation list that make-agent-ashlar embeds in its result node.
struct
role : symbol? content : (or/c string? hash? (listof hash?)) tool-calls : list? call-id : (or/c string? #f) metadata : hash?
role — 'system, 'user, 'assistant, or 'tool.
content — free text, a parsed structured-output hash, or a list of multimodal content blocks.
tool-calls — list of tool-call records; assistant role only, '() otherwise.
call-id — tool-result correlation id; tool role only, #f otherwise.
metadata — provider-specific escape hatch. Convention: namespaced symbol keys (e.g. 'anthropic/cache-control, 'openai/logprobs, 'vllm/finish-reason) to prevent collision when multiple providers stash bits there.
procedure
(message-text m) → string?
m : (or/c message? #f)
5.4 stone/llm-ashlar
| (require stone/llm-ashlar) | package: Stone |
Bridge constructor that wraps an LLM caller into a multi-turn agent ashlar. See Agents and Tools for the conceptual model.
procedure
(make-agent-ashlar caller #:produces produces [ #:queries queries #:name name #:schema schema #:middleware middleware #:decide decide #:system system-fn #:user user-fn #:max-turns max-turns #:response-format response-format #:model model #:budget budget #:adversary adversary #:heal-with healer #:max-healing max-healing #:outbox outbox #:finalize finalize]) → ashlar-meta? caller : procedure? produces : symbol? queries : (listof symbol?) = '() name : (or/c symbol? #f) = #f schema : (or/c hash? #f) = #f middleware : (listof any/c) = '() decide : procedure? = continue-on-tool-use system-fn : (dag? . -> . string?) = (lambda (dag) "") user-fn : (dag? . -> . string?) = (lambda (dag) "") max-turns : exact-positive-integer? = 15 response-format : (or/c hash? #f) = #f model : (or/c string? #f) = #f budget : exact-nonnegative-integer? = 16384 adversary : (or/c ashlar-meta? #f) = #f healer : (or/c ashlar-meta? #f) = #f max-healing : exact-nonnegative-integer? = 3 outbox : any/c = #f finalize : (or/c (any/c . -> . node?) #f) = #f
caller — LLM caller built by make-openai-caller or make-anthropic-caller.
#:produces — node type of the final result.
#:queries — node types read from the outer DAG.
#:name — defaults to produces, else a fresh agent-ashlar-* id.
#:schema — JSON schema attached to metadata. Usually auto-populated from #:response-format.
#:middleware — middleware onion wrapping each turn.
#:decide — loop decision function (context? (listof recommendation?) -> recommendation?). Must be a procedure. Default is continue-on-tool-use. For single-turn behavior, pass #:max-turns 1 with empty middleware.
#:system — builds the system prompt from the DAG.
#:user — builds the first user message from the DAG.
#:max-turns — hard cap on turns inside the agent loop.
#:response-format — when truthy, the final draft must be a hash (parsed from JSON). Failing to parse produces a 'llm-parse-failed failure.
#:model — model identifier; overrides default-model for this ashlar.
#:budget — max response tokens per turn.
#:adversary — quality-gate ashlar. Runs against the pipeline DAG when decide says 'continue. Failure node = rejection; non-failure = pass.
#:heal-with — healer ashlar. When adversary rejects, healer runs and its output enters the agent’s conversation.
#:max-healing — bounds heal cycles (reject → heal → retry), not adversary invocations. The adversary always votes at least once; the budget gates retries only. #:max-healing 0 is the gate idiom: adversary votes once; pass → done, reject → fail without retry. #:max-healing N allows up to N heal cycles, so the adversary may vote up to N+1 times. On the (+ N 1)th reject, returns a 'healing-exhausted failure node.
#:outbox — optional outbox for engine→observer events. When set, emits one 'turn-event per LLM turn and forwards per-token 'token-events from the streaming caller. The Anthropic caller accepts but ignores this argument.
#:finalize — optional hook that receives the parsed final content and returns either a typed node or a failure node. The framework injects 'conversation into typed-node returns; failure-node returns pass through unchanged. Raises if the returned content already contains 'conversation. When absent, the framework builds a typed-node of type produces with the parsed content and the conversation merged in.
5.5 stone/llm-types
| (require stone/llm-types) | package: Stone |
Shared response and exception types used by the LLM caller layer. Tool authors and custom callers require this module; user pipeline code generally does not.
5.5.1 Responses
struct
(struct llm-response (text tool-calls usage))
text : string? tool-calls : (listof tool-call?) usage : hash?
5.5.2 Exceptions
struct
(struct exn:fail:repetition-tripped exn:fail (hit partial) #:extra-constructor-name make-exn:fail:repetition-tripped) hit : repetition-hit? partial : string?
struct
(struct exn:fail:repetition-exhausted exn:fail (attempts last-hit) #:extra-constructor-name make-exn:fail:repetition-exhausted) attempts : exact-nonnegative-integer? last-hit : (or/c repetition-hit? #f)
struct
(struct exn:fail:llm-http-error exn:fail (status body) #:extra-constructor-name make-exn:fail:llm-http-error) status : exact-nonnegative-integer? body : string?
struct
(struct exn:fail:llm-empty-response exn:fail () #:extra-constructor-name make-exn:fail:llm-empty-response)
5.6 stone/llm-client
| (require stone/llm-client) | package: Stone |
LLM caller factories compatible with the make-agent-ashlar caller contract. See Provider constraints for per-provider knobs.
procedure
(make-openai-caller #:url url [ #:api-key api-key #:extra-body extra-body #:repetition-watch repetition-watch #:max-retries max-retries]) → procedure? url : string? api-key : string? = "" extra-body : hash? = (hasheq) repetition-watch : (or/c (-> repetition-watcher?) #f) = #f max-retries : exact-nonnegative-integer? = 3
#:url may be the base URL or the full /v1/chat/completions URL; /v1/chat/completions is appended when missing. #:api-key empty means no Authorization header. #:extra-body is closed over at construction time and shallow-merged into every request body; the reserved keys '(model messages max_tokens tools response_format stream) raise at call time if you try to override them.
#:repetition-watch is a zero-arg factory that returns a fresh repetition-watcher; when supplied, every call constructs independent watchers for the response and thinking channels and feeds them mid-stream. If a watcher trips, the SSE stream aborts; the caller appends an assistant turn carrying the partial output and a user turn explaining the model got stuck, then retries up to #:max-retries times. After exhaustion, raises exn:fail:repetition-exhausted. When #f (default), the retry/watch path is bypassed entirely.
procedure
(make-anthropic-caller #:api-key api-key [ #:url url #:extra-body extra-body #:repetition-watch repetition-watch #:max-retries max-retries]) → procedure? api-key : string? url : string? = "https://api.anthropic.com/v1/messages" extra-body : hash? = (hasheq) repetition-watch : (or/c (-> repetition-watcher?) #f) = #f max-retries : exact-nonnegative-integer? = 3
#:repetition-watch and #:max-retries are accepted for API symmetry with make-openai-caller but currently have no effect — they will be honored when Anthropic streaming lands.
procedure
(call-llm #:url url #:model model [ #:system system] #:messages messages [ #:max-tokens max-tokens #:api-key api-key #:tools tools #:response-format response-format #:extra-body extra-body]) → hash? url : string? model : string? system : string? = "" messages : list? max-tokens : exact-nonnegative-integer? = 4096 api-key : string? = "mock-key" tools : list? = '() response-format : (or/c hash? #f) = #f extra-body : hash? = (hasheq)
procedure
(call-llm-openai #:url url #:model model [ #:system system] #:messages messages [ #:max-tokens max-tokens #:api-key api-key #:tools tools #:response-format response-format #:extra-body extra-body #:outbox outbox #:response-watcher response-watcher #:thinking-watcher thinking-watcher]) → hash? url : string? model : string? system : string? = "" messages : list? max-tokens : exact-nonnegative-integer? = 4096 api-key : string? = "" tools : list? = '() response-format : (or/c hash? #f) = #f extra-body : hash? = (hasheq) outbox : any/c = #f response-watcher : (or/c repetition-watcher? #f) = #f thinking-watcher : (or/c repetition-watcher? #f) = #f
#:response-watcher / #:thinking-watcher are optional repetition-watchers; if either trips during the SSE stream, the call raises exn:fail:repetition-tripped and closes the response. make-openai-caller’s retry layer constructs and supplies fresh watchers per attempt; supply them here directly only if you’re bypassing that layer.
procedure
(extract-text response) → string?
response : hash?
5.6.1 Exceptions raised by the OpenAI caller
This module raises exn:fail:repetition-tripped and exn:fail:repetition-exhausted from stone/llm-types under the conditions documented at stone/llm-types. The retry layer in make-openai-caller catches exn:fail:repetition-tripped internally; user code only observes it if that retry layer is bypassed.
5.7 stone/repetition-watch
| (require stone/repetition-watch) | package: Stone |
Streaming-time detectors for degenerate LLM output: exact n-gram loops (verbatim repetition) and rambling without progress (near-repetition). Wired into call-llm-openai via the #:response-watcher / #:thinking-watcher keywords; consumed by the caller-level retry loop in make-openai-caller via the #:repetition-watch keyword.
5.7.1 Hits
struct
(struct repetition-hit (kind detail position) #:extra-constructor-name make-repetition-hit) kind : symbol? detail : any/c position : exact-nonnegative-integer?
5.7.2 Karp–Rabin n-gram counter
Detects exact verbatim repetition. Maintains a rolling polynomial hash over the latest n bytes; trips when any window’s hash is observed threshold times (with sample-byte verification to filter hash collisions). Sticky after trip — once tripped, ngram-counter-add! is an O(1) no-op.
procedure
(make-ngram-counter [ #:n n #:threshold threshold]) → ngram-counter? n : exact-positive-integer? = 100 threshold : exact-positive-integer? = 4
procedure
(ngram-counter-add! c byte) → void?
c : ngram-counter? byte : byte?
procedure
(ngram-counter-tripped c) → (or/c #f repetition-hit?)
c : ngram-counter?
5.7.3 Compression-ratio detector
Detects rambling / near-repetition where the model produces grammatical-but-going-nowhere output. Maintains a circular byte buffer of size #:window; per tick, deflates the buffer (via file/gzip) and pushes compressed-size / window-size onto a fixed-length ratio history. Trips when the latest #:history-len ratios are all ≤ #:threshold. Sticky.
procedure
(make-compression-detector [ #:window window #:threshold threshold #:history-len history-len]) → compression-detector? window : exact-positive-integer? = 1024 threshold : real? = 0.18 history-len : exact-positive-integer? = 3
procedure
(compression-detector-add! d byte) → void?
d : compression-detector? byte : byte?
procedure
d : compression-detector?
Calls that complete in less than the emit interval will never engage the compression detector — by design, since short outputs are not a rambling failure mode. The n-gram detector remains active per-byte regardless.
procedure
(compression-detector-tripped d) → (or/c #f repetition-hit?)
d : compression-detector?
5.7.4 Combined watcher
Multiplexes both detectors behind a single API. This is what the streaming session and caller layer plug in.
procedure
(make-repetition-watcher [ #:ngram-counter ngram-counter #:compression compression #:on-trip on-trip]) → repetition-watcher?
ngram-counter : (or/c ngram-counter? #f) = (make-ngram-counter)
compression : (or/c compression-detector? #f) = (make-compression-detector) on-trip : (-> repetition-hit? any/c) = (λ (_) (void))
procedure
(repetition-watcher-add-bytes! w bs) → void?
w : repetition-watcher? bs : bytes?
procedure
w : repetition-watcher?
procedure
(repetition-watcher-tripped w) → (or/c #f repetition-hit?)
w : repetition-watcher?
5.8 stone/tools
| (require stone/tools) | package: Stone |
Middleware constructors for LLM tool calls plus the ask-human channel primitives. See Agents and Tools and Ask Human for the conceptual models.
5.8.1 Tool middleware
procedure
(make-tool name #:schema schema #:handler handler [ #:allowed-paths allowed-paths #:confirm? confirm?]) → any/c name : symbol? schema : hash? handler : (hash? . -> . any) allowed-paths : (or/c (listof string?) #f) = #f confirm? : boolean? = #f
procedure
(has-tool-call-for? ctx tool-name) → boolean?
ctx : any/c tool-name : symbol?
procedure
(extract-tool-calls response) → (listof hash?)
response : hash?
procedure
(tool-schema mw) → (or/c hash? #f)
mw : any/c
5.8.2 Built-in tool middleware
procedure
(write-file [ #:allowed-paths allowed-paths #:confirm? confirm?]) → any/c allowed-paths : (or/c (listof string?) #f) = #f confirm? : boolean? = #f
procedure
(edit-file [ #:allowed-paths allowed-paths #:confirm? confirm?]) → any/c allowed-paths : (or/c (listof string?) #f) = #f confirm? : boolean? = #f
procedure
(delete-file* [ #:allowed-paths allowed-paths #:confirm? confirm?]) → any/c allowed-paths : (or/c (listof string?) #f) = #f confirm? : boolean? = #t
procedure
(list-directory [#:allowed-paths allowed-paths]) → any/c
allowed-paths : (or/c (listof string?) #f) = #f
procedure
(file-exists* [#:allowed-paths allowed-paths]) → any/c
allowed-paths : (or/c (listof string?) #f) = #f
procedure
(run-command [ #:timeout default-timeout #:confirm? confirm?]) → any/c default-timeout : real? = 60000 confirm? : boolean? = #f
procedure
(start-command [#:timeout default-timeout]) → any/c
default-timeout : real? = 300000
procedure
(check-command) → any/c
procedure
(wait-commands) → any/c
5.8.3 Ask-human channels
struct
(struct ask-human-channel (name out in cancel) #:transparent) name : symbol? out : any/c in : any/c cancel : any/c
procedure
(make-ask-human-channel name out in cancel) → ask-human-channel?
name : symbol? out : any/c in : any/c cancel : any/c
procedure
→
any/c (listof ask-human-channel?) thunk : (-> any/c)
5.9 stone/decisions
| (require stone/decisions) | package: Stone |
Ready-made decide functions for the #:decide kwarg of make-agent-ashlar. A decide function has the signature (context? (listof recommendation?) -> recommendation?) and is called after every turn to pick 'continue, 'loop, or 'halt.
procedure
(continue-on-tool-use ctx recs) → any/c
ctx : any/c recs : (listof any/c)
procedure
(tool-directed ctx recs) → any/c
ctx : any/c recs : (listof any/c)
5.10 stone/validate
| (require stone/validate) | package: Stone |
Static checks over a composed pipeline. See Validation for the conceptual model.
procedure
(validate-pipeline pipeline) → validation-result?
pipeline : ashlar-meta?
struct
(struct validation-result (errors) #:transparent) errors : (listof validation-error?)
struct
(struct validation-error (type ashlar-name queried-type message) #:transparent) type : symbol? ashlar-name : (or/c symbol? #f) queried-type : symbol? message : string?
procedure
(validation-ok? r) → boolean?
r : validation-result?
procedure
(validation-errors r) → (listof validation-error?)
r : validation-result?
procedure
(enumerate-ashlars pipeline) → (listof symbol?)
pipeline : ashlar-meta?
procedure
(enumerate-paths pipeline) → (listof (listof symbol?))
pipeline : ashlar-meta?
5.11 stone/test
| (require stone/test) | package: Stone |
Test harness for Stone pipelines. See The test harness for the conceptual model and Test ashlars that use tools for the how-to.
A tool-call record is an immutable hasheq with keys 'name (symbol), 'input (hash), 'result-text (string), and 'result-meta (hash). tool-calls and friends return lists of these records in call order.
5.11.1 Harnesses
syntax
(with-live-harness #:caller caller maybe-strict maybe-timeout body ...)
maybe-strict =
| #:strict-tools? strict? maybe-timeout =
| #:timeout seconds
syntax
(with-mock-harness #:caller caller maybe-strict body ...)
(with-mock-harness #:responses responses maybe-strict body ...)
maybe-strict =
| #:strict-tools? strict?
procedure
(ashlar-with-tool-stub s tool-name stub-handler) → ashlar-meta? s : ashlar-meta? tool-name : symbol? stub-handler : (hash? . -> . any)
5.11.2 Assertions
procedure
(tool-calls) → (listof hash?)
procedure
(tool-calls-by-name name) → (listof hash?)
name : symbol?
procedure
name : symbol?
syntax
(check-tool-called? name)
(check-tool-called? name msg)
syntax
(check-tool-not-called? name)
(check-tool-not-called? name msg)
syntax
(check-tool-call-count name n)
(check-tool-call-count name n msg)
5.11.3 Stub helpers
procedure
(stub-answer s) → (hash? . -> . any)
s : string?
5.11.4 Response builders
procedure
(llm-tool-call tool-name [ #:id id #:question question #:input input]) → any/c tool-name : (or/c symbol? string?) id : string? = (fresh-call-id) question : (or/c string? #f) = #f input : (or/c hash? #f) = #f
5.11.5 Parameters
parameter
(harness-current-caller) → (or/c procedure? #f)
(harness-current-caller c) → void? c : (or/c procedure? #f)
= #f
5.12 stone/logging
| (require stone/logging) | package: Stone |
Structured logging on a dedicated Racket logger. See Observability for the design.
value
syntax
(log-stone-debug arg ...)
syntax
(log-stone-info arg ...)
syntax
(log-stone-warning arg ...)
syntax
(log-stone-error arg ...)
syntax
(log-stone-fatal arg ...)
procedure
(stone-event level event data) → void?
level : (or/c 'debug 'info 'warning 'error 'fatal) event : symbol? data : hash?
parameter
(current-trace-id) → (or/c string? #f)
(current-trace-id id) → void? id : (or/c string? #f)
= #f
parameter
(current-span-id) → (or/c string? #f)
(current-span-id id) → void? id : (or/c string? #f)
= #f
parameter
(current-parent-span-id) → (or/c string? #f)
(current-parent-span-id id) → void? id : (or/c string? #f)
= #f
procedure
(generate-id [prefix]) → string?
prefix : string? = ""
5.13 stone/trace
| (require stone/trace) | package: Stone |
Public API for reading and analyzing Stone’s trace.jsonl files. A trace is produced by attaching a logger to stone-logger (see Trace a run for emission); this module covers the consumption side.
The raco stone trace subcommands (tally, lifecycle, payload) are thin wrappers over the data API below — if you want to build a custom inspection tool, require this module and work with the primitives directly.
5.13.1 Loading
procedure
(load-trace path) → (listof hash?)
path : path-string?
5.13.2 Event accessors
Each accessor takes one event hash (as returned by load-trace) and returns a normalized field. Use these rather than hash-refing directly so callers stay robust against future trace-shape changes.
procedure
(event-data e) → hash?
e : hash?
procedure
(event-type e) → string?
e : hash?
procedure
(event-timestamp e) → real?
e : hash?
procedure
(event-ashlar-name e) → string?
e : hash?
procedure
(event-turn-number e) → (or/c exact-nonnegative-integer? #f)
e : hash?
5.13.3 Aggregators
procedure
(tally-events events)
→ (listof (cons/c string? exact-nonnegative-integer?)) events : (listof hash?)
procedure
(lifecycle-events events) → (listof hash?)
events : (listof hash?)
procedure
(find-payloads events [ #:ashlar ashlar #:turn turn]) → (listof hash?) events : (listof hash?) ashlar : (or/c string? #f) = #f turn : (or/c exact-nonnegative-integer? #f) = #f
Note that "api-call-payload" events are only emitted at the 'debug log level; calls captured at the default 'info level have headers ("api-call") but not full message contents.
5.13.4 Formatting
procedure
(format-lifecycle-line e) → string?
e : hash?
5.13.5 CLI entry points
These are the implementations of the raco stone trace subcommands; they’re provided so other tools (test harnesses, ad-hoc scripts) can invoke them programmatically with the same behavior as the CLI.
procedure
(stone-trace-cli) → any
procedure
path : path-string?
procedure
(run-lifecycle path) → any
path : path-string?
procedure
(run-payload path [ #:ashlar ashlar #:turn turn #:last? last?]) → any path : path-string? ashlar : (or/c string? #f) = #f turn : (or/c exact-nonnegative-integer? #f) = #f last? : boolean? = #f
By default picks the first matching payload; pass #:last? #t for the most recent. Equivalent to raco stone trace payload <path> with –ashlar, –turn, and –last flags.
5.14 stone/lens
| (require stone/lens) | package: Stone |
Lightweight path accessor used by ashlar-match extractors and validator lens checks. Re-exported from stone for convenience.
5.15 stone
| (require stone) | package: Stone |
Umbrella module. Re-exports every public binding from stone/edge, stone/dag, stone/decisions, and stone/lens, plus the runtime types documented here (default-model, make-json-schema, and the middleware / context / recommendation support for custom multi-turn behavior).
For code meant to be compact, (require stone) pulls the core vocabulary. Reach for the per-module requires (stone/llm-ashlar, stone/llm-client, stone/tools, stone/test, stone/validate, stone/logging) for the modules the umbrella doesn’t cover.
5.15.1 Parameters
parameter
(default-model) → (or/c string? #f)
(default-model m) → void? m : (or/c string? #f)
= #f
5.15.2 Schema builder
procedure
(make-json-schema name properties required) → hash?
name : string? properties : hash? required : (listof (or/c symbol? string?))
(hasheq 'type "json_schema" 'json_schema (hasheq 'name name 'strict #t 'schema (hasheq 'type "object" 'properties properties 'required required 'additionalProperties #f)))
5.15.3 Middleware onion types
These are the types make-agent-ashlar uses internally. Most users never construct them directly — reach for them when building a custom decide function or a middleware that needs to inspect context state.
struct
(struct context ( messages system tools response-format model budget meta history cursor node-draft) #:transparent) messages : (listof hash?) system : string? tools : (listof hash?) response-format : (or/c hash? #f) model : (or/c string? #f) budget : exact-nonnegative-integer? meta : hash? history : (or/c any/c #f) cursor : (or/c string? #f) node-draft : (or/c hash? #f)
struct
(struct middleware (name guard handler) #:transparent) name : symbol? guard : (context? . -> . boolean?) handler : (context? (context? . -> . context?) . -> . context?)
struct
(struct recommendation (type source reason) #:transparent) type : (or/c 'continue 'halt 'loop) source : any/c reason : any/c
procedure
(make-context [ #:messages messages #:system system #:tools tools #:response-format response-format #:model model #:budget budget #:meta meta #:history history #:cursor cursor #:node-draft node-draft]) → context? messages : list? = '() system : string? = "" tools : list? = '() response-format : (or/c hash? #f) = #f model : (or/c string? #f) = (default-model) budget : exact-nonnegative-integer? = 4096 meta : hash? = (hash) history : any/c = #f cursor : (or/c string? #f) = #f node-draft : (or/c hash? #f) = #f
procedure
(context-set ctx [ #:messages messages #:system system #:tools tools #:response-format response-format #:model model #:budget budget #:meta meta #:history history #:cursor cursor #:node-draft node-draft]) → context? ctx : context? messages : any/c = #f system : any/c = #f tools : any/c = #f response-format : any/c = #f model : any/c = #f budget : any/c = #f meta : any/c = #f history : any/c = #f cursor : any/c = #f node-draft : any/c = (void)
procedure
(make-middleware name guard handler) → middleware?
name : symbol? guard : (context? . -> . boolean?) handler : (context? (context? . -> . context?) . -> . context?)
5.16 Command-line interface
Stone’s command-line surface: the raco stone TUI launcher and the validate, trace, and install-skill subcommands.
5.16.1 raco stone
Synopsis: raco stone [flags...]
Loads a Stone configuration (if present), merges any command-line flag overrides, and launches the Stone TUI with the resulting agent ashlar and its collected ask-human channels.
Flag |
| Argument |
| Description |
–url |
| url |
| LLM API endpoint URL. Overrides url in config. |
–model |
| model |
| Model identifier. Overrides model in config and sets default-model. |
Both flags are #:once-each. No positional arguments are accepted.
5.16.1.1 Configuration
On startup the CLI looks for .stone/settings.rkt (walking up from the current directory to $HOME). If found, it’s loaded with dynamic-require and expected to provide a thunk named build-agent. The loader invokes the thunk inside call-with-collected-ask-human-channels. The thunk’s return value is a ashlar-meta? — the topology to run. Any ask-human channels built during construction flow to the TUI automatically.
#lang racket |
(require stone/llm-ashlar stone/llm-client) |
(provide build-agent) |
(define caller (make-openai-caller #:url "http://localhost:8000")) |
(define (build-agent) |
(make-agent-ashlar caller |
#:produces 'response |
#:system (lambda (dag) "You are helpful.") |
#:user (lambda (dag) ""))) |
5.16.1.2 Exit codes
Code |
| Meaning |
0 |
| Normal TUI exit, or fall-through when no agent is configured. |
The CLI does not call exit itself; exit status is whatever the TUI or the Racket runtime returns on termination.
5.16.2 raco stone validate
Synopsis: raco stone validate <pipeline-file>
Loads a Racket file, extracts the binding named pipeline, and runs validate-pipeline on it. Prints errors and warnings; sets exit code from the result.
5.16.2.1 Pipeline file requirements
The file is loaded via dynamic-require. It must:
Be a valid Racket module that racket can dynamic-require.
provide a binding named exactly pipeline.
Bind pipeline to a ashlar-meta? value.
If the file doesn’t export pipeline, the CLI prints Error: <file> does not export ’pipeline and exits 1.
5.16.2.2 Validation categories
Category |
| Error types |
| Affects exit code |
Hard errors |
| missing-producer, invalid-lens, fanout-not-reduced |
| yes — exit 1 |
Warnings |
| maybe-unavailable |
| no — exit 0 |
5.16.2.3 Output
Pipeline is valid. |
When hard errors are present, a header and one line per error, formatted as [<type>] <ashlar-name>: <message>:
Errors (2): |
[missing-producer] classify: classify queries 'ticket but no upstream Stone produces it |
[invalid-lens] dispatch: lens path '(category) not found in upstream schema properties |
When warnings are present, an analogous Warnings (N): block prints. Both blocks may appear in the same run; the hard-error block (if any) prints first.
5.16.2.4 Exit codes
Code |
| Meaning |
0 |
| Pipeline valid, or only maybe-unavailable warnings present. |
1 |
| Missing pipeline-file argument, file doesn't export pipeline, hard errors reported, or subcommand other than validate supplied. |
5.16.3 raco stone trace
Synopsis: raco stone trace <subcommand> <path> [opts...]
Inspect a trace.jsonl file produced by a Stone run with stone-logger attached. Three subcommands cover the default investigation flow:
Subcommand |
| Description |
tally <path> |
| Print total event count and per-event-type counts. First-pass overview of what happened during the run. |
lifecycle <path> |
| Print the filtered story of the run, one event per line: ashlar start/end, tool dispatch, api-call/response, streaming progress, and failure events. Excludes noise like middleware-run. |
payload <path> [opts] |
| Dump a single api-call-payload event: ashlar, turn, model, system prompt, and one line per message with role + content preview + tool-call count. |
5.16.3.1 payload options
Flag |
| Argument |
| Description |
–ashlar |
| name |
| Filter to payloads from the named ashlar. |
–turn |
| n |
| Filter to the named LLM turn. |
–last |
|
| Pick the most recent matching payload (default is the first). |
"api-call-payload" events are only emitted at the 'debug log level; if payload returns No api-call-payload found, re-run with STONE_LOG_LEVEL=debug in the environment to capture them.
The underlying data API is documented at stone/trace — if you want to script trace analysis beyond what these subcommands provide, require stone/trace and use the primitives directly.
5.16.3.2 Exit codes
Code |
| Meaning |
0 |
| Subcommand ran successfully. |
1 |
| Missing path, missing required option value, unknown subcommand, or unrecognized argument. |
5.16.4 raco stone install-skill
Synopsis: raco stone install-skill [–force] [–target-dir DIR]
Install the designing-ashlars skill bundle into ~/.claude/skills/ by default. The skill ships with Stone and contains the guided interview for scoping an ashlar plus a scribblings snapshot for the design agent to read.
Flag |
| Description |
–force |
| Overwrite an existing installation. Without this, the command refuses to clobber an existing skill directory. |
–target-dir DIR |
| Install under DIR instead of HOME/.claude/skills/. The skill ends up at DIR/designing-ashlars/. |
The command is idempotent in spirit: re-running it without –force after a successful install exits non-zero with an already installed message rather than silently doing nothing. With –force it wipes and re-ships the bundle, picking up any updates to the scribblings snapshot.
5.16.4.1 Exit codes
Code |
| Meaning |
0 |
| Install (or forced re-install) succeeded. |
1 |
| Skill already installed and @tt{--force} not supplied, or filesystem error during install. |