Packages
@loop-engine/core
@loop-engine/core defines the type contract used by every other @loop-engine/* package and ships no runtime implementations.
Install
1npm install @loop-engine/coreOverview
Core provides branded identifiers, actor and guard primitives, and canonical loop lifecycle interfaces. Runtime, DSL, adapters, and SDK layers all import these contracts.
Branded IDs
1export type LoopId = string & { readonly __brand: "LoopId" }2export type StateId = string & { readonly __brand: "StateId" }3export type TransitionId = string & { readonly __brand: "TransitionId" }4export type AggregateId = string & { readonly __brand: "AggregateId" }5export type ActorId = string & { readonly __brand: "ActorId" }6export type GuardId = string & { readonly __brand: "GuardId" }7export type SignalId = string & { readonly __brand: "SignalId" }8export type OutcomeId = string & { readonly __brand: "OutcomeId" }9export type CorrelationId = string & { readonly __brand: "CorrelationId" }Helper constructors cast incoming strings to branded IDs:
1import { aggregateId, transitionId } from "@loop-engine/core"2 3const aggregate = aggregateId("EXP-2026-001")4const transition = transitionId("approve")Loop definition types
1export type ActorType = "human" | "automation" | "ai-agent" | "webhook" | "system"2export type LoopStatus = "OPEN" | "IN_PROGRESS" | "CLOSED" | "ERROR" | "CANCELLED"3 4export interface GuardSpec {5 id: GuardId6 description: string7 failureMessage: string8 severity: "hard" | "soft"9 evaluatedBy: "runtime" | "module" | "external"10}11 12export interface StateSpec {13 id: StateId14 description?: string15 isTerminal?: boolean16 isError?: boolean17}18 19export interface TransitionSpec {20 id: TransitionId21 from: StateId22 to: StateId23 allowedActors: ActorType[]24 guards?: GuardSpec[]25 sideEffects?: SideEffectSpec[]26 description?: string27}28 29export interface LoopDefinition {30 id: LoopId31 version: string32 description: string33 domain: string34 states: StateSpec[]35 initialState: StateId36 transitions: TransitionSpec[]37 outcome: OutcomeSpec38 participants?: string[]39 spawnableLoops?: LoopId[]40 metadata?: Record<string, unknown>41}Runtime state types
1export interface ActorRef {2 type: ActorType3 id: ActorId4 displayName?: string5 sessionId?: string6 agentId?: string7}8 9export interface LoopInstance {10 loopId: LoopId11 aggregateId: AggregateId12 orgId: string13 currentState: StateId14 status: LoopStatus15 startedAt: string16 closedAt?: string17 correlationId: CorrelationId18 metadata?: Record<string, unknown>19}20 21export interface TransitionRecord {22 id: string23 loopId: LoopId24 aggregateId: AggregateId25 transitionId: TransitionId26 fromState: StateId27 toState: StateId28 actor: ActorRef29 evidence: Evidence30 occurredAt: string31 durationMs?: number32}Signal and outcome types
1export interface OutcomeSpec {2 id: OutcomeId3 description: string4 valueUnit: string5 measurable: boolean6 businessMetrics?: BusinessMetric[]7}8 9export interface Signal {10 id: SignalId11 type: string12 subject: string13 confidence?: number14 observedAt: string15 payload: Record<string, unknown>16 triggeredLoopId?: LoopId17}Design principles
- Type-first boundaries keep packages interoperable without runtime coupling.
- Zero runtime dependencies keep
coresafe for shared libraries and edge bundles. - Branded IDs reduce accidental string mixing across loop, actor, transition, and signal contexts.