/* * el_runtime.h — El language C runtime header * * Declares all built-in functions available to compiled El programs. * Include this in every generated .c file. * * Value model: * All El values are represented as el_val_t (= int64_t). * On 64-bit systems a pointer fits in int64_t. * String values are cast: (el_val_t)(uintptr_t)"hello" * Integer values are stored directly. * This lets arithmetic work naturally while still passing strings around. * * Type conventions (El -> C): * String -> el_val_t (holds const char* via uintptr_t cast) * Int -> el_val_t * Bool -> el_val_t (0 = false, nonzero = true) * Any -> el_val_t * Void -> void * * Macros for convenience: * EL_STR(s) cast string literal to el_val_t * EL_CSTR(v) cast el_val_t back to const char* * EL_INT(v) identity — el_val_t is already int64_t * * Link requirements: * -lcurl — required for the HTTP client (http_get, http_post, llm_*). * -lpthread — required for the HTTP server (one detached thread per * connection, capped at 64 concurrent). * -loqs — optional; required only when liboqs is installed and the * pq_* / sha3_256_hex entry points are needed. Detected at * compile time via __has_include(). * -lcrypto — optional; pulled in alongside -loqs. Used for X25519 in * pq_hybrid_* and HKDF-SHA256 derivation. * * Canonical compile command: * cc -std=c11 -I runtime -lcurl -lpthread \ * -o .c runtime/el_runtime.c * * With liboqs (post-quantum stack): * cc -std=c11 -I runtime -lcurl -lpthread -loqs -lcrypto \ * -o .c runtime/el_runtime.c */ #pragma once #include #include typedef int64_t el_val_t; #define EL_STR(s) ((el_val_t)(uintptr_t)(s)) #define EL_CSTR(v) ((const char*)(uintptr_t)(v)) #define EL_INT(v) (v) #define EL_NULL ((el_val_t)0) /* Float values share the el_val_t (int64) slot via a bit-cast. * The codegen emits Float literals as `el_from_float()` so the * underlying bits represent the IEEE 754 double. Float-aware builtins * (math, format, json) round-trip via these helpers. */ static inline double el_to_float(el_val_t v) { union { int64_t i; double f; } u; u.i = (int64_t)v; return u.f; } static inline el_val_t el_from_float(double f) { union { double f; int64_t i; } u; u.f = f; return (el_val_t)u.i; } #ifdef __cplusplus extern "C" { #endif /* ── I/O ──────────────────────────────────────────────────────────────────── */ void println(el_val_t s); void print(el_val_t s); el_val_t readline(void); /* ── String builtins ─────────────────────────────────────────────────────── */ el_val_t el_str_concat(el_val_t a, el_val_t b); el_val_t str_eq(el_val_t a, el_val_t b); el_val_t str_starts_with(el_val_t s, el_val_t prefix); el_val_t str_ends_with(el_val_t s, el_val_t suffix); el_val_t str_len(el_val_t s); el_val_t str_concat(el_val_t a, el_val_t b); el_val_t int_to_str(el_val_t n); el_val_t str_to_int(el_val_t s); el_val_t str_slice(el_val_t s, el_val_t start, el_val_t end); el_val_t str_contains(el_val_t s, el_val_t sub); el_val_t str_replace(el_val_t s, el_val_t from, el_val_t to); el_val_t str_to_upper(el_val_t s); el_val_t str_to_lower(el_val_t s); el_val_t str_trim(el_val_t s); /* ── Math ────────────────────────────────────────────────────────────────── */ el_val_t el_abs(el_val_t n); el_val_t el_max(el_val_t a, el_val_t b); el_val_t el_min(el_val_t a, el_val_t b); /* ── Refcount (ARC) ────────────────────────────────────────────────────────── * Lists and Maps carry a refcount. Strings and ints do not — el_retain and * el_release are safe no-ops on non-refcounted values (they sniff a magic * header at offset 0 and only act if the magic matches). * * Codegen emits these at let-binding shadowing, function entry (params), and * function exit (locals other than the returned value). The refcount lets * el_list_append and el_map_set mutate in place when uniquely owned (cheap) * and copy-on-write when shared (preserves persistent semantics across * accumulator patterns in the compiler itself). */ void el_retain(el_val_t v); void el_release(el_val_t v); /* ── Arena scoping ──────────────────────────────────────────────────────────── * el_arena_push() activates the string arena (if not already active) and * returns a mark; el_arena_pop(mark) frees all strings allocated since that * mark. Used by codegen for per-function/statement scoping and by long-running * EL loops (e.g. the soul daemon's awareness tick) to reclaim per-iteration * allocations. */ el_val_t el_arena_push(void); el_val_t el_arena_pop(el_val_t mark); /* ── List ────────────────────────────────────────────────────────────────── */ el_val_t el_list_new(el_val_t count, ...); el_val_t el_list_len(el_val_t list); el_val_t el_list_get(el_val_t list, el_val_t index); el_val_t el_list_append(el_val_t list, el_val_t elem); el_val_t el_list_empty(void); el_val_t el_list_clone(el_val_t list); /* ── Map ─────────────────────────────────────────────────────────────────── */ el_val_t el_map_new(el_val_t pair_count, ...); el_val_t el_get_field(el_val_t map, el_val_t key); el_val_t el_map_get(el_val_t map, el_val_t key); el_val_t el_map_set(el_val_t map, el_val_t key, el_val_t value); /* ── HTTP ─────────────────────────────────────────────────────────────────── */ el_val_t http_get(el_val_t url); el_val_t http_post(el_val_t url, el_val_t body); el_val_t http_post_json(el_val_t url, el_val_t json_body); el_val_t http_get_with_headers(el_val_t url, el_val_t headers_map); el_val_t http_post_with_headers(el_val_t url, el_val_t body, el_val_t headers_map); el_val_t http_post_form_auth(el_val_t url, el_val_t form_body, el_val_t auth_header); el_val_t http_delete(el_val_t url); el_val_t http_delete_json(el_val_t url, el_val_t json_body); void http_serve(el_val_t port, el_val_t handler); void http_set_handler(el_val_t name); /* HTTP server v2 ───────────────────────────────────────────────────────────── * Same dispatch model as http_serve, but the handler signature is widened: * * el_val_t handler(method, path, headers_map, body) * * `headers_map` is an ElMap from lowercased header name → header value (both * Strings). Repeated headers are joined with ", " per RFC 7230. * * Response value: the handler may return either * (a) a plain body string — same auto-content-type / 200-OK behaviour as * http_serve (3-arg) — or * (b) a response envelope built with `http_response(status, headers_json, * body)`. The runtime detects the envelope discriminator * `"el_http_response":1` at the start of the returned string and * unpacks status / headers / body before sending. * * The 3-arg http_serve(port, handler) remains supported unchanged for * existing handlers (e.g. products/web/server.el): it dispatches with * (method, path, body), hardcodes 200 OK, and auto-detects content type. */ void http_serve_v2(el_val_t port, el_val_t handler); void http_set_handler_v2(el_val_t name); /* Non-blocking variant of http_serve: runs the accept loop in a background * pthread and returns immediately so the caller can continue (used by the * soul daemon to run awareness_run() after starting its HTTP API). */ void http_serve_async(el_val_t port, el_val_t handler); /* Build an HTTP response envelope. `headers_json` should be a JSON object * literal like `{"WWW-Authenticate":"Basic"}` (or "" / "{}" for none). The * returned string carries the discriminator `{"el_http_response":1,...}` * which the runtime's send-path detects and unpacks. Detection happens * uniformly inside http_send_response, so a 3-arg handler may also return * an envelope. The 3-arg variant remains documented as a fixed 200-OK * auto-content-type contract for legacy handlers that return plain bodies. */ el_val_t http_response(el_val_t status, el_val_t headers_json, el_val_t body); /* HTTP timeout — every libcurl request honors EL_HTTP_TIMEOUT_MS (default * 60000ms). Read lazily on first use, so setting the env var any time before * the first http_* call is sufficient. */ /* Streaming variants — write the response body straight to a file via * libcurl's CURLOPT_WRITEFUNCTION = fwrite. These bypass the el_val_t string * wrapper entirely, so binary payloads (audio/mpeg, image/png, etc.) survive * embedded NUL bytes that would truncate a strlen()-based code path. * * Both honor EL_HTTP_TIMEOUT_MS, follow redirects, and accept the same * `headers_map` shape as http_post_with_headers (ElMap of String→String). * * Return value: 1 on success (file fully written), 0 on any failure * (network, file open, partial write). On failure the output file is removed * so callers cannot mistake a partially-written file for a valid one. */ el_val_t http_post_to_file(el_val_t url, el_val_t body, el_val_t headers_map, el_val_t output_path); el_val_t http_get_to_file(el_val_t url, el_val_t headers_map, el_val_t output_path); /* ── URL encoding ────────────────────────────────────────────────────────── */ el_val_t url_encode(el_val_t s); /* RFC 3986 unreserved set */ el_val_t url_decode(el_val_t s); /* '+' → space, %XX → byte */ /* ── HTML allowlist sanitizer ──────────────────────────────────────────────── * el_html_sanitize(input_html, allowlist_json) — strict allowlist HTML * cleaner. State-machine parser; tag/attribute names compared case- * insensitively against the allowlist; `` / `<… src>` URL schemes * validated (http, https, mailto, fragment-only, or relative); whole- * subtree drop for script / style / iframe / object / embed / form; HTML- * escapes free text outside dropped subtrees. * * The allowlist is JSON of the form * {"p":[],"a":["href","title"],"strong":[],...} * where each value is the array of attribute names allowed for that tag. */ el_val_t el_html_sanitize(el_val_t input_html, el_val_t allowlist_json); /* ── Filesystem ──────────────────────────────────────────────────────────── */ el_val_t fs_read(el_val_t path); el_val_t fs_write(el_val_t path, el_val_t content); el_val_t fs_list(el_val_t path); el_val_t fs_exists(el_val_t path); el_val_t fs_mkdir(el_val_t path); /* mkdir -p, mode 0755 */ /* Real on-disk byte count via stat() — not strlen(). Use this (not * str_len(fs_read(path))) when a file may contain binary content, since * fs_read()'s result truncates at the first embedded NUL under strlen-based * string ops. Returns -1 if the path doesn't exist. */ el_val_t fs_size(el_val_t path); /* Binary-safe windowed read: read up to `length` bytes starting at byte * `offset` from `path` and return them base64-encoded. Bytes are read and * encoded in C without ever passing through an el_val_t string as raw * bytes, so embedded NULs (routine in PCM audio) can't truncate the result. * Returns "" on any failure or when offset is past EOF; a final short * window returns only the bytes that actually exist on disk. */ el_val_t fs_read_b64_chunk(el_val_t path, el_val_t offset, el_val_t length); /* Length-explicit binary write. `length` is an Int (el_val_t holding the * byte count). The caller knows the length from context — typically because * `bytes` came from base64_decode (which produces a magic-tagged binary * buffer with embedded NULs possible) and the caller already tracks the * decoded length, OR because the bytes came from a fixed-size source * (sha256_bytes = 32, hmac_sha256_bytes = 32). Bypasses strlen entirely. * * Returns 1 on success, 0 on failure (invalid path, can't open, partial * write, negative length). On partial-write failure, the file is removed * so callers cannot read back a truncated artefact. */ el_val_t fs_write_bytes(el_val_t path, el_val_t bytes, el_val_t length); /* ── JSON ────────────────────────────────────────────────────────────────── */ el_val_t json_get(el_val_t json, el_val_t key); el_val_t json_parse(el_val_t s); el_val_t json_stringify(el_val_t v); el_val_t json_get_string(el_val_t json_str, el_val_t key); el_val_t json_get_int(el_val_t json_str, el_val_t key); el_val_t json_get_float(el_val_t json_str, el_val_t key); el_val_t json_get_bool(el_val_t json_str, el_val_t key); el_val_t json_get_raw(el_val_t json_str, el_val_t key); el_val_t json_set(el_val_t json_str, el_val_t key, el_val_t value); el_val_t json_array_len(el_val_t json_str); el_val_t json_array_get(el_val_t json_str, el_val_t index); el_val_t json_array_get_string(el_val_t json_str, el_val_t index); el_val_t json_escape_string(el_val_t sv); el_val_t json_build_object(el_val_t kvs); el_val_t json_build_array(el_val_t items); el_val_t json_array_push(el_val_t arr_v, el_val_t elem_v); /* defined in el_runtime.c */ /* ── Time ────────────────────────────────────────────────────────────────── */ el_val_t time_now(void); el_val_t time_now_utc(void); el_val_t sleep_secs(el_val_t secs); el_val_t sleep_ms(el_val_t ms); el_val_t time_format(el_val_t ts, el_val_t fmt); el_val_t time_to_parts(el_val_t ts); el_val_t time_from_parts(el_val_t secs, el_val_t ns, el_val_t tz); el_val_t time_add(el_val_t ts, el_val_t n, el_val_t unit); el_val_t time_diff(el_val_t ts1, el_val_t ts2, el_val_t unit); /* ── Instant + Duration: first-class temporal types ────────────────────────── * Both types share the el_val_t (int64) slot. Instants are nanoseconds * since the Unix epoch; Durations are signed nanoseconds. Type discipline * is enforced at codegen-time: BinOps on names registered as Instant or * Duration route through the typed wrappers below; mismatches like * Instant+Instant become #error at the C compiler. * * Postfix literals — `30.seconds`, `1.hour`, `500.millis`, `30.nanos` — are * recognised by the parser as DurationLit AST nodes and lowered to literal * int64 nanoseconds at codegen time. The runtime never sees the units. */ el_val_t el_now_instant(void); el_val_t now(void); el_val_t now_millis(void); /* wall-clock milliseconds (defined in el_runtime.c) */ el_val_t now_ns(void); /* wall-clock nanoseconds (defined in el_runtime.c) */ el_val_t unix_seconds(el_val_t n); el_val_t unix_millis(el_val_t n); el_val_t instant_from_iso8601(el_val_t s); el_val_t el_duration_from_nanos(el_val_t ns); el_val_t duration_seconds(el_val_t n); el_val_t duration_millis(el_val_t n); el_val_t duration_nanos(el_val_t n); el_val_t el_instant_add_dur(el_val_t inst, el_val_t dur); el_val_t el_instant_sub_dur(el_val_t inst, el_val_t dur); el_val_t el_instant_diff(el_val_t a, el_val_t b); el_val_t el_duration_add(el_val_t a, el_val_t b); el_val_t el_duration_sub(el_val_t a, el_val_t b); el_val_t el_duration_scale(el_val_t dur, el_val_t scalar); el_val_t el_duration_div(el_val_t dur, el_val_t scalar); el_val_t el_instant_lt(el_val_t a, el_val_t b); el_val_t el_instant_le(el_val_t a, el_val_t b); el_val_t el_instant_gt(el_val_t a, el_val_t b); el_val_t el_instant_ge(el_val_t a, el_val_t b); el_val_t el_instant_eq(el_val_t a, el_val_t b); el_val_t el_instant_ne(el_val_t a, el_val_t b); el_val_t el_duration_lt(el_val_t a, el_val_t b); el_val_t el_duration_le(el_val_t a, el_val_t b); el_val_t el_duration_gt(el_val_t a, el_val_t b); el_val_t el_duration_ge(el_val_t a, el_val_t b); el_val_t el_duration_eq(el_val_t a, el_val_t b); el_val_t el_duration_ne(el_val_t a, el_val_t b); el_val_t instant_to_unix_seconds(el_val_t i); el_val_t instant_to_unix_millis(el_val_t i); el_val_t instant_to_iso8601(el_val_t i); el_val_t duration_to_seconds(el_val_t d); el_val_t duration_to_millis(el_val_t d); el_val_t duration_to_nanos(el_val_t d); el_val_t el_sleep_duration(el_val_t dur); el_val_t unix_timestamp(void); el_val_t ttl_cache_set(el_val_t key, el_val_t value); el_val_t ttl_cache_get(el_val_t key, el_val_t max_age); el_val_t ttl_cache_age(el_val_t key); /* ── Calendar + CalendarTime + Rhythm + LocalDate/Time/DateTime ───────────── * Phase 1.5 of the time system. Calendar is pluggable: EarthCalendar (IANA * zones, Gregorian, DST) is the user-facing default; MarsCalendar, * CycleCalendar(period), NoCycleCalendar, RelativeCalendar handle non-Earth * domains. * * A Calendar interprets an Instant under a particular cycle convention and * produces a CalendarTime. CalendarTime carries the underlying Instant and * a back-pointer to its Calendar; arithmetic and formatting consult the * Calendar to convert ns since epoch into year/month/day/hour/minute/second * (or sol/phase, or cycle/phase, depending on kind). * * Storage convention: Calendar / CalendarTime / Rhythm / LocalDate / * LocalDateTime are heap-allocated structs whose pointers are cast into * el_val_t. A 24-bit magic header at offset 0 lets the runtime identify * the kind safely. LocalTime is small enough to live in the int64 slot * directly (nanos since midnight, signed). */ /* Zone — opaque IANA zone or fixed offset, used by EarthCalendar. * `zone_id` is either an IANA name ("America/New_York", "UTC") or a fixed * offset string ("+05:30", "-08:00"). The runtime resolves it via tzset() * on first use of the owning EarthCalendar. */ el_val_t zone(el_val_t id); el_val_t zone_utc(void); el_val_t zone_local(void); el_val_t zone_offset(el_val_t hours, el_val_t minutes); /* Calendar constructors. Each returns an el_val_t pointer to a heap- * allocated, magic-tagged Calendar struct. Calendars are interned by * (kind, zone_id, period_ns, epoch_ns) so identical constructors return * the same pointer — equality is reference equality. */ el_val_t earth_calendar(el_val_t z); el_val_t earth_calendar_default(void); el_val_t mars_calendar(void); el_val_t cycle_calendar(el_val_t period_dur); el_val_t no_cycle_calendar(void); el_val_t relative_calendar(el_val_t epoch_inst); /* CalendarTime constructors and methods. Returns a heap-allocated struct * whose pointer fits in el_val_t. */ el_val_t now_in(el_val_t cal); el_val_t in_calendar(el_val_t inst, el_val_t cal); el_val_t cal_format(el_val_t ct, el_val_t pattern); el_val_t cal_to_instant(el_val_t ct); el_val_t cal_cycle_phase(el_val_t ct); el_val_t cal_in(el_val_t ct, el_val_t cal); /* LocalDate / LocalTime / LocalDateTime — calendar-agnostic value types. * LocalTime carries nanoseconds since midnight as a signed int64 directly * in the el_val_t slot (no allocation). LocalDate / LocalDateTime are * heap-allocated structs with magic headers. */ el_val_t local_date(el_val_t y, el_val_t m, el_val_t d); el_val_t local_time(el_val_t h, el_val_t m, el_val_t s, el_val_t ns); el_val_t local_datetime(el_val_t date, el_val_t time); el_val_t zoned(el_val_t date, el_val_t time, el_val_t cal); el_val_t local_date_year(el_val_t ld); el_val_t local_date_month(el_val_t ld); el_val_t local_date_day(el_val_t ld); el_val_t local_time_hour(el_val_t lt); el_val_t local_time_minute(el_val_t lt); el_val_t local_time_second(el_val_t lt); el_val_t local_time_nanos(el_val_t lt); el_val_t el_local_date_add_dur(el_val_t ld, el_val_t dur); el_val_t el_local_time_add_dur(el_val_t lt, el_val_t dur); el_val_t el_local_date_lt(el_val_t a, el_val_t b); el_val_t el_local_date_eq(el_val_t a, el_val_t b); /* Rhythm — pluggable recurrence AST. Returns a heap-allocated struct * pointer in el_val_t; rhythms are immutable so callers may share them. */ el_val_t rhythm_cycle_start(void); el_val_t rhythm_cycle_phase(el_val_t phase); el_val_t rhythm_duration(el_val_t d); el_val_t rhythm_session_start(void); el_val_t rhythm_event(el_val_t name); el_val_t rhythm_and(el_val_t a, el_val_t b); el_val_t rhythm_or(el_val_t a, el_val_t b); el_val_t rhythm_weekday(el_val_t day); el_val_t rhythm_weekly_at(el_val_t day, el_val_t hour, el_val_t minute); el_val_t rhythm_next_after(el_val_t r, el_val_t after, el_val_t cal); el_val_t rhythm_matches(el_val_t r, el_val_t ct); /* ── UUID ────────────────────────────────────────────────────────────────── */ el_val_t uuid_new(void); el_val_t uuid_v4(void); /* ── Environment ─────────────────────────────────────────────────────────── */ el_val_t env(el_val_t key); /* ── In-process state K/V ────────────────────────────────────────────────── */ el_val_t state_set(el_val_t key, el_val_t value); el_val_t state_get(el_val_t key); el_val_t state_del(el_val_t key); el_val_t state_keys(void); /* ── Float formatting ────────────────────────────────────────────────────── */ el_val_t float_to_str(el_val_t f); el_val_t int_to_float(el_val_t n); el_val_t float_to_int(el_val_t f); el_val_t format_float(el_val_t f, el_val_t decimals); el_val_t decimal_round(el_val_t f, el_val_t decimals); el_val_t str_to_float(el_val_t s); /* ── Math (Float-aware) ──────────────────────────────────────────────────── */ el_val_t math_sqrt(el_val_t f); el_val_t math_log(el_val_t f); el_val_t math_ln(el_val_t f); el_val_t math_sin(el_val_t f); el_val_t math_cos(el_val_t f); el_val_t math_pi(void); /* ── String additions ────────────────────────────────────────────────────── */ el_val_t str_index_of(el_val_t s, el_val_t sub); el_val_t str_split(el_val_t s, el_val_t sep); el_val_t str_char_at(el_val_t s, el_val_t i); el_val_t str_char_code(el_val_t s, el_val_t i); el_val_t str_pad_left(el_val_t s, el_val_t width, el_val_t pad); el_val_t str_pad_right(el_val_t s, el_val_t width, el_val_t pad); el_val_t str_format(el_val_t fmt, el_val_t data); el_val_t str_lower(el_val_t s); el_val_t str_upper(el_val_t s); /* ── Text-processing primitives (Phase 1: byte/codepoint, ASCII char classes) * Phase 2 (filed): Unicode-grapheme awareness, NFC/NFD normalization, regex. * is_* predicates: empty input returns false; multi-char requires ALL bytes * to match. ASCII ranges only in Phase 1. */ /* Counting */ el_val_t str_count(el_val_t s, el_val_t sub); /* non-overlapping */ el_val_t str_count_chars(el_val_t s); /* codepoint count */ el_val_t str_count_bytes(el_val_t s); /* alias of str_len */ el_val_t str_count_lines(el_val_t s); el_val_t str_count_words(el_val_t s); el_val_t str_count_letters(el_val_t s); /* ASCII [A-Za-z] */ el_val_t str_count_digits(el_val_t s); /* ASCII [0-9] */ /* Find / position */ el_val_t str_index_of_all(el_val_t s, el_val_t sub); /* [Int] of byte offsets */ el_val_t str_last_index_of(el_val_t s, el_val_t sub); el_val_t str_find_chars(el_val_t s, el_val_t any_of); /* first idx of any ch */ /* Transform */ el_val_t str_repeat(el_val_t s, el_val_t n); el_val_t str_reverse(el_val_t s); /* by codepoint */ el_val_t str_strip_prefix(el_val_t s, el_val_t prefix); el_val_t str_strip_suffix(el_val_t s, el_val_t suffix); el_val_t str_strip_chars(el_val_t s, el_val_t chars); el_val_t str_lstrip(el_val_t s); el_val_t str_rstrip(el_val_t s); /* Char classification (Bool) */ el_val_t is_letter(el_val_t s); el_val_t is_digit(el_val_t s); el_val_t is_alphanumeric(el_val_t s); el_val_t is_whitespace(el_val_t s); el_val_t is_punctuation(el_val_t s); el_val_t is_uppercase(el_val_t s); el_val_t is_lowercase(el_val_t s); /* Split / join */ el_val_t str_split_lines(el_val_t s); el_val_t str_split_chars(el_val_t s); /* alias of native_string_chars */ el_val_t str_split_n(el_val_t s, el_val_t sep, el_val_t n); el_val_t str_join(el_val_t list, el_val_t sep); /* alias of list_join */ /* ── List additions ──────────────────────────────────────────────────────── */ el_val_t list_push(el_val_t list, el_val_t elem); el_val_t list_push_front(el_val_t list, el_val_t elem); el_val_t list_join(el_val_t list, el_val_t sep); el_val_t list_range(el_val_t start, el_val_t end); /* ── Bool helpers ────────────────────────────────────────────────────────── */ el_val_t bool_to_str(el_val_t b); /* ── Numeric parsing ─────────────────────────────────────────────────────── */ el_val_t parse_int(el_val_t s, el_val_t default_val); /* ── Process ─────────────────────────────────────────────────────────────── */ void exit_program(el_val_t code); el_val_t getpid_now(void); /* ── CGI identity ───────────────────────────────────────────────────────────── * Called at the start of main() in CGI programs (those with a `cgi {}` block). * Records the program's DHARMA identity before any other code executes. */ void el_cgi_init(el_val_t name, el_val_t dharma_id, el_val_t principal, el_val_t network, el_val_t engram); /* ── DHARMA network builtins ───────────────────────────────────────────────── * Available to CGI programs (declared with a `cgi {}` block). * * Peers are addressed by `dharma_id` of the form * "@" e.g. "ntn-genesis@http://localhost:7770" * If the @ portion is omitted, transport defaults to * "http://localhost:7770" (the local CGI daemon assumption). * * Wire protocol (all peers expose): * POST /dharma/recv { channel, from, content } → response body * POST /dharma/event { type, payload, source, timestamp } * POST /api/activate { query } → list of nodes * * Hosting application's responsibility: an El program with a `cgi {}` block * runs http_serve() with its own request handler; that handler should route * "/dharma/event" requests by calling el_runtime_dharma_event_arrive() so * incoming events feed dharma_field() queues. The runtime itself does not * intercept any /dharma path. */ el_val_t dharma_connect(el_val_t cgi_id); el_val_t dharma_send(el_val_t channel, el_val_t content); el_val_t dharma_activate(el_val_t query); void dharma_emit(el_val_t event_type, el_val_t payload); el_val_t dharma_field(el_val_t event_type); void dharma_strengthen(el_val_t cgi_id, el_val_t weight); el_val_t dharma_relationship(el_val_t cgi_id); el_val_t dharma_peers(void); /* Public C API: called by an El program's HTTP handler when a /dharma/event * request arrives. Pushes onto the per-event-type queue and signals any * pending dharma_field() blockers. All three arguments must be NUL-terminated * C strings (or NULL — then treated as empty). */ void el_runtime_dharma_event_arrive(const char* event_type, const char* payload, const char* source); /* ── Geometry: signal as a first-class El value ────────────────────────────── * * A Geometry is an opaque, magic-tagged heap value carried in an el_val_t — * the same discipline as List/Map. It holds a width and a float32 payload, * and it is the medium a non-text modality enters in. Declared HERE, above * the engram block, because transduction is a LANGUAGE concern: every El * program touching any modality needs it, and the engram is merely one El * program that happens to hold a graph. See el_runtime.c ("Geometry: signal * as a first-class el value") for the full rationale. * * El-side type annotation is simply `Geometry` — an opaque boxed pointer, * exactly like Instant / Calendar / Rhythm. No codegen change is required. * * OWNERSHIP: a Geometry is owned by the El caller and released with * geometry_free. node_attach_geometry COPIES, so a node and the caller's * value have independent lifetimes. */ el_val_t geometry_new(el_val_t dim); /* zero-filled; 0 on failure */ el_val_t geometry_dim(el_val_t g); /* width, 0 if not a Geometry */ el_val_t geometry_is(el_val_t g); /* 1 if a live Geometry */ el_val_t geometry_get(el_val_t g, el_val_t i); /* Float component */ el_val_t geometry_set(el_val_t g, el_val_t i, el_val_t x); /* 1 ok / 0 out of range */ el_val_t geometry_norm(el_val_t g); /* Float L2 — lets a caller * check a realizer emitted * signal, not zeros */ el_val_t geometry_free(el_val_t g); /* 1 if freed, 0 if not a Geometry. * Returns a value (not void) so it * is safe in any El expression * position without a codegen * void-builtin table entry. */ /* Wire ADAPTERS — the only place an encoding appears, and only at the edge. * `f32le hex` is little-endian float32, 8 hex chars per component: the * encoding the perception vessel's /voice/embed already emits. The width is * DERIVED from the input length, never supplied by a caller — which is why * there is no max-dim constant here to validate a claimed length against. */ el_val_t geometry_from_f32le_hex(el_val_t hex); /* 0 on empty/odd-length/non-hex */ el_val_t geometry_to_f32le_hex(el_val_t g); /* "" if not a Geometry */ /* ── Realizers + transduce ─────────────────────────────────────────────────── * A REALIZER maps one modality into geometry. Registration is by NAME, so a * new modality never requires a runtime patch: every El `fn name(...)` * compiles to a global C symbol with that exact name, and the registry * resolves it with dlsym against the running binary — the same mechanism * http_set_handler already relies on. * * fn tone_realizer(signal: String) -> Geometry { ... } * realizer_register("tone", "tone_realizer") * let g: Geometry = transduce(sample, "tone") */ el_val_t realizer_register(el_val_t modality, el_val_t fn_name); /* 1 ok / 0 unresolved */ el_val_t realizer_has(el_val_t modality); /* 1 if a realizer is registered */ el_val_t transduce(el_val_t signal, el_val_t modality); /* Geometry, or 0 if no organ */ /* ── Engram local graph primitives ─────────────────────────────────────────── * Operate on the CGI's local Engram knowledge graph. * `engram_activate` queries the local graph only; `dharma_activate` is * network-wide across all connected CGI graphs. */ el_val_t engram_node(el_val_t content, el_val_t node_type, el_val_t salience); el_val_t engram_node_full(el_val_t content, el_val_t node_type, el_val_t label, el_val_t salience, el_val_t importance, el_val_t confidence, el_val_t tier, el_val_t tags); /* Layered consciousness — see el_runtime.c for the layered architecture * design notes (search "Layered consciousness architecture"). The five * canonical layers (safety / core-identity / domain-knowledge / imprint / * suit) are seeded automatically; engram_add_layer extends the registry * with imprint or suit overlays at runtime. Nodes default to layer 1 * (core-identity) when created via engram_node / engram_node_full. */ el_val_t engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label, el_val_t salience, el_val_t certainty, el_val_t confidence, el_val_t status, el_val_t tags, el_val_t layer_id); el_val_t engram_add_layer(el_val_t name, el_val_t priority, el_val_t suppressible, el_val_t transparent, el_val_t injectable); el_val_t engram_remove_layer(el_val_t layer_id); el_val_t engram_list_layers(void); el_val_t engram_get_node(el_val_t id); void engram_strengthen(el_val_t node_id); void engram_forget(el_val_t node_id); el_val_t engram_prune_telemetry(el_val_t older_than_ms); /* Largest byte length <= max_bytes that does not split a UTF-8 codepoint. * Bounded by bytes, not codepoints, so truncated strings never grow. */ size_t el_utf8_safe_len(const char* s, size_t max_bytes); el_val_t engram_node_count(void); /* Attach a Geometry to an existing node, and read the attached width back. * Named for the operation, not the store: a node acquires geometry. This is * the geometry-valued ingest path — nothing about it is hex, and nothing * about it assumes the caller's vector matches the canonical text-embedding * width. node_geometry_dim exists so an attach is VERIFIED by reading it * back rather than by trusting a success return. */ el_val_t node_attach_geometry(el_val_t node_id, el_val_t g); /* 1 ok / 0 otherwise */ el_val_t node_geometry_dim(el_val_t node_id); /* width, 0 if none */ /* DEPRECATED (shipped in #141, superseded 2026-08-16). Equivalent to * geometry_from_f32le_hex + node_attach_geometry, and now implemented as * exactly that. Kept only so anything built against the #141 runtime keeps * linking; `dim` is accepted but treated as an assertion about the vector's * width rather than as its source. New code should not call this — a hex * string is a wire encoding, not a way to move geometry between two pieces * of El. Returns 1 on success, 0 otherwise. */ el_val_t engram_node_set_emb(el_val_t id, el_val_t hex, el_val_t dim); el_val_t engram_search(el_val_t query, el_val_t limit); el_val_t engram_scan_nodes(el_val_t limit, el_val_t offset); void engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation); el_val_t engram_edge_between(el_val_t from_id, el_val_t to_id); el_val_t engram_neighbors(el_val_t node_id); el_val_t engram_neighbors_filtered(el_val_t node_id, el_val_t max_depth, el_val_t direction); el_val_t engram_edge_count(void); /* Three-pass activation: background fan-out → working-memory promotion → * Layer 0 override. See "Three-pass activation" in el_runtime.c. */ el_val_t engram_activate(el_val_t query, el_val_t depth); el_val_t engram_save(el_val_t path); el_val_t engram_load(el_val_t path); /* Tiered paged-store entry points (ENGRAM_STORE=1). engram_store_boot opens the * durable store (import-once / WAL-replay) and loads it resident; checkpoint pushes * the resident graph's current field state (incl. learned hebb + activation-formed * edges) through the WAL and flushes; close checkpoints + closes. No-ops when off. */ el_val_t engram_store_boot(el_val_t data_dir); el_val_t engram_store_checkpoint(void); el_val_t engram_store_close(void); /* JSON-string accessors — return pre-serialized JSON so HTTP handlers * can pass results straight through without round-tripping ElList/ElMap * through json_stringify. */ el_val_t engram_get_node_json(el_val_t id); el_val_t engram_get_node_by_label(el_val_t label); el_val_t engram_search_json(el_val_t query, el_val_t limit); el_val_t engram_retrieve_geometric_json(el_val_t query, el_val_t limit); el_val_t engram_scan_nodes_json(el_val_t limit, el_val_t offset); el_val_t engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset); el_val_t engram_scan_nodes_emb_json(el_val_t limit, el_val_t offset); el_val_t engram_dreams_json(el_val_t since_ms); /* §5 geometry operators as EL builtins (read-only; seed-id CSV args). */ el_val_t engram_geo_descriptor_json(el_val_t seeds); el_val_t engram_geo_overlap_json(el_val_t a_seeds, el_val_t b_seeds); el_val_t engram_geo_subtract_json(el_val_t a_seeds, el_val_t b_seeds, el_val_t mode); el_val_t engram_geo_combine_json(el_val_t a_seeds, el_val_t b_seeds); el_val_t engram_geo_distance_json(el_val_t a_seeds, el_val_t b_seeds); el_val_t engram_geo_analogy_json(el_val_t a_seeds, el_val_t b_seeds); /* reasoning layer (compositions over §5 operators). ANALOGY maps cleanly to the * flat-CSV seed ABI; the other modes take set/point/timestamp inputs deferred from * this ABI (see engram_reason.h / the reasoning-operators runbook). */ el_val_t engram_reason_analogy_json(el_val_t a_seeds, el_val_t b_seeds, el_val_t c_seeds); /* COGNITION (2026-08-14): THE ONE OPERATION + grounding, surfaced live. */ el_val_t engram_think_json(el_val_t seeds, el_val_t faculty); el_val_t engram_ground_json(el_val_t claim, el_val_t evidence, el_val_t for_whom); el_val_t engram_assert_json(el_val_t claim_id, el_val_t for_whom, el_val_t floor); el_val_t engram_attend_json(el_val_t node_id, el_val_t observer, el_val_t salience); el_val_t engram_correspondence_beat_json(el_val_t seeds, el_val_t faculty, el_val_t keystone); el_val_t engram_consolidate_permanence(el_val_t node_id); el_val_t engram_age_field(el_val_t delta_ms); el_val_t engram_age_field_catchup(void); el_val_t engram_chrono_persist_tick(void); el_val_t engram_chrono_tick(void); el_val_t engram_boundary_beat(el_val_t op_name); /* API-reshape decorator-seam auto-emit */ el_val_t engram_self_anchor_capture(void); el_val_t engram_self_drift_json(void); el_val_t engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction); el_val_t engram_activate_json(el_val_t query, el_val_t depth); el_val_t engram_stats_json(void); el_val_t engram_act_stats_json(void); el_val_t engram_text_health_json(void); el_val_t engram_cosine_sim(el_val_t id_a, el_val_t id_b); /* M10 reified-neighborhood read-only HTTP surface (2026-08-13). List/detail of * the resident reify index; [] until the offline reify writer has run. */ el_val_t engram_geo_reify_list_json(void); el_val_t engram_geo_reify_get_json(el_val_t id); /* WRITE: run reification — persist Neighborhood nodes + member edges, rebuild the * resident index. Wires the previously-dormant engram_geo_reify_store. */ el_val_t engram_geo_reify_run_json(void); /* SELF-REIFICATION (2026-08-14). ON-BEAT autonomous neighborhood formation: * gated by ENGRAM_SELF_REIFY (default off → returns {"enabled":false}, writes * nothing — the live binary is byte-inert until the flag is set). When on, runs * ONE bounded, incremental, idempotent reification pass (change-detection skips * unchanged hubs → no re-append; grounded names; residue-preserving supersession; * soft/overlapping membership; nests only when something changed). Meant to be * pumped every heartbeat next to Hebbian consolidation. Returns * {"enabled":true,"reified":N,"skipped":S,"superseded":P,"nested":X,"resident":M,"wrote":b}. */ el_val_t engram_self_reify_beat_json(void); /* ASYNC EXPLICIT OVERRIDE (degenerate manual case). Rename a live neighborhood * by id: writes a superseding record with the new name, prepends a residue entry * (cause="explicit-override", prior name), keeps the same geometry/members. Never * blocks the beat. Returns {"ok":true,"renamed":,"new_id":,"name":..}. */ el_val_t engram_neighborhood_rename_json(el_val_t id, el_val_t name); /* Orphan prevention: form up to k semantic-similar edges to a node's nearest * embedded neighbors (kNN). engram_nearest_json is the read-only probe. */ el_val_t engram_autoconnect_node(el_val_t id, el_val_t k, el_val_t min_sim_pct); el_val_t engram_nearest_json(el_val_t id, el_val_t k); /* Telemetry off-graph: append one ISE JSON line to the state-event log tier. */ el_val_t engram_ise_log_append(el_val_t content); /* Destructively pop up to `max` newly-formed Hebbian associations as a JSON * array of {from_id,to_id,weight,hebb}. The learning process (soul daemon) is * not the process that owns persistence (engram HTTP server); this is how a * self-formed association crosses that boundary. (2026-08-07 self-review.) */ el_val_t engram_hebb_drain_json(el_val_t max); /* Document frequency of a term across node labels — term-specificity signal * for curiosity seed selection. (2026-08-03 self-review.) */ el_val_t engram_label_df(el_val_t term); /* Best curiosity seed from one node: argmax over idf·position·casing across * the candidate tokens of its label, falling back to its content when the * label is a sentinel. Excludes pipe-delimited tabu terms during selection * and gates candidates to the df band [min_df, max_df]. Returns "" when * nothing qualifies. (2026-08-13 self-review.) */ el_val_t engram_salient_term(el_val_t node_id, el_val_t max_df, el_val_t min_df, el_val_t tabu); el_val_t engram_embed_backfill(el_val_t count); /* op_assert seam: grounded assertion envelope {subject,grounding} for the realizer. */ el_val_t engram_op_assert_json(el_val_t node_id, el_val_t depth); /* Parametric mutation (purview write-side): purview==0 => G=live (default), else refuse. */ el_val_t engram_node_full_in(el_val_t purview, el_val_t content, el_val_t node_type, el_val_t label, el_val_t salience, el_val_t importance, el_val_t confidence, el_val_t tier, el_val_t tags); void engram_connect_in(el_val_t purview, el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation); el_val_t engram_list_layers_json(void); /* Working memory introspection — count, mean weight, and top-N snapshot. * Ported from runtime on 2026-06-30 self-review. */ el_val_t engram_wm_count(void); el_val_t engram_wm_avg_weight(void); el_val_t engram_wm_top_json(el_val_t n); /* Merge-load: add nodes/edges from a snapshot without resetting the store. */ el_val_t engram_load_merge(el_val_t path); /* ── WAL + compaction + integrity (ENGRAM_WAL=on; design doc §§3-14,§18) ──── */ int engram_wal_enabled(void); el_val_t engram_crc32(el_val_t s); el_val_t engram_wal_boot(el_val_t dir); /* replay + open; returns records */ el_val_t engram_wal_open_dir(el_val_t dir); el_val_t engram_wal_node_put(el_val_t dir, el_val_t id); el_val_t engram_wal_edges_since(el_val_t dir, el_val_t start_count); el_val_t engram_wal_hebb_batch(el_val_t dir, el_val_t start_count); el_val_t engram_wal_forget(el_val_t dir, el_val_t id); el_val_t engram_wal_compact(el_val_t dir); el_val_t engram_wal_maybe_compact(el_val_t dir); el_val_t engram_resolve_data_dir(void); /* §18.2 fail-loud default */ el_val_t engram_is_protected(el_val_t id); /* §18.1/18.3 derived set */ el_val_t engram_protected_json(void); /* engram_compile_layered_json — produce a prompt-ready text block split * into "[LAYER 0 — STRUCTURAL]" (non-suppressible layers, sacred fire) * and "[ENGRAM CONTEXT]" (standard suppressible layers). Returns "" if * no nodes promoted to working memory. */ el_val_t engram_compile_layered_json(el_val_t intent, el_val_t depth); /* ── LLM (Anthropic API client) ───────────────────────────────────────────── * All functions call https://api.anthropic.com/v1/messages with the API key * from env ANTHROPIC_API_KEY. Default model when empty: claude-sonnet-4-5. */ el_val_t llm_call(el_val_t model, el_val_t prompt); el_val_t llm_call_system(el_val_t model, el_val_t system_prompt, el_val_t user_prompt); el_val_t llm_call_agentic(el_val_t model, el_val_t system, el_val_t user, el_val_t tools); /* LLM token telemetry (CCR §4.4): usage.{input,output}_tokens parsed from every * response. Returns Map{input_tokens,output_tokens,total_input_tokens, * total_output_tokens,calls}. */ el_val_t llm_last_usage(void); /* Fold usage.{input,output}_tokens from a raw messages response into the counters. * Called internally on every LLM response; exported for offline testing. */ void llm_record_usage(const char* resp); el_val_t llm_vision(el_val_t model, el_val_t system, el_val_t prompt, el_val_t image_url_or_b64); el_val_t llm_models(void); /* Register a tool handler by name. The handler is looked up via dlsym * (mirroring http_set_handler), so any El `fn (input)` compiles to * a global C symbol that this function can locate at runtime. * Handler signature: `el_val_t handler(el_val_t input_json)` — receives * the tool input as a JSON-string el_val_t and returns a JSON-string * el_val_t result. Used by llm_call_agentic. */ void llm_register_tool(el_val_t name, el_val_t handler_fn_name); /* ── args() ───────────────────────────────────────────────────────────────── * Provides access to command-line arguments passed to the program. * Populated by el_runtime_init_args() before main() runs. */ el_val_t args(void); void el_runtime_init_args(int argc, char** argv); /* ── Crypto primitives ───────────────────────────────────────────────────── * SHA-256, HMAC-SHA-256, and base64 (standard + URL-safe). * Self-contained — no OpenSSL/libcrypto dependency. The implementations are * adapted from public-domain reference code (Brad Conte / RFC 4648). * * Bytes-returning variants (sha256_bytes, hmac_sha256_bytes) return a string * value whose contents are raw binary; callers usually feed these into * base64_encode. Note that el_val_t strings are NUL-terminated by convention, * so the binary payload may contain embedded NULs — pass it directly into * base64_encode (which uses an explicit length) rather than treating it as * a printable C string. * * The "base64" variants emit/accept RFC 4648 standard alphabet with padding. * The "base64url" variants use URL-safe alphabet (`-`/`_`) with no padding, * as used in JWTs. */ el_val_t sha256_hex(el_val_t input); el_val_t sha256_bytes(el_val_t input); el_val_t hmac_sha256_hex(el_val_t key, el_val_t message); el_val_t hmac_sha256_bytes(el_val_t key, el_val_t message); el_val_t base64_encode(el_val_t input); el_val_t base64_decode(el_val_t input); el_val_t base64url_encode(el_val_t input); el_val_t base64url_decode(el_val_t input); /* Length-aware variants (internal — exposed for the rare caller that already * has a known-length binary buffer and doesn't want to round-trip through * a NUL-terminated el_val_t string). Sha256_bytes and hmac_sha256_bytes feed * these implicitly. */ el_val_t el_sha256_bytes_n(const unsigned char* data, size_t len); el_val_t el_base64_encode_n(const unsigned char* data, size_t len, int url_safe); /* ── Post-quantum primitives (liboqs-backed) ──────────────────────────────── * All inputs/outputs hex-encoded. Algorithm choices: * Signature: CRYSTALS-Dilithium-3 (NIST level 3, balanced) * KEM: CRYSTALS-Kyber-768 (NIST level 3) * Hash: SHA3-256 (Keccak) (PQ-aware protocols favour SHA3 over SHA2) * * If liboqs is not linked (detected via __has_include() at compile * time), the pq_* entry points return a JSON-shaped error string so callers * fail loudly rather than silently fall back to classical schemes: * {"error":"liboqs not linked, post-quantum primitives unavailable"} * * The hybrid handshake pairs X25519 with Kyber-768 per NIST PQ guidance and * CNSA 2.0. Combined shared secret is HKDF-SHA256(x25519_ss || kyber_ss). * Even if Kyber falls, X25519 holds; if X25519 falls under quantum attack, * Kyber holds. SHA3-256 also remains usable independent of liboqs (the * Keccak permutation is PQ-OK as a primitive). */ el_val_t pq_keygen_signature(void); el_val_t pq_sign(el_val_t secret_key_hex, el_val_t message); el_val_t pq_verify(el_val_t public_key_hex, el_val_t message, el_val_t signature_hex); el_val_t pq_kem_keygen(void); el_val_t pq_kem_encaps(el_val_t public_key_hex); el_val_t pq_kem_decaps(el_val_t secret_key_hex, el_val_t ciphertext_hex); el_val_t pq_hybrid_keygen(void); el_val_t pq_hybrid_handshake(el_val_t remote_pub_combined); el_val_t sha3_256_hex(el_val_t input); /* ── AEAD: AES-256-GCM (libcrypto-backed) ─────────────────────────────────── * Symmetric authenticated encryption used to wrap envelopes after a KEM * handshake. Caller MUST supply a 32-byte key (64 hex chars) — typically the * Kyber-768 / hybrid shared_secret, optionally normalized via SHA3-256. * * aead_encrypt returns a JSON map {"nonce":"...","ciphertext":"..."} where * ciphertext is the AES-256-GCM output with the 16-byte auth tag appended. * Nonce is a fresh 12-byte CSPRNG draw — callers never pick the nonce, which * structurally rules out the GCM nonce-reuse footgun. * * aead_decrypt returns the plaintext String, or "" on any failure (including * auth-tag mismatch). Callers MUST check for "" before trusting the result. */ el_val_t aead_encrypt(el_val_t key_hex, el_val_t plaintext); el_val_t aead_decrypt(el_val_t key_hex, el_val_t nonce_hex, el_val_t ciphertext_hex); /* ── Native VM builtin aliases (for compiled El source) ───────────────────── * These match the El VM's native_* builtins so that El source compiled * to C can call the same names without modification. */ el_val_t native_list_get(el_val_t list, el_val_t index); el_val_t native_list_len(el_val_t list); el_val_t native_list_append(el_val_t list, el_val_t elem); el_val_t native_list_empty(void); el_val_t native_list_clone(el_val_t list); el_val_t native_string_chars(el_val_t s); el_val_t native_int_to_str(el_val_t n); /* ── Method-call shorthand aliases ────────────────────────────────────────── * The El method-call convention `obj.method(args)` compiles to * `method(obj, args)`. These aliases expose the runtime functions under * the short names that result from method calls in El source. * * Example: `myList.append(x)` → `append(myList, x)` (calls this alias) * `myList.len()` → `len(myList)` (calls this alias) */ el_val_t append(el_val_t list, el_val_t elem); /* el_list_append */ el_val_t len(el_val_t list); /* el_list_len */ el_val_t get(el_val_t list, el_val_t index); /* el_list_get */ el_val_t map_get(el_val_t map, el_val_t key); /* el_map_get */ el_val_t map_set(el_val_t map, el_val_t key, el_val_t value); /* el_map_set */ /* ── OTLP/HTTP Observability ─────────────────────────────────────────────── */ /* See bottom of el_runtime.c for the implementation. * Configured by env vars OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_SERVICE_VERSION. * No-op when OTLP_ENDPOINT is unset. Drop-on-failure semantics. */ /* ── Subprocess execution ────────────────────────────────────────────────── */ el_val_t exec_command(el_val_t cmd); /* run shell command, return exit code */ el_val_t exec_capture(el_val_t cmd); /* run shell command, capture stdout */ el_val_t exec(el_val_t cmd); /* exec(cmd) → stdout String (30s timeout) */ el_val_t exec_bg(el_val_t cmd); /* exec_bg(cmd) → PID String (non-blocking) */ el_val_t emit_log(el_val_t level, el_val_t msg, el_val_t fields_json); el_val_t emit_metric(el_val_t name, el_val_t value, el_val_t tags_json); el_val_t trace_span_start(el_val_t name); el_val_t trace_span_end(el_val_t span_handle); el_val_t emit_event(el_val_t name, el_val_t duration_ms); el_val_t __thread_create(el_val_t fn_name_v, el_val_t arg_v); el_val_t __thread_join(el_val_t tid_v); /* Mutex + channel seed primitives (defined in el_runtime.c). Declared here so * that compiled El programs which use runtime/thread.el's with_mutex helper or * runtime/channel.el's Go-style channels see real prototypes instead of an * implicit int-return declaration (which the C11 ABI mis-truncates el_val_t). */ el_val_t __mutex_new(void); void __mutex_lock(el_val_t m_v); void __mutex_unlock(el_val_t m_v); el_val_t __channel_new(el_val_t capacity_v); el_val_t __channel_send(el_val_t ch_v, el_val_t msg_v); el_val_t __channel_recv(el_val_t ch_v); el_val_t __channel_try_recv(el_val_t ch_v); el_val_t __channel_close(el_val_t ch_v); /* ── __ prefixed aliases (self-hosting compiler ABI) ───────────────────────── * The El self-hosting compiler emits calls to __-prefixed names. These are * forwarding wrappers around the existing el_runtime functions above. */ /* I/O */ el_val_t __println(el_val_t s); el_val_t __print(el_val_t s); el_val_t __readline(void); /* String */ el_val_t __int_to_str(el_val_t n); el_val_t __str_to_int(el_val_t s); el_val_t __float_to_str(el_val_t f); el_val_t __str_to_float(el_val_t s); el_val_t __str_len(el_val_t s); el_val_t __str_char_at(el_val_t s, el_val_t i); el_val_t __str_cmp(el_val_t a, el_val_t b); el_val_t __str_ncmp(el_val_t a, el_val_t b, el_val_t n); el_val_t __str_concat_raw(el_val_t a, el_val_t b); el_val_t __str_slice_raw(el_val_t s, el_val_t start, el_val_t end); el_val_t __str_alloc(el_val_t n); el_val_t __str_set_char(el_val_t s, el_val_t i, el_val_t c); /* URL encoding */ el_val_t __url_encode(el_val_t s); el_val_t __url_decode(el_val_t s); /* Environment */ el_val_t __env_get(el_val_t key); /* Cross-cutting concerns declared by a `program` block (spec §18). * All three are COMPILER-INJECTED at the head of main() — they are not meant to * be written by hand, which is the point: the guarantee cannot be forgotten at a * call site because there is no call site. */ el_val_t el_singleton_acquire(el_val_t id); /* §18.1 process identity */ el_val_t el_config_declare(el_val_t name, el_val_t type, el_val_t deflt, el_val_t has_default, el_val_t required); /* §18.2 config schema */ el_val_t el_config_validate(el_val_t program_name); /* §18.2 startup validate */ /* config(key) — the READ side, and the only one programs write by hand. With a * schema declared it is a validated lookup; without one it degrades to getenv. * (Defined in el_runtime.c but previously never prototyped here, so any program * calling it failed to compile under -Werror=implicit-function-declaration.) */ el_val_t config(el_val_t key); /* Subprocess */ el_val_t __exec(el_val_t cmd); el_val_t __exec_bg(el_val_t cmd); /* Process */ el_val_t __exit_program(el_val_t code); /* Filesystem */ el_val_t __fs_exists(el_val_t path); el_val_t __fs_mkdir(el_val_t path); el_val_t __fs_read(el_val_t path); el_val_t __fs_write(el_val_t path, el_val_t content); el_val_t __fs_write_bytes(el_val_t path, el_val_t bytes, el_val_t n); el_val_t __fs_list_raw(el_val_t path); /* HTTP server */ el_val_t __http_response(el_val_t status, el_val_t headers_json, el_val_t body); el_val_t __http_serve(el_val_t port, el_val_t handler); el_val_t __http_serve_v2(el_val_t port, el_val_t handler); /* HTTP conn fd / SSE (weak; overridden by el_seed.c when linked together) */ el_val_t __http_conn_fd(void); el_val_t __http_sse_open(el_val_t conn_id); el_val_t __http_sse_send(el_val_t conn_id, el_val_t data); el_val_t __http_sse_close(el_val_t conn_id); /* HTTP client (requires HAVE_CURL; stubs provided for no-curl builds) */ el_val_t __http_do(el_val_t method, el_val_t url, el_val_t body, el_val_t headers_map, el_val_t timeout_ms); el_val_t __http_do_map(el_val_t method, el_val_t url, el_val_t body, el_val_t headers_json, el_val_t timeout_ms); el_val_t __http_do_map_to_file(el_val_t method, el_val_t url, el_val_t body, el_val_t headers_json, el_val_t output_path); /* JSON */ el_val_t __json_array_get(el_val_t json, el_val_t index); el_val_t __json_array_get_string(el_val_t json, el_val_t index); el_val_t __json_array_len(el_val_t json); el_val_t __json_get(el_val_t json, el_val_t key); el_val_t __json_get_raw(el_val_t json, el_val_t key); el_val_t __json_set(el_val_t json, el_val_t key, el_val_t value); el_val_t __json_parse_map(el_val_t json_str); el_val_t __json_stringify_val(el_val_t val); /* Hashing */ el_val_t __sha256_hex(el_val_t s); /* State K/V */ el_val_t __state_del(el_val_t key); el_val_t __state_get(el_val_t key); el_val_t __state_keys(void); el_val_t __state_set(el_val_t key, el_val_t val); /* UUID */ el_val_t __uuid_v4(void); /* Args */ el_val_t __args_json(void); /* Compiler-support builtins — called by the El compiler's own source * (compiler.el, codegen.el) and registered in codegen.el's builtin_arity. */ el_val_t stdout_to_file(el_val_t path); el_val_t stdout_restore(void); el_val_t el_mem_check(void); /* Allocation accounting — the deterministic signal behind complexity gating. * Gate on counts/bytes; peak RSS is context only. */ el_val_t el_alloc_count(void); el_val_t el_alloc_bytes(void); el_val_t el_peak_rss(void); el_val_t el_black_box(el_val_t v); /* Semantic retrieval surface. NOT interchangeable with engram_search_json, * which is lexical by design — see the note at the definition. */ el_val_t engram_recall_json(el_val_t query, el_val_t limit); /* Edges straight from the store — replaces the engram_save()+fs_read() * whole-graph round trip that /api/graph/edges used to do. */ el_val_t engram_edges_json(el_val_t limit, el_val_t offset); /* Buffer-pool interoception as JSON — live pool health for observation. */ el_val_t engram_pool_stats_json(void); /* CGI identity accessors (read-only). */ el_val_t cgi_principal(void); el_val_t cgi_network(void); el_val_t cgi_engram(void); #ifdef __cplusplus } #endif