@use-crux/core
The base Crux package, package overview, config, registry API, and shared utilities.
@use-crux/core is the base package, every Crux project depends on it. It contains all SDK-agnostic primitives organized into subpath exports. Pick the subpath page that matches what you need:
TypeScript compatibility
@use-crux/core is tested with TypeScript >=5.5 <7. Crux also runs a TypeScript 7 native-preview check with @typescript/native-preview / tsgo, but that is a preview signal rather than a stable support promise.
The public type surface is kept compatible with TypeScript 5.5 so projects do not need the newest compiler just to use Crux definitions.
Config
crux.config.ts options, defaults, stability, and runtime/tooling ownership.
Prompts
prompt, createPrompts, the Prompt type.
Contexts
context, when, match, createContexts.
Tools
tool and SDK-agnostic ToolDef authoring.
Tool Middleware
toolMiddleware, approvalMiddleware, resumable approvals.
Multimodal Content
ContentPart, media sources, text projection, and media errors.
Adapter Interface
defineSingleTurnProviderBundle, defineProviderRuntime, adapter IR, and conformance harnesses.
Cache
createSemanticCache and semantic cache policies.
Retrieval
knowledgeBase, retriever, retrievalRecipe, grounding, and retrieval tools.
Citations
grounding, citationConstraint, resolveCitations.
Indexing
indexer, corpus, canonical documents/chunks, write-time embedding.
Memory
memory, memoryBlock, recentMessages, workingState, episodes, facts, procedures.
Compaction
createSlidingWindow, createBudgetManager, summarizeMessages, extractKeyFacts.
Agent
agent, blackboard, handoff, delegate, compositions.
Flow
flow, signalFlow, cancelFlow, listFlows.
Runtime Engine
node, serverless, createRuntimeHandler, durableTask, runtime diagnostics, and testing helpers.
Plan
plan, plan handles, list, ref, and creation tools.
Tasks
tasks, task, task-ledger handles, and workers.
Skill
skill.inline, skill.fromRegistry, and Node file loading.
Safety
guardrail and constraint primitives for I/O safety and semantic validation.
Scoring
judge, prebuilt metrics, and Safety integration through constraint.judge.
Quality
quality, suite, expect, target, cassette, constraintScorer, and persisted local experiments.
Storage
RecordStore, VectorStore, AssetStore, and storage bundles.
Project Index
Design-plane definitions, relations, source locations, and diagnostics.
Index Lint
Lint config, findings, rule metadata, and suppressions.
Runtime Bridge
Local-dev command-plane schemas and helpers.
Devtools Plugin
withDevtools and enableDevtools.
Subpath imports
import {
prompt,
context,
createPrompts,
createContexts,
config,
when,
match,
contentText,
hasMediaParts,
messageText,
textPart,
} from "@use-crux/core";
import { tool } from "@use-crux/core/tools";
import {
toolMiddleware,
approvalMiddleware,
} from "@use-crux/core/tool-middleware";
import {
adapter,
loopRuntimeAdapter,
defineProviderRuntime,
defineSingleTurnProviderBundle,
} from "@use-crux/core/adapter";
import {
providerRuntimeConformance,
transcriptCodecConformance,
} from "@use-crux/core/adapter/testing";
import { describeCruxAdapterConformance } from "@use-crux/core/adapter/testing/vitest";
import {
memory,
workingState,
episodes,
facts,
procedures,
} from "@use-crux/core/memory";
import {
createSlidingWindow,
createBudgetManager,
extractKeyFacts,
summarizeMessages,
} from "@use-crux/core/compaction";
import { agent, blackboard, handoff, delegate } from "@use-crux/core/agent";
import {
flow,
signalFlow,
cancelFlow,
listFlows,
createFlowId,
} from "@use-crux/core/flow";
import {
node,
serverless,
createRuntimeHandler,
durableTask,
} from "@use-crux/core/runtime";
import { plan } from "@use-crux/core/plan";
import { task, tasks } from "@use-crux/core/tasks";
import { skill } from "@use-crux/core/skill";
import { guardrail } from "@use-crux/core/safety";
import { constraint } from "@use-crux/core/safety";
import { judge, metrics } from "@use-crux/core/scoring";
import {
constraintScorer,
expect,
quality,
suite,
target,
} from "@use-crux/core/quality";
import {
createSemanticCache,
semanticCachePolicies,
} from "@use-crux/core/cache";
import { embedding } from "@use-crux/core/embedding";
import {
knowledgeBase,
retriever,
retrievalRecipe,
retrieve,
rerank,
} from "@use-crux/core/retrieval";
import { grounding, citationSchema } from "@use-crux/core/citations";
import { corpus, indexer } from "@use-crux/core/indexing";
import {
CruxProvider,
usePlan,
useTasks,
createSSETransport,
createPollingTransport,
} from "@use-crux/react";
import { cruxSSEHandler } from "@use-crux/react/server";
import { withDevtools } from "@use-crux/core/observability";
import type { ProjectIndexSnapshot } from "@use-crux/core/project-index";
import { serializeProjectIndex } from "@use-crux/core/project-index/serializers";
import type {
ContentPart,
CruxIndexerConfig,
CruxLintConfig,
MessageContent,
ToolModelOutput,
ToModelOutputArgs,
} from "@use-crux/core";Tooling Config
Use config() for Crux project policy and explicit runtime behavior: Quality discovery, Project
Index lint, Indexer extensions, experimental indexer flags, persistence, generation hooks, devtools,
observability, and plugins. Prompts, contexts, tools, agents, flows, registries, and skills remain
normal TypeScript exports discovered from source.
See Config for the full crux.config.ts option reference,
defaults, stability notes, and crux config inspect behavior.
TypeScript Guarantees
Crux APIs are designed to preserve inference across composition. Context input schemas merge into prompt() inputs, grounded retrieval composes through use, and retriever tool names are typed for common manual cases:
const brand = context({
input: z.object({ brandVoice: z.string() }),
system: ({ input }) => `Voice: ${input.brandVoice}`,
});
const answer = prompt({
use: [brand],
input: z.object({ question: z.string() }),
system: ({ input }) => input.question,
});
answer.resolve({
input: {
question: "How do refunds work?",
brandVoice: "Concise and direct",
},
});
const tools = docs.asTools({
prefix: "docs",
include: ["search", "getSource"],
});
tools.docsSearch;
tools.docsGetSource;@use-crux/core also has a package-local typecheck task that runs strict TypeScript plus compile-time API tests. New production any usage is blocked by an explicit-any checker; existing legacy entries are tracked as debt and should only shrink.
config(config)
The single entry point for configuring Crux project policy and explicit runtime behavior. It applies immediately when the config file is imported. Module caching ensures it runs once per process.
Prompt, context, tool, and registry definitions are authored in normal TypeScript modules and discovered from source by local tooling. They are not repeated in config().
For defaults, stability notes, and every nested option, see Config.
| Domain | Type | Description |
|---|---|---|
quality | QualityConfig | Local quality discovery, persistence root, redaction, and run defaults. |
lint | CruxLintConfig | Project Index lint profile and rule overrides. |
indexer | CruxIndexerConfig | Extension references, extension trust policy, and indexer rule options. |
experimental | CruxExperimentalConfig | Unstable opt-in tooling features such as the TypeScript-Go semantic indexer backend. |
persistence.records | RecordStore | Explicit runtime persistence for flows, plans, and bridge reads. |
generation.middleware | PromptMiddleware | Global generate/stream wrapper. |
generation.tokenizer | (text: string) => number | Custom token counting. |
generation.autoEscape | boolean | Auto-escape top-level XML-like user input fields. Defaults to true. |
generation.securityWarnings | boolean | Warn on suspicious input patterns. Defaults to development-only. |
devtools | CruxDevtoolsConfig | Non-default local, tunnel, remote, or Runtime Bridge behavior. |
observability | CruxObservabilityConfig | Explicit observability export, capture policy, or custom transport behavior. |
plugins | CruxPlugin[] | Composable runtime extensions. |
Returns: Crux, exposes the raw .config, lifecycle .dispose(), and compatibility registry methods. Source-discovered projects should use their authored prompt/context exports directly rather than looking them up through config().
config() is one-config-per-process. Calling it again replaces the previous active installation
before applying the new one, which keeps hot reload from stacking middleware or duplicating plugin
hook fan-out. dispose() restores only the hook layer installed by that config; independent layers
such as imperative devtools remain active.
| Member | Description |
|---|---|
.config | Readonly<CruxConfig> |
.dispose() | Tear down the Runtime Bridge and restore this config's hooks, plugins, and observability |
Observability capture controls live under observability:
config({
observability: {
recordInputs: false,
recordOutputs: false,
},
});Both default to true. When disabled, matching artifacts keep sizeBytes and hash but omit inline previews.
Plugin system
import type { CruxPlugin, CruxHooks, CruxPluginResult } from "@use-crux/core";
import { mergeHooks, applyPlugins } from "@use-crux/core";CruxPlugin
| Field | Type | Description |
|---|---|---|
name | string | Unique plugin name for debugging |
install | (hooks: Readonly<CruxHooks>) => CruxPluginResult | Install hooks into the hook store |
CruxPluginResult extends Partial<CruxHooks> with optional dispose?: () => void.
| Built-in plugin | Package |
|---|---|
withDevtools(options) | @use-crux/core/observability, name 'crux:devtools' |
withTelemetry(options?) | @use-crux/otel, name 'crux:otel' |
mergeHooks(base, patch)
Compose two hook states with fan-out semantics:
- Hooks: both handlers fire for every event
- Middleware: patch wraps base
- Collector: last-write-wins
applyPlugins(plugins, initialHooks)
Process an ordered list of plugins. Returns { hooks, dispose }.
Observability context
import { observe } from "@use-crux/core/observability";
import { withSession, createSessionId, createFlowId } from "@use-crux/core";withSession(sessionId, fn)
Groups all generate() calls within fn under a single session ID for devtools correlation.
createSessionId() / createFlowId()
Generate unique IDs for cross-boundary correlation.
observe.captureContext() / observe.withContext(ctx, fn)
Snapshot and restore observability context across async boundaries. In Convex, prefer @use-crux/convex/server and ctx.crux.runAction() so the boundary helper handles propagation.
Captured observability context
| Field | Type | Description |
|---|---|---|
runId | CruxRunId | Current run ID |
traceId | CruxTraceId | Current trace correlation ID |
spanStack | CruxSpanId[] | Active span stack, deepest last |
currentSpanId | CruxSpanId? | Deepest open span |
Model fallback
import {
fallback,
isFallback,
classifyError,
shouldAttemptFallback,
} from "@use-crux/core";fallback(models, options?)
Wraps multiple models into a single reference that tries each in order on qualifying failure.
const model = fallback([gpt4o, claudeSonnet]);
const model = fallback([gpt4o, claudeSonnet], {
on: ["rate_limit", "timeout"],
timeout: { attempt: 10_000, firstToken: 2_000 },
});| Option | Type | Description |
|---|---|---|
id | string? | Stable id for Project Index and trace joins |
description | string? | Human-readable label for docs/devtools |
on | ErrorCategory[]? | Which categories trigger fallback (default: all) |
shouldFallback | (error: Error) => boolean? | Custom error predicate (takes priority over on) |
when | (result) => boolean | Promise<boolean> | Successful-result predicate that triggers invalid_response fallback when it returns true |
timeout | { attempt?, firstToken? }? | Per-attempt and stream first-token budgets in ms |
onFallback | (info) => void | Promise<void> | Called before moving from a failed model to the next one |
ErrorCategory = 'rate_limit' | 'timeout' | 'server_error' | 'connection_error' | 'auth_error' | 'invalid_response'
classifyError(error)
Classifies an error into an ErrorCategory or null.
| Error | Category |
|---|---|
| HTTP 429 | rate_limit |
| HTTP 500–599 | server_error |
| HTTP 401/403 | auth_error |
TimeoutError, ETIMEDOUT, AbortError | timeout |
ECONNREFUSED, ENOTFOUND | connection_error |
| invalid response predicate | invalid_response |
| HTTP 400, unknown | null (no fallback) |
Validation retry
import {
ValidationExhaustedError,
isValidationExhaustedError,
repairJsonText,
} from "@use-crux/core";
import type { ValidationRetryOptions } from "@use-crux/core";ValidationRetryOptions
| Field | Type | Description |
|---|---|---|
maxRetries | number? | Default 3. Each consumes a step from maxSteps. |
onRetry | (attempt, zodError) => void? | Per-retry hook |
onExhausted | (attempts, lastError) => void? | Final-failure hook |
ValidationExhaustedError
Thrown when validation retries exhaust. Properties: lastRawOutput, zodErrors, attempts, maxAttempts, promptId.
repairJsonText(text)
Zero-cost JSON text repair. Returns repaired text or null if unfixable.
See the Validation retry guide.
Security utilities
import {
safe,
raw,
limit,
wrap,
escapeXml,
truncate,
userContent,
detectSuspiciousPatterns,
} from "@use-crux/core";| Function | Signature | Description |
|---|---|---|
escapeXml(text) | string → string | Escape <>&"' |
truncate(text, max?) | string → string | Truncate (default max: 10,000) |
safe\...`` | tagged template | Auto-escape interpolated values |
raw(text) | string → SafeWrapper | Skip escaping inside safe templates |
limit(text, max?) | string → SafeWrapper | Truncate + escape inside safe templates |
wrap(text, opts?) | string → SafeWrapper | Escape + wrap in <user-input> |
userContent(text) | string → string | Standalone escape + wrap |
detectSuspiciousPatterns(text) | string → { suspicious, patterns[] } | Scan for prompt injection patterns |
Embedding
import { embedding } from "@use-crux/core/embedding";Creates a reusable dense or sparse embedding primitive with built-in batching, governance, and concurrency control. See the Embeddings guide for usage.
Think of embedding() as provider policy, not search policy:
const dense = embedding({ kind: 'dense', ... }) // semantic vector
const sparse = embedding({ kind: 'sparse', ... }) // keyword vector
indexer({ dense, sparse }) // write vectors
retriever({ dense, sparse }) // query vectorsHybrid search uses both embeddings through retriever() or VectorStore.search(). It is not kind: 'hybrid'.
| Field | Type | Description |
|---|---|---|
kind | 'dense' | 'sparse' | Vector type |
name | string | Embedding model name |
dimensions | number? | Required for 'dense' |
maxInputTokens | number | Hard limit per text |
batch | { maxSize?, concurrency? }? | Batch behavior |
preprocess | EmbeddingPreprocessor | EmbeddingPreprocessor[]? | Normalization before cache/provider calls |
truncate | { strategy: 'fail' } | { strategy: 'chars'; maxChars: number }? | Input-limit policy |
retry | { maxAttempts; baseDelayMs?; maxDelayMs?; shouldRetry? }? | Provider batch retry policy |
cache | EmbeddingCache? | Policy-aware per-text vector cache |
rateLimit | { concurrency: number }? | Cross-call provider concurrency cap |
countTokens | (text) => number | Optional tokenizer for maxInputTokens checks |
embed | (texts) => Promise<{ embeddings }> | Provider call |
Hybrid retrieval is composed above this layer through stores or retrievers.
import {
embedding,
embeddingCache,
normalizeText,
} from "@use-crux/core/embedding";
const dense = embedding({
kind: "dense",
name: "docs-embedding",
dimensions: 1536,
maxInputTokens: 8191,
batch: { maxSize: 100, concurrency: 3 },
preprocess: normalizeText({ trim: true, collapseWhitespace: true }),
truncate: { strategy: "fail" },
retry: { maxAttempts: 3, baseDelayMs: 250 },
cache: embeddingCache({ store, namespace: "embed-cache" }),
rateLimit: { concurrency: 3 },
embed: async (texts) => ({ embeddings: await provider.embedMany(texts) }),
});Retrieval
import {
knowledgeBase,
retriever,
retrievalRecipe,
retrieve,
rerank,
} from "@use-crux/core/retrieval";Creates a query-first retriever that turns text into scored hits, prompt context, and query tools. See the Retrieval guide for the full architecture.
Use retriever() as the app-facing search object:
const docs = retriever({
id: "docs",
namespace: "product-docs",
records,
vectors,
dense,
sparse,
search: { mode: "hybrid" },
});
const hits = await docs.retrieve("refund policy");
const answer = prompt({
use: [docs],
system: "Answer from the retrieved docs.",
});| Field | Type | Description |
|---|---|---|
id | string | Stable retriever identifier |
indexerId | string? | Writer indexer id when it differs from id |
namespace | string | Required corpus boundary |
records | RecordStore? | JSON record hydration for record-backed retrieval |
vectors | VectorStore? | Dense, sparse, or hybrid vector search |
dense | DenseEmbedding? | Dense query embedding |
sparse | SparseEmbedding? | Sparse query embedding |
retrieve | (query, options) => Promise<RetrieverHit[]>? | Fully custom retrieval path |
Retrievers expose:
retrieve(query, options?)inject: 'context' | 'tool' | 'both'when used inprompt({ use })asContext()andasTools()for advanced/manual integrations
Use retrievalRecipe() with the rerank() step when retrieved candidates need model-backed ordering before they reach a prompt, tool, or grounding flow.
Use grounding() from @use-crux/core/citations when retrieval context must become a cited answer contract.
Indexing
import { indexer } from "@use-crux/core/indexing";Creates the write-time document indexing primitive. It owns chunking, transforms, embedding at write time, and store writes.
| Field | Type | Description |
|---|---|---|
id | string | Stable indexer identifier |
namespace | string | Required corpus boundary |
records | RecordStore | Chunk, parent, and corpus record storage |
vectors | VectorStore? | Dense, sparse, or hybrid vector index |
dense | DenseEmbedding? | Dense write-time embedding |
sparse | SparseEmbedding? | Sparse write-time embedding |
chunker | Chunker? | Override default chunker |
transforms | ChunkTransform[]? | Transform chunks before write |
Indexers expose:
chunk()indexDocuments()indexChunks()deleteSource()clear()
Routing
@use-crux/core/routing exports router, split, retry, fallback, cascade, their type guards, routing receipt helpers, and resolveModel. Stable id fields are optional for execution but recommended for index and trace joins. See the Routing guide for full coverage.
Internal orchestration
@use-crux/core exports orchestration primitives (orchestrateGenerate, orchestrateStream, resolveModel, wrapStreamIterable, withBudget) used by adapter packages. They're stable for adapter authors but not intended for general application code. See Building custom adapters.
Types
import type {
Prompt,
AnyPrompt,
Context,
PromptConfig,
ContextDef,
PromptResolution,
ResolvedPrompt,
SystemBlock,
InspectResult,
InspectPart,
DroppedContext,
GenerationSettings,
PromptAdaptation,
ProviderAdaptations,
ModelInfo,
PromptHooks,
PrepareHookArgs,
GenerateHookArgs,
ErrorHookArgs,
PromptMiddleware,
MergedInput,
Message,
FallbackOptions,
ErrorCategory,
CruxConfig,
Crux,
PromptRegistry,
FlowScope,
FlowResult,
FlowSnapshot,
FlowSummary,
StepOptions,
SuspendOptions,
} from "@use-crux/core";PromptResolution, SystemBlock, ResolvedPrompt, ModelInfo, and AnyPrompt are re-exported from @use-crux/core for adapter authors who consume .resolve() output directly. Adapter packages constrain their context tuple as readonly Context<z.ZodType>[] (rather than Context<any>[]) so MergedInput<TOwnInput, TContexts> carries through generate() / stream() without explicit type arguments at the call site.
Related
- Guide: Foundations, mental model and primitives tour
- Reference: Adapters, execution layer
- Reference: Storage, persistent RecordStore, VectorStore, and AssetStore implementations