Skip to content

Stand up an agent

This is the front door for building on the agent. You import a layer, open a durable session, and drive it. Pick the company agent (@alphacro/coframe-agent) if you want the Coframe opinions, or the bare framework (@alphacro/agent) if you want a clean prompt loop with no tools or planning.

The smallest possible program. runAgent runs one prompt to completion over the in-memory backend and streams the events it appends — nothing persists past the call.

import { runAgent } from "@alphacro/agent";
for await (const event of runAgent({ prompt: "hello", env: {} })) {
console.log(event.kind); // run.started ... run.completed
}

defineAgent(config) returns an Agent. Open a session per identity with agent.session(id) — a plain string is branded as the canonical session id, so the same name always resolves the same event log and transcript.

import { defineAgent } from "@alphacro/agent";
const agent = defineAgent({ env: {} });
const session = agent.session("greet-session");
for await (const event of session.send("hello there")) {
console.log(event.kind);
}
const transcript = await session.messages(); // ReadonlyArray<UIMessage>
await session.dispose();

The session is dual-mode. The same durable handle serves a run-to-completion consumer (CLI, evals) and a streaming UI from one composition.

send(text) runs a user turn to completion and yields the events it appended — the mode the CLI and eval harness drive.

for await (const event of session.send("Improve the hero CTA")) {
// react to run / file / process / tool events as they land
}

A session also exposes events() (the full ordered event log), host() (the underlying headless host — an escape hatch for live subscription and tool approvals), and dispose() (release backend + host + MCP resources; safe to call repeatedly).

A backend supplies the three pieces of infrastructure that differ by where a session runs: the append-only event store, the workspace tools read and write, and the persisted transcript. Choosing one is the only infrastructure decision an app makes.

Ephemeral event log, workspace, and transcript. Nothing survives the process — the zero-config default for tests and one-off runs. Used implicitly when you set no backend.

import { defineAgent, memory } from "@alphacro/agent";
const agent = defineAgent({ backend: memory() });

The model is a plain catalog id string, resolved through the canonical factory (BYOK / AI Gateway routing and the no-key→mock rule behave identically to the Worker and CLI). Omit it and the env-level fallback applies — the scenario mock when no provider key is present.

const agent = defineAgent({ model: "anthropic:claude-sonnet-4-6" });

You can also pass an already-resolved LanguageModelV3 instance (a scripted mock or scenario model) — the seam deterministic tests and evals drive through.

For a real CRO agent, use Layer 2. defineCoframeAgent(options) bundles the company prompt, common tools, planning + requirements spine, delegation, evidence wiring, and Jarvis MCP — every field is optional, and the defaults are the company agent. It returns the same Layer 1 Agent, so sessions, backends, and dual-mode all work identically.

import { defineCoframeAgent } from "@alphacro/coframe-agent";
const agent = defineCoframeAgent({ env: {} });
const session = agent.session("operator-42");
for await (const event of session.send("Improve the hero CTA")) {
console.log(event.kind);
}
await session.dispose();

memory, the session types, and Backend are re-exported from @alphacro/coframe-agent, and the Node filesystem() backend from @alphacro/coframe-agent/node, so an app needs a single import:

import { defineCoframeAgent, memory } from "@alphacro/coframe-agent";
import { filesystem } from "@alphacro/coframe-agent/node";
OptionDefaultWhat it does
modelenv/mockCatalog id string or a LanguageModelV3 instance.
backendmemory()Where the event log / workspace / transcript live.
instructionsCOFRAME_AGENT_BASE_PROMPTOverride the company base prompt.
extraToolsHost tools registered after the common suite (e.g. apply_variant).
extraPluginsExtra plugins composed after the planning/requirements slice.
includeRunShellfalseEnable run_shell in the common tool suite.
morphApiKeyEnables the edit_file tool; omit to drop it.
jarvis{ apiKey, apiUrl? } — exposes Jarvis MCP tools. Omit to run without Jarvis.
delegationtrueEnable sub-agent delegation.
onComposeObserve the composed session internals once per open.

The company agent connects Jarvis as a declarative MCP server — pass credentials and its tools are bridged in at session open:

const agent = defineCoframeAgent({
jarvis: {
apiKey: process.env.JARVIS_API_KEY!,
apiUrl: process.env.JARVIS_API_URL,
},
});

Under the hood this is L1’s declarative mcp config (AgentConfig.mcp); the company agent fills it in for you from buildJarvisMcpServerConfig.

apps/agent-cli is the reference: one defineCoframeAgent session, in-memory for fake mode and the CLI’s filesystem adapters for live.

import { defineCoframeAgent, memory, type AgentSession } from "@alphacro/coframe-agent";
const agent = defineCoframeAgent({
backend: options.mode === "fake"
? memory()
: cliFilesystemBackend({ workspaceDir, eventsDir }),
source: "cli",
callerUserId,
includeRunShell: false,
onChildEvent: logDelegationEvent,
});
const session: AgentSession = agent.session(sessionId);
const host = await session.host(); // subscribe to events, drive approvals

The eval harness (apps/agent-evals) drives the same composition through defineCoframeAgent, using onCompose to capture the live tool registry and planning store for its assertions. Both apps prove that one import stands up the full company agent.

Tools, planning, requirements, delegation, skills, and permissions are all built as plugins — the extension surface beneath L1 and L2. When you need a new capability, you contribute a plugin (via extraPlugins, or by composing your own) rather than forking the harness. See the Plugin system guide and Build your own plugin.