Build your first agent.
Install one package, create one file, and keep ownership of every important choice.
npm install @noir-agent/agent
A useful agent in one file
import { noir } from '@noir-agent/agent'
import { slack } from '@noir-agent/agent/channels/slack'
export default noir({
model,
personality: 'Funny, direct, and deeply useful.',
channels: { slack: slack() },
database: db,
memory,
sandbox,
plugins: [stripe({ client: stripeClient })],
})Your code is the product.
Noir is a reliability layer, not an agent cloud. It wraps your agent with durable execution, policy, approvals, delivery, and observability.
Use OpenRouter, the AI SDK, an agent framework, or a function.
Pass your Drizzle database, memory client, and sandbox.
Add Stripe, GitHub, MCP, or your own plugins.
Deploy the same code to any Node-compatible host.
One tree. No ceremony.
The top-level object mirrors the architecture of a real agent. SDK-native values stay native.
| KEY | WHAT GOES THERE |
|---|---|
model | Your model or model adapter |
channels | Slack, Telegram, email, or custom adapters |
database | Your database-backed Noir store |
memory | Your memory implementation |
sandbox | Your code-execution environment |
plugins | Product capabilities such as Stripe |
tools | Agent-callable functions |
Meet users where work happens.
The Slack adapter verifies requests, normalizes mentions and direct messages, preserves threads, receives files, and renders approvals.
import { slack } from '@noir-agent/agent/channels/slack'
channels: {
slack: slack({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
}),
}- Create a Slack app and add
app_mentionandmessage.im. - Point Event Subscriptions to
https://your-host/webhooks/slack. - Add the Slack token and signing secret to your host.
- Invite the bot and mention it.
Effects are explicit.
A tool declares what it can change. Noir can allow reads, require approval for writes, and audit every side effect.
const issueRefund = tool({
description: 'Refund a Stripe payment',
effect: 'destructive',
input: z.object({ paymentId: z.string() }),
execute: ({ paymentId }) => stripe.refunds.create({
payment_intent: paymentId,
}),
})Plugins package related tools, routes, services, and lifecycle hooks. Use Stripe, GitHub, PostHog, MCP, or create a plain plugin in your own codebase.
Memory is infrastructure.
Pass an implementation directly. Noir supplies the lifecycle and retrieval seams; your provider owns the data.
import { supermemory } from '@noir-agent/agent/memory/supermemory'
export default noir({
memory: supermemory({ client }),
})A custom memory adapter can wrap pgvector, Redis, a filesystem, or an internal service.
Bring your own computer.
Sandbox is a first-class boundary because execution policy, files, timeout, and cleanup affect the whole agent.
export default noir({
sandbox: {
create: ({ executionId }) => mySandbox.start(executionId),
execute: (box, command) => box.run(command),
dispose: (box) => box.stop(),
},
})Use E2B, Daytona, Modal, Docker, a VM pool, or an internal executor. Noir does not proxy or own the sandbox.
Long work survives.
Noir persists each run, tool result, approval, and outbound delivery so a restart does not restart the user's intent.
const draft = await noir.run('write-post', writePost)
const approved = await noir.ask({
title: 'Open this pull request?',
details: draft.summary,
})
if (approved) await noir.run('open-pr', openPullRequest)
Cap steps, time, retries, and output.
Pause safely and resume durable state.
Run recurring work with context.
Accept direction while work runs.
Your database, not ours.
Use the in-memory store locally, then provide a Postgres-backed store for durable production runs.
import { postgresStore } from '@noir-agent/agent/store/postgres'
const database = postgresStore({ db })
await database.migrate()The store persists inbox events, run state, outbox delivery, approvals, schedules, leases, and idempotent step results.
Ship the Node app you own.
npx noir start ./dist/agent.js --port 3000
- Use durable storage and run migrations.
- Keep credentials in your host's secret manager.
- Expose HTTPS webhook routes without login protection.
- Run
noir doctorbefore receiving traffic. - Forward events to your observability stack.
- Test duplicate delivery and approval replay.