Levels
The five built-in level definitions and pure level functions
The five levels
The level model is fixed. getEnergyLevels() returns these definitions, ordered highest to lowest, all deeply frozen:
| Value | Key | Label | Description |
|---|---|---|---|
100 | peak | Peak | High capacity. Planning, complex decisions, creative work. |
75 | active | Active | Good capacity. Focused execution, problem-solving. |
50 | steady | Steady | Moderate capacity. Routine tasks, familiar work. |
25 | low | Low | Limited capacity. Simple tasks, review, light work. |
0 | rest | Rest | Recovery. Consumption only - reading, reflecting. |
Cognitive profiles per level:
| Level | decisionCapacity | focusDuration | taskComplexity | interruptionTolerance |
|---|---|---|---|---|
100 | high | extended | complex | high |
75 | moderate | moderate | moderate | moderate |
50 | low | short | routine | low |
25 | minimal | minimal | simple | minimal |
0 | none | none | consumption | none |
getEnergyLevels
function getEnergyLevels(): ReadonlyArray<Readonly<EnergyLevelDefinition>>Get all energy level definitions, ordered highest to lowest (100, 75, 50, 25, 0).
getEnergyLevel
function getEnergyLevel(level: EnergyLevel): Readonly<EnergyLevelDefinition>Get the definition for a specific energy level. Throws Error('Invalid energy level: ...') for values outside the model.
import { getEnergyLevel } from '@kumbatio/energy-system'
getEnergyLevel(75).label // 'Active'cycleEnergyLevel
function cycleEnergyLevel(current: EnergyLevel): EnergyLevelCycle to the next energy level: 100 -> 75 -> 50 -> 25 -> 0 -> 100. Returns 100 when current is not a known level.
isEnergyLevel
function isEnergyLevel(value: unknown): value is EnergyLevelValidate that an unknown value is a valid EnergyLevel.
isEnergySource
function isEnergySource(value: unknown): value is EnergySourceValidate that an unknown value is a valid EnergySource.
isHigherEnergy
function isHigherEnergy(a: EnergyLevel, b: EnergyLevel): booleanReturns true if level a represents higher energy than level b (numeric comparison).
createEnergyOrigin
function createEnergyOrigin(): stringMint a unique producer identity for deterministic cross-context ordering. Uses crypto.randomUUID() when available, falling back to crypto.getRandomValues() and then to a time-plus-counter string, so it never throws in older runtimes.
The engine calls this internally when you don't pass originId to createEnergyEngine - you only need it yourself to mint a stable per-install identity that survives restarts. Both shipped desktop integrations do exactly that:
import { createEnergyOrigin, createEnergyEngine } from '@kumbatio/energy-system'
function stableOriginId(): string {
const KEY = 'myapp:energy-origin'
let id = localStorage.getItem(KEY)
if (!id) {
id = `myapp:app:${createEnergyOrigin()}`
localStorage.setItem(KEY, id)
}
return id
}
const engine = createEnergyEngine({ originId: stableOriginId() })Available since 0.5.4; earlier versions used it internally without exporting it. See Production Patterns for the write-identity discipline around origins and revisions.
createEnergyState
function createEnergyState(
level: EnergyLevel,
source?: EnergySource, // default: 'manual'
timestamp?: number, // default: Date.now()
revision?: number, // default: module-level auto-incrementing counter
origin?: string, // default: a stable per-process random origin id
): EnergyStateCreate a frozen EnergyState for the current moment. Intended for standalone use (tests, persistence adapters, external producers); the engine constructs its own states via setLevel.
Validation - each check throws an Error on failure:
| Parameter | Requirement |
|---|---|
level | Must pass isEnergyLevel |
source | Must pass isEnergySource |
timestamp | Finite number, >= 0 |
revision | Safe integer, >= 0 |
origin | Non-empty string |
import { createEnergyState } from '@kumbatio/energy-system'
const state = createEnergyState(50, 'scheduled')
// { level: 50, source: 'scheduled', timestamp: ..., revision: ..., origin: '...' }The unproduced state
An engine starts on a state nobody chose: the initialLevel default, before any user
action, persisted value or observed update has replaced it. That default is stamped with
sentinels rather than a real clock reading and a real identity.
const UNPRODUCED_TIMESTAMP = 0
const UNPRODUCED_ORIGIN = '0-initial'Two reasons they exist. Construction reads neither the clock nor the random source, which is what makes building an engine during a React render prerender-safe - Next.js Cache Components fails a build on unstable values baked into static output. And both sentinels sort below any real value, which is also the semantics you want: a persisted or observed state must always beat the default it replaces.
isUnproducedState
function isUnproducedState(state: Pick<EnergyState, 'timestamp' | 'origin'>): booleanTrue for the untouched default - its age and identity are not meaningful. Use it before
treating state.timestamp as a real moment.
import { isUnproducedState } from '@kumbatio/energy-system'
// Without the guard, an untouched engine reports an age measured from the epoch.
const ageMs = isUnproducedState(state) ? 0 : Date.now() - state.timestampgetEnergyMetrics already applies
this guard, reporting stateAgeMs: 0 for a state nobody has set yet.
