Type System
Input merging, conditional return types, and generation settings.
Crux infers your prompt's input and output types from its schemas and contexts: input fields merge across use entries, conditional contexts become optional, and adapter return types change when a prompt declares output. This page explains the merging and inference rules you will hit in practice, plus the resolution and inspection APIs for checking what a prompt will actually send.
Input merging
When a prompt uses contexts with input schemas, all fields are merged into a single type:
import { , } from "@use-crux/core";
import { } from "zod";
const = ({
: .({ : .().() }),
: ({ }) => "...",
});
const = ({
: [],
: .({ : .() }),
});
type Input = .<typeof .>;If two contexts declare the same input key, prompt throws at definition time. The prompt's own fields take precedence over context fields.
If two contexts legitimately need the same field, extract it into a shared
context and have both depend on it, or namespace the fields (e.g., brandTone
vs userTone).
Conditional contexts and input types
Conditional contexts (those wrapped with when(), placed inside match(), or using context-level when) have their input keys automatically wrapped as Partial<> in the merged type:
import { context, prompt, when } from "@use-crux/core";
import { z } from "zod";
const brand = context({
input: z.object({ brandVoice: z.string() }),
system: ({ input }) => `## Brand\n${input.brandVoice}`,
});
const p = prompt({
use: [when((i) => !!i.brandVoice, brand)],
input: z.object({ instruction: z.string() }),
});
// brandVoice is optional because the context is conditional
p.resolve({ input: { instruction: "Edit this" } }); // OK: brandVoice omitted
p.resolve({ input: { instruction: "Edit", brandVoice: "Pro" } }); // OK: brandVoice providedThe same applies to context-level when:
const lang = context({
input: z.object({ lang: z.string() }),
when: ({ input }) => input.lang !== "English",
system: ({ input }) => `Respond in ${input.lang}.`,
});
// lang's input key becomes optional in the merged typeThe ContextEntry type accepted by use is:
type ContextEntry =
| Context<any> // required input keys
| ConditionalContext<any> // Partial input keys (from when())
| MatchSpec // Partial input keys (from match())
| false
| null
| undefined; // filtered out, no type contributionConditional return types
Adapters return different types based on whether output is defined:
// With output → result.object is typed
const result = await generate(structured, { model, input: { ... } })
result.object // z.infer<typeof OutputSchema>
// Without output → result.text is a string
const result = await generate(textOnly, { model, input: { ... } })
result.text // stringGeneration settings
GenerationSettings provides SDK-agnostic fields (temperature, maxTokens, topP, topK, stopSequences, frequencyPenalty, presencePenalty, reasoning) plus portable tool-loop controls (toolChoice, stopWhen, maxSteps). Each adapter maps these to its SDK's naming conventions. Provider-specific options belong in that adapter's typed extra object, not in prompt settings.
Resolution & inspection
Every prompt has .resolve() and .inspect() methods that work without executing any model call.
.resolve(): returns the composed, SDK-agnostic prompt data:
const resolved = editDraft.resolve({
input: { ... },
provider: 'openai',
tokenBudget: 4000,
})
// → { system, prompt, schema, tools, settings }.inspect(): shows how the system message was assembled:
const debug = editDraft.inspect({ input: { ... }, tokenBudget: 2000 })
debug.system.total // the full assembled system text
debug.system.parts // InspectPart[]: source, text, tokens, skipped
debug.system.totalTokens // total system tokens
debug.prompt // { text, tokens } | undefined
debug.totalTokens // system + prompt tokens
debug.droppedContexts // DroppedContext[]: dropped by token budget
debug.excludedContexts // ExcludedContext[]: excluded by when/match conditions
debug.tools // string[]: resolved prompt-time tool namesIntrospection properties
editDraft.id; // 'draft-edit'
editDraft.description; // string | undefined
editDraft.tags; // readonly string[]
editDraft.contexts; // the contexts tuple
editDraft.inputSchema; // merged Zod schema
editDraft.outputSchema; // Zod output schema | undefined
editDraft.hasOutput; // boolean
editDraft.config; // raw config object