Getting Started Guide
Your fastest path from zero to a running AI agent — one line, three lines, or five, depending on how much you need.
Table of Contents
- Installation
- Environment Setup
- Core Concepts
- Provider Configuration
- Level 1 — Single Text Generation
- Level 2 — Stateful Agent Session
- Level 3 — Multi-Agent Agency
- First End-to-End Example
- What's Next
Installation
npm install @framers/agentos
TypeScript is strongly recommended. AgentOS ships full .d.ts types and expects
"moduleResolution": "bundler" or "node16" in your tsconfig.json.
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true
}
}
Environment Setup
AgentOS resolves credentials in three layers, highest priority first:
- Inline
apiKey/provider/baseUrlon the call. - Module-level default set via
setDefaultProvider()(see below). - Environment variable auto-detect chain:
OPENROUTER_API_KEY→OPENAI_API_KEY→ANTHROPIC_API_KEY→GEMINI_API_KEY→GROQ_API_KEY→TOGETHER_API_KEY→MISTRAL_API_KEY→XAI_API_KEY→REQUESTY_API_KEY→MINIMAX_API_KEY(text) →which claude→which gemini→OLLAMA_BASE_URL→STABILITY_API_KEY→REPLICATE_API_TOKEN→STABLE_DIFFUSION_LOCAL_BASE_URL→BFL_API_KEY→FAL_API_KEY→MINIMAX_API_KEY(images). An empty or whitespace-onlyOLLAMA_BASE_URLorSTABLE_DIFFUSION_LOCAL_BASE_URLis unset here, as the call reads it, so the chain goes on to the next runtime.
You only need one of these — pick whichever fits your deployment.
Module-level default (no .env required)
Configure once at app boot, every subsequent call inherits:
import { setDefaultProvider, generateText, agent } from '@framers/agentos';
setDefaultProvider({
provider: 'openai',
apiKey: process.env.MY_OWN_KEY ?? process.env.OPENAI_API_KEY, // any source — Vault, KMS, hard-coded, etc.
// optional:
// model: 'gpt-4o-mini',
// baseUrl: 'https://my-proxy.example.com/v1',
});
// No env vars needed, no inline opts — just works:
const { text } = await generateText({ prompt: 'hello' });
const bot = agent({ instructions: 'You are a coding tutor.' });
// Inline opts always win over the default (per-tenant keys, fallback providers, etc.):
const { text: tenantReply } = await generateText({
apiKey: process.env.SCOPED_OPENAI_API_KEY ?? process.env.OPENAI_API_KEY,
prompt: 'tenant-isolated call',
});
// Reset the default:
setDefaultProvider(undefined);
The default's model is the text model. embedText, editImage and generateImage use it as well unless it is recognizably a chat model (GPT, Claude, Gemini, Llama, MiniMax and similar families) headed for the provider's own endpoint, in which case they use the provider's default model for the task. The endpoint is the base URL the call is sent to: the call's own baseUrl, else the default's, else the provider's base-URL variable such as MINIMAX_BASE_URL. An empty or whitespace-only baseUrl on the call or the default is unset: the variable applies, and with none text and image calls go to the provider's own endpoint (Ollama and stable-diffusion-local, which have none, refuse them with no request). OpenAI's API root https://api.openai.com/v1 and the regional roots OpenAI's data residency guide names (https://us.api.openai.com/v1, https://eu.api.openai.com/v1 and the au, ca, jp, in, sg, kr, gb and ae ones) count as OpenAI's own endpoint, so with OPENAI_BASE_URL=https://api.openai.com/v1 an embedText call takes OpenAI's embedding model, not the default's chat model. MiniMax's API roots, https://api.minimax.io/v1, the China root https://api.minimax.cn/v1 that MiniMax's China documentation names and the older China root https://api.minimaxi.com/v1, count as its own endpoint, not a custom one. A base URL is compared with these roots as fetch sends it, so the letter case of the scheme and the host, a default port, surrounding whitespace and trailing slashes make no difference (HTTPS://API.MINIMAX.IO/v1/ is the global root). generateImage without provider or model tries the default's provider first when that provider makes images, then the image providers whose keys are in the environment. With a custom endpoint (baseUrl) or on Ollama, embedText uses it too, except on Gemini: there a default model that does not name an embedding model is replaced by gemini-embedding-2, so pass model to embedText to use another one. MiniMax publishes no embedding model, so embedText refuses a call that resolves to MiniMax, whatever the endpoint, before sending it. The default's apiKey and baseUrl apply to every call that resolves to the same provider, image calls included.
setDefaultProvider is the recommended path for apps that hold their keys somewhere other than environment variables (secrets manager, runtime config service, etc.). It applies to the high-level functions only. The full AgentOS runtime does not read it: its providers come from modelProviderManagerConfig.providers in AgentOSConfig, where the entry with isDefault: true is the default, and createAgentOSConfig() builds that list from OPENAI_API_KEY, OPENROUTER_API_KEY, REQUESTY_API_KEY and OLLAMA_BASE_URL.
Reordering the auto-detect chain
If you have multiple env-var keys configured but want a different one preferred, install a custom priority list once at boot:
import { setProviderPriority, generateText } from '@framers/agentos';
// Even if OPENAI_API_KEY is set, prefer Anthropic when its key is also present:
setProviderPriority(['anthropic', 'openai', 'ollama']);
const { text } = await generateText({ prompt: 'hello' });
// Picks anthropic if ANTHROPIC_API_KEY is set; falls through to openai;
// then to a local ollama server. Providers not in the list are skipped.
Throws if you list an unknown provider id (typo guard). Pass an empty array to disable auto-detection entirely (callers must then supply a provider inline or via setDefaultProvider). Call clearProviderPriority() (or setProviderPriority(undefined)) to revert to the default order.
Environment variables
For zero-code setup, set any one of the supported env vars:
# Cloud providers (pick one or more)
export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-ant-...
export GEMINI_API_KEY=AIza...
export OPENROUTER_API_KEY=sk-or-...
# Key rotation: each request draws a key from a comma-separated list
export OPENAI_API_KEY=sk-key1,sk-key2,sk-key3
# Local providers (no key required, just a running server)
export OLLAMA_BASE_URL=http://localhost:11434
export STABLE_DIFFUSION_LOCAL_BASE_URL=http://localhost:7860
API Key Rotation explains the key pool behind a comma-separated key list: the LLM providers (OpenAI, Anthropic, Gemini, OpenRouter and Requesty) take a revoked, unpaid or throttled key out and move the request to the next usable one, and AGENTOS_KEY_POOL_STRATEGY_ANTHROPIC=ordered (one variable per provider; AGENTOS_KEY_POOL_STRATEGY for every provider) uses that provider's keys in the order given, credited keys first and the paid key last.
Inline API Keys
Every function also accepts apiKey and baseUrl directly, which override both the module-level default and any environment variable. This is useful for multi-tenant apps, test suites, or dynamic key management:
import { generateText, agent } from '@framers/agentos';
// Pass apiKey inline — skips env var lookup for this call
const { text } = await generateText({
provider: 'openai',
apiKey: 'sk-my-specific-key',
prompt: 'Hello world',
});
// Works on agent() too
const bot = agent({
provider: 'anthropic',
apiKey: process.env.CUSTOMER_ANTHROPIC_KEY,
instructions: 'You are a helpful assistant.',
});
// Local provider with explicit baseUrl
const { text: local } = await generateText({
provider: 'ollama',
baseUrl: 'http://gpu-server:11434',
prompt: 'Summarize this document.',
});
A baseUrl that is an empty or whitespace-only string is unset, on the call, in setDefaultProvider() and in the provider's base-URL variable alike: the next of those sources applies, and with none the call goes to the provider's own endpoint. No request is built on a blank base. Ollama and stable-diffusion-local, which have no endpoint of their own, are refused with no request when every source is blank.
All high-level functions support apiKey: generateText, streamText, generateObject, streamObject, generateImage, generateVideo, generateMusic, generateSFX, embedText, agent, and agency. performOCR passes its apiKey (and baseUrl) to the cloud tier, and needs provider (or a model prefix such as openai:gpt-4o) with them: a key never goes to a provider picked from the environment. An empty or whitespace-only baseUrl is unset and needs no provider.
A key or base URL a call passes goes to the provider the call names, and two calls that would send it elsewhere are refused before any request: a model whose provider: prefix names another provider than provider (the prefix wins, so the key would go to it), and an image call (generateImage, editImage, upscaleImage, variateImage, transferStyle) that names no provider, whose provider detection would pick from the environment. An empty or whitespace-only baseUrl is not one: it is unset and names no endpoint, so neither refusal applies to it and the call runs as one with no baseUrl does; a key beside it is still refused. An agent's apiKey and baseUrl are for the provider the agent names: a generate() or stream() call that names another provider, or a model whose provider: prefix names another, runs there on a key of its own, or on that provider's from the environment, on either runtime. A call's own key, when it differs from the one the environment or the global default holds, also has its own health record: a tenant's revoked key opens the provider's breaker for the calls on that key, calls on other keys go on, and at most 256 such records are kept. AgentOS keeps an initialized provider manager per key for reuse, at most 64, dropping the one used least recently.
Core Concepts
Before the code, four parts and how they fit together.
- Agent — the runtime loop. Receives input, calls the LLM, executes tool calls, retrieves memory, returns a response.
agent()andagency()are the two factories that build one. - LLM — the language model. AgentOS routes through 13 provider adapters; the agent does not care which one you pick.
- Memory — what survives across turns and sessions. On the full runtime, a GMI keeps working memory for its session and, when cognitive memory is attached, episodic, semantic and procedural traces with Ebbinghaus decay.
agent()keeps each session's message history and reaches long-term memory throughmemoryProviderhooks; withruntime: 'gmi'its sessions are GMIs, and withmemoryset as well they share one cognitive memory manager. - Tools — functions the agent can invoke when the LLM decides it needs one. Pre-registered tools work the same way runtime-generated tools do once approved by the LLM judge.
The three levels below build on this picture:
| Level | API | What it adds |
|---|---|---|
| 1 | generateText() / streamText() | a model call, and with tools, a tool loop of up to maxSteps model calls (one by default, five on the prompt-emulated tool path); no sessions, no memory |
| 2 | agent() | the tool-calling loop (up to 5 steps by default), sessions with their own message history, memory hooks |
| 3 | agency() | multiple agents under a coordination strategy; optional human approvals and, with emergent.enabled on hierarchical (or with adaptive: true, which puts a hierarchical manager over the strategy), specialists the manager spawns at run time |
The full runtime runs guardrails, retrieval, human approval and channels. Of these, agency() applies human approval alone: it reports its guardrails as not enforced, adds no retrieved context and wires no channels (with channels set, its connect() rejects), and with voice.enabled its listen() takes JSON text over a local WebSocket, not audio. API paths lists what each surface does with each option.
Provider Configuration
Every entry point (generateText, streamText, generateObject, agent, agency, etc.) accepts the same three provider fields:
| Field | Required? | Default | Notes |
|---|---|---|---|
provider | no | auto-detected from the environment (Environment Setup) | One of openai, anthropic, gemini, openrouter, requesty, groq, together, mistral, xai, ollama, litellm, and the CLI bridges claude-code-cli and gemini-cli. litellm is never auto-detected and needs a model and an apiKey, on the call or in setDefaultProvider(), because it has no default model and reads no environment variable (LiteLLM). Another OpenAI-compatible service runs through openai with baseUrl. |
model | no | provider-specific (gpt-4o, claude-sonnet-4-6, gemini-2.5-flash, llama3.2 for Ollama, etc.) | Pin explicitly for stability across package upgrades. Use a current model id; retired snapshots return 404. |
apiKey | no | env auto-detect (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.; full chain in Environment Setup) | Pass explicitly for multi-tenant apps and to avoid coupling code to env var names. |
The examples below use the same explicit shape across all three levels. If you would rather rely on the auto-detect chain plus the provider's default model, omit model and apiKey — the calls still work as long as the env var is set.
Level 1 — Single Text Generation
One call, no state, no agent loop:
import { generateText } from '@framers/agentos';
const { text } = await generateText({
provider: 'openai',
model: 'gpt-4o',
apiKey: process.env.OPENAI_API_KEY,
prompt: 'Explain the TCP three-way handshake in three bullet points.',
});
console.log(text);
Streaming version:
import { streamText } from '@framers/agentos';
const stream = streamText({
provider: 'anthropic',
model: 'claude-sonnet-4-6',
apiKey: process.env.ANTHROPIC_API_KEY,
prompt: 'Write a haiku about distributed systems.',
});
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
Level 2 — Stateful Agent Session
agent() adds the tool-calling loop and sessions that keep their own message history on top of a Level 1 call:
import { agent } from '@framers/agentos';
const assistant = agent({
provider: 'openai',
model: 'gpt-4o',
apiKey: process.env.OPENAI_API_KEY,
instructions: 'You are a helpful coding assistant.',
});
const session = assistant.session('my-session');
const reply = await session.send('What is a closure in JavaScript?');
console.log(reply.text);
// Follow-up retains context:
const followUp = await session.send('Show me a practical example.');
console.log(followUp.text);
Level 3 — Multi-Agent Agency
agency() coordinates a team of agents under a chosen strategy:
import { agency } from '@framers/agentos';
const team = agency({
provider: 'openai',
model: 'gpt-4o',
apiKey: process.env.OPENAI_API_KEY,
strategy: 'sequential',
agents: {
researcher: { instructions: 'Find key facts about the topic.' },
writer: { instructions: 'Synthesize the facts into a clear summary.' },
reviewer: { instructions: 'Check for accuracy and suggest improvements.' },
},
});
const result = await team.generate('Explain how large language models work.');
console.log(result.text);
Per-agent overrides: any sub-agent in the agents map can declare its own provider, model, and apiKey to route specific roles to specific models (for example, a gpt-5-mini reviewer with a claude-sonnet-4-6 researcher).
Personality (Optional)
HEXACO personality traits are optional. On agent(), personality adds a ## Personality & Communication Style section to the system prompt, with one directive for each trait above 0.65 or below 0.35; traits between those values add nothing. A cognitive memory manager weights encoding and its mechanisms by the traits it is configured with; on the full runtime, gmiManagerConfig.cognitiveMemoryFactory receives the persona and decides which traits to pass.
// ── Personality-driven agent (warm, curious, detail-oriented) ─────────────
const empathicAgent = agent({
provider: 'openai',
instructions: 'You are a supportive mentor.',
personality: {
emotionality: 0.8, // warm, empathetic responses
openness: 0.9, // curious, exploratory
conscientiousness: 0.85,
agreeableness: 0.8,
},
});
// ── No personality (purely objective) ─────────────────────────────────────
const objectiveAgent = agent({
provider: 'openai',
instructions: 'You are a factual analyst. Be precise and neutral.',
// personality: omitted — no personality section in the system prompt
});
// ── Selective traits (only set what matters) ──────────────────────────────
const analyticalAgent = agent({
provider: 'anthropic',
instructions: 'You are a research assistant.',
personality: {
conscientiousness: 0.95, // meticulous, thorough
openness: 0.7, // open to new ideas
// unset traits count as 0.5 and add no directive
},
});
When personality is omitted, the system prompt has no personality section. The section is built once, when the agent is created; a call cannot change it.
First End-to-End Example
This complete example makes a one-shot call, holds a two-turn session and reads its usage:
import { agent, generateText } from '@framers/agentos';
// ── Step 1: One-shot text generation ───────────────────────────────────────
const { text: summary } = await generateText({
provider: 'openai',
prompt: 'Summarise AgentOS in one sentence.',
});
console.log('Summary:', summary);
// ── Step 2: Session that keeps its message history ─────────────────────────
const coder = agent({
provider: 'anthropic',
model: 'claude-sonnet-4-6',
instructions: 'You are an expert TypeScript developer.',
maxSteps: 4,
});
const session = coder.session('quickstart');
const { text: explanation } = await session.send(
'How do I debounce a function in TypeScript? Show a typed example.'
);
console.log('\nDebounce explanation:\n', explanation);
const { text: followUp } = await session.send('Now make it cancel-able with an AbortSignal.');
console.log('\nCancellable version:\n', followUp);
// ── Step 3: Check usage ─────────────────────────────────────────────────────
const usage = await session.usage();
console.log('\nUsage:', usage);
Run it:
npx tsx getting-started.ts
Expected output:
Summary: AgentOS is a modular orchestration runtime for adaptive AI agents.
Debounce explanation:
function debounce<T extends (...args: unknown[]) => void>(
fn: T, delay: number
): T { ... }
Cancellable version:
function debounce<T extends (...args: unknown[]) => void>(
fn: T, delay: number, signal?: AbortSignal
): T { ... }
Usage: {
sessionId: 'quickstart',
personaId: undefined,
promptTokens: 312,
completionTokens: 487,
totalTokens: 799,
costUSD: 0.008241,
calls: 2
}
What's Next
| Topic | Guide |
|---|---|
| Graph pipelines, workflows, missions | ORCHESTRATION.md |
| Connect agents to messaging and social channels | CHANNELS.md |
| Publish to social platforms | SOCIAL_POSTING.md |
| Audit trails and tamper evidence | PROVENANCE.md |
| Episodic, semantic, procedural memory | COGNITIVE_MEMORY.md |
| 8 core cognitive mechanisms (+ optional persona drift analysis) | COGNITIVE_MEMORY.md#mechanism-implementation-reference |
| HEXACO personality traits and on/off configuration | COGNITIVE_MEMORY.md |
| Testing and benchmarking agents | EVALUATION.md |
| Token-efficient capability discovery | DISCOVERY.md |
| Image generation across 8 providers | IMAGE_GENERATION.md |
| Practical cookbook examples | EXAMPLES.md |
Runtime-configured tools and full AgentOS setup | HIGH_LEVEL_API.md |
| Full API hierarchy | AGENCY_API.md |
| Architecture overview | ARCHITECTURE.md |