Crux
API Reference@use-crux/core

Connected Knowledge Assertions

assertions(), AssertionSet, resolution handles, assertion refs, and assertion relations.

import { assertions } from "@use-crux/core/knowledge";
import type {
  AssertionEmitApi,
  AssertionEmitOptions,
  AssertionIdentityRefInput,
  AssertionListOptions,
  AssertionListPage,
  AssertionOf,
  AssertionRelationListOptions,
  AssertionRef,
  AssertionRelateOptions,
  AssertionRelationRecord,
  AssertionRelationType,
  AssertionResolutionHandle,
  AssertionResolutionPolicy,
  AssertionResolutionResult,
  AssertionResolutionStatus,
  AssertionSet,
  AssertionSetOptions,
  AssertionsConfig,
} from "@use-crux/core/knowledge";

Overview

Assertion stages extract schema-typed propositions during indexing. knowledgeBase().assertions(stage) and view.assertions(stage) return lazy read handles over persisted assertions. Resolution handles partition visible assertions into selected, superseded, contested, and unresolved sets.

Prompt Bounds

Model-mode assertion derivation uses these internal bounds:

ConstantValueApplies To
MAX_DERIVE_BATCH_CHARS12000Estimated source content assigned to one generated extraction batch.
MAX_DERIVE_PROMPT_CHARS12000Final derive or repair prompt sent for one batch.

Chunks are sorted by ordinal and assigned whole, in order, to deterministic batches. The stage vocabulary, instructions, source id, and document title repeat per batch. The document body appears as a bounded excerpt in the first batch only. Calls scale with source size, and claims from all successful batches are unioned before cache replacement.

Routine per-chunk truncation is not used. Truncation warnings are returned on result.knowledge from knowledgeBase().index() and knowledgeBase().reindex() only when a single chunk is too large for one batch. The same bounded summary is recorded on the mutation effect receipt evidence.

assertions(config)

Creates a frozen assertion stage.

function assertions<const TTypes extends Record<string, z.ZodType<unknown>>>(
  config: AssertionsConfig<TTypes>,
): AssertionStage<TTypes>;

Parameters

type AssertionsConfig<TTypes extends Record<string, z.ZodType<unknown>>> = {
  readonly id: string;
  readonly version: number;
  readonly types: TTypes;
  readonly targets?: (chunks: readonly CruxChunk[]) => readonly CruxChunk[];
} & (
  | {
      readonly model: KnowledgeModel;
      readonly instructions?: string;
      readonly run?: never;
    }
  | {
      readonly run: AssertionRun<TTypes>;
      readonly model?: never;
      readonly instructions?: never;
    }
);
OptionTypeDefaultConstraints
idstringRequiredMust be non-empty.
versionnumberRequiredMust be an integer greater than or equal to 1.
typesRecord<string, z.ZodType<unknown>>RequiredMust include at least one Zod schema. Type names must be non-empty and cannot contain : or %.
targets(chunks: readonly CruxChunk[]) => readonly CruxChunk[]All chunksOptional. Receives the deterministic visible chunk order; returned chunks are matched back by (sourceId, chunkId). Every assertion evidence ref must point at a target chunk.
modelKnowledgeModelRequired in model modeMutually exclusive with run.
instructionsstringundefinedOnly valid with model.
runAssertionRun<TTypes>Required in run modeMutually exclusive with model. Must be a function.

Returns

The returned stage includes _tag: "AssertionStage", kind: "assertion", id, version, normalized types, mode, and fingerprint().

Target chunks

Every visible chunk is rendered into the derive prompt, but only the chunks selected by targets may be cited as assertion evidence. Selectors receive the deterministic visible chunk order and may return any subset; chunks are matched back by (sourceId, chunkId), so references survive batching and truncation. Returning chunks outside the visible set throws, and duplicates are ignored. With no selector, every visible chunk is a target — the default behavior.

Evidence citing a context-only chunk is rejected during validation with an invalid evidence — context-only chunk error (model stages after repair retries, run stages immediately). An empty target selection skips model generation for that source with a warning manifest; deterministic runs still validate any emitted claims against the empty target set.

Generated stages use a provider-portable wire schema: evidence references are generic chunk ids and are validated locally after generation against the visible target chunks. Generated output must include provenance; emit() keeps it optional and treats an omitted value as derived provenance.

Emit API

interface AssertionRunInput {
  readonly document: CruxDocument;
  readonly chunks: readonly CruxChunk[];
  readonly targets: readonly CruxChunk[];
}

interface AssertionEmitOptions {
  readonly evidence: KnowledgeRef | readonly KnowledgeRef[];
  readonly provenance?: "exact" | "derived";
}

interface AssertionEmitApi<TTypes extends Record<string, z.ZodType<unknown>>> {
  emit<TType extends keyof TTypes & string>(
    type: TType,
    data: z.infer<TTypes[TType]>,
    opts: AssertionEmitOptions,
  ): AssertionRef;
  relate(
    type: AssertionRelationType,
    from: AssertionRelationEndpoint<TTypes>,
    to: AssertionRelationEndpoint<TTypes>,
    opts: AssertionRelateOptions,
  ): void;
}

emit() returns { assertionId: string }. relate() accepts an assertion index from the same run, an AssertionRef, or a typed AssertionIdentityRefInput.

Failures

assertions() throws plain Errors for invalid identity, invalid mode, invalid model, invalid schema map, invalid type names, and invalid instructions.

Example

import { z } from "zod";
import { assertions } from "@use-crux/core/knowledge";

const facts = assertions({
  id: "commercial-facts",
  version: 1,
  types: {
    price: z.object({
      amount: z.number(),
      currency: z.string(),
    }),
  },
  run: (_input, api) => {
    api.emit(
      "price",
      { amount: 12, currency: "EUR" },
      {
        evidence: { kind: "chunk", sourceId: "pricing", chunkId: "main" },
        provenance: "exact",
      },
    );
  },
});

AssertionSet

interface AssertionSet<TTypes, TSelected = keyof TTypes & string> {
  readonly _tag: "AssertionSet";
  readonly id: string;
  readonly namespace: string;
  list(options?: AssertionListOptions): Promise<AssertionListPage<AssertionOf<TTypes, TSelected>>>;
  stream(): AsyncIterable<AssertionOf<TTypes, TSelected>>;
  relations(options?: AssertionRelationListOptions): Promise<AssertionListPage<AssertionRelationRecord>>;
  resolve(policy?: AssertionResolutionPolicy<TTypes, TSelected>): AssertionResolutionHandle<TTypes, TSelected>;
  asContext(options?: AssertionContextOptions): Context<z.ZodType<{}>>;
  inject(args: { input: Record<string, unknown>; promptId?: string }): Promise<InternalPromptInjection>;
}

Options

OptionTypeDefaultConstraints
AssertionSetOptions.typesreadonly TSelected[]All stage typesSelects assertion types at handle creation.
AssertionListOptions.limitnumber100Floored and clamped to at least 0.
AssertionListOptions.cursorstringundefinedPassed to the record store.
AssertionRelationListOptions.typesreadonly AssertionRelationType[]All relation typesSelects persisted assertion relation types.
AssertionRelationListOptions.limitnumber100Floored and clamped to at least 0.
AssertionRelationListOptions.cursorstringundefinedPassed to the record store.
AssertionContextOptions.prioritynumber50Floored and clamped to 0..100.
AssertionContextOptions.limitnumber50Floored and clamped to 0..Number.MAX_SAFE_INTEGER.
AssertionContextOptions.itemCharLimitnumber240Floored and clamped to at least 24.

Return Shapes

interface AssertionListPage<TItem> {
  readonly items: readonly TItem[];
  readonly cursor?: string;
}

type AssertionOf<TTypes, K = keyof TTypes> = {
  readonly assertionId: string;
  readonly type: K;
  readonly data: z.infer<TTypes[K]>;
  readonly evidence: readonly AssertionSupport[];
  readonly provenance: "exact" | "derived";
};

View-bound assertion sets only return assertions with visible evidence support. provenance is "derived" if any visible support is derived, otherwise "exact".

relations() returns full AssertionRelationRecord entries. For view-bound sets, relation evidence is filtered to visible support refs and relations with no visible supports are hidden.

Failures

list(), relations(), stream(), asContext(), and snapshot reads throw Error("knowledgeBase().assertions() requires record storage.") when no record store is configured. resolve().result() throws Error("Assertion resolution requires record storage.") without record storage.

Example

const set = docs.assertions(facts, { types: ["price"] });
const page = await set.list({ limit: 10 });

for (const item of page.items) {
  console.log(item.type, item.data);
}

Resolution

interface AssertionResolutionHandle<TTypes, TSelected> {
  readonly _tag: "AssertionResolution";
  readonly id: string;
  status(): Promise<AssertionResolutionStatus>;
  prepare(): Promise<void>;
  result(): Promise<AssertionResolutionResult<AssertionOf<TTypes, TSelected>>>;
  asContext(options?: AssertionContextOptions): Context<z.ZodType<{}>>;
  inject(args: { input: Record<string, unknown>; promptId?: string }): Promise<InternalPromptInjection>;
}
interface AssertionResolutionStatus {
  readonly state: "idle" | "ready";
  readonly cached: boolean;
  readonly generationId?: string;
  readonly revisionHash?: string;
}

interface AssertionResolutionResult<TItem> {
  readonly selected: readonly TItem[];
  readonly superseded: readonly TItem[];
  readonly contested: readonly TItem[];
  readonly unresolved: readonly TItem[];
  readonly trace: readonly AssertionResolutionTrace[];
}

type AssertionDecisionEvidence =
  | {
      readonly kind: "relation";
      readonly relationId: string;
      readonly type: AssertionRelationType;
      readonly evidence: readonly AssertionSupport[];
      readonly provenance: "exact" | "derived";
      readonly stageId: string;
      readonly stageVersion: number;
      readonly stageFingerprint: string;
    }
  | {
      readonly kind: "policy";
      readonly policyId: string;
      readonly note?: string;
    };

status() starts as { state: "idle", cached: false }. prepare() reads the current snapshot, computes a cache key from generation, optional view revision, selected types, and policy fingerprint, then resolves and caches the result. result() calls prepare() when needed.

Relation trace evidence contains support refs and relation provenance only. It does not hydrate chunk content or inline assertion data.

Policy

type AssertionResolutionPolicy<TTypes, TSelected> =
  | {
      readonly id: string;
      readonly version: number;
      readonly model: KnowledgeModel;
      readonly instructions?: string;
      readonly run?: never;
    }
  | {
      readonly id: string;
      readonly version: number;
      readonly run: (
        input: AssertionPolicyInput<AssertionOf<TTypes, TSelected>>,
        decision: AssertionPolicyDecision<AssertionOf<TTypes, TSelected>>,
      ) => void | Promise<void>;
      readonly model?: never;
      readonly instructions?: never;
    };

Without a policy, explicit assertion relations drive resolution: supersedes puts the target assertion in superseded, and conflictsWith puts both endpoints in contested. Everything else defaults to selected unless a policy marks it unresolved, superseded, or contested.

Example

const resolution = docs.assertions(facts).resolve({
  id: "prefer-latest",
  version: 1,
  run: ({ assertions }, decision) => {
    for (const assertion of assertions) {
      decision.select(assertion, "Accepted by deterministic policy.");
    }
  },
});

const result = await resolution.result();
console.log(result.selected.length);

Assertion References And Relations

interface AssertionRef extends JsonObject {
  readonly assertionId: string;
}

type AssertionRelationType =
  | "supports"
  | "amends"
  | "supersedes"
  | "narrows"
  | "conflictsWith";

interface AssertionIdentityRefInput<TType extends string = string, TData = unknown> {
  readonly type: TType;
  readonly data: TData;
}

Persisted assertion relations have _cruxRecordType: "knowledge-assertion-relation", a stable relationId, from, to, evidence, provenance, stage identity, generation identity, namespace, direction: "directed", and timestamps.

On this page