Skip to content

Agents

Agents are LLM-powered entities with defined personalities, tools, skills, and behavioral rules. The Lota SDK provides the infrastructure and extension points for agents but does not define specific agents itself -- the host application defines its own agent roster, personalities, and capabilities.

Agent Roster

The agent roster is the set of agent identifiers your application supports. Configure it via config.agents when calling createLotaRuntime:

ts
import { createLotaRuntime } from '@lota-sdk/core'

const runtime = await createLotaRuntime({
  // ...other config
  agents: {
    roster: ['chief', 'cto', 'cfo'],
    displayNames: {
      chief: 'Chief Executive',
      cto: 'Chief Technology Officer',
      cfo: 'Chief Financial Officer',
    },
    shortDisplayNames: {
      chief: 'CEO',
      cto: 'CTO',
      cfo: 'CFO',
    },
    teamConsultParticipants: ['chief', 'cto', 'cfo'],
  },
})
FieldTypeDescription
rosterreadonly string[]Array of agent IDs. These are the canonical identifiers used throughout the SDK.
displayNamesRecord<string, string>Human-readable full names, keyed by agent ID. Used in UI and prompt rendering.
shortDisplayNamesRecord<string, string>Optional abbreviated names (e.g., "CTO" instead of "Chief Technology Officer").
teamConsultParticipantsreadonly string[]Subset of the roster that participates in team consultation fan-out calls.

The SDK uses isAgentName(value) and resolveAgentNameAlias(value) internally to validate and resolve agent references, including @mentions in user messages (e.g., @cto).

Creating Agents

The createAgent field provides a factory registry -- a map of agent IDs to factory functions. Each factory receives runtime options and returns a configured agent instance:

ts
agents: {
  createAgent: {
    chief: (options) => createChiefAgent(options),
    cto: (options) => createCTOAgent(options),
    cfo: (options) => createCFOAgent(options),
  },
}

The SDK calls the appropriate factory during workstream turns. The factory receives options including mode, tools, extra instructions, retry limits, and step constraints. The returned agent is a Vercel AI SDK ToolLoopAgent or compatible instance.

Agent Definitions

The SDK provides helper functions for structuring agent behavior into composable rules and skills. Import them from @lota-sdk/core/ai/definitions.

Rules

Rules are behavioral constraints or protocols that an agent must follow. Define them with defineRule:

ts
import { defineRule, renderRuleInstructions } from '@lota-sdk/core/ai/definitions'

const memoryRule = defineRule({
  name: 'memory-usage',
  instructions: 'Always check memory before answering questions about past decisions...',
})

const formalToneRule = defineRule({
  name: 'formal-tone',
  instructions: 'Maintain a professional and formal tone in all responses.',
})

Render rules into prompt text with renderRuleInstructions:

ts
const rulesBlock = renderRuleInstructions([memoryRule, formalToneRule])
// Produces:
// <rules>
//
// Always check memory before answering questions about past decisions...
//
// Maintain a professional and formal tone in all responses.
//
// </rules>

Skills

Skills are capabilities with associated tool sets. Define them with defineSkill:

ts
import { defineSkill, renderSkillInstructions } from '@lota-sdk/core/ai/definitions'

const planningSkill = defineSkill({
  name: 'execution-planning',
  description: 'Create and manage contract-driven execution runs',
  instructions: 'When the user requests a complex multi-step task...',
  tools: ['createExecutionPlan', 'submitExecutionNodeResult', 'getActiveExecutionPlan'],
})
FieldTypeDescription
namestringUnique identifier for the skill.
descriptionstringShort summary of the skill's purpose.
instructionsstringFull prompt instructions for using the skill.
toolsreadonly string[]Tool names associated with this skill.

Render skills into prompt text with renderSkillInstructions:

ts
const skillsBlock = renderSkillInstructions([planningSkill])
// Produces:
// <skills>
//
// When the user requests a complex multi-step task...
//
// </skills>

Built-in Definitions

The SDK ships several reusable definitions:

DefinitionTypePurpose
generalRuleRuleCore behavioral guidelines (concise, direct, professional)
memr3RuleRuleMemR3 evidence-gap retrieval protocol for grounded answers
domainReasoningFallbackRuleRuleFirst-principles reasoning when no specific skill matches
askingUserQuestionsSkillSkillStructured user clarification questions via userQuestions tool
researchSkillSkillDelegated web research via researchTopic, fetchWebpage, inspectWebsite

Learned Skills

Agents can accumulate learned skills from previous interactions. Render them with renderLearnedSkillInstructions:

ts
import { renderLearnedSkillInstructions } from '@lota-sdk/core/ai/definitions'

const learnedBlock = renderLearnedSkillInstructions([
  { name: 'Weekly Report Format', instructions: 'Use bullet points with metrics...' },
])

Prompts

For reusable instruction blocks that are not rules or skills, use definePrompt:

ts
import { definePrompt } from '@lota-sdk/core/ai/definitions'

const onboardingPrompt = definePrompt({
  name: 'onboarding-welcome',
  instructions: 'Welcome the user and explain available capabilities...',
})

Building Agent Tools

The buildAgentTools function assembles the tool set for each agent on every turn. It receives the agent ID and a context object containing workstream, organization, and user references:

ts
agents: {
  buildAgentTools: (agentId, context) => {
    const baseTools = {
      rememberMemory: createRememberMemoryTool(context),
      memoryBlock: createMemoryBlockTool(context),
      memorySearch: createMemorySearchTool(context.orgIdString, agentId),
    }

    if (agentId === 'chief') {
      return { ...baseTools, consultTeam: createConsultTeamTool(context) }
    }

    return baseTools
  },
}

The SDK calls this function during turn preparation. The returned tool map is merged with any toolProviders registered via configureRuntimeExtensions.

Agent Runtime Config

The getAgentRuntimeConfig function returns model selection, reasoning settings, and instruction overrides for each agent. It is called per turn to resolve the agent's runtime behavior:

ts
agents: {
  getAgentRuntimeConfig: ({ agentId, workstreamMode, reasoningProfile }) => {
    return {
      id: agentId,
      model: bifrostChatModel('openrouter/anthropic/claude-sonnet-4'),
      providerOptions: { openai: { reasoningEffort: 'medium' } },
      maxSteps: 15,
      maxOutputTokens: 8192,
      extraInstructions: buildAgentSystemPrompt(agentId),
    }
  },
}

Parameters passed to the function include:

ParameterDescription
agentIdThe agent being configured
workstreamMode'direct' (1:1 agent) or 'group' (multi-agent workstream)
modeResolution mode ('fixedWorkstreamMode' or 'dynamic')
reasoningProfile'fast', 'standard', or 'deep'
onboardingActiveWhether workspace onboarding is in progress
systemWorkspaceDetailsWorkspace context summary
preSeededMemoriesSectionPre-loaded memory context
retrievedKnowledgeSectionRetrieved knowledge base content
additionalInstructionSectionsExtra instruction blocks from hooks
responseGuardSectionGuard instructions for response formatting

System Agents

The SDK includes several internal system agents that power background processing. These are not consumer-facing -- they run autonomously as part of the SDK's internal machinery:

AgentFilePurpose
Context Compactercontext-compacter.agent.tsCompresses long conversation histories to stay within context limits
Memory Extractormemory.agent.tsExtracts durable memories from conversation turns
Memory Rerankermemory-reranker.agent.tsRe-ranks retrieved memories by relevance
Skill Extractorskill-extractor.agent.tsIdentifies reusable skills from conversation patterns
Skill Managerskill-manager.agent.tsManages the learned skill lifecycle
Researcherresearcher.agent.tsPowers the researchTopic delegated agent tool
Title Generatortitle-generator.agent.tsGenerates workstream titles from conversation content
Recent Activity Title Refinerrecent-activity-title-refiner.agent.tsRefines titles for the recent activity feed
Regular Chat Memory Digestregular-chat-memory-digest.agent.tsProduces periodic memory digests from ongoing chats

System agents use the same bifrostChatModel infrastructure as consumer agents and are triggered by BullMQ workers.

Delegated Agents

Delegated agents are single-purpose sub-agents created as tools. They run a complete tool loop to accomplish a specific task and return the result to the calling agent.

ts
import { createDelegatedAgentTool } from '@lota-sdk/core/system-agents/delegated-agent-factory'

const myResearchTool = createDelegatedAgentTool({
  id: 'myResearcher',
  description: 'Research a topic and return a synthesized report',
  model: bifrostChatModel('openrouter/anthropic/claude-sonnet-4'),
  instructions: 'You are a research assistant. Search the web and synthesize findings...',
  tools: { searchWeb: searchWebTool.create(), fetchWebpage: fetchWebpageTool.create() },
  maxSteps: 10,
  maxOutputTokens: 4096,
  temperature: 0.2,
})

The generated tool accepts a { task: string } input. When called by the parent agent, the delegated agent runs its own tool loop (up to maxSteps), then returns the final text result.

For delegated agents that need runtime context (e.g., organization or workstream references), use createDelegatedAgentToolWithContext:

ts
import { createDelegatedAgentToolWithContext } from '@lota-sdk/core/system-agents/delegated-agent-factory'

const contextualTool = createDelegatedAgentToolWithContext<MyContext>({
  id: 'contextualResearcher',
  description: 'Research with org context',
  model: bifrostChatModel('openrouter/anthropic/claude-sonnet-4'),
  instructions: '...',
  createTools: (context) => ({
    searchMemory: createMemorySearchTool(context.orgId),
  }),
})

Both variants automatically inject time-awareness instructions and termination guards into the delegated agent's system prompt.

Core Workstream Profiles

Core workstream profiles define specialized configurations for built-in workstream types. Register a profile resolver via getCoreWorkstreamProfile:

ts
agents: {
  getCoreWorkstreamProfile: (coreType) => {
    if (coreType === 'strategy') {
      return {
        config: { coreType: 'strategy', agentId: 'chief', title: 'Strategy' },
        tools: ['createExecutionPlan', 'submitExecutionNodeResult', 'researchTopic'],
        skills: ['execution-planning', 'research'],
        instructions: 'You manage strategic planning for the organization...',
      }
    }
    // Return a default profile for unknown types
    return { config: { coreType, agentId: '', title: '' }, tools: [], skills: [], instructions: '' }
  },
}

The CoreWorkstreamProfile interface:

FieldTypeDescription
config.coreTypestringThe workstream type identifier
config.agentIdstringDefault agent assigned to this workstream
config.titlestringDisplay title for the workstream
toolsreadonly string[]Tool names available in this workstream
skillsreadonly string[]Skill names active in this workstream
instructionsstringSystem instructions specific to this workstream type