Convex
Run Crux prompts inside Convex actions and persist memory in the crux component.
This quick start gets Crux running in a Convex project. Crux prompts run inside Convex actions; persistent memory uses @use-crux/convex. For cross-action flows, swarm routing, Convex Agent integration, workspaces, skills, blackboards, and devtools, see the full Convex guide.
Crux is in alpha. Public packages are available on npm; use the install command below to get started.
Install
pnpm add @use-crux/core @use-crux/ai @use-crux/convex @ai-sdk/openai zodComponent setup
@use-crux/convex ships a Convex component that handles Crux-owned persistence such as memory records and experimental cross-action swarm state. Install it once in your convex.config.ts:
import { defineApp } from "convex/server";
import crux from "@use-crux/convex/convex.config";
const app = defineApp();
app.use(crux);
export default app;This creates the crux component namespace. The component manages its own tables: no schema definitions needed in your app.
Recommended layout
convex/
prompts/
index.ts ← Prompt definitions
contexts.ts ← Shared contexts
agent/
chat.ts ← Action that calls generate()
memory/
storage.ts ← Convex memory storage setup
convex.config.ts ← Component installationStep 1: Define a prompt
import { context, prompt } from "@use-crux/convex";
import { z } from "zod";
const brand = context({
id: "brand",
priority: 30,
system: "You are a helpful writing assistant.",
});
export const chat = prompt({
id: "chat",
use: [brand],
input: z.object({ message: z.string(), mode: z.string() }),
system: "Help the user with their writing task.",
prompt: ({ input }) => input.message,
});Step 2: Call it from a Convex action
"use node";
import { action } from "@use-crux/convex/server";
import { v } from "convex/values";
import { generate } from "@use-crux/ai";
import { openai } from "@ai-sdk/openai";
import { chat } from "../prompts";
export const respond = action({
args: { message: v.string(), mode: v.string() },
handler: async (ctx, args) => {
const result = await generate(chat, {
model: openai("gpt-4o"),
input: { message: args.message, mode: args.mode },
});
return result.text;
},
});Convex actions that import the AI SDK or OpenAI client need the 'use node'
directive, because they run in a Node.js runtime, not Convex's V8 isolate.
Persistent memory
Use convexRecordStore() for durable Convex-backed memory records:
import { convexRecordStore } from "@use-crux/convex";
import { episodes, memory, workingState } from "@use-crux/convex/memory";
import { components } from "../_generated/api";
import type { ActionCtx } from "../_generated/server";
export function createMemoryRecords(ctx: ActionCtx) {
return convexRecordStore({ component: components.crux, ctx });
}
// Inside an action:
const records = createMemoryRecords(ctx);
const episodic = episodes({ id: "chat", embed: embedFn });
const working = workingState({ id: "session", schema });
const assistantMemory = memory({
id: "assistant",
records,
namespace: "thread:1",
blocks: [working, episodic],
});The component handles record operations (get, put, create, delete, list) automatically. Convex storage is records-only; for dense vector search, pair the record store with an explicit VectorStore such as upstashVectorStore().
With the Convex Agent SDK
If you're using the Convex Agent component, create one Crux Convex profile and let convexAgent() resolve the Crux prompt, memory, skills, and tools for each turn:
"use node";
import { openai } from "@ai-sdk/openai";
import { createCruxConvex } from "@use-crux/convex";
import { components } from "../_generated/api";
import { chat } from "../prompts";
const languageModel = openai("gpt-4o");
const crux = createCruxConvex({
components: {
crux: components.crux,
agent: components.agent,
},
});
export const chatAgent = crux.convexAgent({
name: "Chat",
prompt: chat,
languageModel,
});
await chatAgent.generateText(
ctx,
{ threadId, userId },
{ input: { message, mode } },
);Use crux.run(ctx, target, fn) when lower-level action code needs the same request-scoped storage and runtime binding without going through an agent:
await crux.run(ctx, { threadId, userId }, async ({ records }) => {
await records.put(`blackboard:${threadId}`, { status: "ready" });
});The lower-level Agent export remains available for manual Convex Agent integrations, but the profile-created agent is the default path when Crux should own prompt resolution, memory, skills, and tools for each turn.
It keeps the Convex Agent call shape while Crux handles the request-scoped lifecycle internally.
Use languageModel for new convexAgent() code to match Convex Agent terminology; model remains accepted for existing call sites.
Use crux.run(ctx, target, fn) when custom actions or normal Convex Agent-shaped code need the profile's Crux runtime, storage, and bridge plumbing.
Action timeouts
Convex actions have a 5-minute timeout. For long-running multi-agent workflows:
- Treat
createComponentSwarm()from@use-crux/convex/swarmas experimental cross-action swarm routing. For launch-critical code, run immediate swarm compositions inside a Crux-aware action or useflow()for durable orchestration. - Use
flow()from@use-crux/convex/serverfor suspendable flows that scheduled-resume across action boundaries - Use
ctx.crux.runAction()across action boundaries to keep devtools traces correlated