Distributed work
Run the host locally or remotely, keep tools beside the project, and move runs between servers.
On this page
Work without a checkout
Using RAgents does not require a repository. pnpm build:package creates the npm package
@schlenkr/ragents from this checkout in dist/ragents; with --pack, it also creates
dist/schlenkr-ragents-<version>.tgz. The package contains the server, engine, executor, all
plugins, and the scripts behind its subcommands. From the documentation it includes only
LICENSE and a separate English README.md for the npm listing, sourced from
scripts/package/README.md. It carries the finished web interface and every built-in plugin as a
bundle, so nothing is built on the user's machine. Its package.json records the source commit as
ragents.hostVersion. Install it like any other npm package:
npm install -g @schlenkr/ragents
RAGENTS_TOKEN=<token> ragents connect https://ragents.example.com
For an npx invocation without a global install, see Install from npm.
Node.js 22.19 or newer is required, without Git, pnpm, or a source build. The subcommands are:
ragents run <folder> "<task>",send,journal, andstoplet an agent work on a project.ragents connect <server-url>fetches a client profile with its plugin bundles, provisions tools, and starts a server. It supports--port <n>,--clean, and--no-start, likepnpm connect.ragents start <profile|path>starts an included profile (core,developer, orshowcase), a customragents.config.<profile>.tsanywhere on disk, or a previously fetched version.ragents provision [<profile>|--workspace]installs only the required tools.ragents workspace-client <server-url> [folders ...]registers this machine as a workspace.ragents plugin build <folder...>builds plugin sources into bundles, with type checks; see Build and ship a plugin.ragents --version(or-v) prints the version of the package or standalone installation.
The package runs like the checkout, with the same files in the same places and TypeScript loaded
at runtime through tsx. On first use, the command creates the node_modules/@ragents/*
links that pnpm supplies in a checkout. This is idempotent and requires no elevated privileges.
Custom profiles and plugins
A developer will usually keep a custom profile and plugins somewhere on disk. ragents start <path> starts it from the package, while ragents run --profile <path> runs it without an
interface for an agent. Both resolve the path in the same way:
ragents start ~/projects/mine/ragents.config.mine.ts
ragents run ~/projects/mine "Build this" --profile ~/projects/mine/ragents.config.mine.ts
The file must be named ragents.config.<profile>.ts; that name becomes the profile name. Startup
provisions the plugin tools and then starts the server. Nothing is built: the package carries the
finished web interface, which is the same for every profile, and the server loads the web halves
of the profile's plugins from their bundles at runtime.
The server loads plugins only as bundles. The package carries its built-in plugins as bundles
under bundles/; a custom plugin is built first with ragents plugin build <source-folder...>,
which writes ./dist/plugins/<custom-plugin-id> by default. The package brings everything this
build needs, including the type checks against the host API. The profile names that bundle by a
path relative to the profile file, such as "./dist/plugins/<custom-plugin-id>"; a source folder
in the profile stops the start and names the build command. A bundle imports from the host only
the modules of the host API list, resolved by apps/server/src/host-resolution-hooks.mjs, and
must be rebuilt when the host API changes. Its web/ half is served under /plugins/<id>/web/
and loaded by the browser; its Tailwind classes join the one stylesheet the server compiles at
startup. The build tool bundles libraries such as lucide-react from the host, so the package
also includes the dependencies declared by apps/web/package.json. The host does not check
whether such a bundle still matches its sources; rebuild it before starting.
An external plugin project can use a host symlink to access host tools. With a package
installation, that link points to the package instead of a checkout. Set RAGENTS_HOST to the
appropriate location:
export RAGENTS_HOST="$(npm root -g)/@schlenkr/ragents"
A script in the external repository can create the host symlink from that value. If that script
identifies a checkout by pnpm-workspace.yaml, it should test for something the package shares
instead, such as apps/server/src/main.ts beside package.json. Start with ragents start <path-to-profile>;
the symlink is used only by scripts and tsconfig files in the external repository.
The VS Code extension starts the same profile from the same package. Set ragents.hostPath to
the package directory (<npm-prefix>/lib/node_modules/@schlenkr/ragents) rather than a checkout.
If the setting is empty and the extension is not running from a checkout, it fetches the package
itself as described under Run panel and VS Code extension.
Connect to a server
A developer can run RAgents locally with the profile and models of a central server. This
requires a local host with the same host API as that server: either the @schlenkr/ragents
package, which needs only Node, or this checkout with Git, Node, pnpm, pnpm install,
pnpm build:agent, pnpm build:plugins, and pnpm build:web. It also requires a personal token
for a user in the server profile with profile.fetch and models.use permissions:
RAGENTS_TOKEN=<token> ragents connect https://ragents.example.com
RAGENTS_TOKEN=<token> ragents connect https://ragents.example.com --port 4720 --clean
RAGENTS_TOKEN=<token> ragents connect https://ragents.example.com --no-start
In a checkout, the same command is pnpm connect <server-url> and uses the same implementation.
connect fetches the client-profile description and checks the local host before it downloads
anything. The host must offer the host API number the server's bundles were built against, and it
must have a built-in bundle for every plugin the profile names by ID. The same commit is not
required. On a mismatch, connect stops and names the fix: npm install -g @schlenkr/ragents@<version> for the package, where the server supplies the version, or the
server's commit plus pnpm build:plugins and pnpm build:web in a checkout. It then downloads
the archive, which holds only the profile file and the finished bundles it names by path,
verifies its SHA-256, stores the version under
~/.local/share/ragents/remote/<host>/<profile>/profiles/<version>/, and starts the server with
that profile. Data goes to ~/.local/share/ragents/remote/<host>/<profile>/data/, and the web
interface is the local host's own; nothing is built or installed on the developer machine.
Existing versions are not downloaded again. --clean removes older ones, --no-start stops after
fetching and reports the profile and data paths, and --port <n> overrides the profile port.
ragents start <profile> can restart an included profile or the latest fetched version without
contacting the server. Personal env(...) values from the client profile, such as the relay
token, must exist in the shell. Between download and startup, connect provisions language
servers and the browser; a prerequisite it cannot install, such as dotnet or a separate Chrome,
stops with instructions.
The server needs ragents.profile-distribution with CLIENT_PROFILE_FILE, plus
ragents.model-relay with MODEL_ALIASES in its host section for models. Plugins the client profile names by path
must be bundles built with ragents plugin build; the server checks them at startup. Users
receive token: env("...") and the two permissions. Relay responses and all client-profile
content, including prompts, skills, run scripts, bundles, and configuration, are present on the
developer machine. Only the provider, actual model, and key remain secret. The relay replaces
model with the alias and removes provider, so model context, journal, catalog, and interface
expose only the alias.
Common first-run pitfalls
- Two environment variables, often with the same value, are needed:
connectreadsRAGENTS_TOKEN, while the name used byRELAY_TOKEN: env("...")must also be set. COMPACTION_PROVIDER: "relay"with an emptyCOMPACTION_MODELcan start without a text model at reasoning leveloff; automatic titles stay disabled and Settings explains why. An explicitly named alias must be such a model or startup fails.- The relay must be reachable when the local server starts because it fetches the alias catalog once. Alias changes require restarting the local server.
- Revoking access affects the next model request immediately, not the running server process. The run then ends as failed and records the relay response in the journal.
- Rejected requests appear in the relay server's
logs/server.log, notrelay.log. The latter records only forwarded calls with user, alias, target, status, and token count. - The archive carries no web interface. After updating the host package, a fetched version
starts with the new web interface as long as the host API stays the same. Otherwise the start
refuses the fetched bundles, and
connectnames the version to install.
Run a CLI workstation
ragents workspace-client <server-url> [folders ...] registers this machine with the same tools
and executor as the VS Code extension. No folder means the current directory; --id and --label
set its stable identity and display name. The npm installation includes ripgrep on every supported
platform and the curated Bash on Windows. Plugin tools are provisioned before registration.
Set RAGENTS_TOKEN for a personal or existing session token. For automatic sign-in and renewal,
configure RAGENTS_USER and RAGENTS_PASSWORD in the process environment. Neither is a command-line
option. Invalid or absent credentials when sign-in is required produce a visible failure and a
nonzero exit. A temporary connection loss reconnects and registers the workstation again.
Closing the terminal or SSH session, closing stdin, or ending the owning process unregisters the
workstation and stops its children. SIGINT, SIGTERM, and SIGHUP also stop it. For an intentional
background service, pass --detached and let a service manager own it, or run:
nohup ragents workspace-client https://ragents.example.com /home/user/project --detached >workspace.log 2>&1 </dev/null &
The flag ignores terminal closure, parent loss, and SIGHUP; it does not daemonize. SIGINT and SIGTERM still unregister and shut down. Use the service manager's stop command to end a service.
Transfer a run
A run can move from one server to another and continue there, for example from a notebook to an always-on machine or from a central server to a developer for local inspection.
RAGENTS_TOKEN=<token> pnpm run-transfer http://localhost:4723 http://server.example:4724 <runId>
RAGENTS_TOKEN=<token> pnpm run-transfer <source> <target> <runId> --workspace /path/to/project
The script exports the run from the source with ragents.runs.export and imports it on the target
with ragents.runs.import. If each side uses a different token, set RAGENTS_SOURCE_TOKEN and
RAGENTS_TARGET_TOKEN. The transfer includes the journal and payloads, which also hold the model
contexts of the run's agents, the stored contents the run refers to (attachments and the media of
the model contexts), actor programs, and all plugin storage for the run, including
ragents.documents files and the new folder of a run that works on the server. A folder on a workstation stays there, and the
run keeps its binding to that workstation. Running processes, language servers, and
browsers are not transferred; they are recreated on the target when next used.
The transfer enforces these prerequisites:
- Both servers use the same host version and workspace executor. There is no override.
- The run is stopped, with no active turn or waiting input.
- Its ID is unused on the target. An existing run, folder, or archive entry rejects the import.
- A run bound to a source project folder needs
--workspace <path>pointing to an existing target folder, given as an absolute path on the target server. A workspace binding is retained; that workspace reconnects to the target with the same ID and as the run owner, or anonymously for an ownerless run. - The archive must stay below 16 MiB because it travels as Base64 through the message layer.
A workspace containing
node_moduleswill exceed this; an existing folder on the server usually will not. - Symbolic links travel only when they are relative and stay inside the run's storage. Links under
node_modulesthat point elsewhere, such as a package manager's absolute links, are left out and come back with the next install in the workspace. Any other link pointing outside stops the export withrun-transfer-linkand names the links; replace them with files or relative links.
Export copies the run. It remains on the source and must be deleted there explicitly after a
real move, otherwise two journals with the same ID diverge. On the target, the run appears
stopped and continues with the next message. Its model context still contains absolute source
paths; reusing one fails at the workspace boundary, while a relative path reaches the target.
After transfer, inspect the journal with pnpm driver journal <runId> and plugin storage under
${DATA_DIR}/sessions/<runId>/plugins/.
Work on Windows
The local host and workspace also run on Windows. Requirements are:
- The VS Code extension for Windows brings its own bash. The Marketplace delivers a
win32-x64orwin32-arm64build that contains a slim bash with the GNU tools (coreutils,grep,sed,awk,find,diff,patch,tar,unzip,cygpathand more), taken from a fixed Git for Windows release, plusrg.exe(ripgrep) from a fixed ripgrep release. Thebashtool uses only this bash, for the workspace and for the local host; an installed Git Bash or abash.exeonPATHis never used. PowerShell andcmd.exeare not used either. - Code search runs through
rg, which skipsnode_modules,bin,objand everything else.gitignoreexcludes. The extension builds for macOS and Linux carryrgas well and put it at the front of thebashtool'sPATH, on the workstation and for the local host. The system prompt tells the model to search withrgonly when the machine runningbashhas one; otherwise it tells the model to exclude dependency and build folders fromgrep -r. - Git is your own
git.exeonPATH, for example from Git for Windows. The bundled bash contains no git, so your login works as usual: Git Credential Manager,~/.gitconfigand~/.ssh. On your own machine the bash inherits your whole environment, except the variables of VS Code itself andBASH_ENV/ENV. - CLI workstation (
ragents workspace-client) carries the same Bash and ripgrep as the extension. npm installs only the optional tools package for this machine; keep optional dependencies enabled. A checkout uses the extension build frompnpm bundle:rgandpnpm bundle:bash. A standalone server (ragents start) still takesRAGENTS_BASHandRAGENTS_RGfrom its environment; these variables do not override workstation bundles. - Node.js is enough with
@schlenkr/ragents. A checkout additionally needs pnpm, andpnpm install,pnpm build:agent, andscripts/start.shneed a bash of your own, such as Git Bash. - The .NET SDK is required when the profile includes Roslyn or FSAC. Provisioning downloads the language servers; the TypeScript server comes from the host directory.
- A server on Windows has no process sandbox. Its profile file must switch it off explicitly
with
PROCESS_SANDBOX: "off"in theragents.workspacesection, otherwise startup fails. A Windows workstation connected to a server needs nothing, because the sandbox applies only to the server.
The data directory is %LOCALAPPDATA%\ragents\<profile> and server-provided profiles use
%LOCALAPPDATA%\ragents\remote\<host>\<profile>\. DATA_DIR overrides this. Startup fails when
LOCALAPPDATA is absent. Unix permissions 0700 and 0711 do not apply on Windows, where isolation
per run depends on the user account.
Windows has no process group for command termination, and the bundled MSYS bash does not hang its
children into the Windows process tree, so taskkill /T on the bash alone misses them. Every bash
call therefore carries a marker in its environment; on timeout, stop, and after the call, a short
helper bash lists the MSYS process groups of that call and RAgents ends them and their native trees
with taskkill /T /F. A process that survives this is an error. This is forceful and has no grace
period. There is also no process table: for
runs using a Windows workspace, the process rail explains this limitation. Stopping a run still
terminates Bash process trees, while a deliberately detached service continues. Workspace tools
do not depend on the process rail.
Windows support was exercised on a physical Windows 11 machine on 29.09.2026: the bundled bash and
rg, the default timeout, stop, and the cleanup of background jobs. Beyond that it is covered by
unit tests that simulate the platform. A first real run should verify pnpm connect,
read, edit, bash output and cancellation, diagnostics, and a workspace through
pnpm workspace-client, and with the bundled bash git fetch and git push over HTTPS with Git
Credential Manager and over SSH.
Server process sandbox
What a run starts on the server (Bash, commands, language servers, TypeScript snippets, actor
programs) runs in a process sandbox: it reads and writes only the folders of its run, sees
neither other runs nor the home of the server account, and can reach public web domains by
default. Rules and limits are in plugins.md under "Server process sandbox". It
does not apply on a workstation; there the run works with the developer's Bash and credentials.
Only what such a run starts on the server, such as a Bash in @actors, runs inside it.
Prerequisites that startup checks:
- macOS: nothing extra,
sandbox-execis part of the system. - Linux:
bubblewrap,socat, andripgrep(Debian and Ubuntu:apt-get install bubblewrap socat ripgrep), plus user namespaces. On Ubuntu 24.04 and later this requiressysctl -w kernel.apparmor_restrict_unprivileged_userns=0or an AppArmor profile. If the server runs as root, it needsCAP_SETFCAP; it is better to run it under its own account. - Linux in a container: Docker's default profile forbids the namespaces. Startup has been
verified with
--security-opt seccomp=unconfined --security-opt apparmor=unconfined --security-opt systempaths=unconfined(in Composesecurity_opt) and a non-root user in the container;--privilegedworks too, but grants more than necessary. - Windows: no sandbox. Startup aborts as long as the profile file does not explicitly switch it off.
The profile file controls it in the ragents.workspace section:
"ragents.workspace": {
PROCESS_SANDBOX: "on",
PROCESS_SANDBOX_NETWORK: ["*"], // public web domains on ports 80 and 443 (the default)
},
curl, package downloads and API requests no longer need a domain entry for each public
website. IP literals, localhost and internal services need explicit entries, for example
["*", "127.0.0.1:8080"]; for an internal hostname in public mode, allow its IP and port too.
The server's own address is always allowed. A list such as ["registry.npmjs.org", "*.example.com"]
restricts access to those targets; [] leaves only the own server reachable. Network permission
does not distinguish downloads from uploads or destructive API calls. File isolation remains
active. PROCESS_SANDBOX: "off" explicitly disables the whole sandbox, for example on Windows.
On macOS, pnpm through corepack needs a packageManager in the workspace's
package.json, because corepack otherwise aborts at a blocked folder above it.
Source: docs/operations.md. This chapter as Markdown.