Skip to main content

Context Operations API

Info

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 userId for cascade deletion
  • Multi-Tenant - Automatic tenantId isolation for SaaS platforms (auto-injected from AuthContext)

Relationship to Layers:

Context chains are a separate coordination entity that can reference all layers:

Context API Layer Relationships
Layer 1: ACID Stores

conversations., immutable., mutable.* — Contexts reference via conversationRef

Layer 2: Vector Index

vector.* — Memories can reference contexts via metadata.contextId

Layer 3: Facts Store

facts.* — Facts can be linked to contexts for workflow knowledge

Coordination Layer

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>
Info

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:

ParameterTypeRequiredDefaultDescription
purposestringYesWhat this context is for
memorySpaceIdstringYesMemory space creating this context
parentIdstringNoParent context (omit for root)
userIdstringNoUser this relates to (GDPR-enabled)
conversationRefobjectNoConversation link with conversationId and optional messageIds
dataRecord<string, unknown>NoFlexible key-value storage
statusstringNo'active'One of: 'active', 'completed', 'cancelled', 'blocked'
descriptionstringNoDetailed 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 parentId provided: Updates parent's childIds array
  • If parentId provided: Inherits rootId and increments depth
  • Adds memorySpaceId to participants list

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 empty
  • ContextsValidationError('WHITESPACE_ONLY') - Purpose contains only whitespace
  • ContextsValidationError('INVALID_CONTEXT_ID_FORMAT') - parentId format is invalid
  • ConvexError('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:

ParameterTypeRequiredDefaultDescription
contextIdstringYesThe context ID to retrieve

Options:

ParameterTypeRequiredDefaultDescription
includeChainbooleanNofalseInclude parent/children/siblings
includeConversationbooleanNofalseFetch 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 empty
  • ContextsValidationError('INVALID_CONTEXT_ID_FORMAT') - contextId format is invalid
  • Returns null if 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>
Info

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:

ParameterTypeRequiredDefaultDescription
contextIdstringYesThe context ID to update
statusstringNoOne of: 'active', 'completed', 'cancelled', 'blocked'
dataRecord<string, unknown>NoMerges with existing data
descriptionstringNoUpdated description
completedAtnumberNoUnix timestamp (ms) - auto-set when status='completed'

Returns:

  • Context - Updated context with incremented version

Side Effects:

  • Creates new version
  • Updates updatedAt timestamp
  • If status changes to 'completed', sets completedAt if 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 empty
  • ContextsValidationError('INVALID_CONTEXT_ID_FORMAT') - contextId format is invalid
  • ContextsValidationError('INVALID_STATUS') - Status is not one of valid values
  • ContextsValidationError('INVALID_TYPE') - data is not an object
  • ConvexError('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:

ParameterTypeRequiredDefaultDescription
contextIdstringYesThe context ID to delete

Options:

ParameterTypeRequiredDefaultDescription
cascadeChildrenbooleanNofalseDelete all descendants
orphanChildrenbooleanNofalseIf false and has children, error
Info

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 empty
  • ContextsValidationError('INVALID_CONTEXT_ID_FORMAT') - contextId format is invalid
  • ConvexError('CONTEXT_NOT_FOUND') - Context doesn't exist (from backend)
  • ConvexError('HAS_CHILDREN') - Context has children and cascadeChildren=false (from backend)

Search contexts with filters. This method is an alias for list() with filter support.

Signature:

cortex.contexts.search(
filter?: ListContextsFilter
): Promise<Context[]>

Parameters:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNoFilter by memory space
userIdstringNoFilter by user
statusstringNoOne of: 'active', 'completed', 'cancelled', 'blocked'
parentIdstringNoFilter by parent context
rootIdstringNoFilter by root context
depthnumberNoFilter by hierarchy depth (0 = root)
limitnumberNo100Max 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:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNoFilter by memory space
userIdstringNoFilter by user
statusstringNoOne of: 'active', 'completed', 'cancelled', 'blocked'
parentIdstringNoFilter by parent context
rootIdstringNoFilter by root context
depthnumberNoFilter by hierarchy depth (0 = root)
limitnumberNo100Max 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:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNoFilter by memory space
userIdstringNoFilter by user
statusstringNoOne 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:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNoFilter by memory space
userIdstringNoFilter by user
statusstringNoOne of: 'active', 'completed', 'cancelled', 'blocked'
parentIdstringNoFilter by parent context
rootIdstringNoFilter by root context

Update Parameters:

ParameterTypeRequiredDefaultDescription
statusstringNoNew status value
dataRecord<string, unknown>NoData to merge

Returns:

interface UpdateManyResult {
updated: number;
contextIds: string[];
}
Planned Features

The following options are planned for a future release:

  • dryRun?: boolean - Preview what would be updated without making changes
  • wouldUpdate?: 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:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNoFilter by memory space
userIdstringNoFilter by user
statusstringNoOne of: 'active', 'completed', 'cancelled', 'blocked'
completedBeforenumberNoUnix timestamp (ms)

Options:

ParameterTypeRequiredDefaultDescription
cascadeChildrenbooleanNofalseDelete descendants

Returns:

interface DeleteManyResult {
deleted: number;
contextIds: string[];
}
Planned Features

The following options are planned for a future release:

  • dryRun?: boolean - Preview what would be deleted without making changes
  • requireConfirmation?: boolean - Require explicit confirmation for large deletions
  • confirmationThreshold?: number - Auto-confirm if below this count
  • descendantsDeleted?: number - Return field for cascade deletion count
  • wouldDelete?: 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:

ParameterTypeRequiredDefaultDescription
memorySpaceIdstringNoFilter by memory space
userIdstringNoFilter by user
statusstringNoOne of: 'active', 'completed', 'cancelled', 'blocked'

Options:

ParameterTypeRequiredDefaultDescription
formatstringYesOne of: 'json', 'csv'
includeChainbooleanNoInclude full hierarchy in JSON
includeVersionHistorybooleanNoInclude version history

Returns:

interface ExportResult {
format: string;
data: string; // JSON string or CSV content
count: number;
exportedAt: number; // Unix timestamp (ms)
}
Planned Features

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:

ParameterTypeRequiredDefaultDescription
contextIdstringYesThe parent context ID

Options:

ParameterTypeRequiredDefaultDescription
statusstringNoFilter by status
recursivebooleanNofalseGet 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:

ParameterTypeRequiredDefaultDescription
contextIdstringYesThe context to grant access to
targetMemorySpaceIdstringYesMemory space to grant access to
scopestringYesAccess scope (e.g., 'read-only', 'context-only', 'full')

Returns:

  • Context - Updated context with new grantedAccess entry

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:

ParameterTypeRequiredDefaultDescription
read-onlyscopeNoCan read context data but not modify
context-onlyscopeNoCan read/write context data but not child contexts
fullscopeNoFull 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 empty
  • ContextsValidationError('INVALID_CONTEXT_ID_FORMAT') - contextId format is invalid
  • ConvexError('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:

ParameterTypeRequiredDefaultDescription
contextIdstringYesThe context ID
timestampDateYesThe 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

Planned Features

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:

ParameterTypeRequiredDefaultDescription
list()operationNomemorySpaceId, userId, status, parentId, rootId, depth, limit
search()operationNomemorySpaceId, userId, status, parentId, rootId, depth, limit
count()operationNomemorySpaceId, userId, status
updateMany()operationNomemorySpaceId, userId, status, parentId, rootId
deleteMany()operationNomemorySpaceId, userId, status, completedBefore
export()operationNomemorySpaceId, userId, status

Real-World Patterns

// 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",
});
Note

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",
},
});
// 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 user
  • memorySpaceId → 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:

ParameterTypeRequiredDefaultDescription
MISSING_REQUIRED_FIELDerrorNoRequired field is missing (null, undefined, or empty string)
WHITESPACE_ONLYerrorNoField contains only whitespace (purpose has no actual content)
INVALID_CONTEXT_ID_FORMATerrorNoContext ID format is invalid (must match ctx-{timestamp}-{random})
INVALID_CONVERSATION_ID_FORMATerrorNoConversation ID format invalid (must start with conv-)
INVALID_STATUSerrorNoStatus value is invalid (not one of: active/completed/cancelled/blocked)
INVALID_FORMATerrorNoExport format is invalid (not one of: json/csv)
INVALID_RANGEerrorNoNumeric value out of range (depth < 0, limit <= 0 or > 1000, version < 1)
INVALID_TYPEerrorNoType is incorrect (data is not an object, etc.)
INVALID_DATEerrorNoDate is invalid (not a valid Date object)
EMPTY_UPDATESerrorNoUpdates object is empty (must include at least one field to update)
EMPTY_FILTERSerrorNoFilters object is empty (must include at least one filter)
EMPTY_ARRAYerrorNoArray is empty (required array has no elements)

Backend Errors (ConvexError)

These errors are returned from the Convex backend:

ParameterTypeRequiredDefaultDescription
CONTEXT_NOT_FOUNDerrorNoContext doesn't exist (invalid or non-existent contextId)
PARENT_NOT_FOUNDerrorNoParent context doesn't exist (invalid parentId in create())
HAS_CHILDRENerrorNoContext 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)!
Performance

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.