Agent Management API
Last Updated: 2026-01-08
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.
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
| Feature | Memory Spaces | Agent Registry |
|---|---|---|
| Purpose | Isolation boundary | Metadata & analytics |
| Status | Active, required concept | Active, optional feature |
| When to use | Define data scope | Track agent info |
| Hive Mode | Required | Not required |
| Registration | Optional | Optional |
| Analytics | Manual queries | Auto-computed stats |
| Discovery | By type/participants | By capabilities/team |
| Cascade deletion | By memorySpaceId | By 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
participantIdfield 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:
participantIdfield 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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | — | Agent ID (must match ID used in memory ops) |
tenantId | string | No | — | Multi-tenancy: SaaS platform isolation |
name | string | Yes | — | Display name |
description | string | No | — | What this agent does |
metadata | object | No | — | Custom metadata including team, capabilities, version, owner, and any custom fields |
config | object | No | — | Agent 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)
}
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 providedAgentValidationError('EMPTY_AGENT_ID')- Agent ID is empty stringAgentValidationError('AGENT_ID_TOO_LONG')- Agent ID exceeds 256 charactersAgentValidationError('MISSING_AGENT_NAME')- Agent name not providedAgentValidationError('EMPTY_AGENT_NAME')- Agent name is empty stringAgentValidationError('INVALID_METADATA_FORMAT')- Metadata is not a plain objectConvexError('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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agentId | string | Yes | — | Agent ID to retrieve |
Returns:
RegisteredAgent- Complete agent registrationnull- 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 providedAgentValidationError('EMPTY_AGENT_ID')- Agent ID is empty string
Agents don't need to be registered to function. This just retrieves registry info if it exists.
search()
Find registered agents by metadata. This is an alias for list() with filters.
Signature:
cortex.agents.search(
filters: AgentFilters
): Promise<RegisteredAgent[]>
Note: search() requires a filters object. To list all agents without filters, use list() instead.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
tenantId | string | No | — | Multi-tenancy filter |
metadata | Record<string, unknown> | No | — | Filter by metadata fields (exact match) |
name | string | No | — | Search by name (partial match) |
capabilities | string[] | No | — | Has these capabilities |
capabilitiesMatch | 'any' | 'all' | No | 'any' | Match mode for capabilities |
status | 'active' | 'inactive' | 'archived' | No | — | Filter by agent status |
registeredAfter | number | No | — | Unix timestamp (ms) - filter by registration date |
registeredBefore | number | No | — | Unix timestamp (ms) - filter by registration date |
lastActiveAfter | number | No | — | Unix timestamp (ms) - filter by activity |
lastActiveBefore | number | No | — | Unix timestamp (ms) - filter by activity |
limit | number | No | 100 | Max results (1-1000) |
offset | number | No | 0 | Skip first N results |
sortBy | 'name' | 'registeredAt' | 'lastActive' | No | — | Sort field (validated but not yet implemented) |
sortOrder | 'asc' | 'desc' | No | — | Sort direction (validated but not yet implemented) |
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 valueAgentValidationError('INVALID_OFFSET_VALUE')- Invalid offset valueAgentValidationError('INVALID_STATUS')- Invalid status valueAgentValidationError('INVALID_TIMESTAMP_RANGE')- registeredAfter >= registeredBefore
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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
tenantId | string | No | — | Multi-tenancy filter (backend-applied) |
metadata | Record<string, unknown> | No | — | Filter by metadata (client-side) |
name | string | No | — | Search by name (client-side, partial match) |
capabilities | string[] | No | — | Has these capabilities (client-side) |
capabilitiesMatch | 'any' | 'all' | No | 'any' | Match mode |
status | 'active' | 'inactive' | 'archived' | No | — | Status filter (backend-applied) |
registeredAfter | number | No | — | Unix timestamp (ms) |
registeredBefore | number | No | — | Unix timestamp (ms) |
lastActiveAfter | number | No | — | Unix timestamp (ms) (client-side) |
lastActiveBefore | number | No | — | Unix timestamp (ms) (client-side) |
limit | number | No | 100 | Max results (max: 1000) |
offset | number | No | 0 | Skip first N results |
sortBy | 'name' | 'registeredAt' | 'lastActive' | No | — | Sort field |
sortOrder | 'asc' | 'desc' | No | — | Sort direction |
Pagination Limitation: The offset and limit parameters are applied at the database level BEFORE the following client-side filters:
metadatanamecapabilitieslastActiveAfter/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/limitwithstatusfilter only (backend-applied) - Use
offset/limitwithout 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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
filters.status | 'active' | 'inactive' | 'archived' | No | — | Filter by agent status |
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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agentId | string | Yes | — | Agent ID to check |
Returns:
boolean-trueif agent is registered,falseotherwise
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 providedAgentValidationError('EMPTY_AGENT_ID')- Agent ID is empty string
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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agentId | string | Yes | — | Agent to update |
updates | Partial<AgentRegistration> | Yes | — | Fields 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 providedAgentValidationError('MISSING_UPDATES')- No update fields providedAgentValidationError('EMPTY_AGENT_NAME')- Name is empty string (if provided)AgentValidationError('INVALID_METADATA_FORMAT')- Metadata is not a plain objectAgentValidationError('INVALID_STATUS')- Invalid status valueConvexError('AGENT_NOT_REGISTERED')- Agent not found in registry
configure()
Update agent-specific configuration.
Signature:
cortex.agents.configure(
agentId: string,
config: AgentConfig
): Promise<void>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agentId | string | Yes | — | Agent ID to configure |
config.memoryVersionRetention | number | No | — | -1 = unlimited, 1 = no history, 10 = default |
config.[key] | any | No | — | Custom 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 providedAgentValidationError('INVALID_CONFIG_FORMAT')- Config is not a plain objectAgentValidationError('EMPTY_CONFIG_OBJECT')- Config object is emptyConvexError('AGENT_NOT_REGISTERED')- Agent not found in registry
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.
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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agentId | string | Yes | — | Agent ID to unregister |
options.cascade | boolean | No | false | Delete all data where participantId = agentId |
options.verify | boolean | No | true | Verify deletion completeness |
options.dryRun | boolean | No | false | Preview 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):
- Collection: Query all memory spaces for records where participantId = agentId
- Backup: Create rollback snapshots
- Execution: Delete in reverse dependency order (facts → memories → conversations → graph → registration)
- 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`,
);
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
| Feature | cortex.users.delete() | cortex.agents.unregister() |
|---|---|---|
| Filter key | userId | participantId |
| Scope | Across all layers | Across all memory spaces |
| Purpose | GDPR compliance | Convenience (cleanup) |
| Required | Legal requirement | Optional feature |
| Registration | Works even if no user profile | Works even if never registered |
| Query logic | WHERE userId = X | WHERE participantId = X |
| Orphan detection | Included | Included |
| Rollback | Transaction-like | Transaction-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 providedAgentValidationError('EMPTY_AGENT_ID')- Agent ID is empty stringAgentValidationError('CONFLICTING_OPTIONS')- dryRun=true with verify=falseAgentCascadeDeletionError- Cascade deletion failed (all changes rolled back)ConvexError('AGENT_NOT_REGISTERED')- Agent not in registry (when cascade=false)
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.
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,
});
}
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.
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 millisecondslastActiveBefore/After- Unix timestamps in milliseconds (client-side)
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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
filters | AgentFilters | Yes | — | Filter criteria to select agents (see list() for filter options) |
updates.name | string | No | — | New display name |
updates.description | string | No | — | New description |
updates.metadata | Record<string, unknown> | No | — | New metadata (replaces existing) |
updates.config | Record<string, unknown> | No | — | New 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 providedAgentValidationError('INVALID_METADATA_FORMAT')- Metadata is not a plain objectAgentValidationError('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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
filters | AgentFilters | Yes | — | Filter criteria for agents to unregister |
options.cascade | boolean | No | false | Delete all data where participantId = agentId |
options.verify | boolean | No | true | Verify deletion completeness |
options.dryRun | boolean | No | false | Preview 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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.filters | AgentFilters | No | — | Filter agents to export |
options.format | 'json' | 'csv' | Yes | — | Output format |
options.includeStats | boolean | No | false | Include agent statistics |
options.includeMetadata | boolean | No | true | Include 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,lastActivemetadata,config(ifincludeMetadata !== false, as JSON strings)totalMemories,totalConversations,totalFacts,memorySpacesActive(ifincludeStats: true)
Errors:
AgentValidationError('MISSING_OPTIONS')- Options not providedAgentValidationError('MISSING_FORMAT')- Format not specifiedAgentValidationError('INVALID_FORMAT')- Invalid format value
Agent Lifecycle
Typical Lifecycle
Just use memorySpaceId string — No registration needed
cortex.agents.register() — Enables analytics, team organization, agent discovery
cortex.agents.configure() — Custom retention rules, team-specific settings
cortex.agents.unregister() — Can still use with simple ID afterward
Cloud Mode Features (Planned)
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:
agentIdfrom Memories (agent → memories, 1-to-many)agentIdfrom 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.