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 @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. The install command below is the intended package set for the first public npm alpha; until that release lands, consume Crux from this repository workspace.
Install
pnpm add @crux/core @crux/ai @crux/convex @ai-sdk/openai zodComponent setup
@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 '@crux/convex/convex.config'
const app = defineApp()
app.use(crux)
export default appThis 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/
store.ts ← Convex memory store setup
convex.config.ts ← Component installationStep 1: Define a prompt
import { context, prompt } from '@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 '@crux/convex/server'
import { v } from 'convex/values'
import { generate } from '@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 — they run in a Node.js
runtime, not Convex's V8 isolate.
Persistent memory
cruxConvexStore() creates a CruxStore backed by the crux component's memories table. Use it with any memory primitive:
import { cruxConvexStore } from '@crux/convex'
import { episodes, memory, workingState } from '@crux/convex/memory'
import { components } from '../_generated/api'
import type { ActionCtx } from '../_generated/server'
export function createMemoryStore(ctx: ActionCtx) {
return cruxConvexStore({
component: components.crux,
ctx,
})
}
// Inside an action:
const store = createMemoryStore(ctx)
const episodic = episodes({ id: 'chat', embed: embedFn })
const working = workingState({ id: 'session', schema })
const assistantMemory = memory({
id: 'assistant',
store,
namespace: 'thread:1',
blocks: [working, episodic],
})The component handles all CRUD operations (get, set, remove, list) automatically. Vector search uses ctx.vectorSearch from the action context.
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 '@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 store and runtime binding without going through an agent:
await crux.run(ctx, { threadId, userId }, async ({ store }) => {
await store.set(`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 for Crux-native prompts.
It keeps the Convex Agent call shape while Crux handles the request-scoped prompt, tools, memory, skills, and observability lifecycle internally.
Use languageModel for new convexAgent() code to match Convex Agent terminology; model remains accepted for existing call sites.
Action timeouts
Convex actions have a 5-minute timeout. For long-running multi-agent workflows:
- Treat
createComponentSwarm()from@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@crux/convex/serverfor suspendable flows that scheduled-resume across action boundaries - Use
ctx.crux.runAction()across action boundaries to keep devtools traces correlated