# Soma Builtins

> GENERATED by `soma docs builtins` — do not edit by hand.
> The source of truth is `compiler/src/interpreter/builtins/registry.rs`.

248 builtins. ✗ marks the nondeterministic set (random_token, random, now, now_ms, today) — calls to these
are tracked by `soma replay` as potential sources of replay divergence.
`deterministic` is membership in that replay set, not a purity claim:
think/http_*/read_*/next_id are NOT in the log: replay calls them again (think needs a key or
SOMA_LLM_MOCK, files are re-read) — pass their results in as handler arguments to replay exactly.
The `native` section is usable inside `[native]` handlers only.

## string

| Builtin | Signature | Description |
|---|---|---|
| `concat` | `concat(a, b) -> String \| concat(a: List, b: List) -> List` | Concatenate strings, or join two lists (numeric list `+` is elementwise, so this is THE list concat). |
| `pad_left` | `pad_left(s, width: Int, fill?: String) -> String` | Left-pad to `width` characters: pad_left("7", 4, "0") = "0007". Default fill is a space. |
| `pad_right` | `pad_right(s, width: Int, fill?: String) -> String` | Right-pad to `width` characters. |
| `split` | `split(s: String, delim: String) -> List<String>` | Split a string on a delimiter into a list of substrings. |
| `replace` | `replace(s: String, old: String, new: String) -> String` | Replace every occurrence of `old` with `new`. |
| `contains` | `contains(haystack: String, needle: String) -> Bool \| contains(list: List, x) -> Bool \| contains(m: Map, key) -> Bool` | Substring test; list membership (structural equality); map key membership. |
| `starts_with` | `starts_with(s: String, prefix: String) -> Bool` | True if `s` begins with `prefix`. |
| `ends_with` | `ends_with(s: String, suffix: String) -> Bool` | True if `s` ends with `suffix`. |
| `lowercase` | `lowercase(s: String) -> String` | Lowercase the string (non-strings are stringified first). |
| `uppercase` | `uppercase(s: String) -> String` | Uppercase the string (non-strings are stringified first). |
| `trim` | `trim(s: String) -> String \| trim(s: String, chars: String) -> String` | Strip leading and trailing whitespace — or any of the characters in `chars` (Go's strings.Trim(s, cutset)). |
| `sha256` | `sha256(s: String) -> String` | SHA-256 of the text, as 64 hex characters (store `sha256(salt + password)` or better an hmac_sha256 with a server secret, never the password). |
| `hmac_sha256` | `hmac_sha256(key: String, message: String) -> String` | HMAC-SHA-256 as hex: sign a session cookie or a webhook payload with a server secret. |
| `random_token` ✗ | `random_token(bytes?: Int) -> String` | Cryptographically secure random bytes from the OS, as hex (default 32 bytes = 64 characters): session tokens, API keys, salts. random() is NOT for secrets. |
| `secure_eq` | `secure_eq(a: String, b: String) -> Bool` | Constant-time equality for secrets (tokens, signatures): `==` returns early and leaks a prefix by timing. |
| `format` | `format(fmt: String, args...) -> String` | printf subset: %d %s %f %.2f %8.2f %e %.3e %3d %-8s %05d %% — %e is C-style scientific (6.022000e+23); widths, precision (rounded half away from zero on the decimal text), left-align with '-', zero-pad with '0'. |
| `fields` | `fields(s: String) -> List<String>` | Split on any run of whitespace, no empty pieces (Go's strings.Fields; split(s, " ") keeps empties). |
| `index_of` | `index_of(s: String, sub: String) -> Int  \|  index_of(xs: List, x) -> Int` | Character index of the first occurrence of `sub` in a String, or the position of the first element equal to `x` in a List; -1 if absent. |
| `substring` | `substring(s: String, start: Int, end: Int) -> String` | Character-based slice [start, end) — both indexes are clamped to 0..len(s), end exclusive. A negative end gives an empty string; use slice() for indexes relative to the end. |
| `escape_html` | `escape_html(s: String) -> String` | Escape &, <, >, double and single quotes for safe HTML embedding. |
| `str_len` | `str_len(s: String) -> Int` | Byte length of a string (cf. len(), which counts characters). |
| `str_at` | `str_at(s: String, i: Int) -> Int` | Byte value at index `i`; errors if out of range. |
| `str_eq` | `str_eq(a: String, b: String) -> Bool` | Exact string equality (fast path for [native] code). |
| `chr` | `chr(n: Int) -> String` | The character with code point n: chr(65) == "A". Raises kind range outside 0..0x10FFFF or for surrogate code points. |
| `ord` | `ord(s: String) -> Int` | Code point of the first character: ord("A") == 65. |
| `regex_count` | `regex_count(text: String, pattern: String) -> Int` | Number of non-overlapping matches (Rust regex syntax). Same in [native] (pattern must be a literal there). |
| `regex_match` | `regex_match(text: String, pattern: String) -> Int` | 1 when the pattern matches anywhere in text, else 0. |
| `regex_replace` | `regex_replace(text: String, pattern: String, replacement: String) -> String` | Replace every match; $1 refers to the first capture group. |

## types

| Builtin | Signature | Description |
|---|---|---|
| `len` | `len(x: String\|List\|Map) -> Int` | Characters of a string, elements of a list, or entries of a map. |
| `to_string` | `to_string(x) -> String` | Render any value with its display formatting. |
| `to_int` | `to_int(x) -> Int` | Convert to Int (floats truncate, strings parse, BigInt-exact); returns () on failure. |
| `to_float` | `to_float(x) -> Float` | Convert to Float; returns () if a string fails to parse. |
| `to_json` | `to_json(x) -> String` | Serialize a value as JSON (strings escaped, NaN/inf become null). |
| `from_json` | `from_json(s: String) -> Any` | Parse a JSON string into a Map/List/scalar; maps and lists pass through. Invalid JSON RAISES (kind "json") — wrap LLM output in try { from_json(s) }. |
| `type_of` | `type_of(x) -> String` | Type name: "Int" (any size), "Float", "String", "Bool", "List", "Map", "Function", "Variant", or "Unit". |
| `is_type` | `is_type(value: Map, type_name: String) -> Bool` | True if a record's `_type` field equals `type_name`. |
| `is_a` | `is_a(value: Map, type_name: String) -> Bool` | Alias of is_type. |
| `fail` | `fail(kind: String, detail?) -> never \| fail(r: TryResult) -> never` | Raise a domain error. `try { f() }` yields {value, error, kind, detail}: branch on r.kind ("not_found", "invalid_transition", "guard_failed", "invariant", a `require … else Tag` tag, …); fail(r) re-raises a caught error unchanged. |

## math

| Builtin | Signature | Description |
|---|---|---|
| `to_fixed` | `to_fixed(x: Float, digits: Int) -> String` | x with exactly `digits` decimals ("%.2f"), rounded half away from zero on the decimal text: to_fixed(1.005, 2) = "1.01". |
| `div_round` | `div_round(n: Int, d: Int) -> Int` | Exact integer division rounded to the nearest, half away from zero (BigDecimal HALF_UP): div_round(10125 * 600, 120000) == 51, div_round(-7, 2) == -4. Money in cents stays exact. |
| `floor_div` | `floor_div(a: Int, b: Int) -> Int` | Division rounded toward -∞ (Ruby/Python `//`): floor_div(-150, 100) = -2. `idiv` truncates toward zero; `/` is exact. |
| `mod` | `mod(a: Int, b: Int) -> Int` | Modulo with the DIVISOR's sign (Ruby/Python `%`): mod(-150, 100) = 50. The `%` operator keeps the dividend's sign (C/Rust): -150 % 100 = -50. |
| `divmod` | `divmod(a: Int, b: Int) -> [q, r]` | [floor_div(a, b), mod(a, b)] — q * b + r == a with 0 <= r < \|b\|. |
| `abs` | `abs(x: Int\|Float) -> Int\|Float` | Absolute value, arbitrary precision (abs(-9223372036854775808) is 9223372036854775808). |
| `round` | `round(x: Float) -> Int \| round(x: Float, digits: Int) -> Float` | Round half away from zero to the nearest integer, or keep `digits` decimals: round(2.345, 2) = 2.35. |
| `floor` | `floor(x: Float) -> Int` | Largest integer <= x. |
| `ceil` | `ceil(x: Float) -> Int` | Smallest integer >= x. |
| `sqrt` | `sqrt(x: Int\|Float) -> Float` | Square root. |
| `sin` | `sin(x: Int\|Float) -> Float` | Sine (radians). Also cos, tan, atan, atan2(y, x). |
| `cos` | `cos(x: Int\|Float) -> Float` | Cosine (radians). |
| `tan` | `tan(x: Int\|Float) -> Float` | Tangent (radians). |
| `atan` | `atan(x: Int\|Float) -> Float` | Arc tangent. |
| `atan2` | `atan2(y: Float, x: Float) -> Float` | Arc tangent of y/x, quadrant-aware. |
| `asin` | `asin(x: Float) -> Float` | Arc sine (x in [-1, 1], else kind range). |
| `acos` | `acos(x: Float) -> Float` | Arc cosine (x in [-1, 1], else kind range). |
| `pi` | `pi() -> Float` | The constant π. |
| `log` | `log(x: Int\|Float) -> Float` | Natural logarithm. |
| `ln` | `ln(x: Int\|Float) -> Float` | Alias of log (natural logarithm). |
| `exp` | `exp(x: Int\|Float) -> Float` | e raised to the power x. |
| `log10` | `log10(x: Int\|Float) -> Float` | Base-10 logarithm. |
| `pow` | `pow(base: Int\|Float, exp: Int\|Float) -> Float` | base raised to exp (always a Float; `ipow` for an exact Int power). |
| `ipow` | `ipow(base: Int, exp: Int) -> Int` | Exact Int power (exp ≥ 0; kind range past the Int size limit). `to_int(pow(3, 40))` is off by 33, `ipow(3, 40)` is exact. |
| `min` | `min(a, b) -> Int\|Float \| min(list: List) -> Int\|Float` | Smaller of two numbers, or the minimum of a list (Float if any element is). An empty list gives () (no minimum exists). |
| `max` | `max(a, b) -> Int\|Float \| max(list: List) -> Int\|Float` | Larger of two numbers, or the maximum of a list (Float if any element is). An empty list gives () (no maximum exists). |
| `sum` | `sum(list: List) -> Int\|Float` | Sum of a list of numbers (Int-exact unless any element is a Float); 0 when empty. Floats are added left to right without compensation (NumPy's pairwise / Python's fsum can differ in the last bits). |
| `product` | `product(list: List) -> Int\|Float` | Product of a list of numbers; 1 when empty. |
| `avg` | `avg(list: List) -> Int\|Float` | Mean of a list of numbers: finite operands are averaged exactly before Float rounding, avoiding intermediate overflow and cancellation. An exact mean of Ints stays an Int; () when empty. |
| `parse_int` | `parse_int(s: String, base: Int?) -> Int \| ()` | Strict integer parse: () unless the WHOLE string is an integer ("1.5", "12abc", "" → ()). parse_int("ff", 16) = 255 (base 2..36, no 0x prefix). to_int() is lenient and truncates. |
| `parse_float` | `parse_float(s: String) -> Float \| ()` | Strict float parse: () unless the whole string is a finite number. |
| `idiv` | `idiv(a: Int, b: Int) -> Int` | Integer division truncating toward zero; errors on division by zero. |
| `clamp` | `clamp(v, lo, hi) -> Int\|Float` | Constrain numeric v to [lo, hi] using exact Int/Float comparisons; return the selected operand with its type. Reject nonnumeric operands, reversed bounds and NaN bounds; a NaN value stays NaN. |
| `random` ✗ | `random() -> Float \| random(max: Int) -> Int \| random(min: Int, max: Int) -> Int` | Time-seeded PRNG: float in [0,1), or int in [0,max) / [min,max). There is no seed: for reproducible runs write your own generator (an LCG over Ints), and for secrets use random_token(). |
| `gcd` | `gcd(a: Int, b: Int) -> Int` | Greatest common divisor (Euclid, absolute values). |
| `sqrt_int` | `sqrt_int(n: Int) -> Int` | Integer square root; errors on negative input. |
| `pow_mod` | `pow_mod(base: Int, exp: Int, m: Int) -> Int` | Modular exponentiation base^exp mod m; errors if m is zero. |
| `band` | `band(a: Int, b: Int) -> Int` | Bitwise AND. |
| `bor` | `bor(a: Int, b: Int) -> Int` | Bitwise OR. |
| `bxor` | `bxor(a: Int, b: Int) -> Int` | Bitwise XOR. |
| `bnot` | `bnot(a: Int) -> Int` | Bitwise NOT. |
| `shl` | `shl(a: Int, n: Int) -> Int` | Exact left shift (a * 2^n), arbitrary precision like every Int op. For a 64-bit wrapping shift (xorshift), mask: band(shl(x, 13), M) with M = shl(1, 64) - 1 bound once (a literal beyond 64 bits is not allowed in [native]); values past 2^63 run [native] code in BigInt mode — prefer 32-bit xorshift masks for speed. |
| `shr` | `shr(a: Int, n: Int) -> Int` | Arithmetic shift right by a nonnegative Int count; arbitrarily large counts give 0 (nonnegative a) or -1 (negative a). |
| `bit_test` | `bit_test(a: Int, i: Int) -> Int` | 1 if bit i of a is set, else 0. |
| `bit_set` | `bit_set(a: Int, i: Int) -> Int` | a with bit i set. |
| `bit_clr` | `bit_clr(a: Int, i: Int) -> Int` | a with bit i cleared. |
| `bit_next` | `bit_next(a: Int, i: Int) -> Int` | Index of the lowest set bit at or above i, or -1 if none. |
| `bit_len` | `bit_len(a: Int) -> Int` | Exact number of significant bits in the magnitude, including BigInt. |
| `median` | `median(xs: List) -> Int \| Float` | Order Int/Float values exactly and preserve the middle value (mean of the two middles for even n). An Int middle stays exact; any NaN input makes the result NaN. |
| `pstdev` | `pstdev(xs: List) -> Float` | Population standard deviation (divide by n). Preserve finite and subnormal deviations even when their variance is outside Float range. |
| `stddev` | `stddev(xs: List) -> Float` | Same as pstdev (population). |
| `stdev` | `stdev(xs: List) -> Float` | SAMPLE standard deviation (divide by n - 1); needs two values. Preserve finite and subnormal deviations even when their variance is outside Float range. |
| `variance` | `variance(xs: List) -> Float` | SAMPLE variance (divide by n - 1) — statistics.variance; pvariance is the population form. |
| `pvariance` | `pvariance(xs: List) -> Float` | Population variance (divide by n) — statistics.pvariance. |

## collection

| Builtin | Signature | Description |
|---|---|---|
| `list` | `list(items...) -> List` | Build a list of the arguments; list(list(1, 2), 3) is [[1, 2], 3]. Use push(xs, x) to append. |
| `map` | `map(key, value, ...) -> Map \| list \|> map(x => expr) -> List` | Build a map from key-value pairs (even arg count), or — with a lambda — transform each list element. |
| `push` | `push(list: List, items...) -> List` | Return a new list with the items appended (the original is unchanged). |
| `nth` | `nth(list: List, i: Int) -> Any` | Element at index i, or () when out of bounds. |
| `reverse` | `reverse(list: List) -> List` | Return the list in reverse order. |
| `range` | `range(start: Int, end: Int, step?: Int) -> List<Int>` | Integers from start toward end (exclusive); bounds and step are 64-bit Ints. A negative step counts down; zero raises kind range. Direct for-loops do not materialize the list, including stepped ranges. |
| `sort` | `sort(list: List, order?: "desc") -> List` | Sort scalars ascending (or "desc"); errors on incomparable element types. |
| `flatten` | `flatten(list: List) -> List` | Flatten one level of nested lists. |
| `zip` | `zip(a: List, b: List) -> List<{left, right}>` | Pair elements positionally; stops at the shorter list. |
| `enumerate` | `enumerate(list: List) -> List<{index, value}>` | Attach a 0-based index to each element. |
| `with` | `with(m: Map, key, value, ...) -> Map \| with(list: List, i: Int, value) -> List` | Copy of the map with every key-value pair inserted, or copy of the list with element i replaced. An incomplete pair raises kind type; large Int map keys are stringified, while list indices must fit signed 64 bits. |
| `without` | `without(m: Map, keys...) -> Map` | Return a copy of the map with the given keys removed. |
| `merge` | `merge(a: Map, b: Map) -> Map` | Copy of `a` with all entries of `b` inserted (b wins on conflict). |
| `join` | `join(list: List, sep: String) -> String \| join(left: List, right: List, key) -> List` | Join list elements into a string — or, with two lists, an inner data join on `key`. |
| `slice` | `slice(xs: List\|String, start: Int, end?: Int) -> List\|String` | Sub-list / substring, end exclusive; negative indexes count from the end (slice(xs, -2) = last two). Clamped, never raises. |
| `keys` | `keys(m: Map) -> List<String>` | Keys of a map VALUE, in insertion order. (Memory slots: slot.keys().) |
| `values` | `values(m: Map) -> List` | Values of a map VALUE, in insertion order. (Memory slots: slot.values().) |
| `entries` | `entries(m: Map) -> List<{key, value}>` | Key/value records of a map VALUE: for e in entries(m) { e.key  e.value }. |

## pipeline

| Builtin | Signature | Description |
|---|---|---|
| `filter_by` | `filter_by(rows: List<Map>, field, op: ">"\|">="\|"<"\|"<="\|"=="\|"!=", value) -> List<Map>` | Keep rows whose `field` compares true against `value` (op defaults to == with 3 args). |
| `sort_by` | `sort_by(rows: List<Map>, field, order?: "desc") -> List<Map> \| sort_by(list, x => key, order?: "desc") -> List` | Stable sort by a field (numbers by value, strings lexicographically) or by a key function; a list key sorts on several keys: sort_by(rows, r => [0 - r.total, r.name]). |
| `top` | `top(rows: List, n: Int) -> List` | First n elements, capped at the list length. n must be a nonnegative Int; negative raises kind range, wrong types raise kind type. |
| `bottom` | `bottom(rows: List, n: Int) -> List` | Last n elements, capped at the list length. n must be a nonnegative Int; negative raises kind range, wrong types raise kind type. |
| `sum_by` | `sum_by(rows: List<Map>, field) -> Int \| Float` | Sum of a field across rows: exact Int when every value is an Int, else a Float; a numeric String ("5") counts as its number. |
| `avg_by` | `avg_by(rows: List<Map>, field) -> Int\|Float` | Mean of a field; Int when whole, () on an empty list. |
| `min_by` | `min_by(rows: List<Map>, field) -> Map` | Row with the smallest integer value of `field`, or (). |
| `max_by` | `max_by(rows: List<Map>, field) -> Map` | Row with the largest integer value of `field`, or (). |
| `pluck` | `pluck(rows: List<Map>, field) -> List` | Extract one field from every row (missing fields become ()). |
| `group_by` | `group_by(rows: List<Map>, field) -> Map<String, List>` | Group rows into a map keyed by the field's stringified value: 1 and "1" (true and "true") share a group, a row without the field goes to "unknown" and a () value to "null" — normalise the field first when those differ in your data. |
| `distinct` | `distinct(rows: List, field?) -> List` | Unique elements — or, with `field`, the unique VALUES of that field (distinct_by keeps the rows). |
| `distinct_by` | `distinct_by(rows: List<Map>, field: String) -> List<Map>` | The first row per distinct value of `field` (lodash uniqBy / dedup by id). Alias: unique_by. |
| `count_by` | `count_by(rows: List<Map>, field, value) -> Int` | Number of rows whose `field` stringifies equal to `value`. |
| `select` | `select(rows: List<Map>, fields...) -> List<Map>` | Project each row down to the named fields. |
| `agg` | `agg(rows: List<Map>, group_field, "col:func"...) -> List<Map>` | Group + aggregate: func is sum\|avg\|min\|max\|count; every group also gets a `count`. Groups are keyed like group_by (the field's text). |
| `inner_join` | `inner_join(left: List<Map>, right: List<Map>, key) -> List<Map>` | Merge rows whose `key` matches in both lists (left fields win). |
| `left_join` | `left_join(left: List<Map>, right: List<Map>, key) -> List<Map>` | Keep every left row, merging matching right-row fields when found. |

## lambda

| Builtin | Signature | Description |
|---|---|---|
| `filter` | `filter(list: List, x => Bool) -> List` | Keep elements where the lambda returns true (it answers a Bool; () counts as false). |
| `find` | `find(list: List, x => Bool) -> Any` | First element where the lambda returns true, or (). |
| `any` | `any(list: List, x => Bool) -> Bool` | True if the lambda returns true for at least one element. |
| `all` | `all(list: List, x => Bool) -> Bool` | True if the lambda returns true for every element (true on empty). |
| `count` | `count(list: List, x => Bool) -> Int` | Number of elements where the lambda returns true. |
| `reduce` | `reduce(list: List, initial, p => expr) -> Any` | Fold the list; the lambda receives {acc, val} and returns the next acc. |

## io

| Builtin | Signature | Description |
|---|---|---|
| `print` | `print(args...) -> ()` | Print arguments space-separated, then a newline. |
| `read_file` | `read_file(path: String) -> String \| {error}` | Read a file as a string; returns {error: ...} on failure. |
| `write_file` | `write_file(path: String, content) -> Bool \| {error}` | Write content (stringified) to a file; true on success. |
| `read_csv` | `read_csv(path: String, opts: Map?) -> List<Map> \| {error}` | Parse an RFC 4180 CSV (quoted fields, "" escapes, multi-line quoted cells, CRLF) with a header row into maps. Unquoted cells are auto-typed Int/Float/String; a quoted cell and a leading-zero id (007) stay Strings; a short row is padded with "", extra fields are dropped. map("raw", true) keeps every cell as text (exact money: "1.00"); map("delimiter", ";") reads a `;`-separated file; any other option is refused. |
| `to_csv` | `to_csv(rows: List<Map>) -> String` | The CSV text write_csv would write (header from the first row; quoting that from_csv reads back exactly) — for a download without a temp file. |
| `from_csv` | `from_csv(text: String, opts: Map?) -> List<Map>` | read_csv on CSV text already in memory (an uploaded body): same header, typing, raw and delimiter rules. |
| `write_csv` | `write_csv(path: String, rows: List<Map>) -> Bool \| {error}` | Write rows as CSV using the first row's keys as the header. A cell read_csv would split, trim or re-type is quoted (separators, quotes, newlines, edge spaces, and a String that reads as a number: "12" comes back "12"); a List/Map is its JSON text, () an empty cell; true comes back as the String "true". |
| `read_files` | `read_files(dir: String, count: Int) -> List<{path, content}>` | Read the first `count` files of a directory, in file-name order, with their content (a file that is not UTF-8 text is skipped). There is no listing, move or delete: remember the names you processed in a slot. |
| `par_read_files` | `par_read_files(dir: String, count: Int) -> List<{path, content}>` | Thread-parallel variant of read_files. |
| `word_count` | `word_count(text: String \| docs: List) -> Map<String, Int>` | Lowercased word frequency of a string or of {content} docs (Rust-speed). |
| `par_word_count` | `par_word_count(docs: List) -> Map<String, Int>` | Thread-parallel variant of word_count over a list. |
| `read_stdin` | `read_stdin() -> String` | The whole standard input (for `soma run` filters). |
| `write_str` | `write_str(s: String) -> Int` | Write s to stdout without a newline; returns the byte count. |

## template

| Builtin | Signature | Description |
|---|---|---|
| `load_template` | `load_template(path: String, key, value, ...) -> String` | Read a file and substitute each {key} placeholder with its value. |
| `load` | `load(path: String, key, value, ...) -> String` | Alias of load_template. |
| `include` | `include(path: String, key, value, ...) -> String` | Alias of load_template. |
| `render` | `render(template: String, key, value, ...) -> String` | Substitute {key} placeholders in an in-memory template string. |
| `render_each` | `render_each(rows: List<Map>, template: String) -> String` | Render the template once per row, substituting {field} from each map. |

## web

| Builtin | Signature | Description |
|---|---|---|
| `html` | `html(body) -> Response \| html(status: Int, body, header_key, header_value, ...) -> Response` | text/html response; auto-injects HTMX on full pages that use hx- attributes. |
| `response` | `response(status: Int, body, header_key, header_value, ...) -> Response` | Response with explicit status, body, and optional headers. |
| `redirect` | `redirect(url: String) -> Response` | 302 redirect to `url`. |
| `sse` | `sse(streams...) -> Response` | Open a Server-Sent-Events connection that receives only the named streams (no name: every stream). |
| `publish` | `publish(stream: String, data) -> ()` | Push data to a runtime-chosen SSE stream name on the event bus. |

## http

| Builtin | Signature | Description |
|---|---|---|
| `http_get` | `http_get(url: String, opts?: {timeout, max_bytes, headers}) -> Map\|List\|String` | GET a URL. 2xx: the body (JSON parsed). Never raises on the network or a status: otherwise {error, kind, status, body}; an invalid or unknown option raises kind type — kind http_status (status + the upstream body), timeout, refused or network. timeout defaults to 30000 ms. |
| `http_post` | `http_post(url: String, body, opts?: {timeout, max_bytes, headers}) -> Map\|List\|String` | POST body (a Map/List is sent as JSON, a String as is). Same result shape and default timeout as http_get. Also http_put, http_patch, http_delete(url, opts?). |
| `http_put` | `http_put(url: String, body, opts?) -> Map\|List\|String` | PUT; same shape as http_post. |
| `http_patch` | `http_patch(url: String, body, opts?) -> Map\|List\|String` | PATCH; same shape as http_post. |
| `http_delete` | `http_delete(url: String, opts?) -> Map\|List\|String` | DELETE; same shape as http_get. |
| `ws_connect` | `ws_connect(url: String) -> Map` | Open a WebSocket connection; incoming messages dispatch as signals. |
| `ws_send` | `ws_send(msg) -> ()` | Send a message on the current WebSocket connection; errors if not connected. |
| `link` | `link(addr: "host:port") -> ()` | Open a TCP signal-bus link to a peer node. |
| `subscribe` | `subscribe(url: String) -> ()` | Subscribe to a remote event stream; an {"event", "data"} message runs on event(data) only for an event this program emits or soma.toml [bus] accept lists (else refused); other text runs on ws(msg). |
| `refusal` | `refusal(kind: String, detail?: String) -> Map` | An HTTP response — status from the kind (`conflict` → 409, `not_found` → 404, `unauthorized` → 401, your own tag → 400) and body {error, kind, detail}, exactly what RAISING that error would answer. Return it when the handler must refuse AND keep what it wrote (an audit row): raising rolls the writes back, returning does not. It is a response envelope like response(): read the fields through `_body` (`r._body.kind`), and return it as the handler's value. |

## time

| Builtin | Signature | Description |
|---|---|---|
| `parse_date` | `parse_date(s: "YYYY-MM-DD") -> {year, month, day, weekday, epoch_day}` | Strict ISO date to its parts (weekday 1 = Monday); raises kind "date" otherwise. |
| `add_days` | `add_days(date: String, n: Int) -> String` | The ISO date n days later (negative n goes back), across month and year ends. |
| `add_months` | `add_months(date: String, n: Int) -> String` | Same day n months later, clamped to the month's length (Ruby's Date >> n): add_months("2026-01-31", 1) = "2026-02-28". |
| `days_between` | `days_between(a: String, b: String) -> Int` | Days from a to b (negative when b is earlier). |
| `months_between` | `months_between(a: String, b: String) -> Int` | Whole months from a to b ("YYYY-MM-DD"), day-of-month aware like java.time MONTHS.between: 2026-01-15 → 2026-04-14 is 2, → 2026-04-20 is 3. |
| `days_in_month` | `days_in_month(year: Int, month: Int) -> Int` | 28–31, leap years included. |
| `now` ✗ | `now() -> Int` | Current Unix timestamp in seconds. |
| `now_ms` ✗ | `now_ms() -> Int` | Current Unix timestamp in milliseconds. |
| `today` ✗ | `today() -> String` | Today's date as "YYYY-MM-DD" (UTC). |
| `format_date` | `format_date(ts: Int) -> String` | Format a Unix-seconds timestamp as "YYYY-MM-DD" (UTC). |
| `sleep` | `sleep(ms: Int) -> ()` | Block the current handler for `ms` milliseconds. Requires one Int (other types raise kind type); values outside 0 to 86400000 raise kind range. Under serve it holds the handler lock the whole time. |

## state

| Builtin | Signature | Description |
|---|---|---|
| `next_id` | `next_id() -> Int` | Monotonic per-cell counter, in its own table (persistent under run/serve; per test cell in tests); journaled — a refused request burns no id; the ids drawn inside a `try` that fails are kept (they may have escaped into a local), so ids are unique, not always dense. Exhaustion at 2^63 - 1 raises range; a malformed or negative stored counter raises storage. Legacy migration is confined to the current cell. |
| `transition` | `transition(id, target_state: String) -> {id, from, to}` | Move instance `id` to `target_state` (read the new state with get_status(id)); raises kind "invalid_transition" with the valid targets, or "guard_failed". Rolled back if the handler later fails. |
| `get_status` | `get_status(id) -> String` | Current state of instance `id` — the INITIAL state when `id` was never transitioned (an unknown id looks like a fresh instance; use has_state(id) to tell them apart). |
| `has_state` | `has_state(id) -> Bool` | True when instance `id` was transitioned at least once (a recorded state exists). get_status(id) alone cannot distinguish an unknown id from a fresh one. |
| `valid_transitions` | `valid_transitions(id) -> List<String>` | States reachable from instance `id`'s current state. |

## memory

| Builtin | Signature | Description |
|---|---|---|
| `remember` | `remember(key, value) -> ()` | Persist typed data in this cell's agent memory; rejects functions and storage encodings deeper than 100 levels. |
| `recall` | `recall(key: String) -> Any` | The value this cell remember()ed under the key, or (); preserves JSON-shaped Strings and never reads another cell's memory. |
| `append` | `slot.append(value) -> ()` | Memory-slot method: append a value to a list-backed slot (alias: slot.push). |

## agent

| Builtin | Signature | Description |
|---|---|---|
| `think` | `think(prompt: String, system?: String, opts?: {max_tokens, timeout, max_rounds, tools_allowed, requires}) -> String` | Call the configured LLM with tool-calling, multi-turn context, and budget enforcement. A think() offers the model every tool of the cell's face, or only those named in map("tools_allowed", ["lookup"]) (a call to another is refused and told to the model); `requires` lists model capabilities checked by `soma check`. A timeout is not retried (429/5xx are, up to 3 times). |
| `think_json` | `think_json(prompt: String, system?: String, opts?: {max_tokens, timeout, max_rounds, tools_allowed, requires}) -> Map` | Like think(), but parses the response as JSON into a Map (tools are offered too: pass max_rounds 1 to keep the cost bound at one round). There is no schema option — check the fields yourself. |
| `horde` | `horde(handler, inputs: List, opts?: {concurrency, max_attempts, budget_tokens, seed, snapshot, instance, on_result, apply, on_done, on_error}) -> String` | Run `handler` (mark it [task]) once per input with a bounded pool of workers; returns the horde id (`Cell:h1`) at once. The queue is persisted with the program's data and resumes after a restart; each result is recorded — and on_result(result) / on_result(input, result) called — with the handler's last step, so once. budget_tokens: a hard ceiling (every think() reserves its bound first; state `exhausted`). on_error(input, error: {error, kind, detail}) after max_attempts (default 1); on_done(id) once at the end. Rounds: snapshot (handler takes (input, snapshot)), apply (in input order at the end), seed (reproducible random()), instance (per-agent memory). concurrency default 8, at most 1000. Without workers (soma test) it runs right after the caller commits. |
| `vote` | `vote(handler, input, k: Int) -> Map` | k agents (1..25) answer the same input; {winner, count, k, unanimous, errors, votes}. The most common answer wins, ties to the earliest voter. In a [task] step under serve / run the k calls run at once outside the lock, else in turn; each voter starts a fresh model context; a voter that raises counts in `errors` (all raising: the first error). Voters' writes are theirs — keep them read-only. |
| `horde_status` | `horde_status(id: String) -> Map` | {state: running \| cancelling \| exhausting \| done \| cancelled \| exhausted, total, queued, running, done, failed, cancelled, tokens, budget_tokens?} of a horde (its id names its cell). |
| `horde_results` | `horde_results(id: String) -> List` | The results of a horde in input order; () for a task not done (failed, cancelled or still queued). |
| `horde_cancel` | `horde_cancel(id: String) -> Bool` | Stop a horde: no new task starts, running ones finish, on_done is called. false when it was not running. |
| `delegate` | `delegate(cell: String, signal: String, args...) -> Any` | Invoke another cell's handler and return its result. |
| `set_budget` | `set_budget(max_tokens: Int) -> ()` | Hard cap on LLM tokens; think() fails once exhausted. |
| `tokens_used` | `tokens_used() -> Int` | LLM tokens consumed since the budget was set. |
| `tokens_remaining` | `tokens_remaining() -> Int` | Tokens left in the budget, or -1 if unlimited. |
| `trace` | `trace() -> List` | Structured execution log: every think(), tool call, and approval. |
| `clear_trace` | `clear_trace() -> ()` | Empty the agent trace log. |
| `clear_context` | `clear_context() -> ()` | Reset the multi-turn LLM conversation history. |
| `approve` | `approve(action: String) -> Bool` | Human-in-the-loop gate. Answered by `mock approve true\|false` in tests, by SOMA_APPROVE=always\|never, or by a person at the terminal under `soma run`; otherwise (e.g. under soma serve) it RAISES kind "approval_required" — it never approves on its own. |

## linalg

| Builtin | Signature | Description |
|---|---|---|
| `matrix` | `matrix("1 2; 3 4") -> List<List<Float>>` | MATLAB-style matrix literal: ';' separates rows, whitespace/',' separates entries. |
| `mat` | `mat(rows: Int, cols: Int, values: List<Float>) -> List<List<Float>>` | Reshape a flat list into an r×c matrix; errors if the count mismatches. |
| `reshape` | `reshape(values, rows: Int, cols: Int) -> Matrix` | Lay a flat list or matrix out row-major as rows×cols; also m.reshape(r,c). |
| `transpose` | `transpose(m: Matrix) -> Matrix` | Transpose; also m.transpose(). |
| `shape` | `shape(m) -> List<Int>` | [rows, cols] for a matrix, [n] for a vector; also m.shape(). |
| `matmul` | `matmul(a: Matrix, b: Matrix) -> Matrix` | Matrix product (also the `*` operator on two matrices); inner dims must agree. |
| `det` | `det(m: Matrix) -> Float` | Determinant of a square matrix (LU with partial pivoting). |
| `diag_sum` | `diag_sum(m: Matrix) -> Float` | Matrix trace: sum of the diagonal. |
| `identity` | `identity(n: Int) -> Matrix` | n×n identity matrix (alias of eye). |
| `scale` | `scale(m: Matrix, k) -> Matrix` | Scalar-multiply every entry (also `k * m`). |
| `flatten_mat` | `flatten_mat(m: Matrix) -> List<Float>` | Flatten a matrix to a row-major vector. |
| `rows` | `rows(r1: List<Float>, r2: List<Float>, ...) -> List<List<Float>>` | Build a matrix from row vectors. |
| `cols` | `cols(c1: List<Float>, c2: List<Float>, ...) -> List<List<Float>>` | Build a matrix from column vectors (transposes). |
| `eye` | `eye(n: Int) -> List<List<Float>>` | n×n identity matrix. |
| `zeros` | `zeros(r: Int, c: Int) -> List<List<Float>>` | r×c matrix of zeros. |
| `ones` | `ones(r: Int, c: Int) -> List<List<Float>>` | r×c matrix of ones. |
| `diag` | `diag(values: List<Float>) -> List<List<Float>>` | Square diagonal matrix from a list. |
| `to_sampled` | `to_sampled(A: List<List<Float>>, opts?: {max_rows, max_cols}) -> Map` | Build a BST-backed length-squared sampling handle (Tang); O(log n) per sample after. |
| `sample_row` | `sample_row(A) -> Map` | Draw one row index by ℓ²-norm importance sampling (time-seeded PRNG). |
| `drop_sampled` | `drop_sampled(handle: Map) -> Bool` | Free a to_sampled() registry entry; true if it existed. |
| `importance_sample_rows` | `importance_sample_rows(A, opts: {samples}) -> Map` | Sample rows by squared-norm importance (time-seeded PRNG). |
| `svd_lowrank` | `svd_lowrank(A, opts: {row_samples, col_samples, rank, max_dim}) -> Map` | Sublinear randomized low-rank SVD with declared sampling bounds. |
| `regress_sgd` | `regress_sgd(A, b: List<Float>, opts: {eps, lambda, max_iter, max_dim}) -> Map` | Ridge regression via stochastic gradient descent with declared bounds. |
| `clean_covariance` | `clean_covariance(returns: List<List<Float>>, opts: {method: "rie"\|"clip"\|"raw", eta, center, max_assets, max_obs}) -> Map` | RMT (Bouchaud-Potters) covariance cleaning: rows are observations (T), columns assets (N); the sample covariance divides by T (population — numpy cov uses T-1); center (default true) subtracts each column mean; clip replaces the eigenvalues inside the Marchenko-Pastur bulk by their mean; .matrix is the cleaned N×N, .eigenvalues the cleaned spectrum (no eigenvectors). max_obs / max_assets past the data raise kind range. |
| `impact_sqrt` | `impact_sqrt(qty: Float, daily_volume: Float, sigma: Float, opts?: {Y}) -> Map` | Bouchaud square-root market-impact law; .bps is expected slippage. |
| `quantile` | `quantile(values: List<Float>, q: Float) -> Float` | q-th quantile with linear interpolation between the two nearest sorted values (numpy's default): quantile(xs, 0.5) == median(xs). |
| `var_historical` | `var_historical(returns: List<Float>, opts?: {alpha, max_obs}) -> Float` | Historical Value-at-Risk as a POSITIVE loss: -quantile(returns, 1 - alpha) (returns positive for gains); alpha in (0, 1), else kind range; more observations than max_obs raise kind range. |
| `expected_shortfall_historical` | `expected_shortfall_historical(returns: List<Float>, opts?: {alpha, max_obs}) -> Float` | Historical expected shortfall (CVaR) as a positive loss: minus the mean of the returns at or below the (1 - alpha) quantile; alpha in (0, 1); max_obs as for var_historical. |
| `var_gaussian` | `var_gaussian(returns: List<Float>, opts?: {alpha, mu, sigma}) -> Float` | Gaussian VaR assuming N(mu, sigma^2); moments inferred unless overridden. |

## native

| Builtin | Signature | Description |
|---|---|---|
| `buffer` | `buffer(n: Int) -> Buf   [native] only` | Array of n Ints, zeroed. Random access with buf_get / buf_set. Not available in interpreted handlers. |
| `buf_get` | `buf_get(b: Buf, i: Int) -> Int   [native] only` | Read b[i]. |
| `buf_set` | `buf_set(b: Buf, i: Int, v: Int) -> ()   [native] only` | Write b[i] = v. |
| `buffer_f` | `buffer_f(n: Int) -> BufF   [native] only` | Array of n Floats, zeroed (buf_get_f / buf_set_f). |
| `buf_get_f` | `buf_get_f(b: BufF, i: Int) -> Float   [native] only` | Read b[i]. |
| `buf_set_f` | `buf_set_f(b: BufF, i: Int, v: Float) -> ()   [native] only` | Write b[i] = v. |
| `hashmap` | `hashmap() -> HMap   [native] only` | Int → Int hash map (hm_get / hm_set / hm_inc / hm_len / hm_has). |
| `hm_get` | `hm_get(m: HMap, k: Int) -> Int   [native] only` | Value at k, 0 when absent. |
| `hm_set` | `hm_set(m: HMap, k: Int, v: Int) -> ()   [native] only` | m[k] = v. |
| `hm_inc` | `hm_inc(m: HMap, k: Int) -> ()   [native] only` | m[k] += 1 (inserting 1). |
| `hm_len` | `hm_len(m: HMap) -> Int   [native] only` | Number of keys. |
| `hm_has` | `hm_has(m: HMap, k: Int) -> Bool   [native] only` | Whether k is present. |
| `strbuf` | `strbuf(capacity?: Int) -> SBuf   [native] only` | Growable string builder (sb_push / sb_push_int / sb_push_char / sb_len / sb_finish). |
| `sb_push` | `sb_push(b: SBuf, s: String) -> ()   [native] only` | Append a string. |
| `sb_push_int` | `sb_push_int(b: SBuf, n: Int) -> ()   [native] only` | Append an Int's decimal digits. |
| `sb_push_char` | `sb_push_char(b: SBuf, c: Int) -> ()   [native] only` | Append one character by code point. |
| `sb_len` | `sb_len(b: SBuf) -> Int   [native] only` | Bytes so far. |
| `sb_finish` | `sb_finish(b: SBuf) -> String   [native] only` | The built String. |

## internal

| Builtin | Signature | Description |
|---|---|---|
| `_coalesce` | `_coalesce(a, b) -> Any` | Desugared form of `a ?? b`: returns b only when a is (). |
