Types & Interfaces
KERN has a full type system that compiles to TypeScript. Types, interfaces, discriminated unions, and configs — all defined in .kern, all emitted as idiomatic TS.
Type aliases
The type node creates union types or aliases. Use values for pipe-separated unions:
type name=StepState values="pending|running|completed|failed|skipped"
type name=ApprovalLevel values="auto|plan|step"
type name=StepEffect values="read|write|exec|network"Compiles to:
export type StepState = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
export type ApprovalLevel = 'auto' | 'plan' | 'step';
export type StepEffect = 'read' | 'write' | 'exec' | 'network';Aliasing existing types
Use alias instead of values to alias an existing type expression:
type name=UserMap alias="Record<string, User>"Export control
Add export=false to keep a type internal:
type name=InternalState values="init|ready" export=falseInterfaces
The interface node defines object shapes with typed fields:
interface name=StepAttempt
field name=startedAt type=string
field name=finishedAt type=string optional=true
field name=exitCode type=number optional=true
field name=error type=string optional=trueCompiles to:
export interface StepAttempt {
startedAt: string;
finishedAt?: string;
exitCode?: number;
error?: string;
}Fields
Each field child specifies a property:
name— field nametype— any TypeScript type:string,number,boolean,"Type[]","Record<K,V>", unionsoptional=true— generates the?modifier
Complex field types
interface name=Plan
field name=id type=string
field name=steps type="PlanStep[]"
field name=state type=PlanState
field name=currentStepId type="string|null"
field name=action type=PlanAction
field name=workspace type=WorkspaceSnapshotQuoted types allow any TypeScript expression: arrays, unions, generics, string literal unions like "'patch'|'diff'|'output'".
Inheritance
Use extends to inherit from another interface:
interface name=AdminUser extends=User
field name=role type=string
field name=permissions type="string[]"Discriminated unions
The union node creates tagged unions with a discriminant field (defaults to type):
union name=ContentSegment discriminant=type
variant name=prose
field name=text type=string
variant name=code
field name=language type=string
field name=code type=stringCompiles to:
export type ContentSegment =
| { type: 'prose'; text: string }
| { type: 'code'; language: string; code: string };The discriminant field is automatically added to each variant. Variants can have optional=true fields just like interfaces.
Config
The config node generates an interface plus a defaults constant:
config name=AgonConfig
field name=timeout type=number default=120
field name=forgeEnableSynthesis type=boolean default=false
field name=approvalLevel type=ApprovalLevel default=planCompiles to:
export interface AgonConfig {
timeout?: number;
forgeEnableSynthesis?: boolean;
approvalLevel?: ApprovalLevel;
}
export const DEFAULT_AGON_CONFIG: Required<AgonConfig> = {
timeout: 120,
forgeEnableSynthesis: false,
approvalLevel: 'plan',
};Fields with default values become optional in the interface. The defaults object uses Required<T> to guarantee all fields are present.
Error classes
The error node generates typed Error subclasses:
error name=PlanStateError extends=AgonError message="Invalid plan state: expected ${expectedStr}, got ${actual}"
field name=expected type="string|string[]"
field name=actual type=stringCompiles to:
export class PlanStateError extends AgonError {
constructor(
public readonly expected: string | string[],
public readonly actual: string,
) {
const expectedStr = Array.isArray(expected)
? expected.join(' | ') : expected;
super(`Invalid plan state: expected ${expectedStr}, got ${actual}`);
this.name = 'PlanStateError';
}
}Fields become readonly constructor parameters. Array fields are auto-joined to strings when used in the message template.
Supported type expressions
Any valid TypeScript type expression works inside type="...":
| Pattern | Example |
|---|---|
| Primitives | string, number, boolean |
| Arrays | "string[]", "PlanStep[]" |
| Unions | "string|null", "'patch'|'diff'" |
| Generics | "Record<string, unknown>", "Promise<Data>" |
| Function types | "(key: string) => Promise<Data>" |
| Tuples | "[string, number, boolean]" |
| Intersections | "BaseUser & AdminPerms" |
The parser is depth-aware — it correctly handles nested angle brackets, parentheses, and braces inside type expressions.
Full example
A real-world .kern file combining types, interfaces, and config:
type name=StepState values="pending|running|completed|failed|skipped"
type name=ApprovalLevel values="auto|plan|step"
interface name=ArtifactRef
field name=type type="'patch'|'diff'|'output'|'manifest'"
field name=path type=string
field name=engineId type=string optional=true
interface name=Plan
field name=id type=string
field name=steps type="PlanStep[]"
field name=state type=PlanState
field name=currentStepId type="string|null"
config name=AgonConfig
field name=timeout type=number default=120
field name=approvalLevel type=ApprovalLevel default=planNext: Functions — how KERN declares functions, methods, and services.