Work with TypeScript
Combine functions, program deterministic workflows, and involve models where needed.
On this page
TypeScript as the AI's way of working
The model uses RAgents capabilities by writing TypeScript. The workspace provides callable functions for tasks such as reading a file or creating an agent. The model combines them into a program, keeping results in variables, checking conditions, and running independent steps in parallel.
The same approach supports larger setups. In the word game, code creates the participants and controls their handoffs while four models supply the words. A mini-app displays progress from the same program. The AI therefore does not need to derive the workflow again from conversation instructions after every response.
For one-off work, a snippet is enough: a short TypeScript program for one execution. Its result returns to the model, which can then decide what to do next. When a program needs to retain state or react to later messages, it becomes an actor program. Both forms use the same functions.
One-off snippets
Every function available to an actor is also a native model tool: read, write, edit,
bash, agent creation, and workflow functions are called directly, without a TypeScript wrapper.
typescript_api returns exact types and detailed guidance, and typescript_eval runs TypeScript
code in which every function is available as context.functions.<name>(input). A snippet combines
calls, filters results, and passes values onward without transcription. Both belong to the server
foundation, even without the optional actor-program plugin. A function can stay snippet-only with
nativeTool: false; the engine does so for the low-level journal event functions event_query,
event_subscribe, event_unsubscribe, and event_subscription_list. Native and snippet calls use
the same implementation, working-directory resolution, permission checks, and journal recording.
Plugins register each implementation once; snippets and persistent actor programs use the same API.
Every equipped LLM actor receives its native tools as tool definitions with their short and
detailed descriptions. Its system prompt additionally explains the snippet path and lists the
snippet-only functions available to it with names and short descriptions. This applies to
coordinators and subagents. Both update even during a turn, including functions that an actor
program activates. Neither grants additional functions or permissions; plain LLMs with
tools: [] receive neither.
Short descriptions and details
A function registered with defineRunFunction has a technical name for calls and a readable
label for people. description briefly explains its purpose and appears in the automatic
overview. An optional longDescription adds detailed rules, prerequisites, and examples. Input
and result types come from schemas; descriptions do not replace those contracts. Local recursive
schema references create named TypeScript aliases, keeping nested recursive contracts
fully typed in snippets, actor programs, and the public run API. Resolution includes local
$defs references; external references are not loaded.
Without selected names, typescript_api returns the compact catalog. query searches names and
short descriptions. With names, the result includes the entries of those functions in
RAgentsCapabilityMap as TypeScript declarations, available long descriptions, and guidance
attached to those exact functions. The declarations of context itself (run, actor, state,
log, std with its mediators) are the same for every function, so context: true returns them
once with the general guidance instead of every answer repeating them. A result type made of
journal events lists only the event types the function actually produces. Declarations include each
property's description as a comment. A schema shared by several functions appears once as a
named alias. JSON Schemas with validation rules such as lengths and patterns are added to a name
selection only with schemas: true. This input asks for the actor-list contract; it is a tool
input, not a snippet:
{"names":["actor_list"]}
Execute code
typescript_eval accepts exactly one of code or path. The source is the body of an async
function with context; return produces the result. For path, the executor of the machine that
holds the file reads it (files.read): a path relative to the run root from the machine the run is
bound to, a path under an alias such as @actors from the server, also for a connected workspace.
Execution always takes place on the server. Before execution, the shared compiler checks
the code against the current API contract. Native execution uses the same executor, function
resolver, and cancellation path as actor programs. A snippet requires no actor package,
activation, or separate actor. Its variables live for that execution.
Compilation uses a warm pool of up to eight long-lived worker threads; additional requests wait
for a free worker. Each worker loads TypeScript once and caches parsed library and declaration
files by name and content, up to 256 entries with oldest-first eviction. Snippet sources are
always parsed fresh, while the previous program enables structural reuse. Results, diagnostics,
hashes, and emitted code match a cold compilation. The default 60-second limit, configurable by
callers up to 180 seconds, includes queue time. A worker that times out, exits, or receives run
cancellation is terminated and replaced when next needed; its request fails with TIMEOUT,
WORKER_FAILURE, or ABORTED. A compiler error in submitted code does not terminate the worker.
Resource limits apply per worker.
A function with an empty or entirely optional input schema can be called without an argument;
context.functions.status() and context.functions.status({}) are equivalent. An undefined
object property is treated like a missing key on both input and result. The compiler does not
enable exactOptionalPropertyTypes; the host accepts the value and omits the key from its JSON
result. undefined as an array element or the result itself remains an error.
This typescript_eval input reads the existing actors and returns the actual response to the
model:
{"code":"const actors = await context.functions.actor_list({}); return actors;"}
In a file, the same function body can combine several independent queries. For this example,
load the actor_list and model_list contracts first:
const [actors, models] = await Promise.all([
context.functions.actor_list({}),
context.functions.model_list({}),
]);
return { actors, models };
State, continuation, and errors
Snippets can read data, combine results, and set up participants, programs, subscriptions, or views. Actor programs handle later events, persistent state, and mini-apps. The choice follows the task; a setup does not need a dedicated setup actor. Domain-specific skill templates describe the desired result rather than prescribing a technical solution. Technical contracts and guides belong in the discoverable environment.
A snippet acts as its caller. onInput acts as the receiving TypeScript actor. An actor function
uses its owner's state but calls run functions under the caller's identity. Accordingly,
event_subscribe creates the subscription for the acting caller. For a persistent actor to
subscribe on its own behalf, it makes the call from onInput. Calling another actor's function
does not transfer actor identity.
Completed function calls remain effective if a later step fails. A snippet is not a transaction across its calls. Retries inspect the existing setup and continue missing steps. A snippet does not wait for future responses; subscriptions deliver them as later ActorInputs.
Source: docs/spec/typescript-platform.md. This chapter as Markdown.