Skip to main content

Graph Operations API

Info

Last Updated: 2026-01-08 | Version: v0.24.0 | Status: Production Ready

Complete API reference for graph database integration, multi-hop queries, and knowledge graph operations.

Release 0.37 graph scope​

Import graph APIs from the public @cortexmemory/sdk root export. SDK/provider automatic configuration shares resolveGraphConnection; Python mirrors the provider-specific selection. Explicit off performs no driver work; missing/ambiguous enabled credentials and failed connection are errors. SDK init/install and template init share optional graph setup, with separate project-owned start/stop/status; stop preserves volumes and graph off needs no Docker.

Managed version 2 operations use physical source identity and exact tenant/user/space scope. They preserve legacy/manual namespaces. Current managed labels use CortexV2* and captured physical source keys; unprefixed low-level query examples describe manual/legacy graphs and do not provide tenant authorization. Administrator rebuild and erasure use fixed createGraphRebuildTransport/createGraphErasureTransport factories with explicit URL/deployment key, not caller-supplied identity. User/space cascade additionally needs its separate erasure transport and matching graph participant, with residual/partial/unknown receipts. The low-level adapter clearDatabase below is an explicit database-wide operation and is not used by scoped SDK deletion or project graph stop.

Overview​

The Graph Operations API (@cortexmemory/sdk/graph) provides advanced graph database capabilities for Cortex, enabling multi-hop relationship queries, knowledge discovery, and cross-layer context enrichment.

Key Characteristics:

  • Optional - Graph integration is completely optional
  • Multi-Database - Works with Neo4j, Memgraph (single codebase)
  • Real-Time Sync - Reactive worker for automatic synchronization
  • Orphan-Safe - Sophisticated deletion with circular reference protection
  • Cross-Layer - Connects L1a, L2, L3, L4 via relationships
  • Backward Compatible - Existing code works unchanged
  • Multi-Tenant - Full tenantId support for SaaS isolation (NEW)

When to Use Graph:

  • Deep context chains (5+ levels)
  • Knowledge graphs with entity relationships
  • Multi-hop reasoning requirements
  • Provenance and audit trail needs
  • Complex multi-agent coordination
  • Large-scale fact databases (100s+ facts)

Setup & Configuration​

Installation​

Terminal
$ 
# Install Neo4j driver
npm install neo4j-driver

# Start graph database (Docker)
docker-compose -f docker-compose.graph.yml up -d neo4j

See Graph Database Integration Guide for complete setup.

Configuration​

import { Cortex } from "@cortexmemory/sdk";
import {
CypherGraphAdapter,
initializeGraphSchema,
} from "@cortexmemory/sdk";

// 1. Setup graph adapter
const graphAdapter = new CypherGraphAdapter();
await graphAdapter.connect({
uri: "bolt://localhost:7687",
username: "neo4j",
password: "your-password",
});

// 2. Initialize schema (one-time)
await initializeGraphSchema(graphAdapter);

// 3. Initialize Cortex with graph
const cortex = new Cortex({
convexUrl: process.env.CONVEX_URL!,
graph: {
adapter: graphAdapter,
orphanCleanup: true, // Enable orphan detection (default: true)
autoSync: true, // Auto-start sync worker (default: false)
syncWorkerOptions: {
batchSize: 100,
retryAttempts: 3,
verbose: false,
},
},
});

Core Operations​

GraphAdapter​

Low-level graph database operations.

connect()​

Connect to graph database.

const adapter = new CypherGraphAdapter();
await adapter.connect({
uri: "bolt://localhost:7687",
username: "neo4j",
password: "password",
});

createNode()​

Create a node in the graph.

const nodeId = await adapter.createNode({
label: "Memory",
properties: {
memoryId: "mem-123",
content: "User prefers dark mode",
importance: 85,
},
});

createEdge()​

Create a relationship between nodes.

const edgeId = await adapter.createEdge({
type: "REFERENCES",
from: memoryNodeId,
to: conversationNodeId,
properties: {
messageIds: ["msg-1", "msg-2"],
createdAt: Date.now(),
},
});

query()​

Execute Cypher query.

const result = await adapter.query(
`
MATCH (m:Memory)-[:REFERENCES]->(c:Conversation)
WHERE m.importance >= $minImportance
RETURN m, c
LIMIT 10
`,
{ minImportance: 80 },
);

for (const record of result.records) {
console.log(record.m.properties, record.c.properties);
}

traverse()​

Multi-hop graph traversal.

const connected = await adapter.traverse({
startId: nodeId,
relationshipTypes: ["CHILD_OF", "PARENT_OF"],
maxDepth: 5,
direction: "BOTH",
});

console.log(`Found ${connected.length} connected nodes`);

findPath()​

Find shortest path between nodes.

const path = await adapter.findPath({
fromId: aliceNodeId,
toId: bobNodeId,
maxHops: 10,
});

if (path) {
console.log(`Path length: ${path.length} hops`);
console.log(`Nodes: ${path.nodes.map((n) => n.label).join(" → ")}`);
console.log(
`Relationships: ${path.relationships.map((r) => r.type).join(" → ")}`,
);
}

disconnect()​

Close connection to graph database.

await adapter.disconnect();
// Connection closed, resources freed

isConnected()​

Test if adapter is connected to the database.

const connected = await adapter.isConnected();
if (!connected) {
console.log("Reconnecting to graph database...");
await adapter.connect(config);
}

mergeNode()​

Create or update a node using MERGE semantics (idempotent).

Added in v0.19.1 for resilient, idempotent operations. Safe for concurrent operations and re-running scripts.

// Idempotent - safe to call multiple times
const nodeId = await adapter.mergeNode(
{
label: "MemorySpace",
properties: {
memorySpaceId: "space-123",
name: "Main Space",
type: "personal",
createdAt: Date.now(),
},
},
{ memorySpaceId: "space-123" }, // Match properties
);
// Creates if not exists, updates if exists

getNode()​

Get a node by its internal graph ID.

const node = await adapter.getNode(nodeId);
if (node) {
console.log("Label:", node.label);
console.log("Properties:", node.properties);
}

updateNode()​

Update a node's properties.

await adapter.updateNode(nodeId, {
importance: 95,
updatedAt: Date.now(),
});

deleteNode()​

Delete a node from the graph.

// Delete node and all connected relationships
await adapter.deleteNode(nodeId, true); // detach = true

// Delete node only (fails if has relationships)
await adapter.deleteNode(nodeId, false);

findNodes()​

Find nodes by label and optional properties.

// Find all Memory nodes
const allMemories = await adapter.findNodes("Memory");

// Find Memory nodes with specific properties
const importantMemories = await adapter.findNodes(
"Memory",
{ importance: 100 },
50, // limit
);

// Find specific entity by ID property
const entity = await adapter.findNodes("Entity", { name: "Alice" }, 1);

deleteEdge()​

Delete an edge (relationship) by its ID.

await adapter.deleteEdge(edgeId);

findEdges()​

Find edges by type and optional properties.

// Find all REFERENCES relationships
const references = await adapter.findEdges("REFERENCES");

// Find edges with specific properties
const recentEdges = await adapter.findEdges(
"MENTIONS",
{ role: "subject" },
100, // limit
);

batchWrite()​

Execute multiple operations in a single transaction.

await adapter.batchWrite([
{
type: "CREATE_NODE",
data: {
label: "Entity",
properties: { name: "Alice", type: "person" },
},
},
{
type: "CREATE_NODE",
data: {
label: "Entity",
properties: { name: "Acme Corp", type: "company" },
},
},
{
type: "CREATE_EDGE",
data: {
type: "WORKS_AT",
from: aliceNodeId,
to: acmeNodeId,
properties: { since: 2020 },
},
},
]);
// All operations succeed or fail together

Supported operation types:

  • CREATE_NODE - Create a new node
  • UPDATE_NODE - Update node properties
  • DELETE_NODE - Delete a node
  • CREATE_EDGE - Create a relationship
  • DELETE_EDGE - Delete a relationship

countNodes()​

Count nodes in the database.

// Count all nodes
const total = await adapter.countNodes();
console.log(`Total nodes: ${total}`);

// Count nodes by label
const memoryCount = await adapter.countNodes("Memory");
const factCount = await adapter.countNodes("Fact");
console.log(`Memories: ${memoryCount}, Facts: ${factCount}`);

countEdges()​

Count edges (relationships) in the database.

// Count all edges
const total = await adapter.countEdges();
console.log(`Total relationships: ${total}`);

// Count edges by type
const references = await adapter.countEdges("REFERENCES");
const mentions = await adapter.countEdges("MENTIONS");
console.log(`References: ${references}, Mentions: ${mentions}`);

clearDatabase()​

Clear all data from the database.

Danger

This operation deletes ALL nodes and relationships. Use only for testing or complete reset.

await adapter.clearDatabase();

Sync Operations​

Automatic Graph Sync (v0.29.0+)​

Important: As of v0.29.0, graph synchronization is automatic when CORTEX_GRAPH_SYNC=true is set in your environment variables.

The syncToGraph option has been removed from all APIs. Graph sync is now controlled entirely by:

  1. Setting CORTEX_GRAPH_SYNC=true in your environment
  2. Providing valid graph credentials (e.g., NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD)

Automatic Sync Behavior​

// All operations auto-sync to graph when CORTEX_GRAPH_SYNC=true

// Facts are automatically synced including Entity nodes
await cortex.facts.store({
memorySpaceId: "agent-1",
fact: "Alice works at Acme Corp",
factType: "relationship",
subject: "Alice",
predicate: "works_at",
object: "Acme Corp",
entities: [
{ name: "Alice", type: "person" },
{ name: "Acme Corp", type: "organization" },
],
});
// Fact node + Entity nodes + MENTIONS relationships created automatically!

// Memories are automatically synced
await cortex.vector.store(memorySpaceId, data);
// Memory node synced automatically!

// Conversations are automatically synced
await cortex.conversations.create(input);
// Conversation node synced automatically!

Disabling Graph Sync​

To disable graph sync, simply don't set CORTEX_GRAPH_SYNC or set it to false:

# Disable graph sync (default)
CORTEX_GRAPH_SYNC=false

# Or simply don't set the variable
# CORTEX_GRAPH_SYNC=...
Note

The syncToGraph option was removed in v0.29.0 as part of simplifying the API. Graph sync is now a deployment-time decision controlled by environment variables.

Manual Sync Functions​

Direct sync control for power users. All sync functions support an optional tenantId parameter for multi-tenant SaaS isolation.

import {
syncMemoryToGraph,
syncFactToGraph,
syncContextToGraph,
syncConversationToGraph,
syncMemorySpaceToGraph,
syncMemoryRelationships,
syncFactRelationships,
syncContextRelationships,
syncConversationRelationships,
syncA2ARelationships,
} from "@cortexmemory/sdk";

// Manual sync workflow
const memory = await cortex.vector.store(memorySpaceId, data);
const nodeId = await syncMemoryToGraph(memory, adapter);
await syncMemoryRelationships(memory, nodeId, adapter);

syncContextToGraph()​

Sync a Context to the graph database using MERGE semantics (idempotent).

async function syncContextToGraph(
context: Context,
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

const context = await cortex.contexts.get(contextId);
const nodeId = await syncContextToGraph(context, adapter, "tenant-acme");
await syncContextRelationships(context, nodeId, adapter);

syncConversationToGraph()​

Sync a Conversation to the graph database using MERGE semantics.

async function syncConversationToGraph(
conversation: Conversation,
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

const conversation = await cortex.conversations.get(conversationId);
const nodeId = await syncConversationToGraph(conversation, adapter);
await syncConversationRelationships(conversation, nodeId, adapter);

syncMemoryToGraph()​

Sync a Memory to the graph database. Content is truncated to 200 characters in the graph node.

async function syncMemoryToGraph(
memory: MemoryEntry,
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

const memory = await cortex.vector.get(memorySpaceId, memoryId);
const nodeId = await syncMemoryToGraph(memory, adapter, "tenant-acme");
await syncMemoryRelationships(memory, nodeId, adapter);

// For A2A memories, also sync A2A relationships
if (memory.sourceType === "a2a") {
await syncA2ARelationships(memory, adapter);
}

syncFactToGraph()​

Sync a Fact to the graph database using MERGE semantics.

async function syncFactToGraph(
fact: FactRecord,
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

const fact = await cortex.facts.get(memorySpaceId, factId);
const nodeId = await syncFactToGraph(fact, adapter);
await syncFactRelationships(fact, nodeId, adapter);

syncMemorySpaceToGraph()​

Sync a MemorySpace to the graph database using MERGE semantics.

async function syncMemorySpaceToGraph(
memorySpace: MemorySpace,
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

const memorySpace = await cortex.memorySpaces.get(memorySpaceId);
const nodeId = await syncMemorySpaceToGraph(memorySpace, adapter);
// MemorySpace nodes don't have additional relationships to sync

syncConversationRelationships()​

Sync Conversation relationships to the graph. Creates IN_SPACE, INVOLVES, and HAS_PARTICIPANT edges.

async function syncConversationRelationships(
conversation: Conversation,
conversationNodeId: string,
adapter: GraphAdapter,
): Promise<void>;

syncA2ARelationships()​

Sync Agent-to-Agent (A2A) communication relationships. Creates SENT_TO edges between memory spaces.

async function syncA2ARelationships(
memory: MemoryEntry,
adapter: GraphAdapter,
): Promise<void>;

Example:

// Only call for A2A memories
if (memory.sourceType === "a2a") {
await syncA2ARelationships(memory, adapter);
}

Helper Functions​

Utility functions for managing graph nodes and lookups.

findGraphNodeId()​

Find a graph node ID by Cortex entity ID.

import { findGraphNodeId } from "@cortexmemory/sdk";

async function findGraphNodeId(
label: string, // 'Context', 'Conversation', 'Memory', 'Fact', 'MemorySpace', 'User', 'Agent', 'Participant'
cortexId: string, // The Cortex entity ID (e.g., memoryId, factId)
adapter: GraphAdapter,
): Promise<string | null>; // Returns graph node ID or null if not found

Example:

// Find a Memory node by memoryId
const nodeId = await findGraphNodeId("Memory", "mem-123", adapter);
if (nodeId) {
const node = await adapter.getNode(nodeId);
console.log("Found memory:", node?.properties);
}

// Find a Fact node by factId
const factNodeId = await findGraphNodeId("Fact", "fact-456", adapter);

ensureUserNode()​

Create or get a User node using MERGE semantics (idempotent).

import { ensureUserNode } from "@cortexmemory/sdk";

async function ensureUserNode(
userId: string,
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

// Ensure user exists in graph before creating relationships
const userNodeId = await ensureUserNode("user-123", adapter, "tenant-acme");

ensureAgentNode()​

Create or get an Agent node using MERGE semantics (idempotent).

import { ensureAgentNode } from "@cortexmemory/sdk";

async function ensureAgentNode(
agentId: string,
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

// Ensure agent exists in graph
const agentNodeId = await ensureAgentNode("assistant-v1", adapter);

ensureParticipantNode()​

Create or get a Participant node for Hive Mode using MERGE semantics.

import { ensureParticipantNode } from "@cortexmemory/sdk";

async function ensureParticipantNode(
participantId: string,
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

// Ensure participant exists for Hive Mode tracking
const participantNodeId = await ensureParticipantNode(
"participant-alice",
adapter,
);

ensureEntityNode()​

Create or get an Entity node using MERGE semantics. Used for fact subject/object entities.

import { ensureEntityNode } from "@cortexmemory/sdk";

async function ensureEntityNode(
entityName: string, // Entity name (e.g., "Alice", "Acme Corp")
entityType: string, // Entity type (e.g., "subject", "object", "person", "company")
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

// Create entity nodes for a fact relationship
const aliceNodeId = await ensureEntityNode("Alice", "person", adapter);
const acmeNodeId = await ensureEntityNode("Acme Corp", "company", adapter);

// Create relationship between entities
await adapter.createEdge({
type: "WORKS_AT",
from: aliceNodeId,
to: acmeNodeId,
properties: { since: 2020 },
});

ensureEnrichedEntityNode()​

Create or get an enriched Entity node with additional metadata from bullet-proof extraction.

import { ensureEnrichedEntityNode } from "@cortexmemory/sdk";

async function ensureEnrichedEntityNode(
entityName: string, // Entity name (e.g., "Alex")
entityType: string, // Specific type (e.g., "preferred_name", "full_name")
fullValue: string | undefined, // Full value if available (e.g., "Alexander Johnson")
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

// Create enriched entity with full value context
const entityNodeId = await ensureEnrichedEntityNode(
"Alex", // Short name
"preferred_name", // Specific entity type
"Alexander Johnson", // Full value
adapter,
);
// Node properties: { name: "Alex", type: "preferred_name", entityType: "preferred_name", fullValue: "Alexander Johnson" }

Real-Time Sync Worker​

Automatic synchronization using Convex reactive queries.

Configuration​

const cortex = new Cortex({
convexUrl: "...",
graph: {
adapter: graphAdapter,
autoSync: true, // ← Auto-start worker!
syncWorkerOptions: {
batchSize: 100, // Items per batch
retryAttempts: 3, // Retry failures
verbose: true, // Enable logging
},
},
});

Worker Control​

// Get worker instance
const worker = cortex.getGraphSyncWorker();

if (worker) {
// Get health metrics
const metrics = worker.getMetrics();
console.log("Worker metrics:", metrics);
/*
{
isRunning: true,
totalProcessed: 150,
successCount: 148,
failureCount: 2,
avgSyncTimeMs: 45,
queueSize: 3,
lastSyncAt: 1635789012345
}
*/
}

// Worker stops automatically when cortex.close() is called
cortex.close();

Manual Worker Control​

import { GraphSyncWorker } from "@cortexmemory/sdk";

// Create worker manually
const worker = new GraphSyncWorker(client, graphAdapter, {
batchSize: 50,
verbose: true,
});

// Start worker
await worker.start();
console.log("Worker started, subscribing to sync queue...");

// Use Cortex normally
await cortex.memory.remember(params);
// Worker processes in background reactively!

// Monitor
setInterval(() => {
const metrics = worker.getMetrics();
console.log(
`Processed: ${metrics.totalProcessed}, Queue: ${metrics.queueSize}`,
);
}, 5000);

// Stop worker
worker.stop();

Delete Operations with Orphan Cleanup​

Cascading Deletes​

All delete operations support sophisticated orphan cleanup:

// Delete memory - checks if conversation becomes orphaned
// Graph cleanup is automatic when CORTEX_GRAPH_SYNC=true (v0.29.0+)
await cortex.memory.forget("agent-1", "mem-123", {
deleteConversation: true,
});

// What happens:
// 1. Deletes memory from Convex (L2)
// 2. Deletes conversation from Convex (L1a) if requested
// 3. Deletes memory node from graph
// 4. Checks if conversation node is orphaned
// 5. If orphaned, deletes conversation node
// 6. Handles circular references safely
// 7. Detects and removes orphan islands

Delete Functions​

Direct delete functions for programmatic graph cleanup.

import {
deleteMemoryFromGraph,
deleteFactFromGraph,
deleteContextFromGraph,
deleteConversationFromGraph,
deleteMemorySpaceFromGraph,
} from "@cortexmemory/sdk";

deleteMemoryFromGraph()​

Delete a memory node with optional orphan cleanup.

const result = await deleteMemoryFromGraph(
"mem-123", // memoryId
adapter,
true, // enableOrphanCleanup (default: true)
);

console.log("Deleted nodes:", result.deletedNodes.length);
console.log("Orphan islands:", result.orphanIslands.length);

deleteFactFromGraph()​

Delete a fact node and cascade to orphaned entities.

const result = await deleteFactFromGraph(
"fact-456", // factId
adapter,
true, // enableOrphanCleanup
);
// Entities mentioned only by this fact are also deleted

deleteContextFromGraph()​

Delete a context node with relationship cleanup.

const result = await deleteContextFromGraph(
"ctx-789", // contextId
adapter,
true, // enableOrphanCleanup
);

deleteConversationFromGraph()​

Delete a conversation node.

const result = await deleteConversationFromGraph(
"conv-abc", // conversationId
adapter,
true, // enableOrphanCleanup
);

deleteMemorySpaceFromGraph()​

Delete a memory space node (use with caution).

Warning

Does NOT cascade to memories/contexts in that space. Use with caution.

const result = await deleteMemorySpaceFromGraph(
"space-123", // memorySpaceId
adapter,
);

deleteImmutableFromGraph()​

Delete an immutable record from the graph. For facts, uses deleteFactFromGraph internally.

import { deleteImmutableFromGraph } from "@cortexmemory/sdk";

async function deleteImmutableFromGraph(
immutableType: string, // Type of immutable record (e.g., "fact")
immutableId: string, // Record ID
adapter: GraphAdapter,
enableOrphanCleanup?: boolean, // Default: true
): Promise<DeleteResult>;

Example:

// Delete a fact via immutable API
const result = await deleteImmutableFromGraph(
"fact",
"fact-123",
adapter,
true,
);

// Delete other immutable types
const result2 = await deleteImmutableFromGraph("document", "doc-456", adapter);

deleteMutableFromGraph()​

Delete a mutable record from the graph. Simple delete with no cascading.

import { deleteMutableFromGraph } from "@cortexmemory/sdk";

async function deleteMutableFromGraph(
namespace: string, // Mutable namespace
key: string, // Mutable key
adapter: GraphAdapter,
): Promise<DeleteResult>;

Example:

const result = await deleteMutableFromGraph(
"user_preferences",
"theme",
adapter,
);

Orphan Detection​

Handles complex scenarios:

// Scenario: Circular references
// Entity A → KNOWS → Entity B
// Entity B → KNOWS → Entity A
// Fact F1 → MENTIONS → Entity A

// Delete F1:
// - A and B reference each other (circular!)
// - But no external references remain (F1 was only anchor)
// - Algorithm: Detects orphan island, deletes both A and B

Orphan Rules:

  • Conversation: Deleted if no Memory/Fact/Context references it
  • Entity: Deleted if no Fact mentions it
  • User: Never auto-deleted
  • MemorySpace: Never auto-deleted
  • Memory/Fact/Context: Only deleted if explicitly requested

Programmatic Orphan Detection​

For advanced use cases, access the orphan detection system directly.

import {
ORPHAN_RULES,
createDeletionContext,
deleteWithOrphanCleanup,
detectOrphan,
canRunOrphanCleanup,
} from "@cortexmemory/sdk";

ORPHAN_RULES​

Default orphan rules for each node type.

const rules = ORPHAN_RULES;
/*
{
Conversation: { keepIfReferencedBy: ['Memory', 'Fact', 'Context'] },
Entity: { keepIfReferencedBy: ['Fact'] },
User: { neverDelete: true },
Participant: { neverDelete: true },
MemorySpace: { neverDelete: true },
Memory: { explicitOnly: true },
Fact: { explicitOnly: true },
Context: { explicitOnly: true },
}
*/

createDeletionContext()​

Create a context for tracking cascading deletes.

const deletionContext = createDeletionContext(
"Delete Memory mem-123", // reason (for logging)
ORPHAN_RULES, // optional custom rules
);

// Use with deleteWithOrphanCleanup
const result = await deleteWithOrphanCleanup(
nodeId,
"Memory",
deletionContext,
adapter,
);

deleteWithOrphanCleanup()​

Low-level delete with orphan cascade.

const result = await deleteWithOrphanCleanup(
nodeId, // Graph node ID
"Memory", // Node label
deletionContext,
adapter,
);

// Result type: DeleteResult
console.log("Deleted nodes:", result.deletedNodes);
console.log("Deleted edges:", result.deletedEdges);
console.log("Orphan islands:", result.orphanIslands);

detectOrphan()​

Check if a node would be orphaned.

const check = await detectOrphan(
nodeId,
"Conversation",
deletionContext,
adapter,
);

console.log("Is orphan:", check.isOrphan);
console.log("Reason:", check.reason);
console.log("Referenced by:", check.referencedBy);
console.log("Part of circular island:", check.partOfCircularIsland);

canRunOrphanCleanup()​

Check if cleanup is safe to run.

const canRun = await canRunOrphanCleanup(adapter);
if (canRun) {
// Safe to proceed with cleanup
}

Belief Revision (Fact Supersession)​

Info

Added in v0.24.0

Functions for managing fact versioning and supersession in the graph database. These functions sync belief revision actions to maintain fact lineage and provenance.

syncFactSupersession()​

Sync a fact supersession to the graph database. Creates a SUPERSEDES relationship from the new fact to the old fact.

import { syncFactSupersession } from "@cortexmemory/sdk";

async function syncFactSupersession(
oldFact: FactRecord, // The fact being superseded
newFact: FactRecord, // The fact that supersedes
adapter: GraphAdapter,
reason?: string, // Optional reason for supersession
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<{
oldNodeId: string; // Graph ID of superseded fact
newNodeId: string; // Graph ID of new fact
relationshipId: string; // Graph ID of SUPERSEDES relationship
}>;

Example:

// When belief revision determines a fact should be superseded
const result = await syncFactSupersession(
oldFact,
newFact,
adapter,
"User corrected their preference",
"tenant-acme",
);

console.log(`Fact ${newFact.factId} supersedes ${oldFact.factId}`);
// Creates: (newFact)-[:SUPERSEDES]->(oldFact)

updateFactGraphStatus()​

Update the status of a fact node in the graph.

import { updateFactGraphStatus } from "@cortexmemory/sdk";

async function updateFactGraphStatus(
factId: string,
status: "active" | "superseded" | "deleted",
adapter: GraphAdapter,
): Promise<void>;

Example:

// Mark a fact as superseded
await updateFactGraphStatus("fact-123", "superseded", adapter);

// Mark a fact as deleted (soft delete)
await updateFactGraphStatus("fact-456", "deleted", adapter);

syncFactUpdateInPlace()​

Sync an in-place fact update to the graph. Used for UPDATE action in belief revision (content refinement without supersession).

import { syncFactUpdateInPlace } from "@cortexmemory/sdk";

async function syncFactUpdateInPlace(
fact: FactRecord, // The updated fact
adapter: GraphAdapter,
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<string>; // Returns graph node ID

Example:

// When belief revision merges/updates an existing fact
const nodeId = await syncFactUpdateInPlace(updatedFact, adapter);
// Fact node is updated with new content, status remains "active"

syncFactRevision()​

Create a REVISED_FROM relationship between facts. Used when a fact is refined (content updated) rather than superseded.

import { syncFactRevision } from "@cortexmemory/sdk";

async function syncFactRevision(
originalFact: FactRecord, // The original fact
revisedFact: FactRecord, // The revised fact
adapter: GraphAdapter,
reason?: string, // Optional reason for revision
tenantId?: string, // Optional: Multi-tenancy isolation
): Promise<{
originalNodeId: string;
revisedNodeId: string;
relationshipId: string;
}>;

Example:

// Create revision relationship for fact refinement
const result = await syncFactRevision(
originalFact,
revisedFact,
adapter,
"Added more specific details",
);
// Creates: (revisedFact)-[:REVISED_FROM]->(originalFact)

getFactSupersessionChainFromGraph()​

Get the complete supersession chain for a fact from the graph.

import { getFactSupersessionChainFromGraph } from "@cortexmemory/sdk";

async function getFactSupersessionChainFromGraph(
factId: string,
adapter: GraphAdapter,
): Promise<
Array<{
factId: string;
fact: string;
supersededAt?: number;
}>
>;

Example:

// Get the full history of a fact
const chain = await getFactSupersessionChainFromGraph("fact-123", adapter);
for (const item of chain) {
console.log(`${item.factId}: ${item.fact}`);
if (item.supersededAt) {
console.log(` Superseded at: ${new Date(item.supersededAt)}`);
}
}
// Returns facts ordered from oldest to newest

removeFactSupersessionRelationships()​

Remove supersession relationships for a fact. Used when undoing a supersession or during cleanup.

import { removeFactSupersessionRelationships } from "@cortexmemory/sdk";

async function removeFactSupersessionRelationships(
factId: string,
adapter: GraphAdapter,
): Promise<number>; // Returns count of deleted relationships

Example:

// Remove all SUPERSEDES relationships for a fact
const deleted = await removeFactSupersessionRelationships("fact-123", adapter);
console.log(`Removed ${deleted} supersession relationships`);

Schema Management​

initializeGraphSchema()​

Create constraints and indexes (one-time setup).

import { initializeGraphSchema } from "@cortexmemory/sdk";

await initializeGraphSchema(adapter);
// Creates:
// - 8 unique constraints (MemorySpace, Context, Memory, Fact, etc.)
// - 22 performance indexes

verifyGraphSchema()​

Check if schema is properly initialized.

import { verifyGraphSchema } from "@cortexmemory/sdk";

const status = await verifyGraphSchema(adapter);
console.log("Schema valid:", status.valid);
console.log("Constraints:", status.constraints.length);
console.log("Indexes:", status.indexes.length);
console.log("Missing constraints:", status.missing);

Return type:

interface VerifySchemaResult {
/** Whether all required constraints exist */
valid: boolean;
/** List of existing constraint names */
constraints: string[];
/** List of existing index names */
indexes: string[];
/** List of missing required constraint names */
missing: string[];
}

dropGraphSchema()​

Remove all constraints and indexes (testing/reset).

Danger

Removes all schema constraints and indexes. Use only for testing or reset scenarios.

import { dropGraphSchema } from "@cortexmemory/sdk";

await dropGraphSchema(adapter);

Graph Enrichment Queries​

Pattern 1: Memory → Facts Enrichment​

// Get memory
const memory = await cortex.vector.get(memorySpaceId, memoryId);

// Find related facts via conversation
const relatedFacts = await adapter.query(
`
MATCH (m:Memory {memoryId: $memoryId})
MATCH (m)-[:REFERENCES]->(conv:Conversation)
MATCH (conv)<-[:EXTRACTED_FROM]-(f:Fact)
RETURN f.fact as fact, f.confidence as confidence
ORDER BY f.confidence DESC
`,
{ memoryId },
);

console.log(
`Memory enrichment: 1 memory → ${relatedFacts.count} related facts`,
);
// Enrichment factor: 5x more context!

Pattern 2: Entity Network Discovery​

// Find who works at same company as Alice
const coworkers = await adapter.query(`
MATCH (alice:Entity {name: 'Alice'})-[:WORKS_AT]->(company:Entity)
MATCH (company)<-[:WORKS_AT]-(coworker:Entity)
WHERE coworker.name <> 'Alice'
RETURN DISTINCT coworker.name as name
`);

console.log(
"Alice's coworkers:",
coworkers.records.map((r) => r.name),
);

Pattern 3: Knowledge Path Discovery​

// Multi-hop path: Alice → Company → Bob → Technology
const path = await adapter.query(`
MATCH path = (alice:Entity {name: 'Alice'})-[*1..4]-(tech:Entity {name: 'TypeScript'})
RETURN [node in nodes(path) | node.name] as pathNodes,
[rel in relationships(path) | type(rel)] as pathRels,
length(path) as hops
LIMIT 1
`);

if (path.count > 0) {
console.log("Path:", path.records[0].pathNodes.join(" → "));
console.log("Via:", path.records[0].pathRels.join(" → "));
console.log("Hops:", path.records[0].hops);
}

Pattern 4: Context Chain Reconstruction​

// Get full context hierarchy via graph
const chain = await adapter.query(
`
MATCH (current:Context {contextId: $contextId})
MATCH path = (current)-[:CHILD_OF*0..10]->(ancestors:Context)
RETURN ancestors
ORDER BY ancestors.depth
`,
{ contextId },
);

console.log("Full context chain:");
for (const record of chain.records) {
const ctx = record.ancestors.properties;
console.log(` Depth ${ctx.depth}: ${ctx.purpose}`);
}

Pattern 5: Provenance Tracing​

// Trace fact back to source conversation
const provenance = await adapter.query(
`
MATCH (f:Fact {factId: $factId})
MATCH (f)-[:EXTRACTED_FROM]->(conv:Conversation)
MATCH (conv)<-[:TRIGGERED_BY]-(ctx:Context)
MATCH (ctx)-[:INVOLVES]->(user:User)
RETURN conv.conversationId as conversation,
ctx.purpose as context,
user.userId as user
`,
{ factId },
);

console.log("Fact provenance:");
console.log(" Conversation:", provenance.records[0].conversation);
console.log(" Context:", provenance.records[0].context);
console.log(" User:", provenance.records[0].user);
// Complete audit trail!

Multi-Tenancy Support​

All graph nodes include tenantId for SaaS multi-tenant isolation:

// When syncing with tenant context
await syncMemoryToGraph(memory, adapter, "tenant-acme");

// Cypher queries respect tenant isolation
const query = `
MATCH (m:Memory)
WHERE m.tenantId = $tenantId AND m.userId = $userId
RETURN m
`;

// GDPR cascade includes tenant context
// User deletion traverses graph nodes with matching tenantId

All Node Types Include:

(:AnyNode {
tenantId: string | null, // SaaS isolation
// ... other properties
})

Node Types​

MemorySpace​

(:MemorySpace {
memorySpaceId: string,
tenantId: string | null, // Multi-tenancy
name: string,
type: string, // 'personal', 'team', 'project', 'custom'
status: string,
createdAt: number
})

Context​

(:Context {
contextId: string,
memorySpaceId: string,
tenantId: string | null, // Multi-tenancy
purpose: string,
status: string,
depth: number,
parentId: string,
rootId: string,
createdAt: number
})

Memory​

(:Memory {
memoryId: string,
memorySpaceId: string,
tenantId: string | null, // Multi-tenancy
content: string, // Truncated (first 200 chars)
importance: number,
sourceType: string,
tags: array,
createdAt: number
})

Fact​

(:Fact {
factId: string,
memorySpaceId: string,
tenantId: string | null, // Multi-tenancy
fact: string,
factType: string,
subject: string,
predicate: string,
object: string,
confidence: number,
createdAt: number
})

Entity​

(:Entity {
name: string,
type: string,
createdAt: number
})

Conversation​

(:Conversation {
conversationId: string,
memorySpaceId: string,
type: string,
messageCount: number,
createdAt: number
})

User​

(:User {
userId: string,
createdAt: number
})

Relationship Types​

Hierarchy​

(Context)-[:PARENT_OF]->(Context)
(Context)-[:CHILD_OF]->(Context)

Isolation​

(Memory|Fact|Context|Conversation)-[:IN_SPACE]->(MemorySpace)

References​

(Memory)-[:REFERENCES]->(Conversation)
(Fact)-[:EXTRACTED_FROM]->(Conversation)
(Context)-[:TRIGGERED_BY]->(Conversation)

Users​

(Memory|Context|Conversation)-[:INVOLVES|RELATES_TO]->(User)

Facts & Entities​

(Fact)-[:MENTIONS]->(Entity)
(Entity)-[:WORKS_AT|KNOWS|USES|LOCATED_IN|...]->(Entity)

Versioning​

(Fact)-[:SUPERSEDES]->(Fact)

Automatic Graph Sync Reference (v0.29.0+)​

Note

Breaking Change in v0.29.0: The syncToGraph option has been removed from all APIs. Graph synchronization is now automatic when CORTEX_GRAPH_SYNC=true is set.

All Cortex APIs automatically sync to the graph database when:

  1. CORTEX_GRAPH_SYNC=true is set in environment variables
  2. Valid graph credentials are configured

Conversations API​

// Automatically syncs when CORTEX_GRAPH_SYNC=true
await cortex.conversations.create(input);
await cortex.conversations.addMessage(input);
await cortex.conversations.delete(id);

Vector API​

// Automatically syncs when CORTEX_GRAPH_SYNC=true
await cortex.vector.store(memorySpaceId, input);
await cortex.vector.update(memorySpaceId, memoryId, updates);
await cortex.vector.delete(memorySpaceId, memoryId);

Facts API​

// Automatically syncs when CORTEX_GRAPH_SYNC=true
// Includes Entity nodes and MENTIONS relationships
await cortex.facts.store(params);
await cortex.facts.update(memorySpaceId, factId, updates);
await cortex.facts.delete(memorySpaceId, factId);

Contexts API​

// Automatically syncs when CORTEX_GRAPH_SYNC=true
await cortex.contexts.create(params);
await cortex.contexts.update(contextId, updates);
await cortex.contexts.delete(contextId);

Memory Spaces API​

// Automatically syncs when CORTEX_GRAPH_SYNC=true
await cortex.memorySpaces.register(params);

Memory API (Convenience)​

// Automatically syncs when CORTEX_GRAPH_SYNC=true
await cortex.memory.remember(params);

// Forget with cascade (graph orphan cleanup is automatic)
await cortex.memory.forget(memorySpaceId, memoryId, {
deleteConversation: true,
});

Batch Sync​

For initial sync or large data imports:

import { initialGraphSync } from "@cortexmemory/sdk";

const result = await initialGraphSync(cortex, adapter, {
limits: {
memorySpaces: 1000,
contexts: 5000,
memories: 10000,
facts: 5000,
},
syncRelationships: true,
onProgress: (entity, current, total) => {
console.log(`Syncing ${entity}: ${current}/${total}`);
},
});

console.log("Sync complete:");
console.log(" Memory Spaces:", result.memorySpaces.synced);
console.log(" Contexts:", result.contexts.synced);
console.log(" Memories:", result.memories.synced);
console.log(" Facts:", result.facts.synced);
console.log(" Errors:", result.errors.length);
console.log(" Duration:", result.duration, "ms");

Performance​

Query Performance​

Query TypeGraph-Lite (Convex)Native GraphWhen to Use
1-hop traversal3-10ms10-25msGraph-Lite
3-hop traversal10-50ms4-10msNative Graph
7-hop traversal50-200ms4-15msNative Graph
Pattern matchingNot feasible10-100msNative Graph only
Entity networksNot feasible20-50msNative Graph only

Sync Performance​

  • Single entity: 30-60ms
  • Batch sync: ~300 entities/second
  • Real-time lag: <1 second with worker
  • Enrichment overhead: +90ms for 2-5x context

Error Handling​

Graph Connection Errors​

try {
await adapter.connect(config);
} catch (error) {
if (error instanceof GraphAuthenticationError) {
console.error(`Auth failed for ${error.username} at ${error.uri}`);
console.error("Check NEO4J_USERNAME and NEO4J_PASSWORD");
} else if (error instanceof GraphConnectionError) {
console.error("Failed to connect to graph database");
// Fall back to Graph-Lite or disable graph features
}
}

Sync Failures​

// Sync failures log warnings but don't throw (v0.29.0+)
// Graph sync is automatic when CORTEX_GRAPH_SYNC=true
await cortex.vector.store(data);
// If graph sync fails: Logs warning, continues
// Convex remains source of truth

Query Errors​

try {
const result = await adapter.query(cypherQuery);
} catch (error) {
if (error.name === "GraphQueryError") {
console.error("Query failed:", error.query);
}
}

Best Practices​

1. Convex as Source of Truth​

// Recommended: Always write to Convex first (v0.29.0+)
// Graph sync is automatic when CORTEX_GRAPH_SYNC=true
await cortex.memory.remember(params);
// Convex write succeeds → then auto-syncs to graph

// Avoid: Writing to graph first
await adapter.createNode({ ... }); // Graph could succeed but Convex fail

2. Use Auto-Sync for Simplicity​

// Recommended: Enable auto-sync, use convenience APIs
const cortex = new Cortex({
convexUrl: "...",
graph: { adapter, autoSync: true },
});

await cortex.memory.remember(params);
// Syncs automatically in background!

3. Monitor Sync Health​

// Check worker periodically
const worker = cortex.getGraphSyncWorker();
if (worker) {
const metrics = worker.getMetrics();

if (metrics.queueSize > 1000) {
console.warn("Sync queue backing up!");
}

if (metrics.failureCount > 100) {
console.error("High failure rate, check graph connection");
}
}

4. Use Graph for Discovery, Not Storage​

// Good: Query graph for relationships
const related = await adapter.query(`
MATCH (m:Memory)-[:REFERENCES]->(conv:Conversation)<-[:EXTRACTED_FROM]-(f:Fact)
RETURN f
`);

// Good: Fetch full data from Convex
for (const record of related.records) {
const fullFact = await cortex.facts.get(
memorySpaceId,
record.f.properties.factId,
);
// Full data with all versions from Convex
}

TypeScript Types​

Core Types​

import type {
// Adapter Interface
GraphAdapter,

// Node & Edge Types
GraphNode,
GraphEdge,
GraphPath,

// Query Types
GraphQuery,
GraphQueryResult,
QueryStatistics,

// Configuration
GraphConnectionConfig,
GraphOperation,
TraversalConfig,
ShortestPathConfig,

// Sync Worker
SyncHealthMetrics,
GraphSyncWorkerOptions,

// Batch Sync
BatchSyncOptions,
BatchSyncResult,

// Orphan Detection
OrphanRule,
DeletionContext,
DeleteResult,
OrphanCheckResult,
} from "@cortexmemory/sdk";

Sync Functions​

import {
// Node Sync
syncContextToGraph,
syncConversationToGraph,
syncMemoryToGraph,
syncFactToGraph,
syncMemorySpaceToGraph,

// Relationship Sync
syncContextRelationships,
syncConversationRelationships,
syncMemoryRelationships,
syncFactRelationships,
syncA2ARelationships,

// Helper Functions
findGraphNodeId,
ensureUserNode,
ensureAgentNode,
ensureParticipantNode,
ensureEntityNode,
ensureEnrichedEntityNode,

// Belief Revision
syncFactSupersession,
updateFactGraphStatus,
syncFactUpdateInPlace,
syncFactRevision,
getFactSupersessionChainFromGraph,
removeFactSupersessionRelationships,

// Delete Functions
deleteMemoryFromGraph,
deleteFactFromGraph,
deleteContextFromGraph,
deleteConversationFromGraph,
deleteMemorySpaceFromGraph,
deleteImmutableFromGraph,
deleteMutableFromGraph,

// Orphan Detection
ORPHAN_RULES,
createDeletionContext,
deleteWithOrphanCleanup,
detectOrphan,
canRunOrphanCleanup,

// Batch Sync
initialGraphSync,
} from "@cortexmemory/sdk";

GraphNode​

Represents a node in the graph database.

interface GraphNode {
/** Node label (e.g., 'Context', 'Memory', 'Fact') */
label: string;

/** Node properties (key-value pairs) */
properties: Record<string, unknown>;

/** Optional node ID (set by graph database after creation) */
id?: string;
}

GraphEdge​

Represents a relationship between nodes.

interface GraphEdge {
/** Relationship type (e.g., 'CHILD_OF', 'MENTIONS', 'SENT_TO') */
type: string;

/** Source node ID */
from: string;

/** Target node ID */
to: string;

/** Optional relationship properties */
properties?: Record<string, unknown>;

/** Optional edge ID (set by graph database after creation) */
id?: string;
}

GraphPath​

Path between nodes in the graph.

interface GraphPath {
/** Nodes in the path */
nodes: GraphNode[];

/** Relationships in the path */
relationships: GraphEdge[];

/** Path length (number of hops) */
length: number;
}

GraphQuery​

Cypher query with parameters.

interface GraphQuery {
/** Cypher query string */
cypher: string;

/** Query parameters (for parameterized queries) */
params?: Record<string, unknown>;
}

GraphQueryResult​

Result from a graph query.

interface GraphQueryResult {
/** Records returned by the query */
records: Record<string, unknown>[];

/** Number of records returned */
count: number;

/** Optional query statistics */
stats?: QueryStatistics;
}

QueryStatistics​

Query execution statistics.

interface QueryStatistics {
nodesCreated?: number;
nodesDeleted?: number;
relationshipsCreated?: number;
relationshipsDeleted?: number;
propertiesSet?: number;
labelsAdded?: number;
labelsRemoved?: number;
indexesAdded?: number;
constraintsAdded?: number;
}

GraphConnectionConfig​

Connection configuration for graph database.

interface GraphConnectionConfig {
/** Bolt URI (e.g., 'bolt://localhost:7687') */
uri: string;

/** Username for authentication */
username: string;

/** Password for authentication */
password: string;

/** Optional database name (Neo4j 4.0+) */
database?: string;

/** Optional connection pool configuration */
maxConnectionPoolSize?: number;

/** Optional connection timeout (ms) */
connectionTimeout?: number;
}

GraphOperation​

Batch operation for graph write.

interface GraphOperation {
/** Operation type */
type:
| "CREATE_NODE"
| "UPDATE_NODE"
| "DELETE_NODE"
| "CREATE_EDGE"
| "DELETE_EDGE";

/** Operation data */
data:
| GraphNode
| GraphEdge
| { id: string; properties?: Record<string, unknown> };
}

TraversalConfig​

Configuration for graph traversal.

interface TraversalConfig {
/** Starting node ID */
startId: string;

/** Relationship types to follow (empty = all types) */
relationshipTypes?: string[];

/** Maximum depth to traverse */
maxDepth: number;

/** Optional direction: 'OUTGOING', 'INCOMING', or 'BOTH' */
direction?: "OUTGOING" | "INCOMING" | "BOTH";

/** Optional filter predicate (Cypher WHERE clause) */
filter?: string;

/** Optional filter parameters */
filterParams?: Record<string, unknown>;
}

ShortestPathConfig​

Configuration for shortest path queries.

interface ShortestPathConfig {
/** Source node ID */
fromId: string;

/** Target node ID */
toId: string;

/** Maximum number of hops to search */
maxHops: number;

/** Optional relationship types to follow */
relationshipTypes?: string[];

/** Optional direction */
direction?: "OUTGOING" | "INCOMING" | "BOTH";
}

Orphan Detection Types​

import type {
OrphanRule,
DeletionContext,
DeleteResult,
OrphanCheckResult,
} from "@cortexmemory/sdk";

Error Classes​

import {
GraphDatabaseError,
GraphConnectionError,
GraphAuthenticationError,
GraphQueryError,
GraphNotFoundError,
} from "@cortexmemory/sdk";

OrphanRule​

Orphan detection rules for node types.

interface OrphanRule {
/** Node types that must reference this node to keep it alive */
keepIfReferencedBy?: string[];

/** Never auto-delete this node type */
neverDelete?: boolean;

/** Only delete if explicitly requested (not cascaded) */
explicitOnly?: boolean;
}

DeletionContext​

Context for tracking cascading deletes.

interface DeletionContext {
/** Set of node IDs being deleted in this operation */
deletedNodeIds: Set<string>;

/** Reason for deletion (for logging/debugging) */
reason: string;

/** Timestamp of deletion */
timestamp: number;

/** Orphan rules to use (can be customized) */
orphanRules?: Record<string, OrphanRule>;
}

DeleteResult​

Result of a delete operation with orphan cleanup.

interface DeleteResult {
/** IDs of all nodes deleted (including cascaded orphans) */
deletedNodes: string[];

/** IDs of all edges deleted */
deletedEdges: string[];

/** Orphan islands that were removed */
orphanIslands: Array<{
nodes: string[];
reason: string;
}>;
}

OrphanCheckResult​

Result of orphan detection check.

interface OrphanCheckResult {
/** Is this node an orphan? */
isOrphan: boolean;

/** Reason for orphan status */
reason: string;

/** IDs of nodes that reference this node */
referencedBy: string[];

/** Is this part of a circular orphan island? */
partOfCircularIsland: boolean;

/** If part of island, the full island node IDs */
islandNodes?: string[];
}

Error Classes​

import {
GraphDatabaseError,
GraphConnectionError,
GraphAuthenticationError,
GraphQueryError,
GraphNotFoundError,
} from "@cortexmemory/sdk";
  • GraphDatabaseError - Base error for graph operations
  • GraphConnectionError - Connection failures (code: CONNECTION_ERROR)
  • GraphAuthenticationError - Authentication failures (subclass of GraphConnectionError)
  • GraphQueryError - Query execution failures (includes query string)
  • GraphNotFoundError - Node or edge not found (code: NOT_FOUND)

GraphAuthenticationError​

Specific error class for authentication failures. Provides additional context for debugging credential issues.

class GraphAuthenticationError extends GraphConnectionError {
readonly name: "GraphAuthenticationError";
readonly uri: string; // The connection URI that failed
readonly username: string; // The username that was used

constructor(message: string, uri: string, username: string, cause?: Error);
}

Example:

try {
await adapter.connect({
uri: "bolt://localhost:7687",
username: "neo4j",
password: "wrong-password",
});
} catch (error) {
if (error instanceof GraphAuthenticationError) {
console.error(
`Authentication failed for ${error.username} at ${error.uri}`,
);
console.error(
"Check NEO4J_USERNAME and NEO4J_PASSWORD environment variables",
);
} else if (error instanceof GraphConnectionError) {
console.error("Connection failed - is the database running?");
}
}

Examples​

Complete examples in:

  • examples/graph-realtime-sync.ts - Real-time worker usage
  • tests/graph/proofs/07-multilayer-retrieval.proof.ts - Multi-layer enhancement
  • tests/graph/end-to-end-multilayer.test.ts - Complete validation

Limitations​

Memgraph Compatibility​

  • Basic operations: 100% compatible
  • Traversal: 100% compatible
  • shortestPath: Not supported (use traversal instead)
  • Overall: ~80% compatible

Real-Time Sync​

  • Reactive (NOT polling)
  • <1s lag typical
  • Requires Convex running
  • Uses client.onUpdate() pattern

Next Steps​


Questions? Ask in GitHub Discussions.