@redlineai/sdk
v0.2.25
Published
Run your own agents inside Redline AI experiments — from your repo, with `redline dev`.
Maintainers
Readme
@redlineai/sdk
Run your own agent inside a Redline experiment — from your repository, on your machine, against the same tasks and rubrics as the agents in the catalog.
Your agent does not move. It stays where it is, keeps its own dependencies and its own model keys, and Redline sends it work.
Install
npm i -D @redlineai/sdk
npx redline initinit writes two files: redline.config.ts (where your Redline lives) and
redline/my-agent.ts (a wrapper around the agent you already have).
Wrap what you already wrote
import { defineAgent } from "@redlineai/sdk";
import { yourAgent } from "../src/agent"; // ← your existing code, unchanged
export const myAgent = defineAgent({
id: "my-agent",
name: "My Agent",
run: (task) => yourAgent(task.prompt), // ← the one line that is yours
});Then:
export REDLINE_API_KEY=rl_… # Agents page → Runner key
npx redline devThat registers the agent with your project and holds a connection open. It now appears on the Agents page, can be selected in an experiment, and runs on your machine when one is launched.
What an experiment gives your agent
An experiment can attach MCP servers, skills, CLIs and repositories. Those arrive as real tools, not as prose in the prompt:
import { defineAgent, redlineTools } from "@redlineai/sdk";
import { generateText } from "ai";
export const myAgent = defineAgent({
id: "my-agent",
name: "My Agent",
async run(task) {
const attached = await redlineTools(task); // MCP tools + machine_run
const { text } = await generateText({
model: yourModel,
prompt: task.prompt,
tools: { ...yourTools, ...attached },
});
return text;
},
});If your agent uses the Vercel AI SDK, you can skip even
that: redline dev hooks the module loader, so generateText, streamText and
Agent receive the experiment's tools without a line of yours changing.
If your agent has its own tool registry — which most do past the prototype
stage — there is a framework-free pair. asOpenAISchema() is what to advertise
to the model, call() is what to do when it picks one:
const attached = await redlineTools(task);
myTools.push(...attached.asOpenAISchema()); // advertise
const result = await attached.call(name, args); // executeNeither knows anything about your framework, and together they are the whole integration for a hand-rolled loop.
machine_run is the interesting one, and it is there in every run — not only
the ones that attached something. Your agent runs on your laptop; this is a
shell on the project's Linux machine, in a directory of the run's own. Attach a
repository and it is already cloned there; attach nothing and you still have a
computer, which is what an agent needs the moment a task says "write a file".
The machine starts when you first call the tool and never before, so a run that does not use it costs nothing.
Commands
| | |
|---|---|
| redline init | write the config and a starter agent |
| redline dev | register your agents and take work |
| redline doctor | check the connection, the key, and what your agent can see |
TypeScript, CommonJS, and what init writes
Your agent file stays TypeScript — redline dev loads it through tsx, so
there is no build step between you and a run.
init writes .mts files in a CommonJS project and .ts in an ESM one, and
that is not cosmetic: .ts means whatever the nearest package.json says, and
npm init -y still says CommonJS. Importing this SDK from a CommonJS-flavoured
.ts file dies inside tsx's module resolution with a message about our
internals rather than anything you could act on.
If you write the files yourself in a CommonJS project, name them .mts.
Monitoring — the same agent after it ships
defineAgent puts your agent inside an experiment, where the attacks are ours.
observe reports it doing real work, checked by the same detectors — one
wrapper around the handler your backend already has:
import { observe } from "@redlineai/sdk";
export const handleMessage = observe(
async (conversationId: string, userId: string, text: string) => {
const { answer } = await yourAgent(text);
return answer;
},
{ agent: "support-agent", sessionArg: 0, userArg: 1, inputArg: 2 },
);Every call becomes a session: the wrapper records the input and the returned
answer itself, and every LLM call, tool call and token count the call emits
through the AI SDK or OpenTelemetry lands in the same session automatically.
Calls sharing the sessionArg value land in the same transcript. Config is
environment only — REDLINE_API_KEY, REDLINE_URL, REDLINE_AGENT — so a
deploy needs no code; without the key the wrapper is inert and the handler
runs untouched. (AI SDK 7 reports with no opt-in; AI SDK ≤6 emits spans only
when a call sets experimental_telemetry: { isEnabled: true }.)
Spans are batched, so nothing in your agent's path waits on us, and a failed post is swallowed rather than thrown — monitoring that can take down the thing it monitors is worse than no monitoring. Every span carries a key derived from your session id, so a batch that times out and is retried lands exactly once.
For a handler shape the wrapper cannot wrap, createMonitor remains as the
explicit low-level client underneath: monitor.session({ id, agent }), then
session.input / tool / output / end.
Monitor mode — no code at all
If your agent already speaks OpenTelemetry (Pydantic AI, LangChain, the Vercel AI SDK's telemetry), you do not have to call any of the above. Set:
REDLINE_MONITOR=1
REDLINE_API_KEY=<a project-scoped key>
REDLINE_AGENT=support-agent # optional; how sessions are labelled
REDLINE_URL=https://tryredlineai.co # optional; where the platform is
REDLINE_PROJECT=<project id> # only if the key is not project-scopedand the same capture that streams your runs inside redline dev streams your
production sessions to the platform instead — one session per trace, closed
when its root span ends, idempotent on retry. The Python SDK behaves
identically, with the decorator shape natural to Python:
from redline import observe
@observe(agent="support-agent", session_arg="conversation_id",
user_arg="user_id", input_arg="text")
async def handle_message(conversation_id: str, user_id: str, text: str) -> str:
return await your_agent(text)Docs
https://tryredlineai.co/docs/agents/your-agent
MIT.
