Agent System
Define agents, resolve prompts and tools, delegate tasks, run heartbeats, and manage external connections.
Overview
Agents are AI actors that operate on entities. They populate fields, answer questions in chat, run background tasks, and delegate work to other agents. The agent system provides:
- Registration -- code-defined or DB-managed agents
- Resolution -- assemble prompt, tools, and permissions for execution
- Execution -- streaming (chat) or one-shot (background tasks)
- Delegation -- agents calling other agents via tools
- Heartbeat -- scheduled autonomous execution
- Connections -- external agents via MCP, A2A, OpenClaw, HTTP
Defining Agents
Code-defined
import { defineAgent } from "@sprinterai/core";
const analyst = defineAgent({
slug: "analyst",
name: "Research Analyst",
description: "Researches entities and populates data fields",
systemPrompt: `You are a PE research analyst. Your job is to:
1. Research companies using web search
2. Extract key financial metrics
3. Submit scored responses for each field`,
model: "claude-sonnet-4-20250514",
toolGroups: ["entity", "web", "response"],
customTools: ["roi-calculator"],
});DB-managed
Agents can also be created through the Admin UI or via the updateAgentConfig admin tool. DB agents are stored in the agents table and resolved at runtime.
Agent Config
interface AgentConfig {
toolGroups?: string[]; // "entity", "web", "response", "workflow", "context", "admin"
customTools?: string[]; // tool slugs
skills?: string[]; // skill identifiers
heartbeat?: {
enabled: boolean;
schedule: HeartbeatSchedule;
prompt: string;
mode: HeartbeatMode;
};
}
// Parse from raw JSON (validates and defaults)
import { parseAgentConfig } from "@sprinterai/core";
const config = parseAgentConfig(rawJson);Agent Resolution
Resolution is the process of assembling everything an agent needs to execute:
import { resolveAgent } from "@sprinterai/runtime";
const resolved = await resolveAgent({
agent: agentDefinition,
stores,
permissions: userPermissions,
context: {
tenantId,
entityId, // optional: current entity context
conversationId, // optional: current chat
},
});
// resolved.prompt -- assembled system prompt
// resolved.tools -- permission-gated tool set
// resolved.model -- model identifier
// resolved.agentSlug -- for trackingWhat resolution does
- Looks up the agent definition (code registry, then DB)
- Builds the system prompt from sections (system, context, memory)
- Resolves tool groups into concrete tools
- Filters tools by the caller's permissions
- Adds delegation tools if other agents are available
- Returns a ready-to-execute agent configuration
Agent Execution
Streaming (chat)
import { executeAgentStream } from "@sprinterai/runtime";
const stream = await executeAgentStream({
agent: resolvedAgent,
messages: conversationHistory,
onToolCall: (call) => {
console.log(`Tool: ${call.name}`, call.args);
},
});
// Stream is compatible with AI SDK v6 streamText responseOne-shot (background)
import { executeAgentGenerate } from "@sprinterai/runtime";
const result = await executeAgentGenerate({
agent: resolvedAgent,
messages: [{ role: "user", content: "Analyze ACME Corp revenue trends" }],
});
// result.text -- generated text
// result.toolCalls -- tools that were called
// result.usage -- token usageDelegation
Agents can delegate tasks to other agents using the built-in delegation tool:
import { createDelegateToAgentTool } from "@sprinterai/runtime";
const delegateTool = createDelegateToAgentTool({
agentRegistry,
stores,
permissions,
});
// When an agent calls this tool:
// "Delegate to researcher: find revenue data for ACME Corp"
// -> The researcher agent executes and returns resultsHeartbeat
Agents can run autonomously on a schedule:
const agent = defineAgent({
slug: "monitor",
name: "Portfolio Monitor",
systemPrompt: "Monitor portfolio companies for changes...",
model: "claude-sonnet-4-20250514",
toolGroups: ["entity", "web"],
});
// Heartbeat config (set via Admin UI or updateAgentConfig tool)
// agent.config.heartbeat = {
// enabled: true,
// schedule: { type: "cron", expression: "0 9 * * *" },
// prompt: "Check for news about portfolio companies",
// mode: "autonomous",
// }In heartbeat mode, the agent uses its own role's permissions (not a user's), and execution is tracked in the agent_heartbeat_runs table.
External Connections
Connect to agents running on other platforms:
import {
discoverMCPTools,
createA2AAgent,
createOpenClawModel,
createHTTPAgent,
} from "@sprinterai/runtime";
// MCP: discover tools from an MCP server
const tools = await discoverMCPTools({
serverUrl: "http://localhost:3001/mcp",
});
// A2A: connect to an agent via the Agent-to-Agent protocol
const externalAgent = createA2AAgent({
endpoint: "https://agent.example.com/a2a",
});
// OpenClaw: use an OpenClaw model
const model = createOpenClawModel({
apiKey: process.env.OPENCLAW_KEY,
});
// HTTP: generic HTTP agent adapter
const httpAgent = createHTTPAgent({
baseUrl: "https://api.example.com/agent",
});Connection types are stored in the agent_connections table and managed via Admin > Connections.
Permission Model
Agents share the same RBAC system as users:
- Each agent has a
role_idin theagentstable - Supervised (chat): agent inherits the user's permissions
- Autonomous (heartbeat): agent uses its own role's permissions
- Tools the agent cannot use are excluded from its tool set -- agents never see unavailable tools
import { getPermissionsForRole } from "@sprinterai/supabase";
// For heartbeat execution
const agentPerms = await getPermissionsForRole(agent.role_id);
const tools = await resolveAgentTools({
agent,
stores,
permissions: agentPerms,
});Agent Registry
import { AgentRegistry } from "@sprinterai/runtime";
const registry = new AgentRegistry();
registry.register(analyst);
registry.register(researcher);
registry.register(monitor);
// Lookup
const agent = registry.get("analyst");
// List all
const all = registry.list();