Browse modules

prelude

v0.1.0 · compiled with katari 0.1.0

prelude

17 declarations

add#

primitive agent
primitive agent add[T extends number](left: T, right: T) -> T

Add two numbers; preserves integer when both operands are integers.

Generics

Ttypeextends
number

Parameters

left
T
right
T

Returns

T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

subtract#

primitive agent
primitive agent subtract[T extends number](left: T, right: T) -> T

Subtract two numbers; preserves integer when both operands are integers.

Generics

Ttypeextends
number

Parameters

left
T
right
T

Returns

T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

multiply#

primitive agent
primitive agent multiply[T extends number](left: T, right: T) -> T

Multiply two numbers; preserves integer when both operands are integers.

Generics

Ttypeextends
number

Parameters

left
T
right
T

Returns

T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

modulo#

primitive agent
primitive agent modulo[T extends number](left: T, right: T) -> T

Floor-modulo two numbers; preserves integer when both operands are integers. A zero divisor panics (guard with a comparison first).

Generics

Ttypeextends
number

Parameters

left

The dividend.

T
right

The divisor.

T

Returns

T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

divide#

primitive agent
primitive agent divide(left: number, right: number) -> number

Divide two numbers; always yields a number. A zero divisor panics (guard with a comparison first) — division never yields Infinity / NaN.

Parameters

left

The dividend.

number
right

The divisor.

number

Returns

number
Wire view (JSON Schema)

negate#

primitive agent
primitive agent negate[T extends number](value: T) -> T

Negate a number; preserves integer.

Generics

Ttypeextends
number

Parameters

value
T

Returns

T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

equal#

primitive agent
primitive agent equal[T](left: T, right: T) -> boolean

Whether two values are equal.

Generics

Ttype

Parameters

left
T
right
T

Returns

boolean

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

not_equal#

primitive agent
primitive agent not_equal[T](left: T, right: T) -> boolean

Whether two values are not equal.

Generics

Ttype

Parameters

left
T
right
T

Returns

boolean

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

less_than#

primitive agent
primitive agent less_than[T extends number](left: T, right: T) -> boolean

Whether the left number is less than the right.

Generics

Ttypeextends
number

Parameters

left
T
right
T

Returns

boolean

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

less_or_equal#

primitive agent
primitive agent less_or_equal[T extends number](left: T, right: T) -> boolean

Whether the left number is less than or equal to the right.

Generics

Ttypeextends
number

Parameters

left
T
right
T

Returns

boolean

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

greater_than#

primitive agent
primitive agent greater_than[T extends number](left: T, right: T) -> boolean

Whether the left number is greater than the right.

Generics

Ttypeextends
number

Parameters

left
T
right
T

Returns

boolean

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

greater_or_equal#

primitive agent
primitive agent greater_or_equal[T extends number](left: T, right: T) -> boolean

Whether the left number is greater than or equal to the right.

Generics

Ttypeextends
number

Parameters

left
T
right
T

Returns

boolean

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

and#

primitive agent
primitive agent and(left: boolean, right: boolean) -> boolean

Logical conjunction.

Parameters

left
boolean
right
boolean

Returns

boolean
Wire view (JSON Schema)

or#

primitive agent
primitive agent or(left: boolean, right: boolean) -> boolean

Logical disjunction.

Parameters

left
boolean
right
boolean

Returns

boolean
Wire view (JSON Schema)

not#

primitive agent
primitive agent not(value: boolean) -> boolean

Logical negation.

Parameters

value
boolean

Returns

boolean
Wire view (JSON Schema)

concat#

primitive agent
primitive agent concat(left: string, right: string) -> string

Concatenate two strings.

Parameters

left
string
right
string

Returns

string
Wire view (JSON Schema)

throw#

request
request throw[T](error: T) -> never

Signal a typed error the caller may recover from. The payload is a domain-specific error data (e.g. json.parse_error); a handler at that payload type catches it — use handler { request throw(error: my_error) -> never { break fallback } } — and an unhandled throw fails the run with the payload. -> never means a throw cannot be resumed: a handler recovers by breaking out of the handle (or rethrows).

Generics

Ttype

Parameters

error

The error payload; a handler naming this payload type catches it.

T

Returns

never

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

prelude.array

11 declarations

get#

primitive agent
primitive agent get[T](target: array[T], index: integer) -> T | null

The element at index (0-based), or null when the index is out of bounds.

Generics

Ttype

Parameters

target
array
T
index
integer

Returns

one of
T
null

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

length#

primitive agent
primitive agent length(target: array[unknown]) -> integer

The number of elements.

Parameters

target
array
unknown

Returns

integer
Wire view (JSON Schema)

append#

primitive agent
primitive agent append[T](target: array[T], value: T) -> array[T]

A copy of the array with value appended.

Generics

Ttype

Parameters

target
array
T
value
T

Returns

array
T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

concat#

primitive agent
primitive agent concat[T](left: array[T], right: array[T]) -> array[T]

The left array followed by the right.

Generics

Ttype

Parameters

left
array
T
right
array
T

Returns

array
T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

slice#

primitive agent
primitive agent slice[T](target: array[T], start: integer, end: integer) -> array[T]

The elements from start (inclusive) to end (exclusive), 0-based, clamped to the bounds.

Generics

Ttype

Parameters

target
array
T
start
integer
end
integer

Returns

array
T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

contains#

primitive agent
primitive agent contains[T](target: array[T], value: T) -> boolean

Whether value occurs among the elements (structural equality, like ==).

Generics

Ttype

Parameters

target
array
T
value
T

Returns

boolean

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

index_of#

primitive agent
primitive agent index_of[T](target: array[T], value: T) -> integer | null

The index of the first element equal to value (structural equality), or null when none is.

Generics

Ttype

Parameters

target
array
T
value
T

Returns

one of
integer
null

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

flatten#

primitive agent
primitive agent flatten[T](target: array[array[T]]) -> array[T]

The arrays' elements concatenated in order — one level of nesting removed. With for's mapping this is also the filter idiom: map each element to [x] or [], then flatten.

Generics

Ttype

Parameters

target
array
array
T

Returns

array
T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

reverse#

primitive agent
primitive agent reverse[T](target: array[T]) -> array[T]

The elements in reverse order.

Generics

Ttype

Parameters

target
array
T

Returns

array
T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

range#

primitive agent
primitive agent range(start: integer, end: integer) -> array[integer]

The integers from start (inclusive) to end (exclusive), in order — for's counted-loop source. Empty when end <= start.

Parameters

start
integer
end
integer

Returns

array
integer
Wire view (JSON Schema)

empty#

primitive agent
primitive agent empty() -> array[never]

The empty array.

Returns

array
never
Wire view (JSON Schema)

prelude.env

3 declarations

missing_secret#

data
data missing_secret(key: string, message: string)

No secret env entry is set under key. Thrown by get_secret — catch it to fall back in-program (use handler { request throw(error: env.missing_secret) -> never { ... } }).

Parameters

key

The key that had no secret entry.

string
message
string
Wire view (JSON Schema)

get_secret#

primitive agent
primitive agent get_secret(key: string) -> string of private with prelude.throw[missing_secret]

Read a secret env entry by key, returning its value as a private string (the value is tainted secret and cannot flow to a user-facing boundary). A missing key throws missing_secret (carrying the key), so a program can fall back for optional config; uncaught, the throw fails the run.

Parameters

key

The secret entry's key in the project's env store.

string

Returns

of private
string

Effects

prelude.throw
missing_secret
Wire view (JSON Schema)

get_all#

primitive agent
primitive agent get_all() -> record[string]

Read every non-secret env entry as a record of bare (public) strings, keyed by the env key.

Returns

record
string
Wire view (JSON Schema)

prelude.files

7 declarations

gone#

data
data gone(message: string)

The file a handle names is gone — it was freed, or the id never named a blob in this project (a model hallucinating a $katari_ref). One error covers both: a slim handle cannot tell which, and a program that catches this reacts the same way (re-fetch, skip). Thrown by every reader below; catch it to recover.

Parameters

message
string
Wire view (JSON Schema)

malformed_base64#

data
data malformed_base64(message: string)

The content passed to from_base64 is not valid base64 (bad alphabet, length, or padding). Thrown by from_base64 — catch it to fall back when a model or upstream API hands back a corrupt base64 payload.

Parameters

message
string
Wire view (JSON Schema)

read_base64#

primitive agent
primitive agent read_base64(value: file) -> string with prelude.throw[gone]

The file's bytes, base64-encoded — the inline shape multimodal model APIs take. The whole content materialises as one string, so mind the size (a large file makes a large request body). Throws gone if the handle no longer names a live blob (freed, or a made-up id).

Parameters

value
file

Returns

string

Effects

prelude.throw
gone
Wire view (JSON Schema)

content_type#

primitive agent
primitive agent content_type(value: file) -> string with prelude.throw[gone]

The file's recorded MIME type (e.g. "image/png"), read from the project's file catalog; "" when none was recorded. Throws gone if the handle no longer names a live blob.

Parameters

value
file

Returns

string

Effects

prelude.throw
gone
Wire view (JSON Schema)

size#

primitive agent
primitive agent size(value: file) -> integer with prelude.throw[gone]

The file's size in bytes, read from the project's file catalog (no content download). Throws gone if the handle no longer names a live blob.

Parameters

value
file

Returns

integer

Effects

prelude.throw
gone
Wire view (JSON Schema)

from_base64#

primitive agent
primitive agent from_base64(content: string, content_type: string) -> file with prelude.throw[malformed_base64]

Make a file from a base64 string — the engine's blob producer, for lifting a model's / API's embedded base64 (a gemini inline_data image) OFF the value plane and INTO a real blob handle, so the heavy bytes travel runtime-side like any other file. The new file is owned by the current run and reclaimed with it (or explicitly, via free). Two calls with the SAME bytes are DIFFERENT files (a file is a resource, not a literal). Throws malformed_base64 if content is not valid base64.

Parameters

content

The file's bytes, base64-encoded.

string
content_type

The MIME type to record (e.g. "image/png"); "" to record none.

string

Returns

file

Effects

prelude.throw
malformed_base64
Wire view (JSON Schema)

free#

primitive agent
primitive agent free(value: file) -> null

Release a file the current run owns, freeing its bytes now rather than at run teardown — for a large, short-lived intermediate (a decoded image already handed to a model). Idempotent and silent: freeing an already-gone file, or one the run does NOT own (a user-uploaded file, whose lifetime is the file API's), is a no-op — so a retried block that frees the same handle twice behaves identically each attempt. A later read of a freed handle throws gone.

Parameters

value
file

Returns

null
Wire view (JSON Schema)

prelude.http

13 declarations

fetch_error#

data
data fetch_error(message: string)

An HTTP request never completed: DNS failure, refused connection, timeout, or a mid-flight runtime restart. Thrown by fetch. (A response that did arrive is never an error — branch on its status.)

Parameters

message
string
Wire view (JSON Schema)

text#

data
data text(content: string of private)

A text body: the content string, sent verbatim (Content-Type is the caller's to set). The content may be a secret (an OAuth refresh_token in a form-encoded body).

Parameters

content
of private
string
Wire view (JSON Schema)

binary#

data
data binary(content: file)

A raw-bytes body: the file content's bytes, sent as-is — the shape an upload API (an S3 PUT) takes. The Content-Type defaults to the file's recorded content type; set a Content-Type header to override it.

Parameters

content
file
Wire view (JSON Schema)

multipart#

data
data multipart(parts: array[multipart_part])

A multipart/form-data body (RFC 7578): the parts, each a named form field — text or file. The transport generates the boundary and sets the Content-Type, so do not set a Content-Type header for it.

Parameters

parts
array
multipart_part
Wire view (JSON Schema)

json#

data
data json(value: unknown of private)

A JSON body from a value value tree (records / arrays / scalars / files, mixed): serialised to a JSON document where every file leaf — and only a file leaf — becomes the base64 of its bytes, in place. This is the REST convention (a GitHub content upload, an AI provider's inline image), NOT an HTTP standard. To transmit a file's HANDLE instead of its bytes (almost never what an API wants), stringify it yourself first (e.g. embed json.stringify(value = a_file) as a string). Everything but a file goes on the wire as the tree you built. The tree may carry secrets (its type is private-capable), revealed only to the destination.

Parameters

value
of private
unknown
Wire view (JSON Schema)

body#

type
type body = text | binary | multipart | json

Definition

one of
text
binary
multipart
json

multipart_text#

data
data multipart_text(name: string, content: string of private)

A text form field: the field named name with the text content (which may be a secret).

Parameters

name
string
content
of private
string
Wire view (JSON Schema)

multipart_file#

data
data multipart_file(name: string, filename: string, content: file)

A file form field: the field named name carrying the file content, advertised with filename. Its per-part Content-Type is the file's recorded content type.

Parameters

name
string
filename
string
content
file
Wire view (JSON Schema)

multipart_part#

type
type multipart_part = multipart_text | multipart_file

Definition

one of
multipart_text
multipart_file

fetch#

external agentreactor: http
external agent fetch(url: string, method: string, headers: record[string of private], body: body) -> {status: integer, headers: record[string], body: string} with prelude.throw[fetch_error] from "http"

Perform an HTTP request to url with method (e.g. "GET" / "POST"), the given headers and body. The body is one of http.text / http.binary / http.multipart / http.json (a file in any of them is read from the blob store at the send boundary, never materialised earlier). Each header value AND a private body surface (a text's content, a json's tree) may be a secret, submitted to url's server, while url and method stay public. Returns the response status code, its headers (names lowercased; repeated headers joined with ", ") and its body text. A request that never completes throws fetch_error (catchable with use handler { request throw(error: http.fetch_error) -> never { ... } }).

Parameters

url

The absolute URL to request; public by type (a URL leaks into logs and proxies, so a secret here is a type error).

string
method

The HTTP method, e.g. "GET" / "POST" (case-insensitive). For GET / HEAD the body is not sent.

string
headers

Request headers by name; each value may be a secret, revealed only to url's server.

record
of private
string
body

The request body: http.text / http.binary / http.multipart / http.json; dropped for GET / HEAD.

body

Returns

object
status
integer
headers
record
string
body
string

Effects

prelude.throw
fetch_error
Wire view (JSON Schema)

fetch_file#

external agentreactor: http
external agent fetch_file(url: string, method: string, headers: record[string of private], body: body) -> {status: integer, headers: record[string], file: file} with prelude.throw[fetch_error] from "http"

Perform an HTTP request to url with method, the given headers and body — EXACTLY fetch's request — but capture the RESPONSE body as a downloaded file rather than as text. The bytes land in the blob store and the reply carries only the slim file HANDLE; the raw bytes (and their base64) never touch the value plane, the durable call record, or the trace. This is the receive-side twin of a binary REQUEST body, and it exists for the same reason: to keep a payload's bytes OFF the value plane. A plain fetch of a megabyte image would read it into the response body STRING, dragging that text (and its base64, once re-encoded) through every value, event, and trace it flows into — whereas fetch_file streams it straight to a blob and hands back a handle. Reach for it to download a file by URL: an image, a PDF, a CSV export, a release artifact. It is NOT for a base64 field buried inside a JSON reply (a data-URI in an API's JSON document) — fetch_file only ever captures the WHOLE response body; decode an embedded base64 string with files.from_base64 after an ordinary fetch. The downloaded file's recorded content type is the response's Content-Type (falling back to application/octet-stream when the server sent none). Returns the response status code (branch on it exactly like fetch — a non-2xx reply's body is still captured to a file, so a caller inspects status before trusting the download), its headers (names lowercased; repeated headers joined with ", "), and the downloaded file. A request that never completes throws fetch_error, exactly like fetch.

Parameters

url

The absolute URL to request; public by type (a URL leaks into logs and proxies, so a secret here is a type error).

string
method

The HTTP method, e.g. "GET" / "POST" (case-insensitive). For GET / HEAD the body is not sent.

string
headers

Request headers by name; each value may be a secret, revealed only to url's server.

record
of private
string
body

The request body: http.text / http.binary / http.multipart / http.json; dropped for GET / HEAD.

body

Returns

object
status
integer
headers
record
string
file
file

Effects

prelude.throw
fetch_error
Wire view (JSON Schema)

status_error#

data
data status_error(status: integer, body: string)

A JSON API responded with a non-2xx status. Thrown by post_json (the raw fetch never treats an arrived response as an error — this wrapper takes the opinionated JSON-API stance).

Parameters

status

The response's non-2xx status code.

integer
body

The response body text.

string
Wire view (JSON Schema)

post_json#

agent
agent post_json(url: string, body: unknown of private, headers: record[string of private]) -> unknown with io | prelude.throw[status_error | fetch_error | parse_error]

POST body — a JSON value tree — to url as application/json with the given headers, and return the reply PARSED into its document value. This is the canonical one-call shape of a JSON API integration: build the body as an ordinary record / array / scalar tree, post it, and read the reply as a value — no manual json.stringify / json.parse around the call. body AND each header value may be a secret (an auth token — an "Authorization" of "Bearer <key>" for OpenAI, an "x-goog-api-key" for Gemini — or a refresh_token in the body of an OAuth exchange): both are submitted only to url's server, which stays public. A file placed anywhere in body rides as base64 (the http.json slot contract). The Content-Type defaults to application/json unless a header overrides it. A non-2xx status throws status_error (its body text attached); a transport failure throws fetch_error; a 2xx body that is not JSON throws json.parse_error (a JSON API that broke its promise) — an empty 2xx body is null. To send already-serialized text, or to keep the raw reply text, call fetch directly.

Parameters

url

The absolute URL to POST to; public by type.

string
body

The JSON body as a value tree (records / arrays / scalars / files, mixed); may carry secrets.

of private
unknown
headers

Extra headers by name (the caller wins over the application/json default on a shared name); values may be secrets.

record
of private
string

Returns

unknown

Effects

one of
io
prelude.throw
one of
status_error
fetch_error
parse_error
Wire view (JSON Schema)

prelude.json

11 declarations

parse_error#

data
data parse_error(message: string)

A JSON document failed to parse: the text is not JSON. Thrown by parse / parse_as.

Parameters

message
string
Wire view (JSON Schema)

validation_error#

data
data validation_error(message: string)

A value does not conform to the requested type's schema. Thrown by validate / parse_as; message names the offending path.

Parameters

message
string
Wire view (JSON Schema)

parse#

primitive agent
primitive agent parse(text: string) -> unknown with prelude.throw[parse_error]

Parse JSON text into the value it denotes — a record / array / string / integer / number / boolean / null, or a file where the text carries a $katari_ref handle. Traverse a document with shape filters (case record(r)) and the readers below, or pin its shape with parse_as[T]. A number without a fractional part parses as integer. Malformed text throws parse_error.

Parameters

text

The text of one complete JSON document.

string

Returns

unknown

Effects

prelude.throw
parse_error
Wire view (JSON Schema)

validate#

primitive agent
primitive agent validate[T](value: unknown) -> T with prelude.throw[validation_error]

Check value against T's schema and return it unchanged, or throw validation_error naming the offending path. A pure check — it never rewrites the value. Instantiate T explicitly (it appears only in the result, so it cannot be inferred).

Generics

Ttype

Parameters

value

The value to check against T.

unknown

Returns

T

Effects

prelude.throw
validation_error

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

parse_as#

agent
agent parse_as[T](text: string) -> T with prelude.throw[parse_error | validation_error]

Parse JSON text directly as a T — parse then validate[T], the typed text boundary. Instantiate T explicitly (it appears only in the result, so it cannot be inferred). Malformed text throws parse_error; a value that does not conform to T's schema throws validation_error naming the offending path.

Generics

Ttype

Parameters

text

The text of one complete JSON document.

string

Returns

T

Effects

prelude.throw
one of
parse_error
validation_error

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

stringify#

primitive agent
primitive agent stringify[T](value: T) -> string

Render ANY value as its (compact) JSON text — TOTAL and CANONICAL (one value, one text). A document value (record / array / string / integer / number / boolean / null) round-trips verbatim, so stringify(parse(s)) is s up to whitespace; a file becomes its $katari_ref handle object, a data value nests its fields under $katari_value ({ $katari_constructor: name, $katari_value: { ...fields } }), and an agent / closure / tool its handle object. stringify is the READ CHANNEL for showing an AI a handle / data value (an error render, a handle embed, a tool result rendered back to a model). A secret operand taints the whole result (the monotonic information-flow rule).

Generics

Ttype

Parameters

value
T

Returns

string

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

entries#

agent
agent entries(target: unknown) -> record[unknown]

The record target holds (its entries), or the empty record if target is not a record.

Parameters

target
unknown

Returns

record
unknown
Wire view (JSON Schema)

items#

agent
agent items(target: unknown) -> array[unknown]

The array target holds (its elements), or the empty array if target is not an array.

Parameters

target
unknown

Returns

array
unknown
Wire view (JSON Schema)

field#

agent
agent field(target: unknown, key: string) -> unknown

The child at object key key (null if target is not a record or has no such key).

Parameters

target
unknown
key
string

Returns

unknown
Wire view (JSON Schema)

element#

agent
agent element(target: unknown, index: integer) -> unknown

The element at array index index (null if target is not an array or index is out of range).

Parameters

target
unknown
index
integer

Returns

unknown
Wire view (JSON Schema)

text#

agent
agent text(target: unknown) -> string

The string target holds (the empty string if it is not a string).

Parameters

target
unknown

Returns

string
Wire view (JSON Schema)

prelude.math

6 declarations

abs#

primitive agent
primitive agent abs[T extends number](value: T) -> T

The absolute value; preserves integer.

Generics

Ttypeextends
number

Parameters

value
T

Returns

T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

min#

primitive agent
primitive agent min[T extends number](left: T, right: T) -> T

The smaller of the two; preserves integer when both operands are integers.

Generics

Ttypeextends
number

Parameters

left
T
right
T

Returns

T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

max#

primitive agent
primitive agent max[T extends number](left: T, right: T) -> T

The larger of the two; preserves integer when both operands are integers.

Generics

Ttypeextends
number

Parameters

left
T
right
T

Returns

T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

floor#

primitive agent
primitive agent floor(value: number) -> integer

The largest integer not above the value. A non-finite value cannot occur (division never yields one).

Parameters

value
number

Returns

integer
Wire view (JSON Schema)

ceil#

primitive agent
primitive agent ceil(value: number) -> integer

The smallest integer not below the value.

Parameters

value
number

Returns

integer
Wire view (JSON Schema)

round#

primitive agent
primitive agent round(value: number) -> integer

The nearest integer (half away from zero, the grade-school rule — not banker's rounding).

Parameters

value
number

Returns

integer
Wire view (JSON Schema)

prelude.mcp

11 declarations

server_error#

data
data server_error(message: string)

The MCP server rejected a listing or a tool call, or the transport failed (the server-reported or transport message). Also raised as the scope backstop: a tool called after its provide returned reports its closed scope this way. Catch it to control retry — a retried call reconnects.

Parameters

message
string
Wire view (JSON Schema)

auth_error#

data
data auth_error(message: string)

The server rejected the headers key material. Retrying with the same material does not help: a human must fix the key the program supplies. Never raised on the oauth path — an unusable oauth credential pauses the run on an authorization escalation instead of failing it. Distinct from server_error so a program can catch the two differently.

Parameters

message
string
Wire view (JSON Schema)

headers#

data
data headers(values: record[string of private])

Header-based access: values ride on every request to the server (each value may be a secret, such as a bearer key). Anonymous access is headers(values = record.empty()).

Parameters

values

The headers sent on every request, by name; a value may be a secret.

record
of private
string
Wire view (JSON Schema)

oauth#

data
data oauth(name: string)

A named OAuth credential, stored server-side. The runtime injects and refreshes its tokens itself; the name is not a secret. While the name has no usable credential, any use pauses the run on a prelude.oauth.authorize escalation — answering it (the runtime-hosted browser flow) establishes the credential and resumes the run.

Parameters

name

The stored credential's registered name; not a secret.

string
Wire view (JSON Schema)

auth#

type
type auth = headers | oauth

Definition

one of
headers
oauth

scope#

effect
effect scope

The built-in scope capability for the dynamic provide path: a nullary phantom marker, carried in a tool's effect row, never performable, never handleable. provide mints it for its continuation's row and discharges it from its own, so a tool tagged scope cannot outlive the provide that introduced it. A generated katari mcp pull binding declares its own module-local marker instead; distinct nullary markers never merge and never widen, so there is no covariance to reason about.

tool#

type
type tool[effect Scope] = agent never -> unknown with io | Scope | prelude.throw[server_error | auth_error]

Generics

Scopeeffect

Definition

agent
input
never
output
unknown
effects
one of
io
Scope
prelude.throw
one of
server_error
auth_error

toolbox#

type
type toolbox[effect Scope] = record[tool[Scope]]

Generics

Scopeeffect

Definition

record
tool
Scope

provide#

external agentreactor: mcp
external agent provide[effect Scope, R, effect E](url: string, auth: auth, continuation: agent(value: toolbox[Scope]) -> R with E | Scope) -> R with E from "mcp"

Plug an MCP server's tools into a program for the extent of continuation, as a scoped provider — the use mcp.provide(url = ..., auth = ...) form. The runtime lists the server (a missing or dead oauth credential pauses on an authorization escalation and retries once answered; rejected headers material throws auth_error; a rejected listing throws server_error), hands continuation a toolbox[Scope] of one runtime-minted agent per tool (each carrying the server signature — for reflection.get_metadata and call-site validation — and the descriptor it was listed with), and settles with the continuation's result. Scope rides the continuation's row (that is what a tool call raises) and is DISCHARGED from provide's own row, so a tool cannot escape the block: the scope closes when continuation returns (or the run is cancelled), and a stray later call is rejected as a typed server_error. Scope is the marker the tools are gated by — the built-in mcp.scope for the dynamic path, or a generated binding's module-local marker; pass it explicitly as the first [...] argument (mcp.provide[mcp.scope](...)), and the toolbox binder's annotation must agree. Discharging Scope is provide's alone (only it opens the connection), so a live tool context cannot be forged. url is a plain routing value: the runtime connects and caches under the {url, auth} descriptor. DECLARE ONE MARKER PER LOGICAL CONNECTION. When you call provide directly you choose Scope yourself: share ONE marker across two connections and their scopes MERGE at the type level (the two servers' tools become mutually substitutable in a row that carries the marker) — the same widening the old dynamic-URL toolbox[string] had, no worse. It is only a TYPE distinction: routing and the live-provide backstop are always enforced by each tool value's own {url, auth} descriptor, so a call never reaches the wrong server — at worst a tool whose provide has closed fails with a typed server_error. A katari mcp pull binding needs no such discipline: its module namespace mints a distinct marker (github.connection) per connection automatically, so pulled servers never share a scope.

Generics

Scopeeffect
Rtype
Eeffect

Parameters

url

The MCP server's endpoint URL (streamable HTTP, with SSE fallback).

string
auth

How the runtime authenticates with the server: headers(...) or oauth(...).

auth
continuation

The scope body (bound by use): receives the toolbox, and the tools are callable exactly while it runs.

agent
input
object
value
toolbox
Scope
output
R
effects
one of
E
Scope

Returns

R

Effects

E

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

call#

external agentreactor: mcp
external agent call[effect Scope, T](url: string, auth: auth, tool: string, arguments: unknown) -> T with Scope | prelude.throw[server_error | auth_error | json.validation_error] from "mcp"

Call one tool on an MCP server directly — the static counterpart of provide, with no listing and no minted agent value. The seam the binding modules katari mcp pull generates are built on: tool is the server-declared tool name, arguments its arguments object as a plain value tree (records / arrays / scalars, and a file becomes base64 in place — the same slot contract as http.json). The result is read as a value and checked against T inside the runtime: a $katari_ref handle object becomes a REAL file value, records / arrays / scalars land as themselves, a plain-text reply reads only into a string-shaped T, and a mismatch throws validation_error. Instantiate T with unknown to keep the reply as a raw document value (the codegen's choice when no outputSchema maps); a $katari_ref in it is still a real file — the wire-to-value conversion is unconditional, never schema-directed. T appears only in the result, so it is never inferable — instantiate it explicitly; the Scope marker is a phantom the row never infers, so a caller writes both to [...], mcp.call[Scope, T](url = ..., ...) — a generated binding passes its own module-local marker. Nothing validates arguments before the server does (unlike a minted tool) — a rejected call throws server_error. Scope-gated the same way as a minted tool (Scope on its row): a generated binding calls this only inside a provide scope over the same marker and descriptor, and the runtime rejects a call with no live scope for that descriptor as a typed server_error.

Generics

Scopeeffect
Ttype

Parameters

url

The MCP server's endpoint URL (the same descriptor the surrounding provide scope was opened with).

string
auth

How the runtime authenticates with the server: headers(...) or oauth(...).

auth
tool

The server-declared tool name.

string
arguments

The tool's arguments object, as a plain value tree; only the server validates it.

unknown

Returns

T

Effects

one of
Scope
prelude.throw
one of
server_error
auth_error
json.validation_error

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

serve#

external agentreactor: mcp
external agent serve[R, effect E](tools: toolbox[scope], subscriber: agent(url: string) -> R with E) -> R with E from "mcp"

Publish tools as a live MCP server for the extent of subscriber. The runtime mints a fresh, unguessable capability URL and serves the record's agents as MCP tools there — each record key is the published tool name, each agent's declared signature the tool's advertised schema. The URL is the key: possession grants access, nobody without it can call in. subscriber receives the URL, hands it to whoever should connect (an AI session, a teammate's MCP client), and stays alive while calls should be served; when it returns the URL deactivates and its result becomes serve's result. Cancelling the run cancels subscriber and deactivates the URL the same way. The subscriber's effects flow to the caller's handlers unchanged; the served agents are self-contained (their effects are handled, or are at most io and this module's throws), so any agent fits once its own effects are handled. A served tool that throws or panics with no handler of its own cancels the whole endpoint (its failure proxies up and fails the run unless caught above subscriber) — wrap a tool's body in a handler for per-request resilience.

Generics

Rtype
Eeffect

Parameters

tools

The agents to publish, keyed by the tool name each is published under.

toolbox
scope
subscriber

The endpoint's owner: receives the capability URL, and the server serves exactly while it runs.

agent
input
object
url
string
output
R
effects
E

Returns

R

Effects

E

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

prelude.oauth

2 declarations

server_error#

data
data server_error(message: string)

The OAuth token could not be resolved for a TRANSIENT reason — a network error, or the token endpoint failing (5xx) while refreshing. Thrown by token. Catch it to control retry — a retried call re-resolves. (A credential that needs a human to (re)authorize is never a throw: the run pauses on an authorization escalation instead.)

Parameters

message
string
Wire view (JSON Schema)

token#

external agentreactor: oauth
external agent token(name: string) -> string of private with io | prelude.throw[server_error] from "oauth"

Resolve a usable OAuth bearer token for the stored credential name, as a private string (the token is tainted secret and may flow only to a submission sink — an http Authorization header — never to a user-facing boundary). The runtime holds the token material and refreshes it on demand, so no token ever enters the program. When name has no usable token (never authorized, or refresh-dead), the run PAUSES on a prelude.oauth.authorize escalation and resumes once the credential is established (the runtime-hosted browser flow, or a proactive login) — it does not fail. A transient resolution failure throws server_error (catchable with use handler { request throw(error: oauth.server_error) -> never { ... } }); uncaught, it fails the run.

Parameters

name

The registered name of a stored credential (e.g. "github"); the name itself is not a secret.

string

Returns

of private
string

Effects

one of
io
prelude.throw
server_error
Wire view (JSON Schema)

prelude.record

10 declarations

get#

primitive agent
primitive agent get[T](target: record[T], key: string) -> T | null

Read the value under key, or null when the key is absent.

Generics

Ttype

Parameters

target
record
T
key
string

Returns

one of
T
null

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

set#

primitive agent
primitive agent set[T](target: record[T], key: string, value: T) -> record[T]

A copy of the record with value under key (replacing an existing entry).

Generics

Ttype

Parameters

target
record
T
key
string
value
T

Returns

record
T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

remove#

primitive agent
primitive agent remove[T](target: record[T], key: string) -> record[T]

A copy of the record without key (unchanged when the key is absent).

Generics

Ttype

Parameters

target
record
T
key
string

Returns

record
T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

keys#

primitive agent
primitive agent keys(target: record[unknown]) -> array[string]

Every key, in sorted order.

Parameters

target
record
unknown

Returns

array
string
Wire view (JSON Schema)

has#

primitive agent
primitive agent has(target: record[unknown], key: string) -> boolean

Whether key is present.

Parameters

target
record
unknown
key
string

Returns

boolean
Wire view (JSON Schema)

size#

primitive agent
primitive agent size(target: record[unknown]) -> integer

The number of entries.

Parameters

target
record
unknown

Returns

integer
Wire view (JSON Schema)

entries#

primitive agent
primitive agent entries[T](target: record[T]) -> array[[string, T]]

Every entry as a [key, value] pair, in sorted key order — the record's for-iterable view.

Generics

Ttype

Parameters

target
record
T

Returns

array
tuple
0
string
1
T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

values#

agent
agent values[T](target: record[T]) -> array[T]

Every value, in sorted key order — the value-side counterpart of keys.

Generics

Ttype

Parameters

target
record
T

Returns

array
T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

merge#

primitive agent
primitive agent merge[T](left: record[T], right: record[T]) -> record[T]

The two records' entries in one record; on a shared key the right value wins (the override direction of layered configuration, e.g. default headers extended per call).

Generics

Ttype

Parameters

left
record
T
right
record
T

Returns

record
T

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

empty#

primitive agent
primitive agent empty() -> record[never]

The empty record.

Returns

record
never
Wire view (JSON Schema)

prelude.reflection

4 declarations

agent_metadata#

data
data agent_metadata(name: string, description: string, input: unknown, output: unknown, requests: unknown)

The public metadata of one callable: its (qualified) name, its @"..." description, and the JSON Schemas of its input, output and requests — each schema a plain document value, ready to embed in an AI tool list. A local (closure) callable has an empty name.

Parameters

name

The qualified name; empty for a local closure.

string
description

The @"..." documentation text; empty when undocumented.

string
input

The input record's JSON Schema, as a document value.

unknown
output

The output's JSON Schema, as a document value.

unknown
requests

The requests' schemas: one { name, input, output } object per request the callable may perform.

unknown
Wire view (JSON Schema)

get_metadata#

primitive agent
primitive agent get_metadata(value: agent never -> unknown with all) -> agent_metadata

Derive agent_metadata from a callable value (a top-level agent, a data constructor, a request, an external, a local closure, or a runtime-minted tool such as an MCP server's). A generic callable instantiated with [T] yields schemas specialised to the instantiation; a runtime-minted tool yields the name / description / input schema its provider declared.

Parameters

value

The callable value to inspect.

agent
input
never
output
unknown
effects
all

Returns

agent_metadata
Wire view (JSON Schema)

call_error#

data
data call_error(message: string)

A dynamic dispatch failed before the target ran: the target is not a callable, or args does not conform to its input schema (message names the offending path). Thrown by call_agent.

Parameters

message

The non-callable kind, or the offending path of the schema mismatch.

string
Wire view (JSON Schema)

call_agent#

primitive agent
primitive agent call_agent[R, effect E](target: agent never -> R with E, args: unknown) -> R with E | prelude.throw[call_error]

Invoke a callable VALUE with a runtime-built argument record — dynamic dispatch for a target an AI picked out of a tool list. args is validated against the target's input schema at the delegation boundary; a mismatch throws call_error at this call site (catch it to let the model retry). The target's effects flow through unchanged.

Generics

Rtype
Eeffect

Parameters

target

The callable value to dispatch (e.g. the tool an AI picked).

agent
input
never
output
R
effects
E
args

The target's whole argument record, keyed by its parameter names.

unknown

Returns

R

Effects

one of
E
prelude.throw
call_error

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

prelude.region

8 declarations

scope#

effect
effect scope

The built-in nursery scope: a nullary phantom marker, carried in a fiber's effect row and every operation's row, never performable, never handleable. provide mints it for its continuation and discharges it from its own row, so a fiber cannot outlive its nursery. Declare your own marker per nursery when nesting, so two regions' scopes never merge.

fiber#

type
type fiber[effect Scope, T] = agent never -> T with Scope

Generics

Scopeeffect
Ttype

Definition

agent
input
never
output
T
effects
Scope

nursery#

type
type nursery[effect Scope, effect E] = agent(gate: agent never -> unknown with Scope, bound: agent never -> unknown with E) -> {gate: agent never -> unknown with Scope, bound: agent never -> unknown with E}

Generics

Scopeeffect
Eeffect

Definition

agent
input
object
gate
agent
input
never
output
unknown
effects
Scope
bound
agent
input
never
output
unknown
effects
E
output
object
gate
agent
input
never
output
unknown
effects
Scope
bound
agent
input
never
output
unknown
effects
E

provide#

external agentreactor: region
external agent provide[effect Scope, effect E, R, effect Eouter](continuation: agent(value: nursery[Scope, E]) -> R with Eouter | Scope) -> R with Eouter from "region"

Open a nursery for the extent of continuation, as a scoped provider — the use region.provide[region.scope, E](...) form. It hands continuation a nursery[Scope, E] handle whose fibers may raise at most the effects E, and settles with the continuation's result. Scope rides the continuation's row (that is what a fork / join / cancel / watch raises) and is DISCHARGED from provide's own row, so no fiber outlives the block: the nursery closes when continuation returns (its still-running fibers are cancelled). Pin Scope and the ceiling E as the first two type arguments (region.provide[region.scope, my_effects]); R and the residual outer row are inferred from the continuation. Discharging Scope is provide's alone (only it opens the nursery), so a live join context cannot be forged. NEST UNDER DISTINCT MARKERS: share one marker across two nurseries and their scopes MERGE (fibers become mutually joinable), so declare a module-local effect per nursery when nesting.

Generics

Scopeeffect
Eeffect
Rtype
Eoutereffect

Parameters

continuation

The nursery body (bound by use): receives the handle, and its fibers run exactly while it does.

agent
input
object
value
nursery
Scope
E
output
R
effects
one of
Eouter
Scope

Returns

R

Effects

Eouter

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

fork#

external agentreactor: region
external agent fork[effect Scope, effect E, A, T](nursery: nursery[Scope, E], task: agent(input: A) -> T with E, argument: A) -> fiber[Scope, T] with Scope from "region"

Spawn task (applied to argument) as a fiber in the nursery nursery, returning immediately with a fiber[Scope, T] handle. task's effect must fit under the nursery's ceiling E (a child that raises less is fine; one that raises more is a type error), so every fiber is bounded by E — which is exactly what makes watch a total obligation. The fiber's escalations do not surface here; they well up at watch. Scope-gated (with Scope): callable only inside the provide that opened this nursery.

Generics

Scopeeffect
Eeffect
Atype
Ttype

Parameters

nursery

The nursery handle from the enclosing provide.

nursery
Scope
E
task

The child agent to run; its effect must fit under the nursery's ceiling E.

agent
input
object
input
A
output
T
effects
E
argument

The argument applied to task.

A

Returns

fiber
Scope
T

Effects

Scope

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

join#

external agentreactor: region
external agent join[effect Scope, effect E, T](nursery: nursery[Scope, E], handle: fiber[Scope, T]) -> T with Scope from "region"

Await handle and return the value it settled with. Scope is pinned by nursery, and handle's own scope must match it, so a fiber from a DIFFERENT nursery (a different scope marker) is a type error here — a fiber is joinable only in the region that spawned it. Scope-gated (with Scope): callable only inside the opening provide.

Generics

Scopeeffect
Eeffect
Ttype

Parameters

nursery

The nursery handle from the enclosing provide.

nursery
Scope
E
handle

A fiber spawned in THIS nursery (its scope marker must match).

fiber
Scope
T

Returns

T

Effects

Scope

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

watch#

external agentreactor: region
external agent watch[effect Scope, effect E](nursery: nursery[Scope, E]) -> never with E | Scope from "region"

The nursery's WHITE HOLE: re-emit the fibers' escalations into the enclosing program as the ceiling effect E. It returns never — it only ever raises, never yields — so install a handler (or any row that covers E) around it to service the fibers' requests. Because every fiber's effect fits under E and watch re-emits the FULL E, covering E here covers every request any fiber can raise. Scope-gated (with Scope): callable only inside the opening provide.

Generics

Scopeeffect
Eeffect

Parameters

nursery

The nursery handle from the enclosing provide.

nursery
Scope
E

Returns

never

Effects

one of
E
Scope

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

cancel#

external agentreactor: region
external agent cancel[effect Scope, effect E, T](nursery: nursery[Scope, E], handle: fiber[Scope, T]) -> null with Scope from "region"

Tear handle down: cancel the fiber and settle. Scope is pinned by nursery, and handle's own scope must match it, so a fiber from a different nursery is a type error. Idempotent on an already-settled fiber. Scope-gated (with Scope): callable only inside the opening provide.

Generics

Scopeeffect
Eeffect
Ttype

Parameters

nursery

The nursery handle from the enclosing provide.

nursery
Scope
E
handle

A fiber spawned in THIS nursery (its scope marker must match).

fiber
Scope
T

Returns

null

Effects

Scope

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

prelude.replay

9 declarations

interrupted#

request
request interrupted[F](failure: F) -> never

The replay signal — the ONE request a replay provider catches, and the ONLY performable request this module exports. A converter performs it to ask that the enclosing block be re-run, carrying the failure it chose to represent as failure (of the converter's own type F). -> never: performing it transfers control to the provider (which applies its delay policy and re-runs) and does not return. This is the whole seam between policy and mechanism — a provider reasons only about interrupted, never about throw or panic.

Generics

Ftype

Parameters

failure

The converter-chosen value standing for the underlying failure (re-raised as throw[F] when a bounded provider exhausts).

F

Returns

never

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

ran#

data
data ran[R](value: R)

One run of the continuation that completed with a value value (it did not signal a replay).

Generics

Rtype

Parameters

value

The completed run's result.

R

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

signalled#

data
data signalled[F](failure: F)

One run of the continuation that performed interrupted, carrying the failure it signalled.

Generics

Ftype

Parameters

failure

The failure the run's interrupted carried.

F

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

run_once#

agent
agent run_once[R, F, effect E](continuation: agent(value: null) -> R with {...E, interrupted[F]}) -> ran[R] | signalled[F] with E

Run the continuation once and reify which happened: ran with its value, or signalled with the failure it performed interrupted on. This is the single boundary where interrupted is caught and turned into a value the loop can match on in ordinary control flow (the mechanism-side analog of the old attempt, but catching only the replay signal — never throw / panic, which the converter already handled). interrupted[F] sits in the continuation's OVERWRITE row because this handler DISCHARGES it (the discord- provider shape); F is inferred from the continuation and preserved into signalled.

Generics

Rtype
Ftype
Eeffect

Parameters

continuation

The rest of the block, run once with interrupted served.

agent
input
object
value
null
output
R
effects
override
base
E
overrides
interrupted
F

Returns

one of
ran
R
signalled
F

Effects

E

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

immediate#

agent
agent immediate[R, F, effect E](continuation: agent(value: null) -> R with {...E, interrupted[F]}) -> R with E

Replay the rest of the block on every interrupted, unbounded and with NO artificial delay: it re-runs at once. The retry cadence is therefore set entirely by whatever the converter does BEFORE it signals — e.g. a converter that performs a human-in-the-loop escalation and parks before signalling gives the attended / re-auth loop, human-paced with no polling. Never re-raises and never returns on its own; a converter that rethrows instead of signalling is how a failure leaves this loop. Written use replay.immediate().

Generics

Rtype
Ftype
Eeffect

Parameters

continuation

The rest of the block (bound by use).

agent
input
object
value
null
output
R
effects
override
base
E
overrides
interrupted
F

Returns

R

Effects

E

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

forever#

agent
agent forever[R, F, effect E](initial_delay_milliseconds: number, factor: number, max_delay_milliseconds: number, continuation: agent(value: null) -> R with {...E, interrupted[F]}) -> R with E | io

Replay the rest of the block on every interrupted, unbounded, with exponential backoff capped at max_delay_milliseconds. For daemons that must stay up across transient failures (e.g. keeping a time.watch alive): it never re-raises and never returns on its own. The backoff delay lives in the loop's own delay var and grows by factor per replay up to the cap (math.min), so the durable state stays flat across any number of replays. Written use replay.forever(initial_delay_milliseconds = ..., factor = ..., max_delay_milliseconds = ...).

Generics

Rtype
Ftype
Eeffect

Parameters

initial_delay_milliseconds

The delay before the first re-run, in milliseconds.

number
factor

The per-replay delay multiplier (e.g. 2 doubles the delay each replay).

number
max_delay_milliseconds

The backoff ceiling, in milliseconds; the delay never grows past it.

number
continuation

The rest of the block (bound by use).

agent
input
object
value
null
output
R
effects
override
base
E
overrides
interrupted
F

Returns

R

Effects

one of
E
io

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

replay_after#

data
data replay_after()

A bounded-retry decision: keep going after sleeping the current delay.

Wire view (JSON Schema)

give_up#

data
data give_up()

The exhausted arm of a bounded-retry decision: no attempts remain, so the provider re-raises the failure.

Wire view (JSON Schema)

exponential#

agent
agent exponential[R, F, effect E](initial_delay_milliseconds: number, factor: number, max_attempts: number, continuation: agent(value: null) -> R with {...E, interrupted[F]}) -> R with E | io | prelude.throw[F]

Replay the rest of the block on interrupted with exponential backoff, up to max_attempts times. On failure number n (1-based) with n < max_attempts: durably sleep initial_delay_milliseconds * factor^(n-1), then re-run. When the budget is spent (the max_attempts-th interrupted): re-raise that failure as a TYPED throw[F]F is the converter's chosen failure representation, so exhaustion keeps the error typed end to end and never needs to re-panic. The attempt count and delay live in the loop's own vars (one owner). The exhausted-vs-continue decision is genuine sum dispatch: the ONE comparison of the count to the budget mints a replay_after | give_up, and the signalled arm matches it once — no code re-tests the counter. Written use replay.exponential(initial_delay_milliseconds = ..., factor = ..., max_attempts = ...).

Generics

Rtype
Ftype
Eeffect

Parameters

initial_delay_milliseconds

The delay before the first re-run, in milliseconds.

number
factor

The per-replay delay multiplier (e.g. 2 doubles the delay each replay).

number
max_attempts

The total run budget, not a retry count: the max_attempts-th interrupted re-raises instead of re-running.

number
continuation

The rest of the block (bound by use).

agent
input
object
value
null
output
R
effects
override
base
E
overrides
interrupted
F

Returns

R

Effects

one of
E
io
prelude.throw
F

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

prelude.string

15 declarations

length#

primitive agent
primitive agent length(value: string) -> integer

The number of Unicode code points.

Parameters

value
string

Returns

integer
Wire view (JSON Schema)

split#

primitive agent
primitive agent split(value: string, separator: string) -> array[string]

The substrings between occurrences of separator. An empty separator splits into single code points.

Parameters

value
string
separator
string

Returns

array
string
Wire view (JSON Schema)

join#

primitive agent
primitive agent join(parts: array[string], separator: string) -> string

The parts joined with separator between them.

Parameters

parts
array
string
separator
string

Returns

string
Wire view (JSON Schema)

slice#

primitive agent
primitive agent slice(value: string, start: integer, end: integer) -> string

The code points from start (inclusive) to end (exclusive), 0-based, clamped to the bounds.

Parameters

value
string
start
integer
end
integer

Returns

string
Wire view (JSON Schema)

contains#

primitive agent
primitive agent contains(value: string, search: string) -> boolean

Whether search occurs in the string.

Parameters

value
string
search
string

Returns

boolean
Wire view (JSON Schema)

starts_with#

primitive agent
primitive agent starts_with(value: string, search: string) -> boolean

Whether the string starts with search.

Parameters

value
string
search
string

Returns

boolean
Wire view (JSON Schema)

ends_with#

primitive agent
primitive agent ends_with(value: string, search: string) -> boolean

Whether the string ends with search.

Parameters

value
string
search
string

Returns

boolean
Wire view (JSON Schema)

index_of#

primitive agent
primitive agent index_of(value: string, search: string) -> integer | null

The code-point index of the first occurrence of search, or null when it does not occur.

Parameters

value
string
search
string

Returns

one of
integer
null
Wire view (JSON Schema)

replace#

primitive agent
primitive agent replace(value: string, search: string, replacement: string) -> string

The string with every occurrence of search replaced by replacement (literal text, not a pattern). An empty search returns the value unchanged.

Parameters

value
string
search
string
replacement
string

Returns

string
Wire view (JSON Schema)

trim#

primitive agent
primitive agent trim(value: string) -> string

The string with leading and trailing whitespace removed.

Parameters

value
string

Returns

string
Wire view (JSON Schema)

to_upper#

primitive agent
primitive agent to_upper(value: string) -> string

The string uppercased (Unicode default casing, no locale).

Parameters

value
string

Returns

string
Wire view (JSON Schema)

to_lower#

primitive agent
primitive agent to_lower(value: string) -> string

The string lowercased (Unicode default casing, no locale).

Parameters

value
string

Returns

string
Wire view (JSON Schema)

to_string#

primitive agent
primitive agent to_string(value: null | boolean | number | string) -> string

Render a scalar as its string form (an integer prints without a decimal point). For a composite value use json.stringify(value = ...) instead.

Parameters

value
one of
null
boolean
number
string

Returns

string
Wire view (JSON Schema)

to_integer#

primitive agent
primitive agent to_integer(value: string) -> integer | null

Read the string as a base-10 integer (an optional sign and digits, nothing else), or null when it is not one — including a magnitude the number model cannot hold exactly. Compose with trim for padded input; for a diagnosed parse use json.parse_as[integer] instead.

Parameters

value
string

Returns

one of
integer
null
Wire view (JSON Schema)

to_number#

primitive agent
primitive agent to_number(value: string) -> number | null

Read the string as a number (any JSON-style numeric form), or null when it is not one. Compose with trim for padded input; for a diagnosed parse use json.parse_as[number] instead.

Parameters

value
string

Returns

one of
number
null
Wire view (JSON Schema)

prelude.time

7 declarations

now#

external agentreactor: time
external agent now() -> number from "time"

The current wall-clock time, as epoch milliseconds (whole milliseconds since 1970-01-01T00:00:00Z). The instant becomes durable together with the work that first observes it, so once any downstream step has seen the value it never changes under replay or recovery. (A restart in the narrow window before that commit re-reads the clock and resolves a fresh instant — sound, because nothing durable saw the first.)

Returns

number
Wire view (JSON Schema)

sleep#

external agentreactor: time
external agent sleep(milliseconds: number) -> null from "time"

Sleep for milliseconds before resolving with null. The wake deadline (now + milliseconds) is persisted: a runtime restart re-arms the timer, and a deadline that already passed while the runtime was down resolves immediately on recovery. A milliseconds that is zero or negative resolves at once.

Parameters

milliseconds

The delay, in milliseconds; zero or negative resolves at once.

number

Returns

null
Wire view (JSON Schema)

sleep_until#

external agentreactor: time
external agent sleep_until(time: number) -> null from "time"

Sleep until the absolute epoch-millisecond instant time, then resolve with null. The same durable deadline as sleep, but pinned to an absolute wall-clock instant rather than a relative delay — an instant already in the past resolves immediately.

Parameters

time

The absolute wake instant, in epoch milliseconds; a past instant resolves at once.

number

Returns

null
Wire view (JSON Schema)

interval#

data
data interval(milliseconds: number)

A fixed interval: an occurrence every milliseconds after the watch starts (the first occurrence is one interval in, not at the start). milliseconds must be positive.

Parameters

milliseconds

The gap between occurrences, in milliseconds; a non-positive gap panics the watch.

number
Wire view (JSON Schema)

cron#

data
data cron(expression: string, timezone: string)

A cron schedule: the occurrences of the standard cron expression (the 5-field form, or the 6-field form whose leading field is seconds) read in the IANA timezone (e.g. "Asia/Tokyo", "UTC"). The timezone is required and explicit — there is no ambient default, because "every day at 09:00" means a different instant in each zone and the runtime must not guess which one.

Parameters

expression

5 fields, or 6 with a leading seconds field; a malformed expression panics the watch.

string
timezone

An IANA zone name; an unknown zone panics the watch.

string
Wire view (JSON Schema)

schedule#

type
type schedule = interval | cron

Definition

one of
interval
cron

watch#

external agentreactor: time
external agent watch[effect E](schedule: schedule, deliver_to: agent(time: number) -> null with E) -> never with E | io from "time"

Call deliver_to once per schedule occurrence, forever, passing the occurrence's scheduled epoch millisecond as time. watch never resolves on its own (-> never); it runs until the run is cancelled, at which point the in-flight delivery is cancelled and the watch tears down. deliver_to's effects E flow to the caller's handlers unchanged.

Durability. The next occurrence is persisted; a restart re-arms it. If one or more occurrences were missed while the runtime was down, watch fires EXACTLY ONCE immediately on recovery (the earliest missed occurrence's scheduled time — it does not backfill every missed one), then continues on schedule. Deliveries are serialized: the next occurrence is not armed until the current delivery settles, so a deliver_to slower than the interval simply rate-limits the ticks rather than queueing them.

No built-in retry. A deliver_to that throws or panics is NOT retried — the failure propagates and kills the watch, exactly as an uncaught failure in any callee does. Resilience is composed at the call site (a replay provider plus a converter around deliver_to), never baked into watch.

Generics

Eeffect

Parameters

schedule

When occurrences fire: an interval(...) or a cron(...) value.

schedule
deliver_to

Called once per occurrence; its time is the occurrence's scheduled epoch millisecond, not the delivery instant.

agent
input
object
time
number
output
null
effects
E

Returns

never

Effects

one of
E
io

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.

prelude.webhook

1 declarations

inbound#

external agentreactor: webhook
external agent inbound[R, effect E](callback: agent never -> unknown with E, subscriber: agent(url: string) -> R with E) -> R with E from "webhook"

Serve a fresh, dynamically generated public webhook URL for the extent of subscriber. The runtime converts every POST to the URL into a call of callback (JSON body in, JSON result out, validated against callback's input schema). subscriber receives the URL, registers it wherever deliveries should come from, and stays alive while they flow; when it returns, the URL is deactivated and its result becomes inbound's result. Both callables' effects flow to the caller's handlers unchanged.

Generics

Rtype
Eeffect

Parameters

callback

Called once per POST. A body failing its input schema is rejected 400 before it runs; its unhandled failure cancels the endpoint.

agent
input
never
output
unknown
effects
E
subscriber

The URL's owner: receives the minted public URL, and the endpoint serves exactly while it runs.

agent
input
object
url
string
output
R
effects
E

Returns

R

Effects

E

Wire view — the wire shape is fixed at the call site, where the generic parameters are instantiated.