# Standard library

URL: /docs/stdlib
Markdown: /docs/stdlib.md

> Public std signatures generated from compiler/std/INDEX.md, with current JS limitations.

{/* Generated by scripts/generate-stdlib.mjs. Edit compiler/std/INDEX.md for signatures. */}

The source-built compiler embeds this standard library. This inventory is generated from
[compiler/std/INDEX.md](https://github.com/icantcodefyi/sakura-lang/blob/master/compiler/std/INDEX.md).
The signature blocks below are API declarations, not standalone programs. Their owning
modules are checked and their tests exercised by `sakura test --std`.
Regenerate this page from `website/` with `bun scripts/generate-stdlib.mjs`.

Use [`sakura doc`](/docs/agents/doc) for offline type-pattern search and symbol documentation.
The [capabilities tour](/docs/tour/capabilities), [contracts tour](/docs/tour/contracts) and
[cassette guide](/docs/agents/cassettes) give executable examples of the recently landed APIs.

## Current scope and limitations [#current-scope-and-limitations]

- `Http.recording`, `Llm.replay`, `Test.fixtures`, `Untrusted.validate` and `Html.from` are implemented.
- Streaming HTTP (`Http.stream`, `Http.read_chunk`, `Body`, `Incoming`, design §17.88) remains planned; it is absent from these signatures.
- `Sql.postgres` is not implemented. The source index records it as deferred past M4; SQLite and memory SQL cover the current examples.
- Direct `Untrusted<String>` SQL interpolation currently fails with `V2032`, despite the broader claim in the source index's deviations. Validate to a refined string first.
- `Task.par_map` lives in `std/task`, not `List`. `Env.host()` takes no scope argument; use `Env.only` for attenuation.
- JS `Int` arithmetic has a safe-integer range of ±(2^53−1); full-range i64 execution is planned for WASM. WASM and native execution are not CLI targets yet.
- M3 still has [exit gates](/docs/roadmap). This signature inventory does not claim that M3, multi-package tooling or static verification is complete.

## Prelude [#prelude]

In scope in every module without an import (design §9, with the additions marked +):

| Kind | Names |
|---|---|
| Compiler-known types | `Unit Bool Int Float String Bytes Never List Map Set Range Handler Secret Untrusted` |
| Std types | `Option Result` · + `Duration Instant Uuid Json Level` (prelude effects mention them; §15.1 and §15.4 use `Instant.format_iso`, `Level.Warn`, `Json.String` unimported) |
| Constructors | `Some None Ok Err` (other variants are used qualified: `Level.Warn`, `Json.String(s)`) |
| Effects | `Fail` (compiler-known) `Console Clock Random Env Log Task` · + `Test` |
| Functions | `show` · + `expect expect_eq expect_fail expect_fails_with expect_defect expect_output expect_snapshot` (they need `Test`, which only `test`/`property` bodies have; elsewhere they are `V3001`) |
| Tags (M3) | `sql html url` (`shell` is reserved by the prelude; no `std/shell` exists yet) |

Not in the prelude, import them: `import std/retry.{Policy, retry}`, `import std/task.{Timeout, timeout}`,
`import std/gen` (`gen.int(0, 9)`), `import std/graph.{Graph, NodeId}`, and the M3 modules.

Home modules (design §4.9): `T.f(…)` and `x.f(…)` with `x: T` call `f` in the module that
declares `T`; for compiler-known types it is `std/<lowercase name>` (`List` → `std/list`). An
effect's home module is the one declaring it, and `Eff.f(…)` calls a home-module function
when `f` is not an operation (`Console.captured()`, `Clock.fixed(t)`, `Random.seeded(1)`).

## Signatures [#signatures]

### `std/core` [#stdcore]

```sakura
fn show<A: Show>(a: A) -> String    // @external
type Violation = { type_name: String, clause: String, value_summary: String }
type Trust
```

### `std/unit` [#stdunit]

```sakura
fn ignore<A>(a: A) -> Unit
```

### `std/option` [#stdoption]

```sakura
type Option<A> = None | Some(A)
fn map<A, B, e>(o: Option<A>, f: fn(A) -> B ! e) -> Option<B> ! e
fn unwrap_or<A>(o: Option<A>, default: A) -> A
fn or_fail<A, E>(o: Option<A>, err: E) -> A ! {Fail<E>}
fn expect<A>(o: Option<A>, msg: String) -> A    // @external
fn is_some<A>(o: Option<A>) -> Bool    // @total
```

### `std/result` [#stdresult]

```sakura
type Result<A, E> = Ok(A) | Err(E)
fn map<A, B, E, e>(r: Result<A, E>, f: fn(A) -> B ! e) -> Result<B, E> ! e
fn map_err<A, E, F>(r: Result<A, E>, f: fn(E) -> F) -> Result<A, F>
fn unwrap_or<A, E>(r: Result<A, E>, default: A) -> A
fn raise<A, E>(r: Result<A, E>) -> A ! {Fail<E>}
fn is_ok<A, E>(r: Result<A, E>) -> Bool    // @total
fn is_err<A, E>(r: Result<A, E>) -> Bool    // @total
```

### `std/list` [#stdlist]

```sakura
fn len<A>(xs: List<A>) -> Int    // @external, @total
fn get<A>(xs: List<A>, i: Int) -> Option<A>    // @external, @total
fn push<A>(xs: List<A>, x: A) -> List<A>    // @external
fn slice<A>(xs: List<A>, start: Int, end: Int) -> List<A>    // @external
fn empty<A>() -> List<A>
fn concat<A>(a: List<A>, b: List<A>) -> List<A>
fn map<A, B, e>(xs: List<A>, f: fn(A) -> B ! e) -> List<B> ! e
fn filter<A, e>(xs: List<A>, f: fn(A) -> Bool ! e) -> List<A> ! e
fn filter_map<A, B, e>(xs: List<A>, f: fn(A) -> Option<B> ! e) -> List<B> ! e
fn flat_map<A, B, e>(xs: List<A>, f: fn(A) -> List<B> ! e) -> List<B> ! e
fn fold<A, B, e>(xs: List<A>, init: B, f: fn(B, A) -> B ! e) -> B ! e
fn find<A, e>(xs: List<A>, f: fn(A) -> Bool ! e) -> Option<A> ! e
fn all<A, e>(xs: List<A>, f: fn(A) -> Bool ! e) -> Bool ! e    // @total
fn any<A, e>(xs: List<A>, f: fn(A) -> Bool ! e) -> Bool ! e    // @total
fn sort<A: Ord>(xs: List<A>) -> List<A>
fn sort_by<A, K: Ord>(xs: List<A>, key: fn(A) -> K) -> List<A>
fn zip<A, B>(a: List<A>, b: List<B>) -> List<(A, B)>
fn enumerate<A>(xs: List<A>) -> List<(Int, A)>
fn reverse<A>(xs: List<A>) -> List<A>
fn join(xs: List<String>, sep: String) -> String
fn sum(xs: List<Int>) -> Int
fn first<A>(xs: List<A>) -> Option<A>    // @total
fn last<A>(xs: List<A>) -> Option<A>    // @total
fn contains<A: Eq>(xs: List<A>, x: A) -> Bool    // @total
fn unique<A: Hash>(xs: List<A>) -> List<A>
fn range(start: Int, end: Int) -> List<Int>
```

### `std/map` [#stdmap]

```sakura
fn empty<K: Hash, V>() -> Map<K, V>    // @external
fn get<K: Hash, V>(m: Map<K, V>, k: K) -> Option<V>    // @external, @total
fn insert<K: Hash, V>(m: Map<K, V>, k: K, v: V) -> Map<K, V>    // @external
fn remove<K: Hash, V>(m: Map<K, V>, k: K) -> Map<K, V>    // @external
fn len<K: Hash, V>(m: Map<K, V>) -> Int    // @external, @total
fn entries<K: Hash, V>(m: Map<K, V>) -> List<(K, V)>    // @external
fn of<K: Hash, V>(pairs: List<(K, V)>) -> Map<K, V>
fn contains<K: Hash, V>(m: Map<K, V>, k: K) -> Bool    // @total
fn update<K: Hash, V, e>(m: Map<K, V>, k: K, f: fn(Option<V>) -> V ! e) -> Map<K, V> ! e
fn keys<K: Hash, V>(m: Map<K, V>) -> List<K>
fn values<K: Hash, V>(m: Map<K, V>) -> List<V>
```

### `std/set` [#stdset]

```sakura
fn empty<A: Hash>() -> Set<A>    // @external
fn insert<A: Hash>(s: Set<A>, x: A) -> Set<A>    // @external
fn contains<A: Hash>(s: Set<A>, x: A) -> Bool    // @external, @total
fn len<A: Hash>(s: Set<A>) -> Int    // @external, @total
fn to_list<A: Hash>(s: Set<A>) -> List<A>    // @external
fn of<A: Hash>(xs: List<A>) -> Set<A>
```

### `std/string` [#stdstring]

```sakura
error Utf8Error(offset: Int)
fn len(s: String) -> Int    // @external, @total
fn chars(s: String) -> List<String>    // @external
fn graphemes(s: String) -> List<String>    // @external
fn split(s: String, sep: String) -> List<String>    // @external
fn trim(s: String) -> String    // @external
fn starts_with(s: String, prefix: String) -> Bool    // @external, @total
fn ends_with(s: String, suffix: String) -> Bool    // @external, @total
fn contains(s: String, needle: String) -> Bool    // @external, @total
fn replace(s: String, from: String, to: String) -> String    // @external
fn to_lower(s: String) -> String    // @external
fn to_upper(s: String) -> String    // @external
fn parse_int(s: String) -> Option<Int>    // @external
fn parse_float(s: String) -> Option<Float>    // @external
fn to_bytes(s: String) -> Bytes    // @external
fn from_bytes(b: Bytes) -> Result<String, Utf8Error>    // @external
fn slice(s: String, start: Int, end: Int) -> String    // @external
fn repeat(s: String, n: Int) -> String requires n >= 0
fn pad_left(s: String, width: Int, fill: String) -> String requires len(fill) > 0
```

### `std/int` [#stdint]

```sakura
fn to_float(i: Int) -> Float    // @external
fn to_string(i: Int) -> String    // @external
fn checked_add(a: Int, b: Int) -> Option<Int>    // @external
fn wrapping_add(a: Int, b: Int) -> Int    // @external
fn abs(i: Int) -> Int    // @total
fn min(a: Int, b: Int) -> Int    // @total
fn max(a: Int, b: Int) -> Int    // @total
fn clamp(i: Int, lo: Int, hi: Int) -> Int requires lo <= hi
fn pow(base: Int, exp: Int) -> Int requires exp >= 0
```

### `std/float` [#stdfloat]

```sakura
fn round(f: Float) -> Int    // @external
fn to_int_checked(f: Float) -> Option<Int>    // @external
fn sqrt(f: Float) -> Float    // @external
fn format(f: Float, decimals: Int) -> String requires decimals >= 0    // @external
fn is_nan(f: Float) -> Bool
```

### `std/bytes` [#stdbytes]

```sakura
fn len(b: Bytes) -> Int    // @external, @total
fn get(b: Bytes, i: Int) -> Option<Int>    // @external, @total
fn from_list(xs: List<Int>) -> Bytes requires List.all(xs, fn(x) { x >= 0 && x <= 255 })    // @external
```

### `std/duration` [#stdduration]

```sakura
type Duration = Duration(Int)
fn millis(n: Int) -> Duration
fn seconds(n: Int) -> Duration
fn minutes(n: Int) -> Duration
fn to_millis(d: Duration) -> Int
```

### `std/instant` [#stdinstant]

```sakura
type Instant = Instant(Int)
error TimeError(input: String)
fn from_unix_millis(ms: Int) -> Instant
fn to_unix_millis(i: Instant) -> Int
fn add(i: Instant, d: Duration) -> Instant
fn diff(a: Instant, b: Instant) -> Duration
fn format_iso(i: Instant) -> String
fn parse(text: String) -> Result<Instant, TimeError>
```

### `std/uuid` [#stduuid]

```sakura
opaque type Uuid = Uuid(String)
error UuidError(input: String)
fn to_string(u: Uuid) -> String
fn parse(text: String) -> Result<Uuid, UuidError>
fn from_canonical(text: String) -> Uuid requires is_canonical(text)
fn is_canonical(text: String) -> Bool    // @total
```

### `std/json` [#stdjson]

```sakura
type Json = Null | Bool(Bool) | Number(Float) | String(String) | Array(List<Json>) | Object(Map<String, Json>)
error DecodeError(path: String, message: String)
fn encode<T: Schema>(value: T) -> String    // @external
fn decode<T: Schema>(text: String) -> Result<T, DecodeError>    // @external
fn parse(text: String) -> Result<Json, DecodeError>    // @external
fn schema<T: Schema>(witness: Option<T>) -> Json    // @external
fn get(j: Json, key: String) -> Option<Json>
fn get_string(j: Json, key: String) -> Option<String>
```

### `std/test` [#stdtest]

```sakura
effect Test {
  fn record_failure(message: String) -> Unit
  fn failures() -> Int
  fn output(line: String) -> Unit
  fn output_lines() -> List<String>
  fn snapshot(name: String, rendered: String) -> Unit
  fn fixtures() -> Dir
}
fn expect(cond: Bool) -> Unit ! {Test}
fn expect_eq<A: Eq + Show>(actual: A, expected: A) -> Unit ! {Test}
fn expect_fail<T, E: Eq + Show, e>(body: fn() -> T ! {Fail<E> | e}, err: E) -> Unit ! {Test | e}
fn expect_fails_with<T, E: Show, e>(body: fn() -> T ! {Fail<E> | e}, pred: fn(E) -> Bool) -> Unit ! {Test | e}
fn expect_defect<T, e>(body: fn() -> T ! e) -> Unit ! {Test | e}    // @external
fn expect_output(lines: List<String>) -> Unit ! {Test}
fn expect_snapshot<A: Show>(name: String, value: A) -> Unit ! {Test}
```

### `std/console` [#stdconsole]

```sakura
effect Console {
  fn print(line: String) -> Unit
  fn read_line() -> Option<String>
}
fn host() -> Handler<Console>    // @external
fn captured() -> Handler<Console, {Test}>
```

### `std/clock` [#stdclock]

```sakura
effect Clock {
  fn now() -> Instant
  fn monotonic() -> Duration
  fn sleep(d: Duration) -> Unit
}
fn host() -> Handler<Clock>    // @external
fn fixed(at: Instant) -> Handler<Clock>
fn virtual() -> Handler<Clock>
```

### `std/random` [#stdrandom]

```sakura
effect Random {
  fn int(lo: Int, hi: Int) -> Int requires lo <= hi
  fn float() -> Float
  fn bytes(n: Int) -> Bytes requires n >= 0
  fn uuid() -> Uuid
}
fn host() -> Handler<Random>    // @external
fn seeded(seed: Int) -> Handler<Random>
```

### `std/env` [#stdenv]

```sakura
effect Env {
  fn get(key: String) -> Option<String>
  fn secret(key: String) -> Option<Secret<String>>
}
fn host() -> Handler<Env>
fn of(vars: Map<String, String>) -> Handler<Env>
fn only(keys: List<String>) -> Handler<Env, {Env}>
```

### `std/log` [#stdlog]

```sakura
type Level = Debug | Info | Warn | Error
effect Log {
  fn event(level: Level, message: String, fields: List<(String, Json)>) -> Unit
}
fn stderr() -> Handler<Log>    // @external
fn host() -> Handler<Log>
fn json() -> Handler<Log>    // @external
fn captured() -> Handler<Log, {Test}>
```

### `std/gen` [#stdgen]

```sakura
type Gen<A> = { sample: fn(Int) -> A ! {Random} }
fn int(lo: Int, hi: Int) -> Gen<Int> requires lo <= hi
fn bool() -> Gen<Bool>
fn one_of<A>(choices: List<A>) -> Gen<A> requires List.len(choices) > 0
fn list<A>(item: Gen<A>, max_len: Int) -> Gen<List<A>> requires max_len >= 0
fn string(max_len: Int) -> Gen<String> requires max_len >= 0
fn map<A, B>(g: Gen<A>, f: fn(A) -> B) -> Gen<B>
fn pair<A, B>(a: Gen<A>, b: Gen<B>) -> Gen<(A, B)>
fn for_all<A: Show, e>(g: Gen<A>, runs: Int, prop: fn(A) -> Unit ! {Test | e}) -> Unit ! {Random, Test | e}
```

### `std/retry` [#stdretry]

```sakura
type Policy<E> = { retry_on: fn(E) -> Bool, max_attempts: Int, backoff: Duration, jitter: Bool, deadline: Option<Duration> }
fn policy<E>(retry_on: fn(E) -> Bool, max_attempts: Int) -> Policy<E> requires max_attempts >= 1
fn retry<T, E, e>(p: Policy<E>, action: fn() -> T ! {Fail<E> | e}) -> T ! {Clock, Random, Fail<E> | e}
```

### `std/task` [#stdtask]

```sakura
type Scope
type Fiber<T>
effect Task {
  fn scope<T, e>(body: fn(Scope) -> T ! e) -> T ! e
  fn spawn<T, e>(s: Scope, body: fn() -> T ! e) -> Fiber<T> ! e
  fn join<T>(f: Fiber<T>) -> T
  fn all<T, e>(bodies: List<fn() -> T ! e>) -> List<T> ! e
  fn race<T, e>(bodies: List<fn() -> T ! e>) -> T ! e
  fn yield_now() -> Unit
}
fn host() -> Handler<Task>    // @external
fn deterministic(seed: Int) -> Handler<Task>    // @external
error Timeout(limit: Duration)
fn timeout<T, e>(limit: Duration, action: fn() -> T ! e) -> T ! {Clock, Task, Fail<Timeout> | e}
fn par_map<A, B, e>(xs: List<A>, f: fn(A) -> B ! e) -> List<B> ! {Task | e}
```

### `std/graph` [#stdgraph]

```sakura
type NodeId = NodeId(Int)
type Graph<N> = { nodes: Map<Int, N>, edges: Map<Int, List<NodeId>>, next: Int }
fn empty<N>() -> Graph<N>
fn insert<N>(g: Graph<N>, n: N) -> (Graph<N>, NodeId)
fn get<N>(g: Graph<N>, id: NodeId) -> Option<N>    // @total
fn connect<N>(g: Graph<N>, a: NodeId, b: NodeId) -> Graph<N> requires get(g, a).is_some() && get(g, b).is_some()
fn neighbors<N>(g: Graph<N>, id: NodeId) -> List<NodeId>
fn len<N>(g: Graph<N>) -> Int
```

### `std/untrusted` [#stduntrusted]

```sakura
fn of<T>(value: T) -> Untrusted<T>    // @external
fn validate<T, V>(u: Untrusted<T>) -> Result<V, Violation>    // @external, compiler-known (§17.71)
fn trust<T>(u: Untrusted<T>, reason: Trust) -> T
fn check<T>(u: Untrusted<T>, clause: String, pred: fn(T) -> Bool) -> Result<T, Violation>
fn len(u: Untrusted<String>) -> Int
```

### `std/secret` [#stdsecret]

```sakura
fn of<T>(value: T) -> Secret<T>    // @external
fn expose<T>(s: Secret<T>, reason: Trust) -> T
fn map<T, U>(s: Secret<T>, f: fn(T) -> U) -> Secret<U>
```

### `std/decimal` [#stddecimal]

```sakura
opaque type Decimal = { whole: Int, frac: List<Int> }
error DecimalError(input: String)
fn from_int(n: Int) -> Decimal
fn zero() -> Decimal
fn parse(text: String) -> Result<Decimal, DecimalError>
fn to_string(d: Decimal) -> String
fn add(a: Decimal, b: Decimal) -> Decimal
fn sub(a: Decimal, b: Decimal) -> Decimal
fn neg(a: Decimal) -> Decimal
fn mul(a: Decimal, b: Decimal) -> Decimal
fn div_checked(a: Decimal, b: Decimal, places: Int) -> Option<Decimal> requires places >= 0
fn round(d: Decimal, places: Int) -> Decimal requires places >= 0
```

### `std/path` [#stdpath]

```sakura
type Path = String where is_relative(it)
fn is_relative(s: String) -> Bool    // @total
fn of(s: String) -> Path requires is_relative(s)
fn join(p: Path, rest: String) -> Path requires is_relative(rest)
fn to_string(p: Path) -> String
```

### `std/fs` [#stdfs]

```sakura
type Dir
type Entry = { name: String, is_dir: Bool }
type Mode = Read | Write | Append
opaque type File = { id: Int, path: Path, mode: Mode }    // @resource (§17.70)
error FsError(path: String, reason: String)
effect Fs {
  fn root() -> Dir
  fn read(path: Path) -> Bytes ! {Fail<FsError>}
  fn read_text(path: Path) -> Untrusted<String> ! {Fail<FsError>}
  fn write(path: Path, data: Bytes) -> Unit ! {Fail<FsError>}
  fn list(dir: Path) -> List<Entry> ! {Fail<FsError>}
  fn exists(path: Path) -> Bool
  fn with_file<T, e>(path: Path, mode: Mode, body: fn(File) -> T ! e) -> T ! {Fail<FsError> | e}
  fn read_chunk(file: File, max: Int) -> Bytes ! {Fail<FsError>} requires max > 0
  fn write_chunk(file: File, data: Bytes) -> Unit ! {Fail<FsError>}
}
fn host() -> Handler<Fs>
fn rooted(dir: Dir) -> Handler<Fs>
fn subdir(d: Dir, name: String) -> Dir ! {Fail<FsError>}
fn memory(files: List<(Path, Bytes)>) -> Handler<Fs>
fn read_all(file: File) -> Bytes ! {Fs, Fail<FsError>}
```

### `std/cli` [#stdcli]

```sakura
effect Cli {
  fn args() -> List<String>
  fn exit(code: Int) -> Never requires code >= 0 && code <= 255
}
error CliError(message: String)
fn host() -> Handler<Cli>
fn of(args: List<String>) -> Handler<Cli>
fn parse<T: Schema>(args: List<String>) -> T ! {Fail<CliError>}    // @external
```

### `std/url` [#stdurl]

```sakura
type Url = String where is_valid(it)
fn is_valid(s: String) -> Bool    // @total
fn host(u: Url) -> Option<String>
fn path(u: Url) -> String
fn query_param(u: Url, name: String) -> Option<String>
fn template(parts: List<String>, args: List<String>) -> Url
fn encode_component(s: String) -> String
```

### `std/request` [#stdrequest]

```sakura
type Method = Get | Head | Post | Put | Patch | Delete
type Request = { method: Method, url: Url, headers: List<(String, String)>, body: String, secret_headers: List<(String, Secret<String>)>, params: List<(String, String)> }
error BadRequest(detail: String)
fn of(method: Method, url: Url) -> Request
fn post(path: String, body: String) -> Request
fn post_json<T: Schema>(url: Url, body: T) -> Request
fn json<T: Schema>(req: Request, limit: Int) -> T ! {Fail<BadRequest>}
fn param(req: Request, name: String) -> Option<String>
fn header(req: Request, name: String) -> Option<String>
```

### `std/response` [#stdresponse]

```sakura
type Status = Int where it >= 100 && it <= 599
type Response = { status: Status, headers: List<(String, String)>, body: String }
error HttpError(status: Option<Status>, reason: String)
fn status(code: Int, body: String) -> Response requires code >= 100 && code <= 599
fn ok_json<T: Schema>(value: T) -> Response
fn html(h: Html) -> Response
fn json<T: Schema>(r: Response, limit: Int) -> T ! {Fail<HttpError | DecodeError>}
fn text(r: Response, limit: Int) -> Untrusted<String> ! {Fail<HttpError>}
```

### `std/http` [#stdhttp]

```sakura
type Matcher = Any | Path(String) | Method(Method)
error RateLimited(retry_after: Option<Duration>)
effect Http {
  fn send(req: Request) -> Response ! {Fail<HttpError>}
}
fn get(url: Url) -> Response ! {Http, Fail<HttpError | RateLimited>}
fn post_json<T: Schema>(url: Url, body: T) -> Response ! {Http, Fail<HttpError | RateLimited>}
fn bearer(req: Request, token: Secret<String>) -> Request
fn host() -> Handler<Http>
fn scoped(hosts: List<String>) -> Handler<Http, {Http}>
fn recording(path: Path) -> Handler<Http, {Http, Fs}>
fn scripted(script: List<(Matcher, Response)>) -> Handler<Http>
```

### `std/html` [#stdhtml]

```sakura
opaque type Html = Html(String)
fn text(s: String) -> Html
fn untrusted(u: Untrusted<String>) -> Html
fn raw(markup: String, reason: Trust) -> Html
fn from<T: Show>(v: T) -> Html    // each non-`Html` html"…" value goes through it (§17.77)
fn concat(parts: List<Html>) -> Html
fn to_string(h: Html) -> String
fn template(parts: List<String>, args: List<Html>) -> Html
```

### `std/router` [#stdrouter]

```sakura
opaque type Router<e> = { routes: List<Route<e>> }
fn new<e>() -> Router<e>
fn route<e>(r: Router<e>, method: Method, pattern: String, h: fn(Request) -> Response ! e) -> Router<e>
fn get<e>(r: Router<e>, pattern: String, h: fn(Request) -> Response ! e) -> Router<e>
fn post<e>(r: Router<e>, pattern: String, h: fn(Request) -> Response ! e) -> Router<e>
fn put<e>(r: Router<e>, pattern: String, h: fn(Request) -> Response ! e) -> Router<e>
fn delete<e>(r: Router<e>, pattern: String, h: fn(Request) -> Response ! e) -> Router<e>
fn handle<e>(r: Router<e>, req: Request) -> Response ! e
```

### `std/serve` [#stdserve]

```sakura
type Address = { host: String, port: Int }
error ServeError(reason: String)
effect Serve {
  fn serve<e>(handle: fn(Request) -> Response ! e, addr: Address) -> Never ! {Fail<ServeError> | e}
}
fn listen<e>(router: Router<e>, addr: Address) -> Never ! {Serve, Fail<ServeError> | e}
fn of(host: String, port: Int) -> Address requires port >= 0 && port <= 65_535
fn host() -> Handler<Serve>
fn scripted(requests: List<Request>) -> Handler<Serve, {Test}>
```

### `std/budget` [#stdbudget]

```sakura
type Limits = { tokens: Option<Int>, usd: Option<Float>, calls: Option<Int> }
type Cost = { tokens: Int, usd: Float, calls: Int }
type Reservation = { estimate: Cost }
type BudgetKind = Tokens | Usd | Calls
error BudgetExceeded(kind: BudgetKind)
effect Budget {
  fn reserve(est: Cost) -> Reservation ! {Fail<BudgetExceeded>}
  fn settle(r: Reservation, actual: Cost) -> Unit
}
fn limits(l: Limits) -> Handler<Budget>
fn unlimited() -> Handler<Budget>
fn zero() -> Cost
```

### `std/prompt` [#stdprompt]

```sakura
type PromptPart = Text(String) | Untrusted(Untrusted<String>) | Image(Bytes)
opaque type Prompt = Prompt(List<PromptPart>)
fn of(parts: List<PromptPart>) -> Prompt
fn text(s: String) -> Prompt
fn parts(p: Prompt) -> List<PromptPart>
```

### `std/llm` [#stdllm]

```sakura
type Model = Model(String)
type Request<T> = { system: String, user: Prompt, model: Model, max_tokens: Int, temperature: Float }
error Refused
error InvalidOutput(violations: List<Violation>)
error Truncated
error ProviderError(status: Int)
type LlmError = Refused | InvalidOutput | Truncated | RateLimited | Timeout | ProviderError | BudgetExceeded
type ToolSpec = { name: String, description: String, schema: Json }
type ToolCall = { id: String, name: String, input: Json }
type ToolResult = { id: String, output: Json }
type Turn = Asked(List<ToolCall>) | Answered(List<ToolResult>)
type Step<T> = Answer(T) | Use(List<ToolCall>)
type Partial<T> = { text: String, step: Int, calls: List<ToolCall> }
effect Llm {
  fn generate<T: Schema>(req: Request<T>) -> T ! {Fail<LlmError>}
  fn generate_text(req: Request<String>) -> Untrusted<String> ! {Fail<LlmError>}
  fn stream<T: Schema, e>(req: Request<T>, on_partial: fn(Partial<T>) -> Unit ! e) -> T ! {Fail<LlmError> | e}
  fn step<T: Schema>(req: Request<T>, tools: List<ToolSpec>, transcript: List<Turn>) -> Step<T> ! {Fail<LlmError>}
}
fn request<T: Schema>(system: String, user: Prompt) -> Request<T>
fn of(name: String) -> Model
fn default() -> Model
fn anthropic(key: Secret<String>, model: Model) -> Handler<Llm, {Http, Budget}>    // @external
fn replay(path: Path) -> Handler<Llm, {Fs}>
fn scripted(responses: List<String>) -> Handler<Llm>
```

### `std/tool` [#stdtool]

```sakura
opaque type Tool<e> = { spec: ToolSpec, run: fn(Json) -> Json ! {Fail<ToolError> | e} }
error ToolError(tool: String, tag: String, payload: Json)
fn from<A: Schema, R: Schema, E: Schema + Show, e>(name: String, description: String, f: fn(A) -> R ! {Fail<E> | e}) -> Tool<e>
fn name<e>(t: Tool<e>) -> String
fn spec<e>(t: Tool<e>) -> ToolSpec
fn call<e>(t: Tool<e>, input: Json) -> Json ! {Fail<ToolError> | e}
```

### `std/agent` [#stdagent]

```sakura
type Agent<T, e> = { request: Request<T>, tools: List<Tool<e>>, max_steps: Int, stop: fn(Partial<T>) -> Bool }
fn run<T: Schema, e>(a: Agent<T, e>) -> T ! {Budget, Llm, Fail<LlmError | ToolError> | e} requires a.max_steps > 0
```

### `std/sql` [#stdsql]

```sakura
type SqlValue = Null | Int(Int) | Float(Float) | Text(String) | Blob(Bytes)
opaque type Query<R> = { text: String, params: List<SqlValue> }
error SqlError(message: String)
effect Sql {
  fn query<R: Schema>(q: Query<R>) -> List<R> ! {Fail<SqlError>}
  fn execute(q: Query<Unit>) -> Int ! {Fail<SqlError>}
  fn transaction<T, e>(body: fn() -> T ! {Sql | e}) -> T ! {Fail<SqlError> | e}
}
fn from<T: Schema>(value: T) -> SqlValue    // each sql"…" value goes through it (§4.11)
fn template<R>(parts: List<String>, args: List<SqlValue>) -> Query<R>
fn migrate(statements: List<Query<Unit>>) -> Unit ! {Sql, Fail<SqlError>}
fn sqlite(path: Path) -> Handler<Sql, {Fs}>    // @external
fn memory() -> Handler<Sql>    // @external
```
