The server
Introduction
Section titled “Introduction”The server side of the kit is two packages. @pilotoo/server is a NestJS module, PilotooModule.forRoot(options), that serves REST routes and a streaming gateway. @pilotoo/agent-runtime is the framework-free core it is built on: it runs an agent, resolves models and tools, and defines the ports your app can implement. Most apps only touch the module.
Mounting the module
Section titled “Mounting the module”This is the whole server of the demo API (apps/api):
import { Module } from '@nestjs/common';import { ConfigModule } from '@nestjs/config';import { createAskHumanTool, createWebSearchTool } from '@pilotoo/agent-runtime';import { PilotooModule } from '@pilotoo/server';
@Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), PilotooModule.forRoot({ env: process.env, tools: (registry) => { registry.register(createWebSearchTool(process.env)); registry.register(createAskHumanTool(), { optIn: true }); }, }), ],})export class AppModule {}Agents, model profiles and providers are YAML files in .pilotoo/, so this module needs no other code. See Agents and Models.
For the install steps and the Nest peer dependencies, follow the quickstart.
Bootstrapping
Section titled “Bootstrapping”Two lines in main.ts belong to your app, not the module:
import { NestFactory } from '@nestjs/core';import type { NestExpressApplication } from '@nestjs/platform-express';import { configureBodyLimit } from '@pilotoo/server';
const app = await NestFactory.create<NestExpressApplication>(AppModule);configureBodyLimit(app);app.enableCors({ origin: true, credentials: false });await app.listen(4317);What you get
Section titled “What you get”Mounting the module serves /health, /protocol, /agents (the catalog), /agent-admin (the agent editor API), /attachments, /usage, /schedules and /model-gateway, plus the websocket gateway that streams runs to a client.
What you supply
Section titled “What you supply”| You provide | Why |
|---|---|
| Tools | The package registers none. Agents can only call what you register. |
| Authentication | Who is calling. Without configuration, everyone is a local owner. |
| Model credentials | API keys in the environment, read when a model is built. |
| PostgreSQL (optional) | Default storage. You can replace each port instead. |
Running without PostgreSQL
Section titled “Running without PostgreSQL”PostgreSQL is decided by which ports you supply. If you supply all five of agentDefinitionSource, agentDefinitionStore (null counts), conversationStore, checkpointer and runEventStorage, no connection is made. A mix is valid when the database is there; without it, boot fails naming the ports that still need it. Background subagent tasks then run only if you also supply backgroundTaskStorage.
To run on a user’s own machine with SQLite instead, see the local backend.
Guarding your own routes
Section titled “Guarding your own routes”IdentityGuard is registered app-wide, so your own controllers are guarded too. Mark a public route with @Anonymous(), and read the caller in a handler with @CallerIdentity(). Both are exported by @pilotoo/server.
Options
Section titled “Options”An option left out keeps its default. The ones a minimal server sets are marked (minimal).
Storage and persistence
Section titled “Storage and persistence”| Option | Default | Meaning |
|---|---|---|
usageStore |
PostgreSQL; null keeps nothing and /usage answers 501 |
Where every model call is kept. See Usage. |
attachmentStore |
S3 when ATTACHMENTS_S3_BUCKET is set, else off; null is off whatever env says |
Where uploads live. See Attachments. |
scheduleStorage |
PostgreSQL; null means no schedules |
Where schedules and runs are kept. |
conversationStore, checkpointer, runEventStorage, backgroundTaskStorage |
PostgreSQL | Persistence ports. |
database |
{ connectionString } from DATABASE_URL in env |
The PostgreSQL connection. |
Identity
Section titled “Identity”| Option | Default | Meaning |
|---|---|---|
identityResolver |
chosen from env: AUTH_IDENTITY_URL, else everyone is the local owner |
Who each caller is. See Authentication. |
identityHeaders |
none | Request header names handed to the identity resolver as Credentials.headers. |
Models and agents
Section titled “Models and agents”| Option | Default | Meaning |
|---|---|---|
agentDefinitionSource |
system agents from files, in front of the tenant’s agents in PostgreSQL | Every tenant’s agents. See Agents. |
agentDefinitionStore |
the PostgreSQL store; null has no agent editor and /agent-admin answers 501 |
The write side of the definitions. |
modelProfileSource |
the profile files in .pilotoo/profiles (PILOTOO_CONFIG_DIR) |
The profiles an agent may name. |
modelProviders |
the registry built from env and the provider files |
The providers a profile’s model runs on. See Models. |
modelGateway |
none | How pilotoo-gateway providers sign in. See Model gateway. |
claudeLogin |
none | The Claude sign-in that claude-subscription providers run on. Only a host on the person’s own machine supplies one. |
execution |
the runtime default: the unsandboxed local shell | Where the agent’s execute tool runs. |
Tools and skills
Section titled “Tools and skills”| Option | Default | Meaning |
|---|---|---|
tools (minimal) |
none | (registry: ToolRegistry) => void, or a plain StructuredTool[]. See Tools. |
configTools |
none | Tools that let an agent read and change configuration. See Config tools. |
skillSources |
none beyond the skills directory (AGENT_SKILLS_DIR) |
Host skill sources. See Skills. |
skillLayers |
{ globalDir, tenantDir } from AGENT_GLOBAL_SKILLS_DIR and AGENT_TENANT_SKILLS_DIR, each absent when unset |
The global and per-tenant skill layers. |
knowledge |
none: no agent gets passages | The retrieval behind an agent’s knowledge ids. See Knowledge. |
Optional features
Section titled “Optional features”| Option | Default | Meaning |
|---|---|---|
scheduleTools |
false |
Registers the opt-in schedule_* tools. See Schedules. |
usageSink |
a debug log line per run | Where every run’s usage goes. |
usagePrices |
USAGE_PRICES_FILE; null reports tokens only |
The prices /usage computes costs from. |
scheduleCredentials |
none | The credentials a scheduled fire runs with. |
General
Section titled “General”| Option | Default | Meaning |
|---|---|---|
env (minimal) |
process.env |
Where every provider reads its configuration. |
serviceName |
SERVICE_NAME from env, then 'api' |
The name /health reports. |
configLocation |
.pilotoo |
How a client names the configuration folder in the path of every profile, provider and agent record. A shared server keeps the default, so a client never learns a server path. |
Each replacement takes the place of the provider with the same injection token, so Test.createTestingModule(...).overrideProvider(token) keeps working.
Pitfalls
Section titled “Pitfalls”envis read lazily, when a provider is built, so loading a.envfile withConfigModulestays in your app and only has to happen before boot.- A replacement
modelProfileSourcemust hold every profile the shipped system agents and templates name, or boot fails. - The package does not declare
sideEffects: false: Nest decorators andreflect-metadatarun at import.
Assembling a runner by hand
Section titled “Assembling a runner by hand”PilotooModule wires the runner for you. A host without Nest does the same wiring:
import { AgentRunner, createDeepAgentGraphFactory } from '@pilotoo/agent-runtime';
const runner = new AgentRunner({ requiredSaver: checkpointer }, models, { definitions, profiles, defaultAgentId: 'assistant', registry, createGraph: createDeepAgentGraphFactory(process.env), warn: (message) => logger.warn(message),});| Argument | What it is |
|---|---|
checkpointer |
{ requiredSaver }, any LangGraph BaseCheckpointSaver. |
models |
A ModelResolver. See Models. |
definitions, profiles |
Required. Every run resolves its agent (AgentDefinitionSource), then the profile it names (ModelProfileSource). |
defaultAgentId, registry, createGraph |
Required. |
skillSources, knowledge, usage, attachments, tasks, warn |
Optional. AgentRunnerOptions in packages/agent-runtime/src/agent-runner.ts is the authority. |
Next steps
Section titled “Next steps”- Define your first agents and choose their models.
- Register tools and decide how users authenticate.
- Connect a client to the gateway.