Context Operations API
Last Updated: 2026-01-09
Complete API reference for context chain management and multi-agent workflow coordination.
Overview
The Context Operations API (cortex.contexts.*) provides methods for managing hierarchical workflows where agents collaborate on complex tasks. Context chains track task delegation, shared state, and workflow evolution across multiple agents.
Key Characteristics:
- Hierarchical - Parent-child relationships for task decomposition
- Shared - All agents in the workflow can access the chain
- Versioned - Automatic status/data change tracking
- Conversation-linked - Optional links to originating ACID conversations
- GDPR-enabled - Optional
userIdfor cascade deletion - Multi-Tenant - Automatic
tenantIdisolation for SaaS platforms (auto-injected from AuthContext)
Relationship to Layers:
Context chains are a separate coordination entity that can reference all layers:
conversations., immutable., mutable.* — Contexts reference via conversationRef
vector.* — Memories can reference contexts via metadata.contextId
facts.* — Facts can be linked to contexts for workflow knowledge
contexts.* — Workflow/task hierarchy (links to conversations, used by memories)
Use Cases:
- Multi-agent task delegation
- Approval workflows
- Project/campaign coordination
- Hierarchical knowledge sharing
- Workflow state tracking
- Task decomposition
Core Operations
create()
Create a new context (root or child).
Signature:
cortex.contexts.create(
params: CreateContextParams
): Promise<Context>
v0.29.0+: Graph sync is now automatic when CORTEX_GRAPH_SYNC=true is set in your environment. The syncToGraph option has been removed from all APIs.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
purpose | string | Yes | — | What this context is for |
memorySpaceId | string | Yes | — | Memory space creating this context |
parentId | string | No | — | Parent context (omit for root) |
userId | string | No | — | User this relates to (GDPR-enabled) |
conversationRef | object | No | — | Conversation link with conversationId and optional messageIds |
data | Record<string, unknown> | No | — | Flexible key-value storage |
status | string | No | 'active' | One of: 'active', 'completed', 'cancelled', 'blocked' |
description | string | No | — | Detailed description |
Returns:
interface Context {
// Identity
_id: string; // Internal Convex ID
contextId: string; // User-facing context ID (e.g., "ctx-1234567890-abc123")
parentId?: string;
rootId: string; // Auto-computed (self if root)
// Purpose
purpose: string;
description?: string;
// Ownership
memorySpaceId: string; // Which memory space
userId?: string;
// Hierarchy
depth: number; // Auto-computed (0 = root)
childIds: string[]; // Direct children
participants: string[]; // All agents in this context
// Cross-space access grants (Collaboration Mode)
grantedAccess?: Array<{
memorySpaceId: string;
scope: string;
grantedAt: number;
}>;
// Conversation link
conversationRef?: {
conversationId: string;
messageIds?: string[];
};
// Data
data?: Record<string, unknown>;
metadata?: Record<string, unknown>;
// Status
status: "active" | "completed" | "cancelled" | "blocked";
// Timestamps (epoch milliseconds)
createdAt: number;
updatedAt: number;
completedAt?: number;
// Versioning (automatic)
version: number;
previousVersions?: ContextVersion[];
}
interface ContextVersion {
version: number;
status: string;
data?: Record<string, unknown>;
timestamp: number; // epoch milliseconds
updatedBy?: string; // Agent/memory space that made the change
}
Side Effects:
- If
parentIdprovided: Updates parent'schildIdsarray - If
parentIdprovided: InheritsrootIdand incrementsdepth - Adds
memorySpaceIdtoparticipantslist
Example 1: Create root context (linked to conversation)
// User makes request in conversation
const msg = await cortex.conversations.addMessage("conv-456", {
role: "user",
text: "I need a refund for $500",
userId: "user-123",
});
// Create root context for the workflow
const root = await cortex.contexts.create({
purpose: "Process customer refund request",
memorySpaceId: "supervisor-agent-space",
userId: "user-123", // GDPR-enabled
conversationRef: {
conversationId: "conv-456",
messageIds: [msg.id], // Links back to triggering message
},
data: {
importance: 85,
tags: ["refund", "customer-service", "ticket-456"],
amount: 500,
reason: "defective product",
ticketId: "TICKET-456",
},
});
console.log(root.contextId); // 'ctx-1234567890-abc123'
console.log(root.depth); // 0 (root)
console.log(root.rootId); // 'ctx-1234567890-abc123' (self)
Example 1b: Graph sync is automatic
// Graph sync happens automatically when CORTEX_GRAPH_SYNC=true
const root = await cortex.contexts.create({
purpose: "Process customer refund request",
memorySpaceId: "supervisor-agent-space",
userId: "user-123",
conversationRef: {
conversationId: "conv-456",
messageIds: [msg.id],
},
data: {
importance: 85,
tags: ["refund", "customer-service", "ticket-456"],
},
});
// Context is automatically synced to graph if CORTEX_GRAPH_SYNC=true
// Now queryable via graph for multi-hop queries
Example 2: Create child context (delegation)
// Delegate to finance agent
const child = await cortex.contexts.create({
purpose: "Approve and process $500 refund",
memorySpaceId: "finance-agent-space",
parentId: root.contextId, // Links to parent
userId: "user-123",
conversationRef: root.conversationRef, // Inherit conversation link
data: {
importance: 85,
tags: ["refund", "finance", "approval"],
amount: 500,
approvalRequired: true,
},
});
console.log(child.depth); // 1 (child of root)
console.log(child.rootId); // 'ctx-1234567890-abc123' (same as parent)
console.log(child.parentId); // root.contextId
Errors:
ContextsValidationError('MISSING_REQUIRED_FIELD')- Purpose or memorySpaceId is emptyContextsValidationError('WHITESPACE_ONLY')- Purpose contains only whitespaceContextsValidationError('INVALID_CONTEXT_ID_FORMAT')- parentId format is invalidConvexError('PARENT_NOT_FOUND')- Parent context doesn't exist (from backend)
See Also:
get()
Retrieve a context by ID with optional chain traversal.
Signature:
cortex.contexts.get(
contextId: string,
options?: GetOptions
): Promise<Context | ContextWithChain | null>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
contextId | string | Yes | — | The context ID to retrieve |
Options:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
includeChain | boolean | No | false | Include parent/children/siblings |
includeConversation | boolean | No | false | Fetch ACID conversation |
Returns:
// Default (includeChain: false)
Context | null;
// With includeChain: true
interface ContextChain {
current: Context; // This context
parent?: Context; // Parent context
root: Context; // Root of the chain
children: Context[]; // Direct children
siblings: Context[]; // Other children of same parent
ancestors: Context[]; // All ancestors up to root
descendants: Context[]; // All recursive descendants
depth: number; // Current depth
totalNodes: number; // Total nodes in the chain
}
// With includeConversation: true (extends ContextChain)
interface ContextWithConversation extends ContextChain {
conversation?: Conversation; // ACID conversation
triggerMessages?: Message[]; // Messages that started this context
}
Example 1: Get context only
const ctx = await cortex.contexts.get("ctx-1234567890-abc123");
console.log(ctx.purpose);
console.log(ctx.status);
console.log(ctx.data);
Example 2: Get with full chain
const chain = await cortex.contexts.get("ctx-1234567890-child", {
includeChain: true,
});
console.log("Current:", chain.current.purpose);
console.log("Parent:", chain.parent.purpose);
console.log("Root:", chain.root.purpose);
console.log(
"Siblings:",
chain.siblings.map((s) => s.purpose),
);
console.log("Depth:", chain.depth);
Example 3: Get with conversation context
const enriched = await cortex.contexts.get("ctx-1234567890-abc123", {
includeChain: true,
includeConversation: true,
});
// Context data
console.log("Workflow:", enriched.current.purpose);
// Original conversation that triggered this workflow
if (enriched.conversation) {
console.log("Original request:", enriched.triggerMessages[0].content);
console.log(
"Full conversation:",
enriched.conversation.messages.length,
"messages",
);
}
Errors:
ContextsValidationError('MISSING_REQUIRED_FIELD')- contextId is emptyContextsValidationError('INVALID_CONTEXT_ID_FORMAT')- contextId format is invalid- Returns
nullif context doesn't exist
update()
Update a context (status, data, etc.). Creates new version automatically.
Signature:
cortex.contexts.update(
contextId: string,
updates: ContextUpdate
): Promise<Context>
v0.29.0+: Graph sync is now automatic when CORTEX_GRAPH_SYNC=true is set in your environment. The syncToGraph option has been removed from all APIs.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
contextId | string | Yes | — | The context ID to update |
status | string | No | — | One of: 'active', 'completed', 'cancelled', 'blocked' |
data | Record<string, unknown> | No | — | Merges with existing data |
description | string | No | — | Updated description |
completedAt | number | No | — | Unix timestamp (ms) - auto-set when status='completed' |
Returns:
Context- Updated context with incremented version
Side Effects:
- Creates new version
- Updates
updatedAttimestamp - If status changes to 'completed', sets
completedAtif not provided
Example:
// Update status (creates v2)
await cortex.contexts.update("ctx-1234567890-abc123", {
status: "completed",
data: {
result: "success",
completedBy: "finance-agent",
confirmationNumber: "REF-789",
},
});
// Update data only (creates v3)
await cortex.contexts.update("ctx-1234567890-abc123", {
data: {
notes: "Customer satisfied with resolution",
},
});
// View version history
const ctx = await cortex.contexts.get("ctx-1234567890-abc123");
console.log(`Current status: ${ctx.status} (v${ctx.version})`);
ctx.previousVersions.forEach((v) => {
console.log(`v${v.version}: status=${v.status}, timestamp=${v.timestamp}`);
});
Errors:
ContextsValidationError('MISSING_REQUIRED_FIELD')- contextId is emptyContextsValidationError('INVALID_CONTEXT_ID_FORMAT')- contextId format is invalidContextsValidationError('INVALID_STATUS')- Status is not one of valid valuesContextsValidationError('INVALID_TYPE')- data is not an objectConvexError('CONTEXT_NOT_FOUND')- Context doesn't exist (from backend)
See Also:
delete()
Delete a context and optionally its descendants.
Signature:
cortex.contexts.delete(
contextId: string,
options?: DeleteContextOptions
): Promise<DeleteResult>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
contextId | string | Yes | — | The context ID to delete |
Options:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
cascadeChildren | boolean | No | false | Delete all descendants |
orphanChildren | boolean | No | false | If false and has children, error |
v0.29.0+: Graph deletion with orphan cleanup is now automatic when CORTEX_GRAPH_SYNC=true is set in your environment. The syncToGraph option has been removed from all APIs.
Returns:
interface DeleteResult {
deleted: boolean; // true if deletion succeeded
contextId: string;
descendantsDeleted: number; // Number of descendants deleted (0 if no cascade)
orphanedChildren?: string[]; // IDs of children promoted to roots (if orphanChildren: true)
}
Example:
// Delete single context (must have no children)
await cortex.contexts.delete("ctx-1234567890-abc123");
// Delete context and all descendants
const result = await cortex.contexts.delete("ctx-1234567890-root", {
cascadeChildren: true,
});
console.log(`Deleted: ${result.deleted}`); // true
console.log(`Descendants: ${result.descendantsDeleted}`);
// Allow orphaning (remove parent, promote children to roots)
const orphanResult = await cortex.contexts.delete("ctx-1234567890-parent", {
orphanChildren: true,
});
console.log("Orphaned children:", orphanResult.orphanedChildren);
Errors:
ContextsValidationError('MISSING_REQUIRED_FIELD')- contextId is emptyContextsValidationError('INVALID_CONTEXT_ID_FORMAT')- contextId format is invalidConvexError('CONTEXT_NOT_FOUND')- Context doesn't exist (from backend)ConvexError('HAS_CHILDREN')- Context has children and cascadeChildren=false (from backend)
search()
Search contexts with filters. This method is an alias for list() with filter support.
Signature:
cortex.contexts.search(
filter?: ListContextsFilter
): Promise<Context[]>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
memorySpaceId | string | No | — | Filter by memory space |
userId | string | No | — | Filter by user |
status | string | No | — | One of: 'active', 'completed', 'cancelled', 'blocked' |
parentId | string | No | — | Filter by parent context |
rootId | string | No | — | Filter by root context |
depth | number | No | — | Filter by hierarchy depth (0 = root) |
limit | number | No | 100 | Max results (max: 1000) |
Returns:
Context[]- Array of matching contexts
Example:
// Find active contexts for an agent
const active = await cortex.contexts.search({
memorySpaceId: "finance-agent-space",
status: "active",
});
// Find contexts for a user
const userContexts = await cortex.contexts.search({
userId: "user-123",
});
// Find all root contexts
const roots = await cortex.contexts.search({
depth: 0,
});
Errors:
ContextsValidationError- Filters are malformed
See Also:
list()
List contexts with filters.
Signature:
cortex.contexts.list(
filter?: ListContextsFilter
): Promise<Context[]>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
memorySpaceId | string | No | — | Filter by memory space |
userId | string | No | — | Filter by user |
status | string | No | — | One of: 'active', 'completed', 'cancelled', 'blocked' |
parentId | string | No | — | Filter by parent context |
rootId | string | No | — | Filter by root context |
depth | number | No | — | Filter by hierarchy depth (0 = root) |
limit | number | No | 100 | Max results (max: 1000) |
Returns:
Context[]- Array of matching contexts
Example:
// List all contexts (up to default limit)
const all = await cortex.contexts.list();
// List contexts for a memory space
const spaceContexts = await cortex.contexts.list({
memorySpaceId: "finance-agent-space",
limit: 50,
});
// List active root workflows
const activeRoots = await cortex.contexts.list({
status: "active",
depth: 0, // Root contexts only
});
Errors:
ContextsValidationError- Invalid filter values
count()
Count contexts matching filters.
Signature:
cortex.contexts.count(
filter?: CountContextsFilter
): Promise<number>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
memorySpaceId | string | No | — | Filter by memory space |
userId | string | No | — | Filter by user |
status | string | No | — | One of: 'active', 'completed', 'cancelled', 'blocked' |
Example:
// Total contexts
const total = await cortex.contexts.count();
// Count by status
const active = await cortex.contexts.count({ status: "active" });
const completed = await cortex.contexts.count({ status: "completed" });
// Count for a memory space
const agentContexts = await cortex.contexts.count({
memorySpaceId: "finance-agent-space",
});
// Count active contexts for a user
const userActive = await cortex.contexts.count({
userId: "user-123",
status: "active",
});
updateMany()
Bulk update contexts matching filters.
Signature:
cortex.contexts.updateMany(
filters: UpdateManyFilters,
updates: UpdateManyUpdates
): Promise<UpdateManyResult>
Filter Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
memorySpaceId | string | No | — | Filter by memory space |
userId | string | No | — | Filter by user |
status | string | No | — | One of: 'active', 'completed', 'cancelled', 'blocked' |
parentId | string | No | — | Filter by parent context |
rootId | string | No | — | Filter by root context |
Update Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
status | string | No | — | New status value |
data | Record<string, unknown> | No | — | Data to merge |
Returns:
interface UpdateManyResult {
updated: number;
contextIds: string[];
}
The following options are planned for a future release:
dryRun?: boolean- Preview what would be updated without making changeswouldUpdate?: number- Return field showing count for dryRun mode
Example:
// Archive completed contexts for a memory space
await cortex.contexts.updateMany(
{
memorySpaceId: "finance-agent-space",
status: "completed",
},
{
data: { archived: true },
},
);
// Boost priority for blocked contexts
await cortex.contexts.updateMany(
{
status: "blocked",
},
{
data: { importance: 95 },
},
);
deleteMany()
Bulk delete contexts matching filters.
Signature:
cortex.contexts.deleteMany(
filters: DeleteManyFilters,
options?: DeleteManyOptions
): Promise<DeleteManyResult>
Filter Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
memorySpaceId | string | No | — | Filter by memory space |
userId | string | No | — | Filter by user |
status | string | No | — | One of: 'active', 'completed', 'cancelled', 'blocked' |
completedBefore | number | No | — | Unix timestamp (ms) |
Options:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
cascadeChildren | boolean | No | false | Delete descendants |
Returns:
interface DeleteManyResult {
deleted: number;
contextIds: string[];
}
The following options are planned for a future release:
dryRun?: boolean- Preview what would be deleted without making changesrequireConfirmation?: boolean- Require explicit confirmation for large deletionsconfirmationThreshold?: number- Auto-confirm if below this countdescendantsDeleted?: number- Return field for cascade deletion countwouldDelete?: number- Return field for dryRun mode
Example:
// Delete old cancelled contexts (completed more than 90 days ago)
const result = await cortex.contexts.deleteMany({
status: "cancelled",
completedBefore: Date.now() - 90 * 24 * 60 * 60 * 1000,
});
console.log(`Deleted ${result.deleted} old contexts`);
// Delete all contexts in a memory space with cascade
await cortex.contexts.deleteMany(
{ memorySpaceId: "test-space" },
{ cascadeChildren: true },
);
export()
Export contexts to JSON or CSV.
Signature:
cortex.contexts.export(
filters?: ExportFilters,
options?: ExportOptions
): Promise<ExportResult>
Filter Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
memorySpaceId | string | No | — | Filter by memory space |
userId | string | No | — | Filter by user |
status | string | No | — | One of: 'active', 'completed', 'cancelled', 'blocked' |
Options:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
format | string | Yes | — | One of: 'json', 'csv' |
includeChain | boolean | No | — | Include full hierarchy in JSON |
includeVersionHistory | boolean | No | — | Include version history |
Returns:
interface ExportResult {
format: string;
data: string; // JSON string or CSV content
count: number;
exportedAt: number; // Unix timestamp (ms)
}
The following options are planned for a future release:
outputPath?: string- Write directly to file (currently returns data string)includeConversations?: boolean- Include linked ACID conversations
Example:
// Export user's workflows (GDPR compliance)
const result = await cortex.contexts.export(
{
userId: "user-123",
},
{
format: "json",
includeChain: true,
includeVersionHistory: true,
},
);
console.log(`Exported ${result.count} contexts`);
// Write to file manually if needed
fs.writeFileSync("exports/user-123-contexts.json", result.data);
Hierarchy Operations
getChain()
Get the complete context chain from a context ID.
Signature:
cortex.contexts.getChain(
contextId: string
): Promise<ContextChain>
Returns:
interface ContextChain {
current: Context; // This context
parent?: Context; // Parent context (if not root)
root: Context; // Root of the chain
children: Context[]; // Direct children
siblings: Context[]; // Other children of same parent
ancestors: Context[]; // All ancestors from root to parent
descendants: Context[]; // All recursive descendants
depth: number; // Current depth in hierarchy
totalNodes: number; // Total nodes in the chain
}
Example:
const chain = await cortex.contexts.getChain("ctx-1234567890-child");
console.log("Root:", chain.root.purpose);
console.log("Current:", chain.current.purpose);
console.log("Parent:", chain.parent.purpose);
console.log(
"Siblings:",
chain.siblings.map((s) => s.purpose),
);
console.log(
"Children:",
chain.children.map((c) => c.purpose),
);
console.log("Total workflow nodes:", chain.totalNodes);
getRoot()
Get the root context of a chain.
Signature:
cortex.contexts.getRoot(
contextId: string
): Promise<Context>
Example:
// From any context in the chain, get the root
const root = await cortex.contexts.getRoot("ctx-1234567890-nested");
console.log("Original purpose:", root.purpose);
console.log("Started by:", root.memorySpaceId);
console.log("Conversation:", root.conversationRef?.conversationId);
getChildren()
Get all direct children of a context.
Signature:
cortex.contexts.getChildren(
contextId: string,
options?: ChildrenOptions
): Promise<Context[]>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
contextId | string | Yes | — | The parent context ID |
Options:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
status | string | No | — | Filter by status |
recursive | boolean | No | false | Get all descendants |
Example:
// Get direct children
const children = await cortex.contexts.getChildren("ctx-1234567890-root");
// Get only active children
const activeChildren = await cortex.contexts.getChildren(
"ctx-1234567890-root",
{
status: "active",
},
);
// Get all descendants recursively
const allDescendants = await cortex.contexts.getChildren(
"ctx-1234567890-root",
{
recursive: true,
},
);
findOrphaned()
Find contexts whose parent no longer exists.
Signature:
cortex.contexts.findOrphaned(): Promise<Context[]>
Example:
// Find orphaned contexts
const orphaned = await cortex.contexts.findOrphaned();
console.log(`Found ${orphaned.length} orphaned contexts`);
// Clean them up
for (const ctx of orphaned) {
await cortex.contexts.delete(ctx.contextId);
}
Advanced Operations
addParticipant()
Add an agent to a context's participant list.
Signature:
cortex.contexts.addParticipant(
contextId: string,
participantId: string
): Promise<Context>
Example:
// Add agent to existing workflow
await cortex.contexts.addParticipant(
"ctx-1234567890-abc123",
"legal-agent-space",
);
const ctx = await cortex.contexts.get("ctx-1234567890-abc123");
console.log("Participants:", ctx.participants);
// ['supervisor-agent-space', 'finance-agent-space', 'legal-agent-space']
grantAccess()
Grant cross-space access to a context (Collaboration Mode). Allows agents from other memory spaces to access this context with specified permissions.
Signature:
cortex.contexts.grantAccess(
contextId: string,
targetMemorySpaceId: string,
scope: string
): Promise<Context>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
contextId | string | Yes | — | The context to grant access to |
targetMemorySpaceId | string | Yes | — | Memory space to grant access to |
scope | string | Yes | — | Access scope (e.g., 'read-only', 'context-only', 'full') |
Returns:
Context- Updated context with newgrantedAccessentry
Example:
// Grant read-only access to partner agent's memory space
await cortex.contexts.grantAccess(
"ctx-1234567890-abc123",
"partner-agent-space",
"read-only",
);
// Grant full context access for collaboration
await cortex.contexts.grantAccess(
"ctx-1234567890-abc123",
"collaborator-space",
"context-only",
);
// Check granted access
const ctx = await cortex.contexts.get("ctx-1234567890-abc123");
console.log("Granted access:", ctx.grantedAccess);
// [{ memorySpaceId: 'partner-agent-space', scope: 'read-only', grantedAt: 1699876543210 }]
Access Scopes:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
read-only | scope | No | — | Can read context data but not modify |
context-only | scope | No | — | Can read/write context data but not child contexts |
full | scope | No | — | Full access including creating child contexts |
Use Cases:
- Multi-team workflows where different agents need varying access levels
- Partner integrations with limited permissions
- Temporary access grants for specific tasks
Errors:
ContextsValidationError('MISSING_REQUIRED_FIELD')- contextId, targetMemorySpaceId, or scope is emptyContextsValidationError('INVALID_CONTEXT_ID_FORMAT')- contextId format is invalidConvexError('CONTEXT_NOT_FOUND')- Context doesn't exist (from backend)
removeParticipant()
Remove an agent from a context's participant list.
Signature:
cortex.contexts.removeParticipant(
contextId: string,
participantId: string
): Promise<Context>
getByConversation()
Get all contexts originating from a specific conversation.
Signature:
cortex.contexts.getByConversation(
conversationId: string
): Promise<Context[]>
Example:
// Find all workflows triggered by this conversation
const contexts = await cortex.contexts.getByConversation("conv-456");
console.log(`Conversation spawned ${contexts.length} workflows`);
contexts.forEach((ctx) => {
console.log(`- ${ctx.purpose} (${ctx.status})`);
});
Version Operations
getVersion()
Get a specific version of a context.
Signature:
cortex.contexts.getVersion(
contextId: string,
version: number
): Promise<ContextVersion | null>
Returns:
interface ContextVersion {
version: number;
status: string;
data?: Record<string, unknown>;
timestamp: number; // epoch milliseconds
updatedBy?: string;
}
Example:
// Get version 1 (original state)
const v1 = await cortex.contexts.getVersion("ctx-1234567890-abc123", 1);
console.log(`v1 status: ${v1.status}`);
console.log(`v1 data:`, v1.data);
getHistory()
Get all versions of a context.
Signature:
cortex.contexts.getHistory(
contextId: string
): Promise<ContextVersion[]>
Example:
const history = await cortex.contexts.getHistory("ctx-1234567890-abc123");
console.log(`Context has ${history.length} versions:`);
history.forEach((v) => {
console.log(`v${v.version} (${v.timestamp}): status=${v.status}`);
console.log(` Updated by: ${v.updatedBy}`);
});
getAtTimestamp()
Get context state at a specific point in time.
Signature:
cortex.contexts.getAtTimestamp(
contextId: string,
timestamp: Date
): Promise<ContextVersion | null>
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
contextId | string | Yes | — | The context ID |
timestamp | Date | Yes | — | The point in time to query |
Returns:
interface ContextVersion {
version: number;
status: string;
data?: Record<string, unknown>;
timestamp: number; // epoch milliseconds
updatedBy?: string;
}
Example:
// What was the status on October 20th?
const historical = await cortex.contexts.getAtTimestamp(
"ctx-1234567890-abc123",
new Date("2025-10-20T10:00:00Z"),
);
if (historical) {
console.log(`Status on Oct 20: ${historical.status}`);
console.log(`Version: ${historical.version}`);
}
Filter Reference
Currently Available Filters
The following filters are implemented and available across context operations:
// ListContextsFilter - used by list(), search()
interface ListContextsFilter {
memorySpaceId?: string; // Filter by memory space
userId?: string; // Filter by user
status?: "active" | "completed" | "cancelled" | "blocked";
parentId?: string; // Filter by parent context
rootId?: string; // Filter by root context
depth?: number; // Filter by hierarchy depth (0 = root)
limit?: number; // Default: 100, max: 1000
}
// CountContextsFilter - used by count()
interface CountContextsFilter {
memorySpaceId?: string;
userId?: string;
status?: "active" | "completed" | "cancelled" | "blocked";
}
// UpdateManyFilters - used by updateMany()
interface UpdateManyFilters {
memorySpaceId?: string;
userId?: string;
status?: "active" | "completed" | "cancelled" | "blocked";
parentId?: string;
rootId?: string;
}
// DeleteManyFilters - used by deleteMany()
interface DeleteManyFilters {
memorySpaceId?: string;
userId?: string;
status?: "active" | "completed" | "cancelled" | "blocked";
completedBefore?: number; // Unix timestamp (ms)
}
// ExportFilters - used by export()
interface ExportFilters {
memorySpaceId?: string;
userId?: string;
status?: "active" | "completed" | "cancelled" | "blocked";
}
Planned Filter Enhancements
The following filters are planned for a future release to provide more powerful querying:
// Planned additions to ContextFilters
interface PlannedContextFilters {
// Text search
purposeContains?: string; // Search by purpose text
// Data field queries
"data.importance"?: number | RangeQuery;
"data.tags"?: string[];
data?: Record<string, unknown>;
// Conversation link
"conversationRef.conversationId"?: string;
// Date ranges
createdBefore?: Date;
createdAfter?: Date;
updatedBefore?: Date;
updatedAfter?: Date;
completedAfter?: Date;
// Version filtering
version?: number | RangeQuery;
}
// Range query syntax (planned)
interface RangeQuery {
$gte?: number;
$lte?: number;
$gt?: number;
$lt?: number;
}
Operations and their filter support:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
list() | operation | No | — | memorySpaceId, userId, status, parentId, rootId, depth, limit |
search() | operation | No | — | memorySpaceId, userId, status, parentId, rootId, depth, limit |
count() | operation | No | — | memorySpaceId, userId, status |
updateMany() | operation | No | — | memorySpaceId, userId, status, parentId, rootId |
deleteMany() | operation | No | — | memorySpaceId, userId, status, completedBefore |
export() | operation | No | — | memorySpaceId, userId, status |
Real-World Patterns
Pattern 1: Task Decomposition with Conversation Links
// User request in conversation
const msg = await cortex.conversations.addMessage("conv-789", {
role: "user",
text: "I need the Q4 financial report by Friday",
userId: "user-cfo",
});
// Create root context linked to conversation
const root = await cortex.contexts.create({
purpose: "Build quarterly financial report",
memorySpaceId: "ceo-agent-space",
userId: "user-cfo",
conversationRef: {
conversationId: "conv-789",
messageIds: [msg.id],
},
data: {
quarter: "Q4",
year: 2025,
deadline: new Date("2025-10-30"),
importance: 90,
tags: ["financial", "report", "q4"],
},
});
// Decompose into subtasks (all inherit conversation link)
await cortex.contexts.create({
purpose: "Gather revenue data",
memorySpaceId: "finance-agent-space",
parentId: root.contextId,
userId: "user-cfo",
conversationRef: root.conversationRef, // Same source
});
await cortex.contexts.create({
purpose: "Compile expense reports",
memorySpaceId: "accounting-agent-space",
parentId: root.contextId,
userId: "user-cfo",
conversationRef: root.conversationRef,
});
// All agents can trace back to original user request
const chain = await cortex.contexts.get("ctx-1234567890-finance", {
includeChain: true,
includeConversation: true,
});
console.log("Original request:", chain.triggerMessages[0].content);
Pattern 2: Workflow Status Tracking
// Check workflow completion
async function checkWorkflowComplete(rootContextId: string) {
const chain = await cortex.contexts.getChain(rootContextId);
// Check if all children completed
const allComplete = chain.children.every((c) => c.status === "completed");
if (allComplete && chain.current.status !== "completed") {
// Mark root as complete
await cortex.contexts.update(rootContextId, {
status: "completed",
completedAt: new Date(),
});
return true;
}
return false;
}
Pattern 3: Context-Based Memory Storage
Link memories to contexts for workflow knowledge:
// Store memory with context reference using Layer 4 API (recommended)
await cortex.memory.remember({
memorySpaceId: "finance-agent-space",
userId: "user-123",
agentId: "finance-agent",
userMessage: "Please process the refund",
agentResponse: "Approved $500 refund for defective product",
metadata: {
importance: 85,
tags: ["refund", "approval", "finance"],
contextId: "ctx-1234567890-refund", // Link to context!
},
});
// Later: Search memories (contextId tracking is application-level)
const results = await cortex.memory.recall({
memorySpaceId: "finance-agent-space",
userId: "user-123",
query: "refund approval",
});
Context tracking in memories is an application-level pattern. Store the contextId in memory metadata and filter/search as needed.
Pattern 4: Multi-Agent Coordination
// Supervisor creates workflow
const workflow = await cortex.contexts.create({
purpose: "Launch marketing campaign",
memorySpaceId: "supervisor-agent-space",
data: {
campaignName: "Q4 Sale",
importance: 80,
tags: ["marketing", "campaign", "q4"],
},
});
// Marketing agent checks in
await cortex.contexts.addParticipant(
workflow.contextId,
"marketing-agent-space",
);
// Finance agent checks in
await cortex.contexts.addParticipant(workflow.contextId, "finance-agent-space");
// All agents can see who's involved
const ctx = await cortex.contexts.get(workflow.contextId);
console.log("Team:", ctx.participants);
// ['supervisor-agent-space', 'marketing-agent-space', 'finance-agent-space']
Best Practices
1. Descriptive Purposes
// Avoid: Vague
await cortex.contexts.create({
purpose: "Process request",
});
// Better: Specific
await cortex.contexts.create({
purpose: "Process $500 refund for defective product (ticket #456)",
data: {
amount: 500,
ticketId: "TICKET-456",
},
});
2. Link to Originating Conversations
// Always link when context comes from user conversation
await cortex.contexts.create({
purpose: "Handle support ticket",
conversationRef: {
conversationId: "conv-456",
messageIds: ["msg-initial-request"],
},
});
// Benefits:
// - Audit trail back to user request
// - Can retrieve full conversation context
// - Compliance and debugging
3. Use Importance for Prioritization
// Set importance (0-100)
await cortex.contexts.create({
purpose: "Critical security issue",
data: { importance: 100 }, // Maximum priority
});
// Find active contexts and filter by importance (application-level)
const active = await cortex.contexts.search({ status: "active" });
const urgent = active.filter((ctx) => (ctx.data?.importance ?? 0) >= 90);
4. Update Status Promptly
// When work starts
await cortex.contexts.update(contextId, {
status: "active",
data: { startedAt: new Date() },
});
// When blocked
await cortex.contexts.update(contextId, {
status: "blocked",
data: { blockedReason: "Waiting for API access" },
});
// When complete
await cortex.contexts.update(contextId, {
status: "completed",
completedAt: new Date(),
});
5. Clean Up Old Contexts
// Archive completed contexts (filter by status, then update)
await cortex.contexts.updateMany(
{ status: "completed" },
{ data: { archived: true } },
);
// Delete old cancelled contexts (completedBefore supported by deleteMany)
await cortex.contexts.deleteMany({
status: "cancelled",
completedBefore: Date.now() - 180 * 24 * 60 * 60 * 1000,
});
Graph-Lite Capabilities
Contexts form a hierarchical graph (tree structure) for workflow coordination:
Context as Graph Node:
- Represents a workflow task or sub-task
- Part of directed acyclic graph (DAG) structure
Edges:
parentId→ Parent context (upward edge)childIds→ Child contexts (downward edges)conversationRef→ Originating conversation (source edge)userId→ Related usermemorySpaceId→ Owning memory space
Graph Operations:
// Tree traversal (built-in)
const chain = await cortex.contexts.get(contextId, {
includeChain: true // ← Multi-hop graph walk
});
// Nodes accessed in this graph query:
chain.current // This node
chain.parent // 1-hop up
chain.root // N-hops to root
chain.ancestors // All nodes on path to root
chain.children // 1-hop down
chain.siblings // Lateral (parent's other children)
chain.descendants // All nodes below (recursive)
// Example graph structure:
Root (depth=0)
├── Child-1 (depth=1)
│ ├── Grandchild-1 (depth=2)
│ └── Grandchild-2 (depth=2)
├── Child-2 (depth=1)
└── Child-3 (depth=1)
└── Grandchild-3 (depth=2)
Graph Queries:
// Find all root workflows (depth=0 nodes)
const roots = await cortex.contexts.search({ depth: 0 });
// Find all child nodes of a parent
const children = await cortex.contexts.search({ parentId: "ctx-001" });
// Find entire workflow tree (all nodes with same rootId)
const workflowTree = await cortex.contexts.search({ rootId: "ctx-root" });
// Find workflows triggered by conversation (traverse conversationRef edge)
const workflowsFromConvo = await cortex.contexts.search({
"conversationRef.conversationId": "conv-456",
});
Performance:
- Context hierarchy traversal: 50-150ms for typical depth (1-5 levels)
- Entire workflow tree: 100-300ms for <50 contexts
- Deep hierarchies (>10 levels): Consider graph database for <100ms queries
Learn more: Graph Capabilities
Error Reference
Client-Side Validation Errors (ContextsValidationError)
These errors are thrown by the SDK before the request reaches the backend:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
MISSING_REQUIRED_FIELD | error | No | — | Required field is missing (null, undefined, or empty string) |
WHITESPACE_ONLY | error | No | — | Field contains only whitespace (purpose has no actual content) |
INVALID_CONTEXT_ID_FORMAT | error | No | — | Context ID format is invalid (must match ctx-{timestamp}-{random}) |
INVALID_CONVERSATION_ID_FORMAT | error | No | — | Conversation ID format invalid (must start with conv-) |
INVALID_STATUS | error | No | — | Status value is invalid (not one of: active/completed/cancelled/blocked) |
INVALID_FORMAT | error | No | — | Export format is invalid (not one of: json/csv) |
INVALID_RANGE | error | No | — | Numeric value out of range (depth < 0, limit <= 0 or > 1000, version < 1) |
INVALID_TYPE | error | No | — | Type is incorrect (data is not an object, etc.) |
INVALID_DATE | error | No | — | Date is invalid (not a valid Date object) |
EMPTY_UPDATES | error | No | — | Updates object is empty (must include at least one field to update) |
EMPTY_FILTERS | error | No | — | Filters object is empty (must include at least one filter) |
EMPTY_ARRAY | error | No | — | Array is empty (required array has no elements) |
Backend Errors (ConvexError)
These errors are returned from the Convex backend:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
CONTEXT_NOT_FOUND | error | No | — | Context doesn't exist (invalid or non-existent contextId) |
PARENT_NOT_FOUND | error | No | — | Parent context doesn't exist (invalid parentId in create()) |
HAS_CHILDREN | error | No | — | Context has children (can't delete without cascade/orphan) |
See Also:
Graph Integration
Context chains integrate with graph databases for advanced queries:
// Create context (graph sync is automatic when CORTEX_GRAPH_SYNC=true)
await cortex.contexts.create(params);
// Query via graph for multi-hop traversal
const hierarchy = await graphAdapter.query(
`
MATCH (root:Context {contextId: $contextId})
MATCH path = (root)<-[:CHILD_OF*0..10]-(descendants:Context)
RETURN descendants
ORDER BY descendants.depth
`,
{ contextId: root.contextId },
);
// Result: Entire hierarchy in single query (<10ms)!
Graph integration provides 3.8x faster queries for deep hierarchies (7+ levels).
See Graph Operations API for complete graph integration reference.
Next Steps
Questions? Ask in GitHub Discussions.