SESI PROGRAMMING LANGUAGE DOCS
⌕

Sesi Built-in Functions Reference

This doc demonstrates both normal usage and piping operators.

You can write scripts in whichever format you are most comfortable with.

I/O Functions

show(...args)

Print values to standard output, separated by spaces.

show "Hello"
show 42
show "Value:" 10 + 20
show [1, 2, 3]

Returns: null

<content

---

input(prompt) -> string

Prompt the user for terminal input, wait for them to press Enter, and return their response.

let name = input("Enter your name: ")
let age = "Enter your age" | input
show "Hello," name ". You are" age "years old!"

Parameters:

  • prompt (string): The text to display as a prompt before reading input. Optional.

Returns: string

---

Speech Functions

These built-ins provide speech synthesis and microphone transcription.

speech(text, voice = null, gemini_model = null) -> bool|string

Speak text through the system's installed voice. On macOS this uses say, on Windows it uses System.Speech, and on Linux it uses espeak-ng.

speech("Your local build is complete.")
speech("Bonjour tout le monde", "Thomas")

"You are learning a new coding language!" | speech
"Hola, buenos dias!" | speech("Mónica")

Returns: true after playback finishes.

Pass a Gemini text-to-speech model as the third argument to return base64-encoded audio instead of using the system voice.

---

from_speech(audio_path, language = null, gemini_model = null) -> string

Transcribe an audio file using nodejs-whisper. language is optional (for example, "en" or "fr").

let transcript = from_speech("meeting.wav", "en")
let lyrics = "song.wav" | from_speech("en", "gemini-3.1-flash-lite")

show transcript
show lyrics

Returns: the transcript as text. nodejs-whisper and a downloaded model must be installed (npx nodejs-whisper download base.en).

Pass a Gemini model as the third argument to transcribe through Gemini instead of Whisper.

---

Type Functions

type(value) -> string

Get the type name of a value.

type(42)           // "number"
42 | type

type("hello")      // "string"
"hello" | type

type(true)         // "bool"
true | type

type(null)         // "null"
null | type

type([1, 2, 3])    // "array"
([1, 2, 3]) | type

type({})           // "object"
({}) | type

Returns: string - one of: "number", "string", "bool", "null", "array", "object", "unknown"

---

str(value) -> string

Convert any value to a string.

str(42)            // "42"
42 | str

str(3.14)          // "3.14"
3.14 | str

str(true)          // "true"
true | str

str([1, 2, 3])     // "[1, 2, 3]"
([1, 2, 3]) | str

str({ "a": 1 })        // "{'a': 1}"
({ "a": 1 }) | str

Returns: string

---

to_json(value) -> string

Convert an array or object into a valid, formatted JSON string.

to_json({ "a": 1, "b": [1, 2] })
({ "a": 1, "b": [1, 2] }) | to_json

/*
{
  "a": 1,
  "b": [1, 2]
}
*/

Returns: string (valid JSON)

---

from_json(string) -> any

Parse a valid JSON string back into a native Sesi primitive, array, or object.

let raw = "{\"a\": 1, \"b\": [1, 2]}"

let obj = from_json(raw)
let piped = raw | from_json

show obj.a     // 1
show piped.b[1]   // 2

Returns: any - native Sesi primitive, array, or object, or null if parsing fails

---

encrypt(content, password) -> string

Encrypt UTF-8 string content with AES-256-CBC. The result uses the same iv:ciphertext envelope format as Sesi CLI encryption.

let secret = encrypt("private notes", "passphrase")
show secret

Returns: string - encrypted iv:ciphertext payload

---

decrypt(content, password) -> string

Decrypt an AES-256-CBC iv:ciphertext payload produced by encrypt(...) or the compatible Sesi CLI encryption format.

let secret = encrypt("private notes", "passphrase")
let plain = decrypt(secret, "passphrase")
show plain // "private notes"

Returns: string - decrypted UTF-8 content

---

num(value) -> number

Convert a value to a number.

num(42)            // 42
42 | num

num("3.14")        // 3.14
"3.14" | num

num(true)          // 1
true | num

num(false)         // 0
false | num

num("hello")       // null (can't convert)
"hello" | num

Returns: number or null if conversion fails

---

float(value) -> number

Convert a value to a floating-point number.

float(42)        // 42
42 | float

float("3.14")      // 3.14
"3.14" | float

float(true)        // 1
true | float

float(false)       // 0
false | float

float("hello")     // null (can't convert)
"hello" | float

Returns: number or null if conversion fails

---

bool(value) -> bool

Convert a value to boolean.

bool(1)            // true
1 | bool

bool(0)            // false
0 | bool

bool("")           // false
"" | bool

bool("hello")      // true
"hello" | bool

bool([])           // true
[] | bool

bool(null)         // false
null | bool

Returns: bool - Uses truthiness rules

---

Type Assertion Helpers

Return true if the value matches the respective type.

  • is_array(value) -> bool

value | is_array

  • is_object(value) -> bool

value | is_object

  • is_string(value) -> bool

value | is_string

  • is_number(value) -> bool

value | is_number

  • is_bool(value) -> bool

value | is_bool

  • is_null(value) -> bool

value | is_null

---

Collection Functions

len(collection) -> number

Get the length of a string, array, or object.

len("hello")       // 5
"hello" | len

len([1, 2, 3])     // 3
[1, 2, 3] | len

len({ "a": 1, "b": 2 })  // 2
({ "a": 1, "b": 2 }) | len

len(null)          // null (invalid)
null | len

Returns: number or null if not a collection

---

range(n) -> array

Create an array of numbers from 0 up to (but not including) n.

range(5)           // [0, 1, 2, 3, 4]
5 | range

Returns: array<number>

---

push(array, value) -> array

Add an element to the end of an array.

let arr = [1, 2, 3]
push(arr, 4)

show arr          // [1, 2, 3, 4]

let newArr = arr
newArr | push(5)

show newArr      // [1, 2, 3, 4, 5]

Note: Modifies array in-place and returns it.

Returns: array

---

append(collection, value) -> array | string

Append to an array or concatenate to a string.

let arr = [1, 2]
append(arr, 3)

show arr            // [1, 2, 3]

let newArr = arr
newArr | append(4)

show newArr         // [1, 2, 3, 4]

append("Hello from", " Sesi")  // "Hello from Sesi"
"Welcome to " | append("Sesi!")  // "Welcome to Sesi!"

Note: For arrays, this modifies in-place and returns the same array.

Returns: array, string, or null for unsupported types

---

pop(array) -> any

Remove and return the last element of an array.

let arr = [1, 2, 3]
let last = pop(arr)

show last         // 3
show arr          // [1, 2]

let newLast = arr | pop

show newLast      // 2

Returns: The removed element, or null if array is empty

---

join(array, separator) -> string

Join array elements into a string with separator.

let arr = [1, 2, 3]
let pairs = [11, 22, 33, 44, 55]

join(arr, "-")     // "1-2-3"
join(["a", "b"], ", ")  // "a, b"

["code", "files", "memory", "folders"] | join(",\n")
pairs | join("a_") | append("a")

Returns: string

---

split(string, separator) -> array

Split a string into an array by separator.

split("a,b,c", ",")     // ["a", "b", "c"]
split("hello sesi", " ")  // ["hello", "sesi"]

"1-2-3-4-5" | split("-")
"This is how
the split function
handles multi-lines" | split("\n")

Returns: array<string>

The returned names can be passed directly to model() to enable automatic orchestration:

fn summarize(text: string) -> string {return "Summary: " + text}
define_tool("summarize", summarize, "Summarize supplied text")

let answer = model("gemini-3.8-flash") {tools: list_tools(), max_tool_calls: 4} {"Summarize the user's text."}

Sesi derives JSON parameter schemas from the registered functions, executes model-selected calls, and sends each result back to the model until it produces final text. tools: true exposes every registered tool; an array of names exposes only those tools. max_tool_calls defaults to 8. Sensitive system functions cannot be invoked through automatic orchestration.

---

regex(pattern, text, options = null) -> array | bool | string

Run a JavaScript-compatible regular expression without dropping into js().

The default match mode returns every match with its index, capture groups,

and named groups.

let matches = regex("(?<word>[A-Z]+)", "IDs ABC and XYZ")

show matches[0].match
show matches[0].groups.word

let valid = regex("^[a-z]+$", "Sesi", {mode: "test", flags: "i"})

let parts = "[,;]\\s*" | regex("one, two; three", {mode: "split"})

let cleaned = "\\s+" | regex("too   much space", {mode: "replace", flags: "g", replacement: " "})

Options may be a flags string such as "i", or an object:

  • mode: "match" (default), "test", "replace", or "split".
  • flags: JavaScript regular-expression flags such as i, m, s, or g.
  • replacement: Required string for replace mode.
  • limit: Maximum matches or split parts; defaults to 10000.

Returns: an array for match/split, a boolean for test, or a string for replace

---

tokenize(string, options = null) -> array

Tokenize text into model token IDs (OpenAI-compatible tiktoken-style encoding).

let ids = tokenize("Hello Sesi.")
show ids | length

let ids2 = "Hello Sesi" | tokenize("gpt-5.6-sol")

let words = "  Sesi   language   rocks  " | tokenize("simple")
// ["Sesi", "language", "rocks"]

Options:

  • model (string, default "gpt-5.6-sol"): Model name used to pick tokenizer encoding.
  • encoding (string, optional): Explicit tiktoken encoding override (for example "o200k_base").
  • mode (string, optional): Set to "simple" for basic whitespace tokenization.

You can also pass a string as the second argument directly:

  • tokenize(text, "simple") for simple word splitting
  • tokenize(text, "gpt-5.6-sol") to inspect plain-text token IDs for the current GPT flagship

Returns: array<number> (model token IDs), array<string> in simple mode, or null if input/options are invalid

---

count_tokens(string, options = null) -> number

Count request tokens with the model provider's native counting endpoint.

let count = count_tokens("Summarize this text.", "gpt-5.6-sol")
let gemini_count = count_tokens(text, "gemini-3.8-flash")
let local_count = text | count_tokens("local")

GPT models use OpenAI's responses/input_tokens endpoint. Gemini models use

Gemini's models.countTokens endpoint. Local models use their cached tokenizer

without an API request. Provider-backed counting requires the corresponding API

key.

Returns: number, or null if the input/options are invalid

---

estimate_tokens(string, options = null) -> number

Estimate tokens locally without making a provider request. The options match tokenize; unknown and non-OpenAI model names explicitly fall back to o200k_base.

let approximate = estimate_tokens(long_text, "gemini-3.8-flash")
let guess = longer_prompt | estimate_tokens("gpt-5.6-sol")

Use this only when an offline approximation is acceptable. For native Gemini counts, use count_tokens.

Returns: number, or null if the input/options are invalid

---

estimate_cost(model, input, output = 0, rates = null) -> object

Estimate paid-tier text-token cost in USD. input and output may each be a token count or text.

let planned = estimate_cost("gemini-3.8-flash", prompt, 2000)

show planned.input_tokens
show planned.total_cost_usd

// Unknown/private models can provide USD rates per one million tokens.
let custom = "private-model" | estimate_cost(500000, 100000, {
  "input_per_million": 2,
  "output_per_million": 8
})

When input or output is text, GPT models use local tokenization and Gemini models use native Gemini counting. For supported image models, pass the prompt and local reference image paths as an input array. Gemini image output sizes ("512", "1K", "2K", or "4K") are accepted as output where supported by that model. Passing numeric counts never makes a provider request.

let image_cost = estimate_cost("gemini-3.1-flash-image", ["prompt text", "local_image.png"], "1K")
show image_cost.total_cost_usd

// For multiple images
let images = ["local_image1.png", "local_image2.png"]
let multiple_image_cost = estimate_cost("gemini-3.1-flash-image", ["prompt text", images], "1K")
show multiple_image_cost.total_cost_usd

The result includes token counts, separate input/output costs, total cost, per-million rates, pricing source, and pricing snapshot date. Built-in rates include supported Gemini and GPT image models.

Returns: object, or null for an unsupported model without custom rates

---

model_usage() -> object

Return provider-reported token usage and estimated cost for the most recent model() call in the current interpreter.

let answer = model("gpt-5.6-sol") {"Explain closures briefly."}
let usage = model_usage()

show "Tokens:" usage.total_tokens
show "Estimated USD:" usage.total_cost_usd

Token counts come from the provider response. Gemini thinking tokens are reported separately as thinking_tokens and included in billable_output_tokens, total_tokens, and cost. Local logic-cache hits return cached: true, zero tokens, and zero added cost. Cost fields are null when the model has no built-in pricing entry.

Returns: object, or null before the first model call

---

to_upper(string) -> string

Converts all alphabetic characters in a string to uppercase.

to_upper("hello")      // "HELLO"
"hello" | to_upper

to_upper("Sesi V1.9.0")  // "SESI V1.9.0"
"Sesi V1.9.0" | to_upper

Returns: string or null if not a string

---

to_lower(string) -> string

Converts all alphabetic characters in a string to lowercase.

to_lower("SESI")      // "sesi"
"SESI" | to_lower

to_lower("Sesi V1.9.0")  // "sesi v1.9.0"
"Sesi V1.9.0" | to_lower

Returns: string or null if not a string

---

trim(string) -> string

Removes whitespace from both ends of a string.

trim("  spaces  ")  // "spaces"
"  spaces  " | trim

Returns: string or null if not a string

---

slice(collection, start, end = null) -> string | array

Extracts a section of a string or array and returns it as a new string or array, without modifying the original.

slice("abcdef", 1, 4)         // "bcd"
"abcdef" | slice(1, 4)

slice([10, 20, 30, 40], 2)    // [30, 40]
[10, 20, 30, 40] | slice(2)

Parameters:

  • collection (string or array): The collection to slice.
  • start (number): The zero-based index at which to begin extraction.
  • end (number, optional): The zero-based index before which to end extraction. If omitted, slices to the end of the collection.

Returns: string or array based on the input collection type, or null if arguments are invalid

---

swap(string, target, replacement) -> string

Replaces all occurrences of a target substring within a string with a replacement substring.

swap("a_b_c", "_", "-")    // "a-b-c"
"a_b_c" | swap("_", "-")

Parameters:

  • string (string): The source string.
  • target (string): The substring to be replaced.
  • replacement (string): The substring that replaces the target.

Returns: string or null if arguments are invalid

---

contains(string, sub) -> bool

Returns true if the string contains the given substring, false otherwise.

contains("hello.sesi", ".sesi")  // true
"hello.sesi" | contains(".sesi)

contains("hello.sesi", ".ts")    // false
"hello.sesi" | contains(".ts")

Parameters:

  • string (string): The string to search within.
  • sub (string): The substring to search for.

Returns: bool or null if arguments are invalid

---

locate(string, sub) -> number

Returns the index of the first occurrence of a substring within a string. Returns -1 if the substring is not found.

locate("hello.sesi", ".")    // 5
"hello.sesi" | locate(".")

locate("hello.sesi", "ts")   // -1
"hello.sesi" | locate("ts)

Parameters:

  • string (string): The string to search within.
  • sub (string): The substring to find.

Returns: number (zero-based index, or -1 if not found), or null if arguments are invalid

---

keys(object) -> array

Get all keys of an object.

let obj = { "name": "Alice", "age": 30 }

keys(obj)          // ["name", "age"]
obj | keys

Returns: array<string> or null if not an object

---

values(object) -> array

Get all values of an object.

let obj = {name: "Alice", age: 30}

values(obj)        // ["Alice", 30]
obj | values

Returns: array<any> or null if not an object

---

Additional Array / String Utilities

The following utilities work on strings and arrays natively:

  • starts_with(string, prefix) -> bool

string | starts_with(prefix)

  • ends_with(string, suffix) -> bool

string | ends_with(suffix)

  • index_of(collection, value) -> number

collection | index_of(value)

  • includes(collection, value) -> bool

collection | includes(value)

  • repeat(string, count) -> string

string | repeat(count)

  • reverse(array) -> array

array | reverse

  • sort(array, compareFn?) -> array

array | sort(compareFn?)

  • unique(array) -> array

array | unique

  • flatten(array) -> array

array | flatten

---

map(array, callback) -> array

Creates a new array populated with the results of calling a provided function on every element in the calling array.

let numbers = [1, 2, 3]

fn square(x) { return x * x }
let squares = map(numbers, square) // [1, 4, 9]

fn cube(x) { return x * x * x }
let cubed = numbers | map(cube) // [1, 8, 27]

Parameters:

  • array (array): The source array.
  • callback (fn): Function to execute on each element. Receives arguments: (item, index, array).

Returns: array

---

filter(array, callback) -> array

Creates a shallow copy of a portion of a given array, filtered down to just the elements from the given array that pass the test implemented by the provided function.

let numbers = [1, 2, 3, 4]

fn isEven(x) { return x % 2 == 0 }
let evens = filter(numbers, isEven) // [2, 4]

fn isOdd(x) { return x % 2 != 0 }
let odd = numbers | filter(isOdd) // [1, 3]

Parameters:

  • array (array): The source array.
  • callback (fn): Function is a predicate, to test each element of the array. Return a truthy value to keep the element. Receives arguments: (item, index, array).

Returns: array

---

reduce(array, callback, initialValue = null) -> any

Executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value.

let numbers = [1, 2, 3, 4]

fn sum(acc, x) { return acc + x }

let total = reduce(numbers, sum) // 10
let totalWithInitial = numbers | reduce(sum, 10) // 20

Parameters:

  • array (array): The source array.
  • callback (fn): A function to execute on each element in the array (except the first, if no initialValue is provided). Receives arguments: (accumulator, currentValue, index, array).
  • initialValue (any, optional): A value to which accumulator is initialized on the first call. If no initial value is supplied, the first element in the array is used as the initial accumulator value, and reduce() starts executing the callback from the second element (index 1).

Returns: any

---

find(array, callback) -> any

Returns the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, null is returned.

let numbers = [1, 3, 4, 7]

fn isEven(x) { return x % 2 == 0 }
let match = find(numbers, isEven) // 4

fn isOdd(x) { return x % 2 != 0 }
let odd = numbers | find(isOdd) // [1]

Parameters:

  • array (array): The source array.
  • callback (fn): Function to execute on each value in the array. Receives arguments: (item, index, array).

Returns: any or null

---

File System Functions

read_file(path, mode = "text") -> string

Read the contents of a file as a string.

Modes:

  • "text" (default): Reads UTF-8 text
  • "base64": Reads raw bytes and returns Base64 text

let text = read_file("input.txt")
show text

let image_b64 = read_file("logo.png", "base64")
show image_b64

let pkg = "package.json | read_file
let audio = "audio.wav" | read_file("base64")

Note: Paths are resolved relative to the current working directory.

Returns: string (or null for unsupported mode)

---

write_file(path, content, encoding = null) -> bool

Write content to a file. Overwrites the file if it exists.

Encodings:

  • null (default): writes UTF-8 text
  • "base64": decodes Base64 content and writes raw bytes

let success = write_file("output.txt", "Hello, Sesi!")
if success {show "File written successfully"}

let image_b64 = read_file("logo.png", "base64")
write_file("logo-copy.png", image_b64, "base64")

let saved = "filename.txt" | write_file("This was creating with the function piping method!")
let copy = "favicon.png" | read_file("base64")
if saved {"favicon_copy.png" | write_file(copy, "base64")}

Note: Paths are resolved relative to the current working directory.

Returns: bool (true on success, throws on error)

---

append_file(path, content) -> bool

Append string content to the end of a file. Creates the file if it does not exist.

let success = append_file("log.txt", "new line\n")
if success {"log.txt" | append_file("File appended successfully!")}

Note: Paths are resolved relative to the current working directory.

Returns: bool (true on success, throws on error)

---

write_image(path, base64_content) -> bool

Write base64 encoded string content as an image file. Overwrites the file if it exists.

let success = write_image("logo.png", logo_data)
if success {show "Image safely stored"}

let copy = "favicon.png" | read_file("base64")
if success {"favicon_copy.png" | write_image(copy)}

Note: Paths are resolved relative to the current working directory.

Returns: bool (true on success, throws on error)

---

open(target, options = null) -> bool

Open a URL or local file using the OS default app, or a specific browser/editor/viewer.

open("https://code-with-sesi.netlify.app")
"https://code-with-sesi.netlify.app" | open

open("docs/logo.png")
"docs/logo.png" | open

"https://code-with-sesi.netlify.app", {browser: "Google Chrome"})
"https://code-with-sesi.netlify.app" | open({browser: "Google Chrome"})

open("reports/dashboard.html", {browser: "Firefox"})
"reports/dashboard.html" | open({browser: "Firefox"})

open("notes/todo.txt", {editor: "Visual Studio Code"})
"notes/todo.txt" | open({editor: "Visual Studio Code"})

open("docs/logo.png", {image_viewer: "Preview"})
"docs/logo.png" | open ({image_viewer: "Preview"})

Options:

  • browser (string, optional): Preferred browser app name.
  • editor (string, optional): Preferred text editor app name.
  • viewer (string, optional): Preferred image viewer app name.
  • image_viewer (string, optional): Alias for viewer.
  • mode (string, optional): One of "auto", "browser", "editor", "viewer", "image_viewer".

In auto mode (default), Sesi chooses based on file extension and the options you provide.

Returns: bool (true on success, throws on error)

---

open_file(path, options = null) -> bool

Open a local file with OS default behavior, or force a preferred editor/viewer/browser.

open_file("README.md")
"README.md" | open_file

"README.md" | open_file({editor: "Visual Studio Code"})
"README.md" | open_file({editor: "Visual Studio Code"})

open_file("favicon.png", {viewer: "Preview"})
"favicon.png" | open_file({viewer: "Preview"})

open_file("index.html", {mode: "browser", browser: "Google Chrome"})
"index.html" | open_file({mode: "browser", browser: "Google Chrome"})

Note: Paths are resolved relative to the current working directory.

Returns: bool (true on success, throws on error)

---

list_dir(path) -> array

List the contents of a directory as an array of strings.

let files = list_dir(".")
show files

/*
  You can also just simply use "folder_name"
  Including the "/" helps with IDE highlighting and can help with debugging/verification
*/
let docs = "docs/" | list_dir
show docs

Note: Paths are resolved relative to the current working directory.

Returns: array<string>

---

make_dir(path) -> bool

Create a new directory recursively. Returns true on success, false or throws on failure.

let success = make_dir("new_directory")
if success {show "Directory created successfully"}

let dirCheck = "new_directory" | list_dir
if dirCheck | is_null {"new_directory" | make_dir}

Note: Paths are resolved relative to the current working directory.

Returns: bool (true on success, throws on error)

---

rename(oldPath, newPath) -> bool

Rename or move a file or directory.

let success = rename("old_name.txt", "new_name.txt")
if success {show "File renamed successfully"}

let reverted = "new_name.txt" | rename("old_name.txt")

Parameters:

  • oldPath (string): The current path of the file or directory.
  • newPath (string): The target path.

Returns: bool (true on success, throws on error)

---

archive(sourcePath, destPath = null) -> bool

Recursively backup/copy a file or directory.

// Backs up to a target destination
archive("src/index.ts", "backup/index.ts")
"src/index.ts" | archive("backup/index.ts")

// Automatically backs up to the hidden `.archive` directory in project root
archive("src/index.ts") // Saves to .archive/index.ts
"src/index.ts" | archive

Parameters:

  • sourcePath (string): The file or directory to back up.
  • destPath (string, optional): The destination path. Defaults to .archive/<basename> in the current working directory if omitted.

Returns: bool (true on success, throws on error)

---

get_ext(path) -> string

Return the lowercase file extension without the leading dot. Compound archive extensions are preserved.

show get_ext("photo.JPG")       // "jpg"
show get_ext("backup.tar.gz")  // "tar.gz"

---

exists(path) -> bool

Return whether a sandbox-approved file or directory exists.

if exists("config.json") { show "Configuration found" }

---

zip(source, destination = null, operation = null)

Create, list, or extract archives. ZIP-compatible containers are handled natively. RAR, 7z, and tar-family formats use an installed 7z, unrar, or tar executable and require local mode.

zip("assets", "assets.zip")       // Create
let entries = zip("assets.zip")   // List
zip("assets.zip", "restored")    // Extract

// The optional operation removes ambiguity:
zip("assets", "assets.7z", "create")
zip("assets.7z", null, "list")
zip("assets.7z", "restored", "extract")

Supported extensions include zip, jar, apk, docx, xlsx, pptx, epub, 7z, rar, tar, tar.gz, tgz, tar.bz2, tbz2, tar.xz, and txz. Available non-ZIP operations depend on installed system tools.

---

trash(path, autoRemove = false) -> bool

Delete a file or directory. By default, moves the item to a local .trash recycle bin directory, uniquely naming it with a timestamp. Can optionally delete permanently.

// Moves file to project's .trash folder with a unique timestamp (e.g. temp_1719253450000.txt)
trash("temp.txt")
"temp.txt" | trash

// Permanently and recursively deletes the file/directory immediately
trash("temp.txt", true)
"temp.txt" | trash(true)

Parameters:

  • path (string): The path of the file or directory to delete.
  • autoRemove (bool, optional): If true, permanently deletes the item instead of moving it to the trash folder. Defaults to false.

Returns: bool (true on success, returns false if path doesn't exist, throws on error)

convert(type) { config } { file } -> string

Convert file or document content between formats.

  • type: doc (documents/text), media (images/video), or audio.
  • config: Object containing conversion parameters:

- file_type: Input format extension (e.g. "md", "csv", "png", "wav"). Optional when input is a local file path (inferred from extension).

- output_type: Target output format extension (e.g. "html", "json", "jpg", "mp3"). Required.

  • file: Input string content or input local file path.

If the input is a local file path, the converted content is saved to a file of the same name and directory with the target extension, and the path to the output file is returned. If the input is raw string content, the converted content is returned directly.

Native document conversions currently include:

  • md -> html
  • html -> md
  • html -> txt
  • csv -> json
  • tsv -> json
  • json -> csv
  • json -> tsv
  • json -> yaml / json -> yml
  • yaml -> json / yml -> json
  • svg -> html
  • html -> svg
  • svg -> txt

For other doc conversions, Sesi falls back to pandoc for local files when available, then to the AI conversion fallback if no native or local converter exists.

For media, Sesi still prefers external tools such as ImageMagick or ffmpeg, but it also has a native fallback for rasterizing svg files into png, jpg, or jpeg.

SVG image conversion is available through convert(media) for file-path inputs:

  • svg -> png
  • svg -> jpg
  • svg -> jpeg
  • png -> svg
  • jpg -> svg
  • jpeg -> svg
  • gif -> svg
  • webp -> svg
  • bmp -> svg
  • tiff -> svg
  • avif -> svg

Raster image to SVG conversion creates an SVG wrapper with the original image embedded as a data URI. It is intended to make SVG output work reliably for image workflows; it does not perform vector tracing.

// Raw content conversion
let html = convert(doc) {file_type: "md", output_type: "html"} {"# Hello"}
show html // "<h1>Hello</h1>"

let yaml = convert(doc) {file_type: "json", output_type: "yaml"} {"[{\"name\":\"Alice\"}]"}
show yaml

let markdown = convert(doc) {file_type: "html", output_type: "md"} {"<h1>Hello</h1><p>World</p>"}
show markdown

// File path conversion
let out_path = convert(doc) {output_type: "html"} {"document.md"}
show out_path // "document.html"

let image_path = convert(media) {output_type: "png"} {"diagram.svg"}
show image_path // "diagram.png"

let svg_path = convert(media) {output_type: "svg"} {"photo.png"}
show svg_path // "photo.svg"

let audio_path = convert(audio) {output_type: "mp3"} {"audio.wav"}
show audio_path // "audio.mp3"

Returns: string (converted content or path to the converted file)

---

gif(input, output, options = null) -> string

Create an animated GIF from a video file or an ordered array of image-frame

paths. This requires the ffmpeg CLI and is disabled in Sesi safe mode.

let frames = ["frames/001.png", "frames/002.png", "frames/003.png"]
let out_filename = "build/preview.gif"
let config = {
  "fps": 12,
  "width": 640,
  "loop": 0
}
let output = ""
let output_plain = ""

if !(["-version"] | ffmpeg | is_null) {
  show "FFmpeg is installed and available."

  try {
    output = gif(frames, out_filename, config)
    output_plain = frames | gif("build/plain.gif")
  } catch (e) {
    show "Failed to create" out_filename "or build/plain.gif."
  }
} else {
  show "FFmpeg is not installed or not available in the system PATH."
}

Options: fps (default 12), width, loop (0 means forever),

overwrite (default true), and timeout in milliseconds.

Returns: the output path

---

video(input, output, options = null) -> string

video() has two forms: AI generation and local FFmpeg creation.

AI video generation

Use the same model/config/prompt block syntax as image(). It supports Gemini

Omni Flash and Veo models through the current @google/genai SDK and returns

the generated MP4 as Base64 data.

let clip = video("gemini-omni-flash-preview") {ratio: "9:16"} {"A marble rolling through a chain-reaction track, continuous smooth shot"}
write_file("marble.mp4", clip, "base64")

let veo_clip = video("veo-3.1-generate-preview") {images: "first-frame.png", ratio: "16:9", duration: 8, resolution: "1080p", audio: true} {"The camera slowly pushes toward the subject as wind moves through the scene"}
write_file("veo.mp4", veo_clip, "base64")

AI options include images/image, ratio/aspectRatio, duration,

resolution, negative_prompt, audio, and task. Gemini Omni Flash uses

the Interactions API; Veo uses the long-running video-generation API. Veo calls

wait for generation to finish before returning.

Local video creation

With ordinary function arguments, create or transcode video from a media file

or an ordered array of image-frame paths. An optional external audio track can

be attached. This form requires the ffmpeg CLI and is disabled in Sesi safe

mode.

let filename = "build/preview.mp4"
let config = {
  "fps": 30,
  "width": 1280,
  "height": 720,
  "codec": "libx264",
  "crf": 23,
  "audio": "music.wav"
}

let output = video(frames, filename, config)
let output_basic = frames | video("build/basic.mp4")

Options: fps, width, height, codec, crf, pixel_format, preset,

audio, mute, overwrite, and timeout. Defaults are selected for MP4;

WebM output defaults to libvpx-vp9.

Returns: Base64 MP4 data for AI generation; the output path for local creation

---

ffmpeg(args, options = null) -> object

Run FFmpeg directly with a structured argument array. A shell command string is

deliberately rejected. This builtin is disabled in Sesi safe mode.

let args = [
  "-y",
  "-i", "input.mov",
  "-c:v", "libx264",
  "output.mp4"
]

let result = ffmpeg(args)
args | ffmpeg({timeout: 5000})

show result.ok result.code

Options:

  • cwd: validated working-directory path.
  • timeout: maximum runtime in milliseconds.
  • throw_on_error: throw when FFmpeg exits nonzero; defaults to true.

The result contains ok, code, signal, stdout, stderr, and the executed

argument array.

Returns: object

---

Network Functions

web_get(url, headers = {}) -> string

Perform a synchronous HTTP GET request and return the response body as a string.

let response = web_get("https://jsonplaceholder.typicode.com/posts/1")
"https://jsonplaceholder.typicode.com/posts/1" | web_get

show response

Returns: string

---

web_send(url, body, headers = {}) -> string

Perform a synchronous HTTP POST request with a request body and return the response as a string.

let payload = "{\"title\": \"foo\"}"

let response = web_send("https://jsonplaceholder.typicode.com/posts", payload)
"https://jsonplaceholder.typicode.com/posts/1" | web_send(payload)

show response

Returns: string

---

listen(port, handler) -> object

Starts a native HTTP server listening on the specified port. Requests are passed to the handler function (which can be a synchronous function or an async fn).

The handler receives a request object with the following properties:

  • method: The HTTP method (e.g. "GET", "POST").
  • path: The path portion of the URL (e.g. "/test-route").
  • headers: A map of the HTTP request headers.
  • body: The request body as a string.
  • query: A map of the URL query parameters.

The handler can return:

  • A simple string: Sent as the HTTP response body with status 200 and Content-Type: text/html.
  • A structured response object containing:

- "status": HTTP status code (default: 200).

- "headers": Map of response headers (default: Content-Type: text/html).

- "body": Response body (string, or object which gets serialized to JSON).

Returns a server control object with a close() function to stop the server programmatically.

async fn handleRequest(req) {
  show "Request path is:" req.path
  return {
    "status": 200,
    "body": "Hello from Sesi Server!"
  }
}

let server = listen(8080, handleRequest)
8080 | listen(handleRequest)
// ...
server.close()

Returns: object containing a close function.

---

api(port, handler) -> object

Starts a native WebSocket server listening on the specified port. Incoming client messages are passed to the handler function (which can be a synchronous function or an async fn).

The handler receives two arguments:

  • client: A controller object for the connected client containing:

- send(message): Sends a message to the client (automatically converted to a string).

- close(): Closes the connection to this client.

  • message: The incoming message payload as a string.

Returns a server control object with a close() function to stop the server programmatically.

fn handleMessage(client, msg) {
  show "WS received:" msg
  client.send("Echo: " + msg)
}

let server = api(8989, handleMessage)
8989 | api(handleMessage)
// ...
server.close()

Returns: object containing a close function.

---

std/api

Includes Sesi's FastAPI-style HTTP API framework with auto-generated Swagger UI docs at /docs and OpenAPI 3.1 specification at /openapi.json.

allow "std/api" in as API

/**
  List users.

 Returns every user visible to the caller.
*/
fn listUsers(req) {
  return {status: 200, body: {users: []}}
}

let app_config = {
  title: "Users API",
  version: "1.0.0",
  description: "A user management API"
}

let user_config = {
  summary: "List users",
  tags: ["Users"]
}

let app = API.create_app(app_config)
app_config | API.create_app

app.get("/users", listUsers)
app.get("/users", user_config, listUsers)
"/users" | app.get(user_config, listUsers)

let server = app.listen(8080)
8080 | app.listen

API Reference:

##### create_app(config = null) -> app

Creates an API application instance. config options include title, version, description, and base_path.

##### app.get(path, handler) / app.get(path, schema, handler) / app.post / app.put / app.patch / app.delete

Registers an HTTP route. Comments immediately above a user-defined handler provide the generated OpenAPI summary (the first documentation line) and description (the complete documentation). This metadata remains attached when the handler is exported and imported from another Sesi module.

The optional schema can define summary, description, tags, query, body, response, and deprecated. Explicit summary and description values override comment-derived documentation.

##### app.use(middleware)

Registers a request middleware function. Accepts a named function handler handler(req).

##### app.openapi() -> object

Returns the generated OpenAPI 3.1 specification object.

##### app.routes() -> array

Returns an array of registered route objects.

##### app.listen(port, options?) -> server

Starts the HTTP server listening on port. Options:

  • docs_path (default: "/docs"): Path for Swagger UI documentation.
  • openapi_path (default: "/openapi.json"): Path for OpenAPI specification JSON.
  • cors (default: true): Enable CORS headers.
  • cors_origin (default: "*"): Allowed CORS origins.

---

Audio Functions (std/audio)

The std/audio module provides functions for sound synthesis and playback.

allow "std/audio" in as Audio

let freq = 440
let duration = 200

// Play a simple beep (frequency in Hz, duration in ms)
Audio.beep(freq, duration)
freq | Audio.beep(duration)

// Play a musical note
duration = 500

Audio.play("C4", duration)
"C4" | Audio.play

Audio.play("E4", duration)
"E4" | Audio.play(duration)

Audio.play("G4", duration)
"G4" | Audio.play(duration)

// Synthesize a waveform and get base64 WAV data
freq = 440
duration = 1000

let b64 = Audio.synth(freq, duration, "square")
freq | Audio.synth(duration, "square")

// Save a synthesized tone to a file
let filename = "tone.wav"
let note = "A4"
let opts = {attack: 50, release: 500}
duration = 2000

Audio.save(filename, note, duration, "sine", opts)
filename | Audio.save(note, duration, "sine", opts)

// Load a WAV file into an audio_sample object
let sample = Audio.load("drum_loop.wav")
"drum_loop.wav" | Audio.load

// Generate drum hit note objects (base64 WAV)
let k = Audio.kick(300, 1.0)   // kick drum
300 | Audio.kick(1.0)

let s = Audio.snare(200, 0.8)  // snare drum
100 | Audio.snare(0.8)

let h = Audio.hat(50, 0.6)     // hi-hat
50 | Audio.hat(0.6)

// or Native Physical Modeling (Drums)
let kick = {note: "C1", ms: 500, type: "kick"}
let snare = {note: "C4", ms: 500, type: "snare"}
let hat = {note: "G8", ms: 250, type: "hat", pan: 0.3}

// Professional Sample-Based Synthesis (SoundFonts)
let sf2Lib = "GeneralUser-GS.sf2"

let piano = Audio.sf2(sf2Lib, {instrument: 0, gain: 1.5})
sf2Lib | Audio.sf2({instrument: 0, gain: 1.5})

let string_pad = Audio.sf2(sf2Lib, {instrument: 49})
sf2Lib | Audio.sf2({instrument: 49})

// Save a sequence (song) of notes
let song = [
  {note: "C4", ms: 500, vol: 0.8},
  {note: "E4", ms: 500, pan: -0.5},
  {note: "G4", ms: 1000, cutoff: 5000} // LPF Filter
]
filename = "song.wav"

Audio.sequence(filename, song, "triangle")
filename | Audio.sequence(song, "triangle")

// Save tracks to a MIDI file
filename = "song.mid"

Audio.midi(filename, song)
filename | Audio.midi(song)

// Mix multiple tracks (Native Synthesis and SoundFonts) into a single stereo WAV
let lead = [piano("C4", 500), piano("E4", 500)]
["C4" | piano(500), "E4" | piano(500)]

let bass = [k, s] // or let bass = [kick, snare]
let final_arr = [lead, bass]
filename = "mix.wav"
opts = {saturate: 1.5}

Audio.mix(filename, final_arr, "sine", opts)
filename | Audio.mix(final_arr, "sine", opts)

beep(frequency, duration)

Plays a simple sine wave beep.

play(note, duration, options)

Plays a musical note (e.g., "C4", "A#3", "Bb5"). Accepts options for ADSR, volume, and panning.

synth(frequency_or_note, duration, type, options)

Returns a base64 encoded WAV string. type can be "sine", "square", "saw", "triangle", "noise", "kick", "snare", "hat", or "clap".

save(path, frequency_or_note, duration, type, options)

Saves a synthesized WAV file to the specified path.

load(path) -> audio_sample

Loads a WAV file from disk and returns an audio_sample object containing the decoded PCM data.

  • path: Path to a .wav file.
  • Returns: An audio_sample object with samples (array of normalized floats in [-1, 1]) and sampleRate (integer, Hz).

allow "std/audio" in as Audio

let sample = Audio.load("loop.wav")
"loop.wav" | Audio.load

show "Sample rate:" sample.sampleRate
show "Samples:" len(sample.samples)

kick(duration, volume)

Generates a kick drum hit and returns it as a base64-encoded WAV string ready for use in mix or write_file.

  • duration (default: 300): Length in milliseconds.
  • volume (default: 1.0): Amplitude scalar (0.0–1.0).

let k = Audio.kick(300, 1.0)
300 | Audio.kick(1.0)

snare(duration, volume)

Generates a snare drum hit and returns it as a base64-encoded WAV string.

  • duration (default: 200): Length in milliseconds.
  • volume (default: 1.0): Amplitude scalar.

let s = Audio.snare(200, 0.8)
200 | Audio.snare(0.8)

hat(duration, volume)

Generates a hi-hat hit and returns it as a base64-encoded WAV string.

  • duration (default: 50): Length in milliseconds.
  • volume (default: 1.0): Amplitude scalar.

let h = Audio.hat(50, 0.6)
50 | Audio.hat(0.6)

sequence(path, notes_array, type, options)

Saves a multi-note sequence to a single WAV file. notes_array can be an array of note strings (e.g., ["C4", "D4"]), objects (e.g., [{note: "C4", ms: 250, pan: 0.5}]), or pre-rendered SF2 notes.

mix(path, tracks_array, type, options)

Saves a Stereo WAV file by mixing multiple tracks together. tracks_array is an array of note arrays. The mixer supports real-time ADSR envelopes, low-pass filtering (cutoff), stereo pan, soft-clipping saturation (saturate), and automatic high-speed batch rendering of SoundFont instruments.

midi(path, tracks)

Saves one or more tracks (arrays of note objects/strings) directly as a standard MIDI (.mid) file on disk.

sf2(path, options) -> fn(note, duration)

Returns a high-level instrument constructor function bound to a specific SoundFont file.

  • options: {instrument: 0, channel: 0, gain: 1.5}
  • The returned function takes (note, ms) and generates a Sesi-native note object that the mixer will automatically batch-render using FluidSynth.

---

Music Theory Functions (std/theory)

The std/theory module abstracts the mathematics of music into simple, reusable logic, perfect for algorithmic composition.

allow "std/theory" in as Music

// Generate a C Major 7 chord array
let c_maj7 = Music.chord("C4", "M7") // ["C4", "E4", "G4", "B4"]
"C4" | Music.chord("M7")

// Generate an A minor scale array
let a_minor = Music.scale("A3", "minor")
"A3" | Music.scale("minor")

// Transpose notes up by 5 semitones (Perfect 4th)
let shifted = Music.transpose(c_maj7, 5) // ["F4", "A4", "C5", "E5"]
c_maj7 | Music.transpose(5)

chord(root, type) -> array

Generates an array of notes for a given chord type.

  • Supported types: "M", "m", "dim", "aug", "7", "M7", "m7", "sus2", "sus4".

scale(root, type) -> array

Generates an array of notes for a given scale/mode.

  • Supported types: "major", "minor", "dorian", "phrygian", "lydian", "mixolydian", "locrian".

transpose(notes, semitones) -> array

Shifts a note or an array of notes up or down by the specified number of semitones.

duration(minutes, seconds) -> number

Converts minutes and seconds into Sesi-native absolute milliseconds.

bar(bars, bpm, beatsPerBar = 4) -> number

Converts a number of musical bars into milliseconds based on BPM and time signature (default: 4/4).

---

Akai MPC Track Sequencing & Effects (std/mpc)

The std/mpc module brings authentic Akai MPC (Music Production Center / MIDI Production Center) hardware and software workflows to Sesi. It faithfully implements official Akai MPC controls, including 16-pad matrix banks, the Roger Linn MPC swing algorithm (50% to 75%), 16 Levels performance mode, Note Repeat with triplet divisions, voice Choke Groups, multi-track step sequencing, vintage converter modeling (MPC60, MPC3000, SP1200), AIR multi-mode insert effects, and broadcast-quality WAV and multi-track MIDI file export.

allow "std/mpc" in as MPC

// 1. Create a 2-bar 90 BPM sequence with classic 58% MPC boom-bap swing
let seq = MPC.sequence("Boom Bap Groove", 2, 90, "4/4")
seq.set_swing(58)
seq.set_vintage("mpc60") // 12-bit punchy Roger Linn sound

// 2. Setup 16-pad drum kit with choke group
let kit = MPC.drum_kit("classic_boom_bap")
kit.set_pad(1, {sound: "kick", tune: -1, level: 1.0})
kit.set_pad(2, {sound: "rimshot", level: 0.85})
kit.set_pad(3, {sound: "snare", tune: 1, level: 0.95})
kit.set_pad(4, {sound: "clap", level: 0.8})
kit.set_pad(5, {sound: "hat", mute_group: 1, level: 0.75})
kit.set_pad(7, {sound: "open_hat", mute_group: 1, level: 0.85}) // Choked by Pad 5!

// 3. Add drum track with step patterns
let drums = seq.add_track("Drums", "drum", kit)
drums.pattern(1, "x...x...x...x...") // Kick
drums.pattern(3, "....x.......x...") // Snare on 2 and 4
drums.pattern(5, "x.x.x.x.x.x.x.x.") // 16th hats with 58% swing

// 4. Use 16 Levels on bass or sample
let bass_pad = kit.get_pad(13) // 808 sub
let bass_chromatic = MPC.sixteen_levels(bass_pad, "tune")

// 5. Render directly to WAV and MIDI
seq.render_wav("beat.wav")
seq.render_midi("beat.mid")

Core MPC Exports

  • create(config?) / project(config?): Creates an MPC sequence workstation instance with configuration (bpm, swing, vintage, bars, time_signature).
  • sequence(name, bars?, bpm?, time_sig?): Creates an MPC Sequence with configurable tempo, bar length, time signature, and track collections.
  • drum_kit(nameOrPreset?) / program(name, type?, pads?): Creates an Akai MPC 16-pad drum program.
  • pad(pad_number, sound?, options?): Creates an individual pad definition with level, pan, tune, fine cents, choke/mute group, ADR envelope, and filter settings.
  • sixteen_levels(pad, mode?, options?): Official Akai MPC 16 Levels mode. Spreads a sound across all 16 pads by:

- "velocity": 16 stepped velocity gradations from 8 to 127.

- "tune": Chromatic semitone tuning across 16 pads (-12 to +3 semitones or custom range).

- "filter": Stepped lowpass cutoff frequencies from 200 Hz to 18,000 Hz.

- "decay": Stepped decay times from 30 ms to 1,200 ms.

- "attack": Stepped attack envelope from 0 ms to 500 ms.

  • note_repeat(pad, rate?, bars?, options?): Official Akai MPC Note Repeat button. Generates quantized repeats at "1/4", "1/8", "1/16", "1/32", "1/64", and triplet modes ("1/4T", "1/8T", "1/16T", "1/32T"), with swing and optional velocity ramping ("crescendo", "decrescendo").
  • swing(events, percent?, grid?): Applies the Roger Linn MPC swing algorithm (50% straight to 75% heavy swing) to note events.
  • chop(sampleOrPath, slices?, options?) / slice(...): Akai MPC "Chop Shop" sample slicer. Divides audio into equal regions, transients, or beats, and automatically maps slices to Pads 1..16 with voice Choke Groups enabled.
  • vintage(mode, sample): Emulates vintage MPC hardware converters:

- "mpc60": 12-bit linear quantization, non-linear companding, subtle 16kHz lowpass rolloff, and transient punch.

- "mpc3000": 16-bit 44.1kHz warm converter modeling with analog transformer curve.

- "sp1200": 12-bit 26.04kHz downsampling with classic ring aliasing.

  • effect(type, params) / fx(type, params): Defines an AIR effect processor:

- "filter": Multi-mode filter ("lowpass", "highpass", "bandpass"), cutoff, resonance, drive.

- "delay": Sync or ms tape delay with feedback, damping, ping-pong stereo, and dry/wet mix.

- "reverb": Algorithmic studio reverb with room size, decay, pre-delay, and damping.

- "compressor": VCA compressor / master limiter with threshold, ratio, attack, release, makeup gain.

- "lofi": Bit depth reduction (4-16 bit), sample rate downsampling, clipping drive, and mix.

- "eq": 3-band parametric EQ (low, mid, high gain in dB).

  • sample(type_or_path, options?): Loads external WAV files or generates synthesized drum sounds ("kick", "snare", "rimshot", "hat", "open_hat", "clap", "808", "sub", "tom", "perc", "shaker", "crash", "ride").
  • song(steps, name?): Chains sequences into an arranged song with repeat counts and per-step mute overrides.
  • render_wav(target, path, options?): Mixes and exports full stereo 44.1kHz 16-bit PCM WAV audio with choke groups, swing micro-timing, track panning, insert effects, and master limiting. Pass "memory" as path to return the raw sample buffer.
  • render_midi(target, path): Exports multi-track Standard MIDI File (Type 1 SMF) preserving Akai Roger Linn swing micro-timing, velocities, and note durations.
  • play(target, options?): Auditions sequence, song, or sample directly via system audio.

---

Drawing Functions (std/draw)

The std/draw module provides APIs for SVG graphics and raster pixel drawing.

allow "std/draw" in as Draw

// Setup gradients
let config = [
  {offset: "0%", color: "blue"},
  {offset: "100%", color: "black"}
]

Draw.gradient("linear", "sky", config)
"linear" | Draw.gradient("sky", config)

// Setup CSS keyframe animations
let keyframes = "
  @keyframes spin {
    to { transform: rotate(360deg); }
  }
  .spinner { animation: spin 4s infinite linear; transform-origin: 50px 50px; }
"

Draw.style(keyframes)
keyframes | Draw.style

// Render shapes with styling options
// Using piping operators here can get messy, so we reccommend the normal method.
Draw.rect(0, 0, 100, 100, "url(#sky)")
Draw.circle(50, 50, 40, "red", {class: "spinner"})
Draw.ellipse(50, 50, 20, 10, "gold")
Draw.polygon("30,20 85,20 90,75 25,75", "green")
Draw.path("M 10 10 C 20 20, 40 20, 50 10", "none", {stroke: "white", "stroke-width": 2})

// Get the SVG string
let svg = Draw.render(100, 100)
100 | Draw.render(100)

// Save to a file
Draw.save_svg("drawing.svg", 100, 100)
"drawing.svg" | Draw.save_svg

clear()

Clears the current drawing and definition buffers.

circle(x, y, radius, fill, options = {})

Adds a circle to the drawing.

  • options (object, optional): Custom SVG attributes (e.g. {id: "c1", class: "pulse", "stroke-width": 2}).

rect(x, y, width, height, fill, options = {})

Adds a rectangle to the drawing.

  • options (object, optional): Custom SVG attributes.

line(x1, y1, x2, y2, stroke, options = {})

Adds a line to the drawing.

  • options (object, optional): Custom SVG attributes.

text(x, y, content, size, fill, options = {})

Adds text to the drawing.

  • options (object, optional): Custom SVG attributes.

ellipse(cx, cy, rx, ry, fill, options = {})

Adds an ellipse to the drawing.

  • options (object, optional): Custom SVG attributes.

polygon(points, fill, options = {})

Adds a polygon to the drawing.

  • points (string): A space-separated list of coordinate pairs (e.g. "100,10 250,190 10,190").
  • options (object, optional): Custom SVG attributes.

path(d, fill, options = {})

Adds an SVG path to the drawing.

  • d (string): Path data command string (e.g. "M 10 10 L 90 90").
  • options (object, optional): Custom SVG attributes.

gradient(type, id, stops, options = {})

Defines a linear or radial gradient in the <defs> section.

  • type (string): "linear" or "radial".
  • id (string): The identifier to use when referencing the gradient (e.g. "url(#my-id)").
  • stops (array<object>): An array of stop configurations (e.g. [{offset: "0%", color: "red"}, {offset: "100%", color: "blue"}]).
  • options (object, optional): Custom gradient attributes.

style(cssText)

Defines a <style> block in the <defs> section. Used for embedding CSS classes or @keyframes animations.

raw(svgCode)

Injects raw SVG markup directly into the element buffer.

render(width, height) -> string

Returns the complete, formatted SVG string.

save_svg(path, width, height) -> bool

Saves the formatted SVG drawing to the specified path. Return true on success.

pixel(x, y, color)

Sets one pixel in the raster buffer at (x, y). Later calls at the same integer coordinate replace the earlier color. Colors may be common names, #rgb, #rgba, #rrggbb, #rrggbbaa, rgb(...), or rgba(...).

Pixel Shape Functions (pixel_rect, pixel_circle, pixel_line, pixel_ellipse, pixel_polygon, pixel_triangle, pixel_star, pixel_ring, pixel_arc, pixel_bezier, pixel_text)

Draws raster shape primitives directly into the raster pixel buffer:

  • pixel_rect(x, y, w, h, color, filled = true)
  • pixel_circle(cx, cy, r, color, filled = true)
  • pixel_line(x1, y1, x2, y2, color)
  • pixel_ellipse(cx, cy, rx, ry, color, filled = true)
  • pixel_polygon(points, color, filled = true)
  • pixel_triangle(x1, y1, x2, y2, x3, y3, color, filled = true)
  • pixel_star(cx, cy, spikes, outerRadius, innerRadius, color, filled = true)
  • pixel_ring(cx, cy, radius, thickness, color)
  • pixel_arc(cx, cy, radius, startAngle, endAngle, color)
  • pixel_bezier(x1, y1, cx, cy, x2, y2, color)
  • pixel_text(x, y, text, color, scale = 1)

pixel_grid(grid, palette, scale = 1, x = 0, y = 0)

Draws a palette-indexed grid into the raster buffer. Rows may be arrays of palette indexes or strings whose characters are palette keys. scale expands every logical cell to a square of real pixels; x and y offset the grid on the output canvas.

let palatte = {"0": "transparent", "1": "#56d9e9"}
let grid = [
  "0110",
  "1001",
  "1001",
  "0110"
]

Draw.pixel_grid(grid, palatte, 32)
grid | Draw.pixel_grid(palatte, 32)

save_png(path, width, height, background = "transparent") -> bool

Encodes the raster buffer as a true-color RGBA PNG and saves it to path. Pixels outside the requested dimensions are omitted. The optional background accepts the same color formats as pixel.

---

System Functions

spawn(path) -> number

Launch a Sesi script as a concurrent background process. Returns the process ID (PID).

let pid = spawn("worker.sesi")
let pid2 = worker2.sesi" | spawn

show "Launched workers with PIDs:" pid "and" pid2

Returns: number (PID)

---

live(filePath, exportName = "handle") -> fn

Creates a dynamic hot-reloading wrapper function around a Sesi script's exported function. When the returned function is called, it re-reads, re-parses, and re-executes the target file, ensuring changes to the code are instantly reflected without restarting the parent process.

// Wrap handler with hot-reloading
let sesiFile = "server_handler.sesi"

let handler = live(sesiFile, "handleRequest")
sesiFile | live("handleRequest")

// Pass it to the web server
listen(8080, handler)
8080 | listen(handler)

Parameters:

  • filePath (string): Path to the target file.
  • exportName (string, optional): Name of the exported function in the file to run. Defaults to "handle".

Returns: fn - A wrapper function that passes all arguments to the hot-reloaded function and returns its result.

---

exec(command) / run(command) -> string

Execute a shell command synchronously and return its output.

let cmd = "ls -la"

let files = exec(cmd)
cmd | exec
let sameOutput = run(cmd) // Exact alias of exec

show files

Returns: string (stdout)

---

sesi(filePath, local = false, checkOnly = false) -> string

Parse, compile, and run a Sesi file synchronously in the current Sesi process. Unlike exec() or spawn(), this does not create a child process. Use it when one Sesi script needs to run another script and wait for it to finish.

let file = "examples/main/01_hello.sesi"
let localFile = "examples/main/25_webpage_server.sesi"

sesi(file)
file | sesi

// Allow local file access for the invoked script.
sesi(localFile, true)
localFile | sesi(true)

// Check syntax and compilation without running the file.
sesi(file, false, true)
file | sesi(false, true)

Parameters:

  • filePath (string): Path to the Sesi file to run.
  • local (bool, optional): Enables local file access for the invoked script. Defaults to false.
  • checkOnly (bool, optional): Checks syntax and compilation without running the file. Defaults to false.

Returns: string - An empty string after execution, or "✓ Syntax and Compilation valid" when checkOnly is true.

---

python(code, args) -> string

Execute arbitrary Python code using python3 or python on the host system, and return its standard output. This function is disabled in Sesi safe mode.

// Execute simple Python code:
let code = "print('Hello from Python!')"

let output = python(code)
code | python

show output

// Pass arguments to the Python environment:
let customArgs = { "name": "Alice", "score": 98 }

code = "import os, json
# Retrieve structured variables from environment
data = json.loads(os.environ['SESI_ARGS'])
print('Hello, ' + data['name'] + '! Score is ' + str(data['score']))
"

let out = python(code, customArgs)
code | python(customArgs)

show out

Parameters:

  • code (string): The Python source code to execute.
  • args (any, optional): Optional argument data. If provided:

- It is serialized to JSON and stored in the child process environment variable SESI_ARGS.

- If args is an array, individual elements are stringified and passed as positional command-line arguments to the python script (available via sys.argv[1:]).

- Otherwise, the argument itself is stringified and passed as a single command-line argument.

Returns: string (stdout of the Python process)

---

js(code, args) -> string

Execute arbitrary JavaScript code using the same Node.js runtime that is running Sesi, and return its standard output. This function is disabled in Sesi safe mode.

// Execute simple JavaScript code:
let code = "console.log('Hello from JavaScript!')"

let output = js(code)
code | js

show output

// Pass structured data to JavaScript:
let customArgs = { "name": "Alice", "score": 98 }

code = "const data = JSON.parse(process.env.SESI_ARGS)
console.log('Hello, ' + data.name + '! Score is ' + data.score)
"

let out = js(code, customArgs)
code | js(customArgs)

show out

// Pass positional command-line arguments:
let termPass = "console.log(process.argv[2])"
let argIn = ["first"]

let argOut = js(termPass, argIn)
termPass | js(argIn)

show argOut

Parameters:

  • code (string): The JavaScript source code to execute.
  • args (any, optional): Optional argument data. If provided:

- It is serialized to JSON and stored in the child process environment variable SESI_ARGS.

- If args is an array, individual elements are stringified and passed as positional command-line arguments.

- Otherwise, the argument itself is stringified and passed as a single command-line argument.

Returns: string (stdout of the JavaScript process)

---

html(body, options) -> string

Create a complete HTML document string from body markup. Use this with write_file() to generate an entire webpage.

let title = "My Sesi Webpage"

let css = "<style>
  body {
    margin: 0;
    font-family: system-ui, sans-serif;
    background: #f4f6f8;
    color: #1f2933;
  }

  header {
    padding: 48px 24px;
    background: #111827;
    color: white;
    text-align: center;
  }

  main {
    max-width: 760px;
    margin: 32px auto;
    padding: 0 20px;
  }

  section {
    margin-bottom: 28px;
    padding: 20px;
    background: white;
    border: 1px solid #d8dee6;
  }

  input {
    padding: 10px;
    width: 70%;
    border: 1px solid #b8c2cc;
  }

  button {
    padding: 10px 14px;
    border: 0;
    background: #2563eb;
    color: white;
    cursor: pointer;
  }

  li {
    margin-top: 8px;
  }

  footer {
    padding: 20px;
    text-align: center;
    color: #52606d;
  }
</style>"

let body = "
<header>
  <h1>My Sesi Webpage</h1>
  <p>A complete HTML page generated from a Sesi script.</p>
</header>

<main>
  <section>
    <h2>About</h2>
    <p>This page was created with the html() builtin and saved with write_file().</p>
  </section>

  <section>
    <h2>Todo List</h2>
    <input id='todoInput' placeholder='Add a task...' />
    <button onclick='addTodo()'>Add</button>
    <ul id='todoList'></ul>
  </section>
</main>

<footer>
  <p>Generated by Sesi</p>
</footer>

<script>
function addTodo() {
  const input = document.getElementById('todoInput')
  const text = input.value.trim()

  if (!text) return

  const item = document.createElement('li')
  item.textContent = text
  document.getElementById('todoList').appendChild(item)

  input.value = ''
}
</script>
"

let config = {
  "title": title,
  "head": css,
  "lang": "en"
}

let page = html(body, config)
body | html(config)

let filename = "sesi.html"

write_file(filename, page)
filename | write_file(page)

show "Wrote sesi.html"

Parameters:

  • body (string): Markup to place inside the generated document's <body>.
  • options (object, optional): Document metadata and head content:

- title (string): Text for the generated <title> tag. Defaults to "Sesi".

- head (string): Raw markup appended inside <head>, commonly CSS, meta tags, or scripts.

- lang (string): Value for the <html lang="..."> attribute. Defaults to "en".

Returns: string (complete HTML document)

---

env(key = null, defaultValue = null) -> string | object

Retrieve the value of an environment variable, or retrieve all environment variables as an object.

// Get a specific environment variable
let apiKey = env("GEMINI_API_KEY")
"GEMINI_API_KEY" | env

show apiKey

// Get with a fallback default value
let port = env("PORT", "8080")
"PORT" | env("8080")

show port

// Get all environment variables as an object
let allEnvs = env()

show allEnvs.HOME

Parameters:

  • key (string, optional): The name of the environment variable. If omitted or null, the entire environment is returned as an object.
  • defaultValue (any, optional): The fallback value to return if the environment variable is not defined. Defaults to null.

Returns: string (value of the env var), object (all env vars if key is omitted), or the defaultValue if not found.

Environment Variable Shorthand ($VAR)

Sesi provides a concise syntax shorthand using $ directly to look up environment variables:

// Shorthand for env("USER")
show $USER

// Use in expressions and defaults
let port = $PORT || 8080
let home = $HOME

---

time() -> number

Returns the current Unix timestamp in milliseconds.

let start = time()
// ... do work ...
let total = time() - start

show "Elapsed time:" total "ms"

Returns: number

---

format(timestamp, options) -> string

Convert Unix timestamp to a human-readable string.

let timestamp = now()

let formattedDate = format(timestamp, {dateStyle: "short"})
timestamp | format({dateStyle: "short"})

let formattedBasic = format(timestamp)
timestamp | format

show formattedDate
show formattedBasic

Returns: string

---

multi_req(fns) -> array

Concurrently execute multiple Sesi function closures, builtins, or asynchronous functions in parallel and return their resolved results as an array.

async fn job1() {
  return "a"
}
async fn job2() {
  return "b"
}

let jobs = [job1, job2]

let results = multi_req(jobs)
jobs | multi_req

show results // ["a", "b"]

Returns: array<any> containing the resolved returned values of each function in original index order.

---

workflow(steps, input = "") -> object

Run a multi-step reasoning workflow where each step can reference prior outputs.

Default behavior is automatic and requires no special syntax:

  • Step 1 gets the workflow input appended to its prompt
  • Step 2+ gets the previous step output appended to its prompt

Each step is an object with at minimum a "prompt" string. Optional keys include:

  • "model" (default: "gemini-3.7-flash")
  • "temperature", "max_tokens", "top_k", "top_p"
  • "thinkingLevel", "cache", "search", "system"

let steps = [{"prompt": "Summarize:"}, {"prompt": "Critique:"}, {"prompt": "Finalize:"}]
let result = workflow(steps, "Design a landing page brief")

show result.final

Returns: object with keys "input", "steps" (array of step outputs), and "final".

---

set_alias(alias, model) -> bool

Register a custom local name for a model string. Aliases are resolved automatically by model(), image(), and workflow().

let name = "fast"

set_alias(name, "gemini-3.5-flash-lite")
name | set_alias("gemini-3.5-flash-lite")

let answer = model("fast") {"Summarize this paragraph."}

Returns: bool (true when the alias is registered)

---

define_tool(name, fn, description = "") -> bool

Register a custom Sesi function as an AI-accessible tool.

fn summarize(text) {return "Summary: " + text}

// Register the tool
define_tool("summarizer", summarize, "Summarizes text for the AI")

// Invoke the tool using tool_call()
let result = tool_call(summarizer)("Hello world")

Parameters:

  • name (string): The name used to invoke the tool via tool_call.
  • fn (fn): The Sesi function to wrap.
  • description (string, optional): A concise description for the AI explaining when to use this tool.

Returns: bool (true when the tool is successfully registered).

---

tool_call(name)(...args) -> any

Invoke a previously registered tool by name.

// Calling with direct arguments:
let result = tool_call(summarizer)("Hello world")

// Calling with a model output as the argument:
let input = model("gemini-3.5-flash-lite") {"Provide a long sentence."}
let summary = tool_call(summarizer)(input)

Parameters:

  • name: The tool name registered via define_tool.
  • args: Arguments passed to the underlying function.

Returns: The result of the wrapped function, or null if the tool is not found.

---

list_tools() -> array

List custom tool names registered by define_tool.

let tools = list_tools()
show tools

Returns: array<string>

---

error_type(type, message, data = null) -> object

Create a custom typed error object you can throw with raise_error.

let title = "ValidationError"
let message = "Missing email"
let data = {field: "email"}

let err = error_type(title, message, data)
title | error_type(message, data)

Returns: object with keys "type", "message", and "data".

---

raise_error(type_or_error, message = "", data = null) -> never

Throw a custom typed error that can be handled in try/catch.

try {
  "RateLimit" | raise_error("Too many requests", {retryIn: 30})
} catch (e) {show "type:" e.type "message:" e.message}

You can also pass an error_type(...) object directly:

let err = error_type("ValidationError", "Invalid payload", {field: "email"})
"ValidationError" | error_type("Invalid payload", {field: "email"})

raise_error(err)
err | raise_error

Returns: never (always throws)

---

retry(action, options = null) -> any

Executes a function, automatically catching errors and retrying execution with exponential backoff if it fails.

fn volatileTask() {
  if random() > 0.8 {
    return "Success!"
  }
  "TemporaryError" | raise_error("Task failed unpredictably")
}

// Retry up to 5 times (default backoff options)
let result = retry(volatileTask, 5)
volatileTask | retry(5)

// Retry with custom configuration object
let config = {
  max_retries: 4,
  initial_delay: 500,
  backoff_factor: 1.5
}

let result2 = retry(volatileTask, config)
volatileTask | retry(config)

Parameters:

  • action (fn): The function to execute.
  • options (number or object): Either a number representing max_retries, or a configuration object with fields:

- max_retries (number): The maximum number of retry attempts (default: 3).

- initial_delay (number): Initial wait time in milliseconds before the first retry (default: 1000).

- backoff_factor (number): Multiplier applied to the delay after each failure (default: 2.0).

Returns: any (the return value of the successfully executed action function). Throws the last encountered error if all retry attempts are exhausted.

---

lazy(action, ...args) -> lazy

Creates a memoized delayed computation from a function and optional captured arguments. The function is not run until the lazy value is passed to force(...); after the first force, the result is cached.

fn expensive() {
  show "computed once"
  return 42
}

let delayed = lazy(expensive)
show type(delayed) // "lazy"
show force(delayed) // 42
show force(delayed) // 42, cached

force(value) -> any

Resolves a lazy value or promise. Non-lazy values are returned unchanged.

let delayed = lazy(expensive)
let value = force(delayed)

---

timeout(action, ms, fallback = unset) -> any

Runs a function with a millisecond deadline. If the action does not complete before the deadline, timeout(...) returns the optional fallback value; without a fallback, it throws a TimeoutError.

fn slow() {
  sleep(1000)
  return "done"
}

let value = timeout(slow, 100, "too slow")

For whole-script deadlines, use the CLI flag --timeout <ms>.

---

profile(name, action) -> any

Runs a function and records its elapsed time under name. The wrapped function's return value is passed through unchanged.

fn work() {
  let total = 0
  for i = 0 to 1000 { total = total + i }
  return total
}

let result = profile("work-loop", work)
show profile_report("text")

profile_start(name) -> string

Starts a named manual profiling section and returns the normalized section name.

profile_end(name) -> object

Ends a named manual profiling section and returns the latest measurement summary.

profile_report(format = "object") -> array | string

Returns profiler measurements sorted by total runtime. Use profile_report("text") for a printable table.

Use sesi --profile <file> to collect statement, VM, and builtin timings for an entire script.

---

random() -> number

Returns a random floating-point number between 0 (inclusive) and 1 (exclusive).

let rand = random()
if rand > 0.5 {show "Heads"} else {show "Tails"}

---

Memory Functions

memory_search(name, query, top_k = 3) -> array

Search a memory binding's history for entries most semantically similar to a query string. Uses Gemini embedding models (gemini-embedding-001 with gemini-embedding-2 fallback) to generate vector embeddings for each memory chunk, then ranks them by cosine similarity.

Embedding results are locally cached by content hash — repeated searches over the same memory entries will not make redundant API calls.

memory chat {"System: You are a helpful assistant."}
chat = chat + "User: Tell me about databases"
chat = chat + "Assistant: Databases store structured data..."
chat = chat + "User: What about caching?"
chat = chat + "Assistant: Caching improves performance..."

let results = memory_search("chat", "database storage")
"chat" | memory_search("database storage")

for item in results {
  show "Score:" item.score
  show "Text:" item.text
  show "---"
}

Parameters:

  • name (string): The name of the memory binding to search.
  • query (string): The search query string.
  • top_k (number, optional): Maximum number of results to return, sorted by descending similarity score. Defaults to 3.

Returns: array<object> — Each object contains "text" (the matching memory entry) and "score" (cosine similarity from 0.0 to 1.0).

---

memory_config(name, options = {}) -> object

Configure automatic summarization for a memory binding. Configuration should follow the memory declaration and applies whenever that binding is updated.

memory conversation {"System: You are a research assistant."}
let config = memory_config("conversation", {
  "enabled": true,
  "max_tokens": 8000,
  "target_tokens": 4800,
  "summary_model": "gemini-3.5-flash-lite"
})

conversation = conversation + "User: Record this project decision."

  • enabled (bool): Enable automatic summarization. Defaults to true.
  • max_tokens (number): Trigger threshold. Defaults to 900000.
  • target_tokens (number): Post-compaction target. Defaults to 60% of max_tokens.
  • summary_model (string): Model used for compaction. Defaults to gemini-3.5-flash-lite.

The process preserves a recent verbatim tail and rolls older content, including any prior summary, into a new [Memory Summary] section. Concurrent compaction requests for the same binding are serialized. If the summarizer fails, the original memory remains unchanged.

Global defaults can be set with SESI_MEMORY_AUTO_SUMMARIZE, SESI_MEMORY_MAX_TOKENS, SESI_MEMORY_TARGET_TOKENS, and SESI_MEMORY_SUMMARY_MODEL.

Returns: object — The resolved configuration using enabled, max_tokens, target_tokens, and summary_model.

---

memory_trim(name, max_tokens = 900000) -> string

Explicitly compact a memory binding. If the estimated token count exceeds max_tokens, older content is summarized and the recent tail remains verbatim. The configured summary model is used.

If the memory is already within the budget, the full memory text is returned unchanged.

memory conversation {"System: You are a research assistant."}

// After many turns of conversation...
// Trim to stay within a 500K token budget
let trimmed = memory_trim("conversation", 500000)
"conversation" | memory_trim(500000)

show "Memory now:" trimmed | length "characters"

Parameters:

  • name (string): The name of the memory binding to manage.
  • max_tokens (number, optional): Maximum token budget. Defaults to 900000 (suitable for Gemini's 1M context window with headroom).

Returns: string — The compacted memory text, or the original text when already within budget or when summarization fails. Assign the result back to the binding when the updated Sesi variable is needed immediately: conversation = memory_trim("conversation", 500000).

---

Debugging Functions

debug() -> null

Pauses execution and launches an interactive debugger REPL in your shell terminal.

let x = 10
let y = 20

// Pauses execution and opens interactive sesi-debug REPL
debug()

// You can also leave a comment inside the function for referencing, it won't affect your script
debug("Verify x and y using 'show' in eval")
"Verify x and y using 'show' in eval" | debug

show x + y

REPL Commands:

  • env — Displays all variables in the active lexical scope chain.
  • eval <expr> — Evaluates any Sesi expression in-memory within the active scope context.
  • help / ? – Display available debug commands.
  • c / continue — Resumes standard program execution.

Returns: null

---

Global Variables

args

An array of strings containing the command-line arguments passed to the Sesi script. This excludes any Sesi interpreter options (e.g. -l) and the script filename itself.

show "Number of script args:" length(args) // len() works too, length() works best for readability
args | length

if (args | length) > 0 {
  show "First script argument:" args[0]
}

Type: array<string>

---

Native Matrix Functions

Native matrix functions operate on non-empty rectangular arrays containing only finite numbers. They execute in the Sesi runtime rather than interpreted Sesi loops.

matrix_dot(a, b) -> array

Multiply two matrices. The number of columns in a must equal the number of rows in b.

let product = matrix_dot([[1, 2]], [[3], [4]]) // [[11]]
[[1, 2]] | matrix_dot([[3], [4]])

matrix_transpose(matrix) -> array

Exchange matrix rows and columns.

matrix_add(a, b) -> array

Add two matrices. If b has one row, that row is broadcast across every row of a.

matrix_sub(a, b) -> array

Subtract identically shaped matrices.

matrix_mul_elements(a, b) -> array

Multiply corresponding elements of identically shaped matrices.

matrix_scale(matrix, scalar) -> array

Multiply every element by a finite numeric scalar.

matrix_sigmoid(matrix) -> array

Apply the logistic sigmoid function to every element.

matrix_dsigmoid(matrix) -> array

Calculate y * (1 - y) for each element containing a sigmoid output.

matrix_sum_rows(matrix) -> array

Sum each column across all rows and return a single-row matrix.

matrix_mse(a, b) -> number

Return the mean squared error across two identically shaped matrices.

---

Math Functions

floor(value) -> number|null

Rounds a number down to the nearest integer. floor is globally available and does not require importing std/math.

floor(3.7)  // 3
3.7 | floor

Returns: number, or null when the input is not numeric.

---

exp(x) -> number

Returns Euler's number $e$ (approx. 2.71828) raised to the power of $x$.

exp(0)             // 1.0
0 | exp

exp(1)             // 2.718281828459045
1 | exp

let sigmoid = 1.0 / (1.0 + exp(0.0 - 0.5))
1.0 / (1.0 + (0.0 - 0.5 | exp))

show sigmoid      // 0.6224593312018546

Returns: number

---

trunc(value, length = 0) -> number|string

Truncates a value. If the value is a number, it returns the integer part by removing fractional digits. If the value is a string, it truncates the text to the specified length.

// Numeric truncation
trunc(10.5)        // 10
10.5 | trunc

trunc(-10.5)       // -10
-10.5 | trunc

// String truncation
trunc("Hello World", 5)  // "Hello"
"Hello World" | trunc(5)

Parameters:

  • value (number|string): The value to truncate.
  • length (number): The character limit for string truncation. Optional.

Returns: number, string, or null if the first argument is not a number or string.

---

Math Functions (std/math)

The "std/math" module exposes the complete stable JavaScript Math surface supported by Node.js 20+, including constants, trigonometry, logarithms, rounding, integer helpers, and variadic operations:

allow "std/math" in as Math

show Math.sqrt(16) // 4
show Math.atan2(1, 0) // 1.5707963267948966
show Math.min(8, 3, 5) // 3

Constants: E, LN10, LN2, LOG10E, LOG2E, PI, SQRT1_2, SQRT2.

Functions: abs, acos, acosh, asin, asinh, atan, atanh, atan2, cbrt, ceil, clz32, cos, cosh, exp, expm1, floor, fround, hypot, imul, log, log1p, log2, log10, max, min, pow, random, round, sign, sin, sinh, sqrt, tan, tanh, trunc.

min, max, and hypot accept any number of arguments. Bare floor(value) remains globally available without this import.

Function Introspection

name(func) -> string

Returns the name of a given function.

fn my_func() {}

show name(my_func) // "my_func"
my_func | name

Parameters:

  • func (fn): The function to introspect.

Returns: string (or null if the value is not a function)

---

arity(func) -> number

Returns the number of parameters a function expects.

fn add(a, b) { return a + b }

show arity(add) // 2
add | arity

Parameters:

  • func (fn): The function to introspect.

Returns: number (or null if the value is not a function)

---

is_function(value) -> bool

Checks whether a value is a function.

fn my_func() {}

is_function(my_func) // true
my_func | is_function

is_function(42)      // false
42 | is_function

Parameters:

  • value (any): The value to check.

Returns: bool

---

Collection Checks

is_array(value) -> bool

is_object(value) -> bool

is_string(value) -> bool

is_number(value) -> bool

is_bool(value) -> bool

is_null(value) -> bool

Type-checking utility functions that return a boolean indicating whether the provided value is of the specified type.

is_array([1, 2])         // true
[1, 2] | is_array

is_object({a: 1})      // true
{a: 1} | is_object

is_string("hello")       // true
"hello" | is_string

is_number(42)            // true
42 | is_number

is_bool(true)            // true
true | is_bool

is_null(null)            // true
null | is_null

Parameters:

  • value (any): The value to check.

Returns: bool

---

String Functions

length(string) -> number

An alias for len(). Returns the length of the string.

length("hello") // 5
"hello" | length

Returns: number

---

starts_with(string, prefix) -> bool

Checks if a string starts with the given prefix.

starts_with("hello", "he") // true
"hello" | starts_with("he")

Returns: bool

---

ends_with(string, suffix) -> bool

Checks if a string ends with the given suffix.

ends_with("hello", "lo") // true
"hello" | ends_with("lo")

Returns: bool

---

index_of(collection, value) -> number

Returns the first index at which a given value can be found in the collection (string or array), or -1 if it is not present.

index_of("hello", "l") // 2
"hello" | index_of("l")

index_of([1, 2, 3], 2) // 1
[1, 2, 3] | index_of(2)

Returns: number

---

repeat(string, count) -> string

Constructs and returns a new string which contains the specified number of copies of the string concatenated together.

repeat("a", 3) // "aaa"
"a" | repeat(3)

Returns: string

---

(Note: to_upper, to_lower, trim, contains, slice, and swap are documented under Collection Functions.)

---

Array Functions

includes(collection, value) -> bool

Checks if a collection (array or string) includes a certain value.

includes([1, 2, 3], 2) // true
[1, 2, 3] | includes(2)

includes("hello", "e") // true
"hello" | includes("e")

Returns: bool

---

reverse(array) -> array

Reverses an array in place and returns it.

reverse([1, 2, 3]) // [3, 2, 1]
[1, 2, 3] | reverse

Returns: array

---

sort(array, compareFn?) -> array

Sorts the elements of an array and returns it. Optionally takes a comparison function.

sort(["c", "a", "b"]) // ["a", "b", "c"]
["c", "a", "b"] | sort

Returns: array

---

unique(array) -> array

Returns a new array with all duplicate elements removed.

unique([1, 1, 2, 3, 3]) // [1, 2, 3]
[1, 1, 2, 3, 3] | unique

Returns: array

---

flatten(array) -> array

Returns a new array with all sub-array elements concatenated into it recursively up to one level.

flatten([[1, 2], [3, 4]]) // [1, 2, 3, 4]
[[1, 2], [3, 4]] | flatten

Returns: array

---

(Note: map, filter, reduce, and find are documented under Collection Functions.)

---

Error Handling

Sesi supports structured error handling via try/catch blocks.

try {
  let data = "missing.txt" | read_file
} catch (e) {
  show "Caught error:" e
}

---

Tips & Tricks

Converting values

// To string
str(value)
value | str
value + ""        // Works for most types

// To number
num(string)
string | num
string + 0        // Doesn't work (concatenation)

// To bool
bool(value)
value | bool
!(!value)         // Double negation

Checking types

is_array(value | type)
type(value) == "array"
(value | type) == "array"
type(value) | is_array
(value | type) | is_array

is_object(value | type)
type(value) == "object"
(value | type) == "object"
type(value) | is_object
(value | type) | is_object

is_null(value | type)
type(value) == "null"
(value | type) == "null"
type(value) | is_null
(value | type) | is_null

Working with arrays

let arr = [1, 2, 3]

// Length
length(arr)
arr | length

// Last element
let arrLength = arr | length
arr[arrLength - 1]

// Add element
push(arr, 4)
arr | push(4)

// Remove last
pop(arr)
arr | pop

// Join
join(arr, ", ")
arr | join(", ")

Working with objects

let obj = { "a": 1, "b": 2 }

// Get keys
keys(obj)
obj | keys

// Get values
values(obj)
values | obj

// Check key
includes(keys(obj), "a")
keys(obj) | includes("a")
obj | keys | includes("a")

---

Standard Library Modules (Supported natively in v1.x)

std/game

std/game is Sesi's optional Babylon.js/Electron 3D desktop engine. Scenes use left-handed coordinates with Y up, +X right, and +Z forward. Development-time relative asset paths use the current working directory, matching file, drawing, and audio writers. Core Sesi provides the native-object and module bridge; install the heavyweight backend per project:

npm install @misterscan/sesi-game

allow "std/game" in as Game

let game = Game.create({title: "Arena"})
let scene = game.create_scene("main")
let camera = scene.orbit_camera("camera", {radius: 12})
let light = scene.hemispheric_light("light", {intensity: 0.9})
let player = scene.box("player", {position: Game.vec3(0, 1, 0)})

fn update(dt) { player.rotation.y = player.rotation.y + dt }
game.on_update(update)
game.run()

Exports: VERSION, create, vec2, vec3, quat, and color. Game services cover named scenes, explicit assets, input actions, save storage, lifecycle callbacks, run, and desktop build. game.run({icon: "asset_name"}) sets the window/app icon from a registered texture asset or local image path; omitting it uses the default placeholder icon. Scenes provide primitives and glTF/GLB models through load_model, cameras, lights, Standard/PBR materials, raycasts, intersection tests, transform and loaded-model animation, spatial sound, Babylon GUI, and opt-in Havok bodies/collisions. GUI controls support live width, height, left/top offsets, and horizontal/vertical alignment.

Run and build require sesi -l. The Electron window is isolated and packaged applications use a deny-by-default capability manifest. The complete API, installation, lifecycle, physics, permissions, and packaging reference is GAME.md.

std/math

Includes the complete stable JavaScript Math surface supported by Node.js 20+.

Constants: E, LN10, LN2, LOG10E, LOG2E, PI, SQRT1_2, SQRT2.

Functions: abs, acos, acosh, asin, asinh, atan, atanh, atan2, cbrt, ceil, clz32, cos, cosh, exp, expm1, floor, fround, hypot, imul, log, log1p, log2, log10, max, min, pow, random, round, sign, sin, sinh, sqrt, tan, tanh, trunc.

allow "std/math" in with {
  PI,
  LN2,
  sqrt,
  abs,
  floor,
  ceil,
  sin,
  cos,
  tan,
  pow,
  atan2,
  min,
  max,
  round,
  hypot
}

std/time

Includes time, sleep, and timezone formatting functions: now(), sleep(ms), format(timestamp, options).

allow "std/time" in as Time

let t = Time.now()
// Format time with a specific timezone
let formatted = t | Time.format({timeZone: "America/New_York", timeStyle: "medium"})
show formatted // e.g. "2:27:02 AM"

JSON conversion

JSON serialization and parsing are built in as to_json(value) and from_json(text). The former std/json module has been removed.

let original = {
  "project": "Sesi",
  "version": "1.9.0"
}
let encoded = to_json(original)
show from_json(encoded).project

std/base64

Includes Base64 conversion helpers with optional modes:

  • encode(value, mode?)
  • decode(base64_text, mode?)

Modes:

  • "text" (default): encode/decode UTF-8 strings
  • "bytes": encode/decode raw byte arrays (array<number> in range 0..255)

allow "std/base64" in as Base64

let encoded = Base64.encode("Hello, Sesi!")
"Hello, Sesi!" | Base64.encode

show encoded

let decoded = Base64.decode(encoded)
encoded | Base64.decode

show decoded // "Hello, Sesi!"

let bin = [0, 255, 16, 32]

let b64 = Base64.encode(bin, "bytes")
bin | Base64.encode("bytes")

let back = Base64.decode(b64, "bytes")
b64 | Base64.decode("bytes")

show back // [0, 255, 16, 32]

std/db

Includes Sesi's lightweight embedded Document Database engine: db_open(filename, password?).

A database instance supports opening collections, inserting documents, querying/finding, updating, and deleting records.

Encryption & Decryption at Runtime:

If an optional second parameter password is provided, Sesi automatically encrypts database contents stored on disk using AES-256-CBC with a dynamic, randomized initialization vector (IV) on every write, and decrypts it during reads.

allow "std/db" in with {db_open}

let db = "data.db" | db_open("secure-passphrase-here")
let users = "users" | db.collection

/* CRUD API:
users.insert(object) -> Returns inserted document (adds unique _id if missing)
users.find(query_object?) -> Returns array of matching documents (returns all if query omitted)
users.update(query_object, update_object) -> Returns number of updated documents
users.delete(query_object) -> Returns number of deleted documents */

std/terminal

Provides ANSI terminal control primitives for building CLI applications. They write standard ANSI escape sequences, so they work in ANSI-compatible terminals; output panes that do not interpret ANSI may show the raw sequences.

allow "std/terminal" in as Terminal

// Clear the screen
Terminal.clear()

// Move cursor to x=10, y=5
Terminal.cursor(10, 5)

// Output colored text
show "Hello!" | Terminal.color("green")

// Combine text styles and colors
show "Ready" | Terminal.style(["bold", "underline", "brightCyan"])

// Move relative to the current position and update the title bar
Terminal.move(2, -1)
Terminal.title("Sesi dashboard")

Text formatting functions return a formatted string, so they compose naturally with show and pipes:

  • color(text, color) — foreground colors: black, red, green, yellow, blue, magenta, cyan, white, gray/grey, and brightBlack through brightWhite. bold remains supported for compatibility.
  • style(text, styles) — apply one style or an array of styles. Available styles: reset, bold, dim, italic, underline, blink, inverse, hidden, and strikethrough, plus every foreground color.
  • background(text, color) and rgbBackground(text, red, green, blue) — apply a background color.
  • rgb(text, red, green, blue) — apply a 24-bit foreground color. Channels are clamped to 0–255.

Screen and cursor functions write immediately and return none:

  • clear(mode?) — clear screen (default), line, down, or up.
  • eraseLine(mode?) — erase all (default), right, or left of the current line.
  • eraseScreen(mode?) — erase all (default), down, up, or scrollback.
  • cursor(x, y) — move to one-based column and row coordinates.
  • move(x, y), up(amount?), down(amount?), left(amount?), right(amount?) — move relative to the current cursor. Positive x moves right and positive y moves down.
  • saveCursor(), restoreCursor(), hideCursor(), showCursor().
  • write(text), line(text?), title(text), and bell().
  • size() — returns { "columns": number, "rows": number }; both values are 0 when unavailable.

std/browser

Includes Sesi's browser automation capabilities powered by Playwright: launch(options?).

A browser instance supports opening new pages. A page instance supports navigation, selector actions, JavaScript evaluation, base64 screenshot and PDF generation.

allow "std/browser" in with {launch}

// Launch browser (headless or headed)
let browser = {headless: true} | launch

// Open a new page/tab
let page = browser.newPage()

// Navigate to a website
"https://example.com" | page.goto

// Get the page title
let title = page.title()
show "Title:" title

// Get inner text of a selector
let heading = "h1" | page.inner_text
show "Heading:" heading

// Get an attribute of an element
let link_href = "a" | page.attribute("href")

// Evaluate JavaScript on the page
let scrollX = "window.scrollX" | page.evaluate

// Take a base64 screenshot
let b64_screenshot = page.screenshot()

// Take a screenshot and save it to a file
{path: "screenshot.png", fullPage: true} | page.screenshot

// Generate PDF (base64 and saved to file)
{path: "page.pdf", format: "A4"} | page.pdf

// Wait for a selector
"h1" | page.wait_for_selector({state: "visible", timeout: 5000})

// Wait for a specific timeout in milliseconds
1000 | page.wait_for_timeout

// Clean up
page.close()
browser.close()

API Reference:

##### launch(options) -> browser

Launches a browser instance. options is an object with:

  • headless (bool, optional): Whether to run browser in headless mode. Defaults to true.

##### browser.newPage() -> page

Creates a new page/tab.

##### browser.close()

Closes the browser instance.

##### page.goto(url)

Navigates to the specified URL.

##### page.content() -> string

Gets the full HTML content of the page.

##### page.screenshot(options?) -> string (base64)

Takes a screenshot of the page. Returns a base64 encoded string.

options is an optional object containing:

  • path (string): File path to save the screenshot.
  • fullPage (bool): Whether to capture the full scrollable page.

##### page.click(selector)

Clicks the element matching the selector.

##### page.fill(selector, value)

Fills the input matching the selector with the specified value.

##### page.type(selector, value)

Types the specified value into the element matching the selector.

##### page.press(selector, key)

Presses the specified key on the element matching the selector.

##### page.inner_text(selector) -> string

Gets the inner text of the element matching the selector.

##### page.attribute(selector, name) -> string

Gets the value of the attribute name of the element matching the selector.

##### page.evaluate(script) -> any

Evaluates a JavaScript script/expression in the page context and returns the result.

##### page.title() -> string

Gets the page title.

##### page.close()

Closes the page.

##### page.pdf(options?) -> string (base64)

Generates a PDF of the page. Returns a base64 encoded string.

options is an optional object containing:

  • path (string): File path to save the PDF.
  • format (string): Paper format (e.g. "A4", "Letter").

##### page.wait_for_selector(selector, options?)

Waits for the element matching the selector to satisfy the options.

options is an optional object containing:

  • state (string): Wait for state ("attached", "detached", "visible", "hidden").
  • timeout (number): Timeout in milliseconds.

##### page.wait_for_timeout(ms)

Waits for the specified duration in milliseconds.

---

Module Resolution (v1.x)

Sesi resolves local module imports by searching directories in priority order:

Priority Location Notes
1 Script's own directory Same folder as the running .sesi file
2 Current working directory Where you ran sesi from
3 SESI_PATH env var Semicolon (Windows) or colon (Unix) separated paths
4 ~/.sesi/lib Global shared library, available system-wide

Global Library (~/.sesi/lib)

Place any .sesi module in ~/.sesi/lib (Windows: %USERPROFILE%\.sesi\lib) to make it importable from any project on your system:

# Install a module globally (Windows)
copy mymodule.sesi $env:USERPROFILE\.sesi\lib\

# Install a module globally (Linux/Unix)
cp mymodule.sesi ~/.sesi/lib/

// Now importable from any folder
allow "mymodule1" in with {
  function1,
  function2
}
allow "mymodule2" in as Name
allow "mymodule3" in with {function4}

SESI_PATH Environment Variable

Point SESI_PATH to one or more additional directories for shared modules:

# Windows
$env:SESI_PATH = "C:\MyLibs;C:\Projects\shared"

# Unix / macOS
export SESI_PATH="/mylibs:/projects/shared"

Error Output

If a module cannot be found in any search location, Sesi prints a detailed error showing exactly where it looked:

Module not found: "retrorender"
Searched in:
  C:\MyApp
  C:\MyApp
  C:\Users\owner\.sesi\lib
Tip: add a folder to SESI_PATH, or place shared modules in ~/.sesi/lib

---

Performance Notes

  • show() is unbuffered (each call flushes)
  • Array operations are O(n) for most functions
  • Object operations are O(1) for key access
  • String concatenation with + is O(n) (consider pre-allocating)

---

Return Value Reference

Function Return Value on Error
num(value) null
len(value) null
keys(value) null
values(value) null
pop([]) null
type(value) "unknown"
str(value) "null" or string representation

---

See Also