# ship your agent as a live service — any channel, any app

Vibe first.
Then FastAgent.

An agent is just a directory (persona.md, skills/, tools/, channels/) — or embedded right in your app. FastAgent serves it: GitHub, Telegram, Slack, Feishu, or any channel you compose. No rewrite, no new format, no platform.

start
Read https://fastagent.sh/start.md and build an agent in this project.
$npm i -g @fastagent-sh/fastagent# cli
$npm i @fastagent-sh/fastagent# embed in your app

MIT · Node ≥ 22.19 / Bun · built on pi · model and platform neutral · no account, no telemetry

Release highlights: services, session control, and AgentCore →

FastAgent Cloud — managed hosting, coming soon · self-hosting stays free forever · join the waitlist →

# the ecosystem — plugs into your stack and infrastructure
channels
  • GitHub
  • Telegram
  • Slack
  • Feishu · Lark
  • Discord (coming soon)
  • WhatsApp (coming soon)
  • GitLab (coming soon)
  • + more
connect
  • Stripe (coming soon)
  • Shopify (coming soon)
  • Notion (coming soon)
  • Salesforce (coming soon)
  • Google Docs (coming soon)
  • Facebook (coming soon)
  • Pinterest (coming soon)
  • + more
embed in
  • Next.js
  • Hono
  • Express
  • Astro
  • Nuxt
  • SvelteKit
  • Fastify
  • Bun
  • Node
deploy to
  • Fly.io
  • Railway
  • Docker
  • Cloudflare (coming soon)
  • Vercel (coming soon)
  • Render (coming soon)
  • AWS Bedrock AgentCore
  • Kubernetes (coming soon)
  • Google Cloud (coming soon)
models
  • Anthropic
  • OpenAI
  • Gemini
  • DeepSeek
  • Qwen
  • Kimi
  • Grok
  • GLM
  • Llama
  • + more
more integrations coming soon
# 01 · the core

Small core. Clear seams.

FastAgent owns exactly one thing: a single function — the Agent Handler. It decouples your channels, your agent, the harness, and your infra — swap any of them without touching the others.

channelsturn external events into invocations
  • GitHub webhooks
  • Telegram · Slack · Feishu / Lark
  • HTTP API · SSE streaming
  • cron schedules
  • + yours · via the channel kit
modelsthe harness's own axis — a runtime option, not an architecture decision
  • Anthropic Claude · OpenAI GPT · Gemini
  • Kimi · Grok · DeepSeek · GLM
  • + yours · models.json or a provider
agent handler · the contract
invoke(scope, prompt)
  => AsyncIterable<AgentEvent>
powered by pi — the built-in agent harness+ or bring your own harness
infrayours — owns auth, storage, deployment
  • your app's route
  • node · bun server
  • fly · railway · docker · AgentCore
  • + yours · anything fetch-shaped

# one contract collapses the channels × models/harnesses × infra matrix — swap one axis, the other two don't move

each turn streamstextthinkingtool_startedtool_endedcompleted|failed
# the whole system in five words
Agent Definition
the directory that describes the agent
Agent Handler
the callable contract: invoke
Tool
a typed action, validated before execution
Skill
reusable expertise loaded from the definition
Channel
turns an external event — a webhook, a message, even the clock — into invocations

# deliberately not a workflow engine — for deterministic orchestration, call invoke() from your own queue

Read the Agent Handler SPEC → · Design principles →

# 02 · composition

Compose the modules.
Extend with a file.

Every part of FastAgent is a module: model, sessions, providers, channels, tools. Composing them is plain TypeScript — and extending is dropping a file that composes in. Built for your scenario and your stack, without forks or waiting.

compose · every option is a module

const { agent } = await createPiAgentFromDefinition("./agent", {

model: "anthropic/claude-sonnet-4", // any provider

sessions: piSessionRecordStore({ dir: ".state/sessions" }),

providers: [myGateway], // or your model gateway

});

▸ assembly is code — swap any module, the rest keeps working
+extend · tools/lookup-order.ts

// the filename becomes the tool name

export default defineTool({

description: "Look up an order by id.",

input: z.object({ orderId: z.string() }),

async execute({ orderId }) { return db.find(orderId); },

});

▸ drop a file into tools/, channels/, or schedules/ — it composes in

Build a channel adapter → · Public API surface →

# 03 · the definition

The directory is the agent.

Vibed with a coding agent, written by hand, or inherited from a repo — a plain directory is the definition. FastAgent assembles it at boot. No DSL, no rewrite.

agent/
├─ persona.md            # identity + standing instructions
├─ AGENTS.md             # project context — yours, or a host repo's
├─ skills/               # reusable markdown expertise
├─ tools/                # code tools · the filename is the name
│   ├─ lookup-order.ts
│   └─ post-review.ts
├─ channels/             # a file IS a channel
│   ├─ github.ts
│   └─ telegram.ts
├─ schedules/            # cron time triggers
├─ models.json           # optional custom model endpoints
├─ extensions/           # optional pi extensions · chat only
├─ .state/               # runtime sessions + channel state
├─ .secrets/             # credentials · never committed
└─ fastagent.config.mjs  # identifies the agent directory
  • Keep your project. fastagent init . adds a fastagent/ definition beside your existing files. Run from the project root to keep it as the workspace.
  • No registry, no boilerplate. A file in channels/ is a channel. A file in tools/ is a tool. Drop it in — it's live.
  • Typed tools. Zod validates authored tool inputs. Directory agents also have coding tools, including a shell; isolate the process when a security boundary is required.
  • No build step. fastagent dev runs the directory. Ship the directory. That's the artifact.

# fastagent.config.* identifies the agent · the directory you run from is its workspace

Agent development guide → · Custom models →

# 04 · add it to your app

One import.
An agent inside your app.

Call it like any function — from a request handler, a queue consumer, a background job — or mount it as a route in the app you already ship. FastAgent composes with your app, never owns it.

These examples open the workspace created by fastagent init ., using its configured model. Supply credentials through your app's environment or the project login file. Mount invoke behind authentication and session-ownership checks; directory agents include shell access. Use a Node/Bun server with durable storage for file-backed sessions.

// a job, a queue consumer, or a test
import { collect, createPiAgentFromDir } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDir(".");
const { text } = await collect(
  agent.invoke({ session: "job-42" }, { text: "summarize today's failures" })
);
// app/api/invoke/route.ts
import { createInvokeHandler, createPiAgentFromDir } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDir(".");

export const POST = createInvokeHandler(agent);
export const runtime = "nodejs";
// app.ts
import { Hono } from "hono";
import { createInvokeHandler, createPiAgentFromDir } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDir(".");
const invoke = createInvokeHandler(agent);

const app = new Hono();
app.post("/invoke", (c) => invoke(c.req.raw));

export default app;
// app.ts
import express from "express";
import { createInvokeHandler, createPiAgentFromDir, nodeListener } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDir(".");

const app = express();
app.post("/invoke", nodeListener(createInvokeHandler(agent))); // before any body parser
app.listen(8787);
// src/pages/api/invoke.ts (Node adapter)
import type { APIRoute } from "astro";
import { createInvokeHandler, createPiAgentFromDir } from "@fastagent-sh/fastagent";

export const prerender = false;
const { agent } = await createPiAgentFromDir(".");
const invoke = createInvokeHandler(agent);

export const POST: APIRoute = ({ request }) => invoke(request);
// server/api/invoke.post.ts (Node server)
import { defineEventHandler, toWebRequest } from "h3";
import { createInvokeHandler, createPiAgentFromDir } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDir(".");
const invoke = createInvokeHandler(agent);

export default defineEventHandler((event) => invoke(toWebRequest(event)));
// src/routes/invoke/+server.ts (adapter-node)
import type { RequestHandler } from "./$types";
import { createInvokeHandler, createPiAgentFromDir } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDir(".");
const invoke = createInvokeHandler(agent);

export const POST: RequestHandler = ({ request }) => invoke(request);
// server.ts
import Fastify from "fastify";
import { createInvokeHandler, createPiAgentFromDir, nodeListener } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDir(".");
const listener = nodeListener(createInvokeHandler(agent));
const app = Fastify();

app.register(async (scope) => {
  scope.removeAllContentTypeParsers();
  scope.addContentTypeParser("*", (_req, payload, done) => done(null, payload));
  scope.post("/invoke", (req, reply) => { reply.hijack(); listener(req.raw, reply.raw); });
});
await app.listen({ port: 8787 });
// server.ts
import { createInvokeHandler, createPiAgentFromDir } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDir(".");
const invoke = createInvokeHandler(agent);

Bun.serve({
  port: 8787,
  fetch(req) {
    const url = new URL(req.url);
    return url.pathname === "/invoke" ? invoke(req) : new Response("not found", { status: 404 });
  },
});
// server.ts — the whole service, including channels and schedules
import { createAgentService, serveNode } from "@fastagent-sh/fastagent";

const service = await createAgentService(".");
const server = serveNode(service.handler, { port: 8787 });
await Promise.all([service.ready, server.listening]);

// On application shutdown, await service.close() and server.close().

Need the whole service? createAgentService includes declared channels, schedules, health, and optional session control. Enable sessionControl to observe, steer, fork, or manage conversations through an authenticated API; user and tenant authorization stay in your app.

Embedding guide → · Session control →

# 05 · run it as a live service

Leave the terminal.
Become a live service.

In the terminal, your agent runs only while you're watching. The stretch between there and production is where teams either rewrite into a framework or quietly drop the project. FastAgent is that stretch — and it doesn't ask for the rewrite.

  • verified webhooks
  • streaming responses
  • session state
  • cron triggers
  • deployment

Slack uses native threaded streams and two runtime secrets. Feishu/Lark threads inherit recent room context. Scheduled turns deliver through send tools to an explicit recipient; chat channels already deliver normal replies.

Slack → · Feishu / Lark →

# 06 · the directory ships

Deploy anywhere.

Ship to Docker, Fly.io, Railway, or AWS Bedrock AgentCore. Resident deployments use one active replica and durable storage. Fly can suspend webhook-driven agents; clocks, GitHub, and long connections keep it running. AgentCore uses EventBridge for clocks and an S3 snapshot for webhook/schedule state.

 fastagent deploy fly
 wrote fastagent/fly.toml   # idle behavior follows the definition
 wrote fastagent/Dockerfile # the workspace is the build context
 wrote .dockerignore
 runbook printed · or --run drives the deploy to completion
supported today
  • Fly.io
  • Railway
  • Docker
  • AWS Bedrock AgentCore
  • + your own infrastructure
coming soon
  • Cloudflare
  • Vercel
  • Render
  • Kubernetes
  • Google Cloud

Deployment guide → · AWS Bedrock AgentCore →

# 07 · for the org

What you're not signing up for.

Adopting a library is not the same decision as starting a migration. Four things this doesn't ask of you.

No platform to migrate to
No console, no runtime to deploy into. MIT, no account, no telemetry.
Infrastructure stays yours
Auth, database, routing, deployment, policy stay in your app. A security review asks what leaves the boundary — here it can be nothing.
The artifact is a git directory
Reviewed in a PR, diffed, rolled back, owned by a team. Not a prompt buried in a SaaS console.
Delivered where people already are
Nobody changes their habits for a new tool. They do reply in the group chat.

# the contract is one function — stop using it and you lose a few adapter files, not your app

What FastAgent does and doesn't do →

# 08 · start the vibe

Keep vibing. FastAgent will follow.

Paste this into Claude Code, Codex, Cursor, or any agent that reads the web. It inspects your project first, then builds the agent — fresh, from your existing files, or embedded in your app.

Read https://fastagent.sh/start.md and build an agent in this project.

For humans: npm i -g @fastagent-sh/fastagent then fastagent init · full quickstart →