
Convex Components
- 74 installs
- 125 repo stars
- Updated February 4, 2026
- igorwarzocha/opencode-workflows
Install, call, and author Convex Components as isolated backend modules, composing their APIs with ctx.runQuery and re-exports.
About
A skill for using Convex Components, isolated mini-backends with their own schema and functions. A developer uses it to install components like Agent, RAG, Workpool, or Workflow, call their APIs, or author new ones.
- Reference files for Agent, RAG, Workpool, and Workflow components
- Covers convex.config.ts use(), component API calls, and client re-exports with auth
Convex Components by the numbers
- 74 all-time installs (skills.sh)
- +3 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #3,067 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/igorwarzocha/opencode-workflows --skill convex-componentsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 74 |
|---|---|
| repo stars | ★ 125 |
| Last updated | February 4, 2026 |
| Repository | igorwarzocha/opencode-workflows ↗ |
What it does
Install, call, and author Convex Components as isolated backend modules, composing their APIs with ctx.runQuery and re-exports.
Files
<overview> Use Convex Components to add isolated backend features and compose component APIs. </overview>
<reference>
- Components overview: https://docs.convex.dev/components
- Understanding: https://docs.convex.dev/components/understanding
- Using: https://docs.convex.dev/components/using
- Authoring: https://docs.convex.dev/components/authoring
- Directory: https://convex.dev/components
</reference>
<context name="Component References"> You SHOULD consult these reference files for specific component knowledge:
references/agent.md— Agent component: threads/messages, streaming, tools, context, debugging, usage tracking, and install/setup.references/rag.md— RAG component: namespaces, add/search/generateText, filters, chunking, prompt vs tool RAG.references/workpool.md— Workpool component: enqueue, retries, onComplete, parallelism, batching, monitoring.references/workflow.md— Workflow component: durable steps, events, retries, status, cancel/cleanup, limits.
</context>
<rules>
Mental Model
- Components are isolated mini backends with their own schema, tables, file storage, and functions.
- Components MUST NOT access app tables/functions/env unless passed explicitly.
- Calls into components are transactional with the caller, but component mutations are sub-transactions.
Installing Components
- Install package:
npm i @convex-dev/<component>. - Add
convex/convex.config.ts: import { defineApp } from "convex/server";app.use(component); useapp.use(component, { name: "custom" })for multiple instances.- You MUST run
npx convex devto generate component code. - Access via
components.<name>inconvex/_generated/api.
Calling Component APIs
- Use
ctx.runQuery/Mutation/Actionwithcomponents.<name>.<fn>. - Public component functions MUST NOT be called directly from clients (they are internal references).
- Queries remain reactive; mutations are transactional.
Transaction Semantics
- Top-level mutation commits all writes across components together.
- If a component mutation throws, only its writes are rolled back; the caller MAY catch and continue.
Component API Differences
components.<name>exposes ONLY public component functions.Idtypes cross boundaries asstring; You MUST NOT usev.id("table")for external tables.- Each component has its own
_generateddirectory; You MUST use the app'scomponentsreferences.
Environment Variables
- Components MUST NOT access
process.envdirectly. - You MUST pass env values as arguments from the app, or store config in a component table.
HTTP Actions
- Components MUST NOT expose routes directly; app MUST mount handlers in
convex/http.ts.
Auth in Components
ctx.authis NOT available inside component functions.- You MUST authenticate in the app and pass identifiers (userId) to component functions.
Pagination
- Built-in
.paginate()is NOT supported inside components. - You SHOULD use
convex-helperspaginator andusePaginatedQueryfrom convex-helpers if needed.
Authoring Components
- Component folder MUST include
convex.config.ts,schema.ts, functions, and_generated. defineComponent("name")defines component; usecomponent.use(...)for child components.- Local components MAY live in
convex/components/or any folder. - NPM components SHOULD export:
@pkg/convex.config.js@pkg/_generated/component.js@pkg/testhelpers
Function Handles
- You SHOULD use
createFunctionHandle(api.foo.bar)to pass callbacks across boundaries. - Handles are strings; use
v.string()validators and cast back toFunctionHandle.
Testing Components
- You SHOULD register component with
convex-testusing component schema/modules or provided test helpers. - For component packages, You SHOULD use
@pkg/testregister helper.
Best Practices
- You MUST always validate args/returns on public component functions.
- You SHOULD prefer app-level wrappers to add auth/rate limiting when re-exporting component APIs.
- You SHOULD use a single-row globals/config table for static configuration.
</rules>
Agent Component
<reference>
- https://convex.dev/components/agent
- https://docs.convex.dev/agents
- https://docs.convex.dev/agents/agent-usage
- https://docs.convex.dev/agents/threads
- https://docs.convex.dev/agents/messages
- https://docs.convex.dev/agents/context
- https://docs.convex.dev/agents/tools
- https://docs.convex.dev/agents/workflows
- https://docs.convex.dev/agents/files
- https://docs.convex.dev/agents/debugging
- https://docs.convex.dev/agents/usage-tracking
- https://docs.convex.dev/agents/rate-limiting
</reference>
<workflow>
Install and Configure
npm install @convex-dev/agentconvex/convex.config.ts:
import { defineApp } from "convex/server";
import agent from "@convex-dev/agent/convex.config.js";
const app = defineApp();
app.use(agent);
app.use(agent, { name: "agent2" });
export default app;1. You MUST run npx convex dev to generate component API. 2. You MUST instantiate with components.agent in app code.
Typical Flow
import { Agent } from "@convex-dev/agent";
import { openai } from "@ai-sdk/openai";
import { components } from "./_generated/api";
import { action } from "./_generated/server";
const supportAgent = new Agent(components.agent, {
name: "Support Agent",
chat: openai.chat("gpt-4o-mini"),
instructions: "You are a helpful assistant.",
tools: { accountLookup, fileTicket },
});
export const startThread = action({
args: { prompt: v.string() },
handler: async (ctx, { prompt }) => {
const { threadId, thread } = await supportAgent.createThread(ctx);
const result = await thread.generateText({ prompt });
return { threadId, text: result.text };
},
});
export const continueThread = action({
args: { prompt: v.string(), threadId: v.string() },
handler: async (ctx, { prompt, threadId }) => {
const { thread } = await supportAgent.continueThread(ctx, { threadId });
const result = await thread.generateText({ prompt });
return result.text;
},
});</workflow>
<rules>
Core Concepts
- Agents = LLM-powered behaviors (model + prompt + tools).
- Threads persist conversation history and CAN be shared by multiple users/agents.
- Messages are stored and reactive; streaming updates are synced via websockets.
- Built-in hybrid search (text + vector) over thread messages for context.
Tools
- You MUST define tools with
createTooland validated args. - Tool outputs are added to the message history.
- You SHOULD prefer deterministic tools; MUST NOT use side effects unless REQUIRED.
Context and Retrieval
- Thread history is automatically included.
- Context search CAN include thread-only or cross-thread data.
- RAG CAN be prompt-based or tool-based; integrates with RAG component.
Files
- Files CAN be attached to threads; stored in Convex file storage.
- Automatic ref-counting and retrieval in thread history.
Workflows
- Workflows enable multi-step agentic operations across agents/users.
- You SHOULD prefer workflows for long-running or multi-stage tasks.
Debugging and Observability
- Agent playground for prompt/context tuning.
- Debugging hooks to inspect tool calls and metadata.
- Usage tracking for per-agent/model/user attribution.
- Rate limiting via Rate Limiter component.
Best Practices
- You MUST keep tool set minimal and well-scoped.
- You SHOULD use separate agents for distinct roles.
- You SHOULD store workflow IDs in app tables for UI tracking.
- You MUST scope context retrieval to least-privilege data.
</rules>
RAG Component
<reference>
- https://convex.dev/components/rag
- https://docs.convex.dev/agents/rag
</reference>
<workflow>
Install and Configure
npm install @convex-dev/ragconvex/convex.config.ts:
import { defineApp } from "convex/server";
import rag from "@convex-dev/rag/convex.config.js";
const app = defineApp();
app.use(rag);
export default app;Instantiate:
import { RAG } from "@convex-dev/rag";
import { components } from "./_generated/api";
import { openai } from "@ai-sdk/openai";
const rag = new RAG(components.rag, {
textEmbeddingModel: openai.embedding("text-embedding-3-small"),
embeddingDimension: 1536,
filterNames: ["category", "contentType"],
});Add Content
await rag.add(ctx, {
namespace: "global",
text,
filterValues: [
{ name: "category", value: "news" },
{ name: "contentType", value: "article" },
],
});Search
const { results, text, entries, usage } = await rag.search(ctx, {
namespace: "global",
query: "convex components",
limit: 10,
vectorScoreThreshold: 0.5,
chunkContext: { before: 2, after: 1 },
});Generate Text
const { text, context } = await rag.generateText(ctx, {
search: { namespace: userId, limit: 10 },
prompt: "Explain the policy",
model: openai.chat("gpt-4o-mini"),
});</workflow>
<rules>
Core Features
- Namespaces for per-user/team isolation.
- Add/replace content with automatic embeddings.
- Semantic search with vector similarity.
- Custom filters with indexed fields.
- Importance weighting (0–1).
- Chunk context for surrounding text.
- Graceful migrations for entries/namespaces.
RAG Strategies
- Prompt-based RAG: always search and inject context.
- Tool-based RAG: LLM decides when to search; tool returns context.
Ingestion Tips
- PDFs: You SHOULD prefer client-side parsing (pdf.js).
- Images: You SHOULD use LLM to extract text/description.
- Text: You SHOULD chunk or normalize for better embeddings.
Best Practices
- You MUST keep namespaces scoped to users/teams.
- You SHOULD use filters to limit search to relevant subsets.
- You SHOULD set
vectorScoreThresholdto avoid low-signal context.
</rules>
Workflow Component
<reference>
- https://convex.dev/components/workflow
- https://www.npmjs.com/package/@convex-dev/workflow
</reference>
<workflow>
Install and Configure
npm install @convex-dev/workflowconvex/convex.config.ts:
import { defineApp } from "convex/server";
import workflow from "@convex-dev/workflow/convex.config.js";
const app = defineApp();
app.use(workflow);
export default app;Instantiate:
import { WorkflowManager } from "@convex-dev/workflow";
import { components } from "./_generated/api";
export const workflow = new WorkflowManager(components.workflow, {
workpoolOptions: { maxParallelism: 10, retryActionsByDefault: true },
});Define Workflows
export const userOnboarding = workflow.define({
args: { userId: v.id("users") },
handler: async (step, args): Promise<void> => {
await step.runMutation(internal.emails.send, { userId: args.userId });
await step.runAction(internal.llm.enrich, { userId: args.userId }, { retry: true });
await step.runMutation(
internal.emails.followUp,
{ userId: args.userId },
{ runAfter: 24 * 60 * 60 * 1000 },
);
},
});Start / Status / Cancel
const workflowId = await workflow.start(ctx, internal.userOnboarding, { userId });
const status = await workflow.status(ctx, workflowId);
await workflow.cancel(ctx, workflowId);Events
const approvalEvent = defineEvent({
name: "approval",
validator: v.object({ approved: v.boolean() }),
});
// Wait inside workflow
const approval = await step.awaitEvent(approvalEvent);
// Send from mutation/action
await workflow.sendEvent(ctx, { ...approvalEvent, workflowId, value: { approved: true } });Dynamic events:
const eventId = await workflow.createEvent(ctx, { name: "userResponse", workflowId });
await step.awaitEvent({ id: eventId });
await workflow.sendEvent(ctx, { id: eventId, value: { ok: true } });onComplete Handling
import { vWorkflowId } from "@convex-dev/workflow";
import { vResultValidator } from "@convex-dev/workpool";
export const kickoff = mutation({
handler: async (ctx) => {
await workflow.start(ctx, internal.userOnboarding, { userId: "..." }, {
onComplete: internal.workflows.onComplete,
context: { userId: "..." },
});
},
});
export const onComplete = mutation({
args: { workflowId: vWorkflowId, result: vResultValidator, context: v.any() },
handler: async (ctx, args) => {
if (args.result.kind === "success") {
// handle success
}
},
});</workflow>
<rules>
Retry Policies
- You SHOULD configure defaults in WorkflowManager
workpoolOptions. - Per-step override:
{ retry: true | false | { maxAttempts, initialBackoffMs, base } }.
Parallel Steps
You MAY use Promise.all for parallel execution:
await Promise.all([
step.runAction(internal.jobs.a, args),
step.runAction(internal.jobs.b, args),
]);Limits and Caveats
- Workflow data limit ~1 MiB; journal limit ~8 MiB.
- Workflow body MUST be deterministic; You MUST use steps for side effects.
- Changing step order mid-flight MUST NOT be done (causes determinism violations).
</rules>
Workpool Component
<reference>
- https://convex.dev/components/workpool
- https://www.npmjs.com/package/@convex-dev/workpool
</reference>
<workflow>
Install and Configure
npm install @convex-dev/workpoolconvex/convex.config.ts:
import { defineApp } from "convex/server";
import workpool from "@convex-dev/workpool/convex.config.js";
const app = defineApp();
app.use(workpool, { name: "emailWorkpool" });
app.use(workpool, { name: "scrapeWorkpool" });
export default app;Instantiate:
import { Workpool } from "@convex-dev/workpool";
import { components } from "./_generated/api";
const emailPool = new Workpool(components.emailWorkpool, {
maxParallelism: 10,
retryActionsByDefault: true,
defaultRetryBehavior: { maxAttempts: 3, initialBackoffMs: 1000, base: 2 },
});Enqueue Work
await emailPool.enqueueAction(ctx, internal.email.send, args, {
retry: false,
onComplete: internal.email.onComplete,
context: { userId: args.userId },
});Batching:
await emailPool.enqueueActionBatch(ctx, internal.weather.scrape, [
{ city: "New York" },
{ city: "Chicago" },
]);Completion Handling
export const onComplete = emailPool.defineOnComplete<DataModel>({
context: v.object({ userId: v.id("users") }),
handler: async (ctx, { context, result }) => {
if (result.kind === "success") {
await ctx.db.insert("emailLog", { userId: context.userId });
}
},
});</workflow>
<rules>
Retry Semantics
- Exponential backoff with jitter.
- You SHOULD ONLY enable retries for idempotent actions.
- Per-call override with
retry: true/false/custom.
Status and Monitoring
import { vWorkIdValidator } from "@convex-dev/workpool";
export const getStatus = query({
args: { id: vWorkIdValidator },
handler: async (ctx, args) => await emailPool.status(args.id),
});statusTtlcontrols retention (useInfinityfor permanent).- Status kinds:
pending,running,finished.
Parallelism Guidance
- You SHOULD avoid >20 on free tier, >100 on Pro across workpools/workflows.
- You SHOULD use low parallelism to reduce OCC conflicts.
Cancellation
pool.cancel(id)orpool.cancelAll()stops queued/retry work.
</rules>