RAgents
Guide: Work with TypeScript

Guide

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.

Build mini-apps