Tell the AI your rules once and keep them consistent across turns.
Context Compiler helps applications keep explicit user instructions separate from the chat transcript. It stores rules and corrections as compiler state so hosts can apply them consistently on later model calls.
The model writes responses. The compiler stores premise and policy rules.
This package is the TypeScript implementation of the Context Compiler engine, aligned with Python 0.7 behavior.
Context Compiler lets a host application:
- store explicit rules such as
use sqliteorprohibit docker - replace or remove earlier rules without asking the model to infer the correction
- block ambiguous or conflicting rule updates before calling the model
- save and restore rules between requests
Each user input produces a decision for the host:
update-> stored premise/policy rules changedclarify-> ask the user for clarification; do not call the modelpassthrough-> normal chat input
Directive examples:
set premise concise repliesuse sqliteprohibit dockerremove policy dockerclear premise
- Python is the source of truth for semantics.
- TypeScript package versions track Python compatibility by minor version.
- TS
0.N.ytargets semantic compatibility with the Python0.N.xline. - Patch versions evolve independently by language/repo.
- Core engine behavior aligned with Python 0.7 behavior.
- Shared behavior test coverage for:
- single-turn rule updates
- transcript replay
- saving and restoring state
- checkpoint restore
- experimental preprocessor behavior
- public API behavior
- Core public API for engine usage and transcript replay.
- Checkpoint APIs for saving and restoring rules plus pending clarification state.
- Controller APIs for step envelopes, preview/dry-run, and structural state diffs.
- Decision constants for host-side checks.
- Experimental preprocessor module exposed through a package subpath import.
- Fixture parity synced from the Python source-of-truth fixture corpus.
- REPL port
npm install @rlippmann/context-compilerexamples/integrations/nextjs-basic/— minimal Next.js App Router integration- request flow with compiler state where explicit instructions stay consistent across turns
clarifyblocks LLM calls- per-session compiler state via checkpoint export/import so sessions can resume safely
examples/integrations/node-basic/— minimal Node HTTP server integrationexamples/integrations/vercel_ai_sdk_structured_output/— host-side schema selection for Vercel AI SDK structured output
import {
createEngine,
getClarifyPrompt,
getDecisionState,
getPolicyItems,
getPremiseValue,
isClarify,
isPassthrough,
isUpdate
} from '@rlippmann/context-compiler';
const engine = createEngine();
const decision = engine.step('set premise concise replies');
if (isUpdate(decision)) {
const state = getDecisionState(decision);
if (state) {
console.log({
premise: getPremiseValue(state),
policies: getPolicyItems(state)
});
}
} else if (isClarify(decision)) {
console.log(getClarifyPrompt(decision));
} else if (isPassthrough(decision)) {
// passthrough
}State snapshots are intentionally opaque. Prefer helpers such as
getPremiseValue(state) and getPolicyItems(state) for value reads.
createEngine(init?)-> create an engine instance.engine.step(input)-> apply one user input and return aDecision.engine.state-> current stored premise/policy rules snapshot.engine.hasPendingClarification()-> check whether confirmation-only input is currently required.engine.exportJson()/engine.importJson(payload)-> state serialization utilities.engine.exportCheckpoint()/engine.importCheckpoint(payload)-> checkpoint persistence (authoritative_state+ pending confirmation state) that safely resumes pending confirmations.engine.exportCheckpointJson()/engine.importCheckpointJson(payload)-> JSON checkpoint wrapper persistence helpers.compileTranscript(messages)-> replay user messages and returnstateorconfirm.engine.applyTranscript(messages)-> replay user messages onto an existing engine instance.getPremiseValue(state)/getPolicyItems(state, value?)-> read helpers for state.step(engine, input)-> controller step envelope (output_version,mode,decision,state).preview(engine, input)-> dry-run step envelope withstate_before,state_after,diff, andwould_mutate(live engine state is restored).stateDiff(before, after)-> structural state diff used by preview.DECISION_PASSTHROUGH/DECISION_UPDATE/DECISION_CLARIFY-> decision kind constants.
The preprocessor is an optional host-side layer that can recognize some natural-language rule updates before they reach the engine.
For example:
- "keep replies concise"
- "don't suggest docker"
- "forget that previous policy"
Safety guidance:
- Always validate preprocessor output before applying a directive to the engine.
- If
engine.hasPendingClarification()is true, bypass preprocessing and pass raw input directly toengine.step(...). - Boundary behavior is conservative and false-negative-preferred: abstain rather than risk unsafe mutation.
Experimental preprocessor APIs are available via package subpath:
import { preprocessHeuristic, parsePreprocessorOutput, validatePreprocessorOutput } from '@rlippmann/context-compiler/experimental/preprocessor';function stepWithOptionalPreprocessor(engine: ReturnType<typeof createEngine>, userInput: string) {
if (engine.hasPendingClarification()) {
return engine.step(userInput);
}
const heuristic = preprocessHeuristic(userInput);
let engineInput = userInput;
if (heuristic.classification === 'directive' && heuristic.output !== null) {
const parsed = parsePreprocessorOutput(heuristic.output, { sourceInput: userInput });
if (parsed !== null) {
engineInput = parsed;
}
}
return engine.step(engineInput);
}The preprocessor is a convenience layer. The engine remains the authoritative source of state changes.
This module is intentionally experimental and separate from the deterministic core engine API.