Skip to main content

Agent Management API

Info

Last Updated: 2026-01-08

Info

Relationship to Memory Spaces: The Agent Management API provides optional metadata registration for agents, enabling discovery, analytics, and team organization. This is complementary to Memory Space Operations, which define isolation boundaries.

Complete API reference for agent management operations.

Overview

The Agent Management API provides optional metadata registration for agent discovery, analytics, and team organization, plus convenient cascade deletion by participantId across all memory spaces.

Info

Architecture: Cortex uses Memory Spaces as the fundamental isolation boundary. Multiple agents/tools can share one memory space (Hive Mode), or operate in separate spaces (Collaboration Mode). The Agent Registry is an optional metadata layer on top of this architecture for tracking agent information.

Agents vs Memory Spaces: Complementary Systems

FeatureMemory SpacesAgent Registry
PurposeIsolation boundaryMetadata & analytics
StatusActive, required conceptActive, optional feature
When to useDefine data scopeTrack agent info
Hive ModeRequiredNot required
RegistrationOptionalOptional
AnalyticsManual queriesAuto-computed stats
DiscoveryBy type/participantsBy capabilities/team
Cascade deletionBy memorySpaceIdBy participantId

Both can be used together for complete agent management.

Two Key Features

1. Optional Registry (Metadata Layer)

  • Agents work without registration - just use string IDs
  • Registration provides: discovery, analytics, team organization
  • Purely optional enhancement

2. Cascade Deletion by participantId (Convenience)

  • Delete all agent data across ALL memory spaces in one call
  • Filters by participantId field in data (not userId)
  • Works even if agent was never registered
  • Similar to users API but for agent cleanup, not GDPR

Hybrid Approach:

Key Concept:

  • Simple Mode: Just use string IDs ('agent-1', 'support-agent')
  • Registry Mode: Optionally register agents for analytics, discovery, and cascade deletion

Relationship to Four-Layer Architecture:

  • Isolation Boundary: Memory Spaces define data scoping (replaced old agentId role)
    • Layer 1a: Conversations (memorySpace-scoped)
    • Layer 2: Vector memories (memorySpace-scoped)
    • Layer 3: Facts (memorySpace-scoped)
    • Layer 4: Convenience API (memorySpace-scoped)
  • Participant Tracking: participantId field tracks which agent/tool created data within a space
  • Hive Mode: Multiple agents can share one memorySpace
  • Agent Registry: Optional metadata layer for analytics, discovery, and team organization

Hybrid Agent Management

Simple Mode (No Registration Required)

// Works immediately - no registration needed
// Layer 3 - stores in ACID + Vector automatically
await cortex.memory.remember({
memorySpaceId: "my-agent", // Just a string ID (was formerly agentId)
conversationId: "conv-123",
userMessage: "Hello!",
agentResponse: "Hi there!",
userId: "user-1",
userName: "Alex",
});

// Search works (searches Vector index)
const memories = await cortex.memory.search({
memorySpaceId: "my-agent",
query: "hello",
});

// That's it! No setup required - all layers work immediately.

Use when:

  • Getting started quickly
  • Simple applications
  • Don't need agent metadata or analytics
  • Maximum flexibility

Registry Mode (Optional Registration)

// Optionally register for enhanced features
await cortex.agents.register({
id: "my-agent",
name: "Customer Support Bot",
description: "Handles customer inquiries and support tickets",
metadata: {
team: "support",
capabilities: ["empathy", "problem-solving", "escalation"],
version: "2.1.0",
owner: "support-team@company.com",
},
});

// Now get agent with enhanced stats
const agent = await cortex.agents.get("my-agent");
console.log(agent?.stats?.totalMemories); // Stats included in response

// Agent discovery
const supportAgents = await cortex.agents.search({
metadata: { team: "support" },
});

Use when:

  • Multiple agents (need organization)
  • Want analytics and insights
  • Team collaboration
  • Agent discovery needed

Core Operations

register()

Register an agent in the registry (optional, enables enhanced features).

Signature:

cortex.agents.register(
agent: AgentRegistration
): Promise<RegisteredAgent>

Parameters:

ParameterTypeRequiredDefaultDescription
idstringYesAgent ID (must match ID used in memory ops)
tenantIdstringNoMulti-tenancy: SaaS platform isolation
namestringYesDisplay name
descriptionstringNoWhat this agent does
metadataobjectNoCustom metadata including team, capabilities, version, owner, and any custom fields
configobjectNoAgent configuration including memoryVersionRetention and custom config options

Returns:

interface RegisteredAgent {
id: string;
tenantId?: string; // Multi-tenancy isolation
name: string;
description?: string;
metadata: Record<string, unknown>;
config: Record<string, unknown>;
status: "active" | "inactive" | "archived"; // Agent status
registeredAt: number; // Unix timestamp (ms)
updatedAt: number; // Unix timestamp (ms)
lastActive?: number; // Unix timestamp (ms)
stats?: AgentStats;
}

interface AgentStats {
totalMemories: number;
totalConversations: number;
totalFacts: number; // Facts extracted by this agent
memorySpacesActive: number; // Memory spaces with agent data
lastActive?: number; // Unix timestamp (ms)
isApproximate?: boolean; // True if counts are sampled (large datasets)
}
Info

Note on Stats Accuracy: For performance reasons, stats are computed using sampling with a limit of 1000 records per query. For agents with large amounts of data, the counts may be approximate (indicated by isApproximate: true).

Example:

const agent = await cortex.agents.register({
id: "support-agent",
name: "Customer Support Bot",
description: "Handles customer inquiries, issues, and support tickets",
metadata: {
team: "customer-success",
capabilities: ["troubleshooting", "empathy", "escalation"],
version: "2.1.0",
owner: "support@company.com",
maxConcurrentChats: 5,
},
config: {
memoryVersionRetention: 20, // Keep 20 versions instead of default 10
},
});

console.log(`Registered ${agent.name}`);
console.log(`Total memories: ${agent.stats?.totalMemories}`);

Errors:

  • AgentValidationError('MISSING_AGENT_ID') - Agent ID not provided
  • AgentValidationError('EMPTY_AGENT_ID') - Agent ID is empty string
  • AgentValidationError('AGENT_ID_TOO_LONG') - Agent ID exceeds 256 characters
  • AgentValidationError('MISSING_AGENT_NAME') - Agent name not provided
  • AgentValidationError('EMPTY_AGENT_NAME') - Agent name is empty string
  • AgentValidationError('INVALID_METADATA_FORMAT') - Metadata is not a plain object
  • ConvexError('AGENT_ALREADY_REGISTERED') - Agent ID already exists in registry

See Also:


get()

Get registered agent details.

Signature:

cortex.agents.get(
agentId: string
): Promise<RegisteredAgent | null>

Parameters:

ParameterTypeRequiredDefaultDescription
agentIdstringYesAgent ID to retrieve

Returns:

  • RegisteredAgent - Complete agent registration
  • null - If agent not registered (but may still have memories!)

Example:

const agent = await cortex.agents.get("support-agent");

if (agent) {
console.log(`Name: ${agent.name}`);
console.log(`Team: ${agent.metadata.team}`);
console.log(`Capabilities: ${agent.metadata.capabilities.join(", ")}`);
console.log(`Total memories: ${agent.stats?.totalMemories}`);
} else {
console.log("Agent not registered (but may still work with simple ID)");
}

Errors:

  • AgentValidationError('MISSING_AGENT_ID') - Agent ID not provided
  • AgentValidationError('EMPTY_AGENT_ID') - Agent ID is empty string
Info

Agents don't need to be registered to function. This just retrieves registry info if it exists.


Find registered agents by metadata. This is an alias for list() with filters.

Signature:

cortex.agents.search(
filters: AgentFilters
): Promise<RegisteredAgent[]>
Info

Note: search() requires a filters object. To list all agents without filters, use list() instead.

Parameters:

ParameterTypeRequiredDefaultDescription
tenantIdstringNoMulti-tenancy filter
metadataRecord<string, unknown>NoFilter by metadata fields (exact match)
namestringNoSearch by name (partial match)
capabilitiesstring[]NoHas these capabilities
capabilitiesMatch'any' | 'all'No'any'Match mode for capabilities
status'active' | 'inactive' | 'archived'NoFilter by agent status
registeredAfternumberNoUnix timestamp (ms) - filter by registration date
registeredBeforenumberNoUnix timestamp (ms) - filter by registration date
lastActiveAfternumberNoUnix timestamp (ms) - filter by activity
lastActiveBeforenumberNoUnix timestamp (ms) - filter by activity
limitnumberNo100Max results (1-1000)
offsetnumberNo0Skip first N results
sortBy'name' | 'registeredAt' | 'lastActive'NoSort field (validated but not yet implemented)
sortOrder'asc' | 'desc'NoSort direction (validated but not yet implemented)
Warning

Implementation Note: The sortBy and sortOrder parameters are validated but not currently implemented. Results are returned in database order (by registeredAt descending). For custom sorting, sort results client-side.

Returns:

  • RegisteredAgent[] - Array of matching agents

Example:

// Find all support team agents
const supportAgents = await cortex.agents.search({
metadata: { team: "support" },
});

// Find agents with specific capability
const troubleshooters = await cortex.agents.search({
capabilities: ["troubleshooting"],
});

// Find recently registered agents (last 30 days)
const newAgents = await cortex.agents.search({
registeredAfter: Date.now() - 30 * 24 * 60 * 60 * 1000, // Unix timestamp (ms)
sortBy: "registeredAt",
sortOrder: "desc",
});

// Find by name
const found = await cortex.agents.search({
name: "support", // Partial match: "Customer Support Bot"
});

Errors:

  • AgentValidationError('INVALID_LIMIT_VALUE') - Invalid limit value
  • AgentValidationError('INVALID_OFFSET_VALUE') - Invalid offset value
  • AgentValidationError('INVALID_STATUS') - Invalid status value
  • AgentValidationError('INVALID_TIMESTAMP_RANGE') - registeredAfter >= registeredBefore
Info

Only returns registered agents. Unregistered agents (simple ID mode) won't appear.


list()

List all registered agents with optional filters.

Signature:

cortex.agents.list(
filters?: AgentFilters
): Promise<RegisteredAgent[]>

Parameters:

ParameterTypeRequiredDefaultDescription
tenantIdstringNoMulti-tenancy filter (backend-applied)
metadataRecord<string, unknown>NoFilter by metadata (client-side)
namestringNoSearch by name (client-side, partial match)
capabilitiesstring[]NoHas these capabilities (client-side)
capabilitiesMatch'any' | 'all'No'any'Match mode
status'active' | 'inactive' | 'archived'NoStatus filter (backend-applied)
registeredAfternumberNoUnix timestamp (ms)
registeredBeforenumberNoUnix timestamp (ms)
lastActiveAfternumberNoUnix timestamp (ms) (client-side)
lastActiveBeforenumberNoUnix timestamp (ms) (client-side)
limitnumberNo100Max results (max: 1000)
offsetnumberNo0Skip first N results
sortBy'name' | 'registeredAt' | 'lastActive'NoSort field
sortOrder'asc' | 'desc'NoSort direction
Warning

Pagination Limitation: The offset and limit parameters are applied at the database level BEFORE the following client-side filters:

  • metadata
  • name
  • capabilities
  • lastActiveAfter / lastActiveBefore

This means combining offset with any of these filters may produce unexpected results. For example, requesting { metadata: { team: "alpha" }, offset: 10 } skips the first 10 agents regardless of team, then filters the remaining results.

Safe pagination patterns:

  • Use offset/limit with status filter only (backend-applied)
  • Use offset/limit without any client-side filters
  • For paginating with metadata/name/capabilities, fetch all results and paginate client-side

Returns:

  • RegisteredAgent[] - Array of matching agents

Example:

// List all registered agents (safe: status is backend-applied)
const agents = await cortex.agents.list({
limit: 50,
status: "active",
});

console.log(`Found ${agents.length} registered agents`);

agents.forEach((agent) => {
console.log(`${agent.name} (${agent.id})`);
console.log(` Team: ${agent.metadata.team}`);
console.log(` Memories: ${agent.stats?.totalMemories}`);
});

// Safe: client-side filter without offset
const teamAgents = await cortex.agents.list({
metadata: { team: "support" },
});

// WARNING: offset + metadata may produce unexpected results
// const paginated = await cortex.agents.list({
// metadata: { team: "alpha" },
// offset: 10, // Applied before metadata filter
// });

Errors:

  • AgentValidationError('INVALID_LIMIT_VALUE') - Invalid limit value (must be 1-1000)
  • AgentValidationError('INVALID_OFFSET_VALUE') - Invalid offset value (must be >= 0)

count()

Count registered agents.

Signature:

cortex.agents.count(
filters?: { status?: 'active' | 'inactive' | 'archived' }
): Promise<number>

Parameters:

ParameterTypeRequiredDefaultDescription
filters.status'active' | 'inactive' | 'archived'NoFilter by agent status
Warning

Limitation: The count() method only supports the status filter. Other filters like metadata, name, and capabilities are not supported. To count agents with complex filters, use list() and check the array length.

Returns:

  • number - Count of matching agents

Example:

// Total registered agents
const total = await cortex.agents.count();

// Count by status
const activeCount = await cortex.agents.count({
status: "active",
});

console.log(`${activeCount} active agents`);

// For complex filters, use list() instead:
const supportAgents = await cortex.agents.list({
metadata: { team: "support" },
});
console.log(`${supportAgents.length} agents on support team`);

exists()

Check if an agent is registered.

Signature:

cortex.agents.exists(
agentId: string
): Promise<boolean>

Parameters:

ParameterTypeRequiredDefaultDescription
agentIdstringYesAgent ID to check

Returns:

  • boolean - true if agent is registered, false otherwise

Example:

// Check if agent is registered
if (await cortex.agents.exists("my-agent")) {
console.log("Agent is registered");
} else {
console.log("Agent not registered (but may still work with simple ID)");
}

// Conditional registration
if (!(await cortex.agents.exists("support-agent"))) {
await cortex.agents.register({
id: "support-agent",
name: "Support Bot",
});
}

Errors:

  • AgentValidationError('MISSING_AGENT_ID') - Agent ID not provided
  • AgentValidationError('EMPTY_AGENT_ID') - Agent ID is empty string
Info

An agent doesn't need to be registered to function. This just checks the registry.


update()

Update registered agent details.

Signature:

cortex.agents.update(
agentId: string,
updates: Partial<AgentRegistration>
): Promise<RegisteredAgent>

Parameters:

ParameterTypeRequiredDefaultDescription
agentIdstringYesAgent to update
updatesPartial<AgentRegistration>YesFields to update

Returns:

  • RegisteredAgent - Updated agent

Example:

// Update agent metadata
const updated = await cortex.agents.update("support-agent", {
metadata: {
version: "2.2.0", // Update version
capabilities: ["troubleshooting", "empathy", "escalation", "billing"], // Add capability
},
});

// Update configuration
await cortex.agents.update("audit-agent", {
config: {
memoryVersionRetention: -1, // Unlimited retention
},
});

Errors:

  • AgentValidationError('MISSING_AGENT_ID') - Agent ID not provided
  • AgentValidationError('MISSING_UPDATES') - No update fields provided
  • AgentValidationError('EMPTY_AGENT_NAME') - Name is empty string (if provided)
  • AgentValidationError('INVALID_METADATA_FORMAT') - Metadata is not a plain object
  • AgentValidationError('INVALID_STATUS') - Invalid status value
  • ConvexError('AGENT_NOT_REGISTERED') - Agent not found in registry

configure()

Update agent-specific configuration.

Signature:

cortex.agents.configure(
agentId: string,
config: AgentConfig
): Promise<void>

Parameters:

ParameterTypeRequiredDefaultDescription
agentIdstringYesAgent ID to configure
config.memoryVersionRetentionnumberNo-1 = unlimited, 1 = no history, 10 = default
config.[key]anyNoCustom config options

Returns:

  • void

Example:

// Configure version retention
await cortex.agents.configure("audit-agent", {
memoryVersionRetention: -1, // Keep all versions forever
});

await cortex.agents.configure("temp-agent", {
memoryVersionRetention: 1, // Only keep current (no history)
});

// Custom configuration
await cortex.agents.configure("support-agent", {
autoArchiveAfterDays: 90,
maxMemoriesPerUser: 1000,
enableAutoSummarization: true, // Cloud Mode feature
});

Errors:

  • AgentValidationError('MISSING_AGENT_ID') - Agent ID not provided
  • AgentValidationError('INVALID_CONFIG_FORMAT') - Config is not a plain object
  • AgentValidationError('EMPTY_CONFIG_OBJECT') - Config object is empty
  • ConvexError('AGENT_NOT_REGISTERED') - Agent not found in registry
Warning

Configuration requires the agent to be registered first. If the agent is not registered, this method throws AGENT_NOT_REGISTERED.


unregister()

Remove agent from registry with optional cascade deletion by participantId.

Tip

Cascade Deletion: Fully implemented in SDK with graph orphan detection. Filters by participantId across ALL memory spaces.

Signature:

cortex.agents.unregister(
agentId: string,
options?: UnregisterAgentOptions
): Promise<UnregisterAgentResult>

Parameters:

ParameterTypeRequiredDefaultDescription
agentIdstringYesAgent ID to unregister
options.cascadebooleanNofalseDelete all data where participantId = agentId
options.verifybooleanNotrueVerify deletion completeness
options.dryRunbooleanNofalsePreview what would be deleted

Returns:

interface UnregisterAgentResult {
agentId: string;
unregisteredAt: number;

// Per-layer deletion counts
conversationsDeleted: number;
conversationMessagesDeleted: number;
memoriesDeleted: number;
factsDeleted: number;
graphNodesDeleted?: number;

// Verification
verification: {
complete: boolean;
issues: string[];
};

// Summary
totalDeleted: number;
deletedLayers: string[];
memorySpacesAffected: string[]; // Which memory spaces had data
}

Implementation:

Uses four-phase cascade deletion (same pattern as users API):

  1. Collection: Query all memory spaces for records where participantId = agentId
  2. Backup: Create rollback snapshots
  3. Execution: Delete in reverse dependency order (facts → memories → conversations → graph → registration)
  4. Verification: Confirm all records were deleted (if verify: true)

If any deletion fails, automatically rolls back all changes.

Example 1: Simple Unregister (keep data)

// Remove from registry, keep all memories/conversations
const result = await cortex.agents.unregister("old-agent");

console.log(`Unregistered ${result.agentId}`);
console.log(`Total deleted: ${result.totalDeleted}`); // 1 (just registration)
console.log(`Data preserved: memories still accessible with agentId string`);

Example 2: Cascade Delete by participantId

// Delete registration + ALL data where participantId = agentId
const result = await cortex.agents.unregister("old-agent", {
cascade: true,
verify: true,
});

// Per-layer breakdown
console.log(`Conversations deleted: ${result.conversationsDeleted}`);
console.log(
` Messages in those conversations: ${result.conversationMessagesDeleted}`,
);
console.log(`Memories deleted: ${result.memoriesDeleted}`);
console.log(`Facts deleted: ${result.factsDeleted}`);
console.log(`Graph nodes deleted: ${result.graphNodesDeleted || "N/A"}`);

// Summary
console.log(`Total deleted: ${result.totalDeleted}`);
console.log(
`Memory spaces affected: ${result.memorySpacesAffected.join(", ")}`,
);
console.log(`Layers: ${result.deletedLayers.join(", ")}`);

// Verification
if (result.verification.complete) {
console.log("Deletion verified - no orphaned records");
} else {
console.warn("Verification issues:");
result.verification.issues.forEach((issue) => console.warn(` - ${issue}`));
}

Example 3: Dry Run (Preview)

// Preview what would be deleted
const preview = await cortex.agents.unregister("agent-123", {
cascade: true,
dryRun: true,
});

console.log(`Would delete ${preview.totalDeleted} records`);
console.log(`Across ${preview.memorySpacesAffected.length} memory spaces`);
console.log(`Memories: ${preview.memoriesDeleted}`);
console.log(`Conversations: ${preview.conversationsDeleted}`);

// Agent still exists after dry run
const agent = await cortex.agents.get("agent-123");
console.log(`Agent still registered: ${agent !== null}`); // true

Example 4: Cascade Without Registration

// Agent never registered, but created data with participantId
// (This is the key difference from users API!)

// Day 1: Create data (no registration)
await cortex.memory.remember({
memorySpaceId: "space-1",
participantId: "agent-xyz", // Agent never registered
conversationId: "conv-1",
userMessage: "Hello",
agentResponse: "Hi",
userId: "user-1",
userName: "User",
});

// Day 30: Delete all agent data (works without registration when cascade=true!)
const result = await cortex.agents.unregister("agent-xyz", {
cascade: true, // Required: cascade=true works even without registration
});

// Deletes memories even though agent was never registered
// Queries by participantId field in data, not registration
console.log(
`Deleted ${result.memoriesDeleted} memories from unregistered agent`,
);
Info

Note: When cascade: false (default), the agent must be registered or AGENT_NOT_REGISTERED is thrown. When cascade: true, deletion works by querying the participantId field across all data, even if the agent was never registered.

Cascade Deletion: Users vs Agents

Featurecortex.users.delete()cortex.agents.unregister()
Filter keyuserIdparticipantId
ScopeAcross all layersAcross all memory spaces
PurposeGDPR complianceConvenience (cleanup)
RequiredLegal requirementOptional feature
RegistrationWorks even if no user profileWorks even if never registered
Query logicWHERE userId = XWHERE participantId = X
Orphan detectionIncludedIncluded
RollbackTransaction-likeTransaction-like

Why both exist:

  • Users: GDPR compliance requires deleting by userId (users exist across agents)
  • Agents: Convenience requires deleting by participantId (agents create data in spaces)

Key insight: An agent's participantId appears in the data they create (memories, conversations, facts). Cascade deletion queries this field, regardless of registration status.

Errors:

  • AgentValidationError('MISSING_AGENT_ID') - Agent ID not provided
  • AgentValidationError('EMPTY_AGENT_ID') - Agent ID is empty string
  • AgentValidationError('CONFLICTING_OPTIONS') - dryRun=true with verify=false
  • AgentCascadeDeletionError - Cascade deletion failed (all changes rolled back)
  • ConvexError('AGENT_NOT_REGISTERED') - Agent not in registry (when cascade=false)
Info

Note on AgentCascadeDeletionError: This error is thrown when cascade deletion fails partway through. The SDK automatically rolls back all changes made before the failure. The error includes a cause property with the original error for debugging.

Warning

This doesn't prevent using the agent ID again. It just removes registry entry and optionally deletes data.

Graph Integration:

Cascade deletion includes graph nodes if you provide a graph adapter:

import { CypherGraphAdapter } from "@cortex-platform/sdk/graph";

// Configure graph adapter (DIY in free SDK)
const graphAdapter = new CypherGraphAdapter();
await graphAdapter.connect({
uri: process.env.NEO4J_URI,
username: process.env.NEO4J_USER,
password: process.env.NEO4J_PASSWORD,
});

// Initialize Cortex with graph
const cortex = new Cortex({
convexUrl: process.env.CONVEX_URL,
graph: {
adapter: graphAdapter,
orphanCleanup: true,
},
});

// Now cascade includes graph with orphan detection!
await cortex.agents.unregister("agent-123", { cascade: true });
// Deletes from memories, conversations, facts, AND graph
// Includes orphan island detection

Agent Discovery

Querying by Capabilities

// Find agents with specific capabilities
const agents = await cortex.agents.search({
capabilities: ["troubleshooting", "billing"],
});

// Find agents with ALL capabilities
const specialists = await cortex.agents.search({
capabilities: ["troubleshooting", "billing", "escalation"],
capabilitiesMatch: "all", // Must have all three
});

Querying by Team

// Get all agents in a team
const team = await cortex.agents.search({
metadata: { team: "customer-success" },
});

team.forEach((agent) => {
console.log(`${agent.name}: ${agent.metadata.capabilities.join(", ")}`);
});

Active Agent Discovery

// Find recently active agents
const active = await cortex.agents.search({
sortBy: "lastActive",
sortOrder: "desc",
limit: 10,
});

console.log("Most recently active agents:");
active.forEach((agent) => {
console.log(`${agent.name} - last active: ${agent.stats?.lastActive}`);
});

Agent Statistics

Agent statistics are automatically included in the RegisteredAgent.stats property when you call get(), register(), or update().

Accessing Agent Stats

// Get agent with stats
const agent = await cortex.agents.get("support-agent");

if (agent) {
console.log({
totalMemories: agent.stats?.totalMemories,
totalConversations: agent.stats?.totalConversations,
totalFacts: agent.stats?.totalFacts,
memorySpacesActive: agent.stats?.memorySpacesActive,
lastActive: agent.stats?.lastActive, // Unix timestamp (ms)

// Agent metadata (from registration)
name: agent.name,
team: agent.metadata.team,
capabilities: agent.metadata.capabilities,
});
}
Note

PLANNED FEATURE: A dedicated cortex.analytics.getAgentStats() API for retrieving statistics for unregistered agents is planned for a future release. Currently, stats are only computed for registered agents via the agents API.

Info

Stats are computed on-demand and included in RegisteredAgent responses. For unregistered agents, use memory space queries to gather statistics.


Best Practices

1. Register Important Agents

// Production agents - register them
await cortex.agents.register({
id: "production-support-agent",
name: "Production Support Bot",
metadata: { environment: "production", team: "support" },
});

// Experimental agents - simple IDs are fine (Layer 3)
await cortex.memory.remember({
memorySpaceId: "experiment-123", // Not registered, still works
conversationId: "conv-456",
userMessage: "Test message",
agentResponse: "Test response",
userId: "test-user",
userName: "Tester",
});
// All layers work without registration!

2. Use Meaningful IDs

// Good agent IDs
"customer-support-agent";
"finance-analyst-agent";
"hr-recruiter-agent";

// Bad agent IDs
"agent1";
"bot";
"a";

3. Register When You Need Analytics

// Start simple (Layer 3 - all layers work)
await cortex.memory.remember({
memorySpaceId: "my-agent",
conversationId: "conv-123",
userMessage: "Hello",
agentResponse: "Hi",
userId: "user-1",
userName: "User",
});

// Later, when you need insights, register
await cortex.agents.register({
id: "my-agent", // Same ID that already has memories
name: "My Agent",
metadata: { team: "experimental" },
});

// Now agent includes stats and metadata
const agent = await cortex.agents.get("my-agent");
console.log(agent?.name); // "My Agent"
console.log(agent?.stats?.totalMemories); // Stats computed on-demand

4. Keep Registration Up to Date

// Update when capabilities change
await cortex.agents.update("support-agent", {
metadata: {
capabilities: ["troubleshooting", "billing", "refunds"], // Added 'refunds'
version: "2.2.0",
},
});

Migration: Simple to Registry

Agents can be used without registration, then registered later:

// Day 1: Simple usage (Layer 3 - ACID + Vector automatic)
await cortex.memory.remember({
memorySpaceId: "agent-1",
conversationId: "conv-1",
userMessage: "First message",
agentResponse: "First response",
userId: "user-1",
userName: "User",
});

await cortex.memory.remember({
memorySpaceId: "agent-1",
conversationId: "conv-1",
userMessage: "Second message",
agentResponse: "Second response",
userId: "user-1",
userName: "User",
});
// Works fine! ACID + Vector populated automatically

// Day 30: Register for better organization
await cortex.agents.register({
id: "agent-1", // Same ID that already has ACID + Vector data
name: "Sales Agent",
metadata: { team: "sales" },
});

// All existing memories/conversations now associated with registered agent
const agent = await cortex.agents.get("agent-1");
console.log(
`${agent?.stats?.totalMemories} memories (created before registration)`,
);
console.log(`${agent?.stats?.totalConversations} conversations`);

No data migration needed - registration is just metadata!


Universal Filters for Agents

// Filter patterns for agent operations
const filters = {
metadata: {
team: "support",
version: "2.0.0", // Exact match only (no operators like $gte)
},
capabilities: ["troubleshooting"],
registeredAfter: Date.parse("2025-01-01"), // Unix timestamp (ms)
};

// Search
await cortex.agents.search(filters);

// Count
await cortex.agents.count(filters);

// List
await cortex.agents.list({ ...filters, limit: 50 });

// Update many
await cortex.agents.updateMany(filters, { metadata: { updated: true } });

// Unregister many
await cortex.agents.unregisterMany(filters);

// Export
await cortex.agents.export({ filters, format: "json" });

Supported Filters:

  • tenantId - Multi-tenancy isolation (backend-applied)
  • metadata.* - Any metadata field (exact match only, client-side)
  • capabilities - Array of capabilities (client-side)
  • capabilitiesMatch - 'any' or 'all' (default: 'any')
  • name - Name search (partial match, case-insensitive, client-side)
  • status - Agent status (backend-applied)
  • registeredBefore/After - Unix timestamps in milliseconds
  • lastActiveBefore/After - Unix timestamps in milliseconds (client-side)
Info

Metadata filters use exact equality matching. Advanced query operators (like $gte, $in, etc.) are not supported. For complex queries, fetch all results and filter client-side.


Bulk Operations

updateMany()

Update multiple agents matching filters.

Signature:

cortex.agents.updateMany(
filters: AgentFilters,
updates: Partial<AgentRegistration>
): Promise<{ updated: number; agentIds: string[] }>

Parameters:

ParameterTypeRequiredDefaultDescription
filtersAgentFiltersYesFilter criteria to select agents (see list() for filter options)
updates.namestringNoNew display name
updates.descriptionstringNoNew description
updates.metadataRecord<string, unknown>NoNew metadata (replaces existing)
updates.configRecord<string, unknown>NoNew config (replaces existing)

Returns:

{
updated: number; // Number of agents successfully updated
agentIds: string[]; // IDs of updated agents
}

Example:

// Update all agents in a team
const result = await cortex.agents.updateMany(
{
metadata: { team: "support" },
},
{
metadata: {
trainingCompleted: true,
trainingDate: Date.now(), // Unix timestamp (ms)
},
},
);

console.log(`Updated ${result.updated} agents: ${result.agentIds.join(", ")}`);

// Upgrade all agents to new version
await cortex.agents.updateMany(
{
metadata: { version: "2.0.0" },
},
{
metadata: { version: "2.1.0" },
},
);

Errors:

  • AgentValidationError('MISSING_UPDATES') - No update fields provided
  • AgentValidationError('INVALID_METADATA_FORMAT') - Metadata is not a plain object
  • AgentValidationError('INVALID_CONFIG_FORMAT') - Config is not a plain object

unregisterMany()

Unregister multiple agents matching filters.

Signature:

cortex.agents.unregisterMany(
filters: AgentFilters,
options?: UnregisterAgentOptions
): Promise<{ deleted: number; agentIds: string[]; totalDataDeleted?: number }>

Parameters:

ParameterTypeRequiredDefaultDescription
filtersAgentFiltersYesFilter criteria for agents to unregister
options.cascadebooleanNofalseDelete all data where participantId = agentId
options.verifybooleanNotrueVerify deletion completeness
options.dryRunbooleanNofalsePreview what would be deleted

Returns:

{
deleted: number; // Number of agents unregistered
agentIds: string[]; // IDs of unregistered agents
totalDataDeleted?: number; // Total records deleted (if cascade=true)
}

Example:

// Unregister experimental agents (keep data)
const result = await cortex.agents.unregisterMany(
{ metadata: { environment: "experimental" } },
{ cascade: false },
);

console.log(`Unregistered ${result.deleted} experimental agents`);
console.log(`Agent IDs: ${result.agentIds.join(", ")}`);

// Unregister and cascade delete all agent data
const cascaded = await cortex.agents.unregisterMany(
{ status: "archived" },
{ cascade: true, verify: true },
);

console.log(`Deleted ${cascaded.totalDataDeleted} records across all layers`);

// Preview what would be deleted (dry run)
const preview = await cortex.agents.unregisterMany(
{ metadata: { team: "deprecated" } },
{ cascade: true, dryRun: true },
);

console.log(`Would unregister ${preview.agentIds.length} agents`);

export()

Export registered agents matching filters.

Signature:

cortex.agents.export(
options: ExportAgentsOptions
): Promise<ExportAgentsResult>

Parameters:

ParameterTypeRequiredDefaultDescription
options.filtersAgentFiltersNoFilter agents to export
options.format'json' | 'csv'YesOutput format
options.includeStatsbooleanNofalseInclude agent statistics
options.includeMetadatabooleanNotrueInclude full metadata

Returns:

interface ExportAgentsResult {
format: "json" | "csv";
data: string; // The exported data as a string
count: number; // Number of agents exported
exportedAt: number; // Timestamp when export was generated
}

Example:

// Export all agents as JSON
const result = await cortex.agents.export({
format: "json",
});

console.log(`Exported ${result.count} agents`);
fs.writeFileSync("all-agents.json", result.data);

// Export support team agents with statistics
const withStats = await cortex.agents.export({
filters: { metadata: { team: "support" } },
format: "json",
includeStats: true,
});

// Export as CSV for spreadsheet analysis
const csv = await cortex.agents.export({
filters: { status: "active" },
format: "csv",
includeMetadata: false, // Smaller CSV without metadata/config columns
});

// Write to file
fs.writeFileSync("active-agents.csv", csv.data);

CSV Format:

When format: "csv" is specified, the output includes these columns:

  • id, name, description, status, registeredAt, updatedAt, lastActive
  • metadata, config (if includeMetadata !== false, as JSON strings)
  • totalMemories, totalConversations, totalFacts, memorySpacesActive (if includeStats: true)

Errors:

  • AgentValidationError('MISSING_OPTIONS') - Options not provided
  • AgentValidationError('MISSING_FORMAT') - Format not specified
  • AgentValidationError('INVALID_FORMAT') - Invalid format value

Agent Lifecycle

Typical Lifecycle

Agent Lifecycle
1. Created (Implicit)

Just use memorySpaceId string — No registration needed

2. Registered (Optional)

cortex.agents.register() — Enables analytics, team organization, agent discovery

3. Configured

cortex.agents.configure() — Custom retention rules, team-specific settings

4. Unregistered (Optional)

cortex.agents.unregister() — Can still use with simple ID afterward


Cloud Mode Features (Planned)

Info

Cloud Mode (Under Development): These enhanced agent management features will be available when the managed Cortex Cloud service launches. The SDK provides full technical capability today.

Agent Analytics Dashboard

Visual insights per agent:

  • Memory growth over time
  • Conversation volume
  • User engagement
  • Performance metrics
  • Cost attribution

Team Management

  • Team-level dashboards
  • Cross-agent analytics
  • Resource allocation
  • Collaboration patterns

Auto-Discovery

Cortex Cloud can suggest:

  • When to split an agent (too many capabilities)
  • When to merge agents (overlapping domains)
  • Capability gaps in teams
  • Load balancing opportunities

Examples

Example 1: Multi-Agent System

// Register a team of specialized agents
const agents = [
{
id: "triage-agent",
name: "Triage Bot",
metadata: { team: "support", priority: 1 },
},
{
id: "technical-support-agent",
name: "Technical Support Specialist",
metadata: {
team: "support",
priority: 2,
capabilities: ["troubleshooting", "debugging"],
},
},
{
id: "billing-agent",
name: "Billing Specialist",
metadata: {
team: "support",
priority: 2,
capabilities: ["billing", "refunds"],
},
},
];

for (const agent of agents) {
await cortex.agents.register(agent);
}

// Find agent for specific task
const technicalAgents = await cortex.agents.search({
capabilities: ["troubleshooting"],
});

const selectedAgent = technicalAgents[0];
console.log(`Routing to: ${selectedAgent.name}`);

Example 2: Agent Fleet Management

// Get overview of all agents (list() returns RegisteredAgent[])
const allAgents = await cortex.agents.list({
sortBy: "registeredAt", // Valid: "name" | "registeredAt" | "lastActive"
sortOrder: "desc",
});

// Helper function for grouping
const groupBy = <T>(arr: T[], fn: (item: T) => string) =>
arr.reduce(
(acc, item) => {
const key = fn(item);
acc[key] = acc[key] || [];
acc[key].push(item);
return acc;
},
{} as Record<string, T[]>,
);

// Analyze fleet
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
const analysis = {
total: allAgents.length, // Array length, not .total property
byTeam: groupBy(allAgents, (a) => String(a.metadata.team || "unassigned")),
heavyUsers: allAgents.filter((a) => (a.stats?.totalMemories ?? 0) > 10000),
inactive: allAgents.filter(
(a) => (a.stats?.lastActive ?? 0) < thirtyDaysAgo, // Unix timestamp comparison
),
};

console.log("Fleet analysis:", analysis);

Graph-Lite Capabilities

Agents are graph nodes representing AI assistants or human operators:

Agent as Graph Node:

  • Owns memories (via agentId)
  • Handles contexts (via agentId)
  • Participates in A2A communication (via fromAgent/toAgent)

Edges:

  • agentId from Memories (agent → memories, 1-to-many)
  • agentId from Contexts (agent → contexts, 1-to-many)
  • A2A messages create agent-to-agent edges (SENT_TO/RECEIVED_FROM)

Graph Queries:

// Agent → Memories (via memory space)
const agentMemories = await cortex.memory.search({
memorySpaceId: "agent-1-space",
query: "*",
});

// Agent → Contexts (via memory space)
const agentContexts = await cortex.contexts.list({
memorySpaceId: "agent-1-space",
});

// Agent → Agent (collaboration graph via A2A)
const collaborations = await cortex.memory.search({
memorySpaceId: "agent-1-space",
query: "*",
sourceType: "a2a",
});

// Filter for outbound messages in application code
const outbound = collaborations.filter(
(m) => m.metadata?.direction === "outbound"
);
const partners = outbound.map((m) => m.metadata?.toAgent);

// Build agent network
const network = {
agent: "agent-1",
memories: agentMemories.length,
activeContexts: agentContexts.length,
collaborators: partners.filter(Boolean),
};

Performance: Agent-scoped queries are highly optimized (agentId is primary index). Typical queries: 10-30ms.

Learn more: Graph Capabilities


Summary

Agent Management is optional but powerful:

  • Start with simple string IDs (zero configuration)
  • All layers work immediately (ACID, Vector, Facts, Graph)
  • Register when you need organization and analytics
  • Configure retention and behavior per agent
  • Search and discover agents by capabilities
  • Same universal filter patterns as other operations

Registration provides:

  • Agent metadata (name, description, capabilities)
  • Team organization
  • Enhanced analytics with agent context
  • Custom configuration (version retention, etc.)
  • Agent discovery by capabilities

But isn't required - all core functionality (Layer 1/2/3) works with simple IDs!


Next Steps


Questions? Ask in GitHub Discussions.