Documentation•Reference

HazelJS Decision Package

npm downloads

@hazeljs/​decision is the native Decision Runtime for HazelJS Agent OS — bounded, typed, auditable judgment as a first-class primitive.

It is not a thin wrapper around Jev, OpenAI, Gemini, or another external model. The default provider is hazel-agent: an evidence → candidates → judge → critic → confidence pipeline built from HazelJS primitives. External providers are optional adapters; every result still passes policy and Gatekeeper.

Models propose. Agents evaluate. Critics challenge. Confidence informs. Policies govern. Skillgate classifies. Gatekeeper authorizes. Durable Kernel executes. Agent VM isolates effects. Agent OS observes.

Reasoning ≠ Decision ≠ Policy ≠ Authorization ≠ Execution. Confidence is evidence for policy routing, never permission to mutate the world. High confidence never bypasses Skillgate / Agent Gatekeeper.

Quick Reference

  • Purpose: Bounded choice over a closed set (choices: [...] as const) with risk routing, confidence, deterministic policy, Gatekeeper auth, durable HITL, and optional capability execution.
  • When to use: Incident remediation, refund paths, ticket routing, fraud/risk triage, “needs human?” gates — anywhere an agent must pick from a known set instead of free-form LLM JSON.
  • Key APIs: createDecisionRuntime, decide, resumeFromHuman, evaluate, DecisionLab / createDecisionLab, createGovernedDecisionRuntime, DecisionModule / DecisionService, HazelDecisionAgent, MockDecisionProvider, createOpenAiDecisionProvider / createGeminiDecisionProvider / createJevDecisionProvider / createLocalDecisionProvider, createEnsembleDecisionProvider, DecisionCache, DecisionHistory, fitCalibrationFromHistory, projectDecisionFlow, createCostAwareRouter, @Decision.
  • Dependencies: Peer @hazeljs/​agent. Optional peers: @hazeljs/​agent-gatekeeper, @hazeljs/​skillgate, @hazeljs/​audit, @hazeljs/​ai, @hazeljs/​core, @hazeljs/​guardrails, @hazeljs/​observability, @opentelemetry/​api.
  • Common mistakes: Treating confidence as authorization; letting models invent choices outside the closed set; skipping Gatekeeper when execute: true; auto-feeding history into prompts; assuming calibration authorizes; expecting Flow projection to replace DecisionRuntime.

Installation

npm install @hazeljs/​decision @hazeljs/​agent
# recommended for production execution:
npm install @hazeljs/​agent-gatekeeper @hazeljs/​skillgate
# optional:
npm install @hazeljs/​ai @hazeljs/​audit @hazeljs/​cli

Or:

hazel add decision

Minimal example

import { createDecisionRuntime } from '@hazeljs/​decision';

const decisions = createDecisionRuntime();

const result = await decisions.decide({
  objective: 'Choose the safest production remediation',
  state: {
    errorRate: 0.38,
    previousErrorRate: 0.01,
    deploymentAgeMinutes: 4,
    failedHealthChecks: 8,
  },
  choices: ['retry', 'rollback', 'escalate', 'ignore'] as const,
});

/​/​ result.decision: 'retry' | 'rollback' | 'escalate' | 'ignore'

Architecture

APPLICATION
    │
    ▼
decisions.decide({ objective, state, choices, risk, … })
    │
    ▼
DECISION RUNTIME
    │
    ▼
COMPLEXITY /​ RISK ROUTER
    ├── fast           → judge
    ├── standard       → evidence → judge → confidence
    ├── deliberate     → evidence → candidates → judge → critic → confidence
    ├── human-required → … → HITL
    └── rules          → DNA /​ hybrid rules path
    │
    ▼
DETERMINISTIC POLICY  (allow | critique | review | deny | …)
    │
    ▼
GATEKEEPER (authorization)   ← only if execute: true
    │
    ▼
DURABLE CHECKPOINTS + receipts (@hazeljs/​agent)
    │
    ▼
CAPABILITY HANDLER (optional; never without auth)
flowchart TD
App[Application] --> Decide["decide()"]
Decide --> Router[ComplexityRouter]
Router --> Provider[DecisionProvider]
Provider --> Conf[Confidence]
Conf --> Policy[PolicyEvaluator]
Policy -->|allow| GK[Gatekeeper]
Policy -->|review| HITL[HumanTask]
Policy -->|deny| Stop[Denied]
GK -->|allow| Cap[CapabilityHandler]
GK -->|deny| Stop

What this package reuses (does not reimplement)

ConcernPrimitive
Agent Runtime / DNA / HITL / checkpoints@hazeljs/​agent
Call-time authorization@hazeljs/​agent-gatekeeper
Skill risk class (floor only)@hazeljs/​skillgate
Effect isolationHost effectGate / @hazeljs/​agent-vm (documented, not duplicated)
SpansOptional OTel tracer hazeljs

Skillgate curates and classifies skills. Gatekeeper authorizes each invocation. The decision runtime never collapses those layers.

decide() request shape

await decisions.decide({
  name: 'incident-remediation',           /​/​ optional DNA /​ registry key
  objective: 'Choose the safest remediation',
  state: incident,                        /​/​ data — prompt-injection resistant
  choices: ['retry', 'rollback', 'escalate', 'ignore'] as const,
  risk: 'high',                           /​/​ or { level, impact, reversible }
  strategy: 'auto',                       /​/​ fast | standard | deliberate | human-required | rules
  provider: 'hazel-agent',                /​/​ or mock /​ ensemble /​ openai /​ …
  policy: { confidence: { high: 0.95, medium: 0.7 } },
  execute: false,                         /​/​ true → Gatekeeper + capability after allow
  cache: false,                           /​/​ opt-in; requires runtime cache
  calibrate: false,                       /​/​ opt-in reporting remap
  context: { tenantId: 'acme', agentId: 'incident-agent' },
});

Result (selected fields)

FieldMeaning
decisionTyped choice from choices
confidence / confidenceDetailScore + type (heuristic | ensemble | calibrated …); never auth
evidence / candidates / criticPipeline artifacts
strategyResolved execution strategy
policy.outcomeallow | deny | critique | review | …
executionWhether capability was authorized / invoked
hitlHuman task id / status when review required
provenance.stagesStage names for Lab / audit
statusRun machine status (COMPLETED, WAITING_FOR_HUMAN, DENIED, …)

Providers

ProviderRole
hazel-agentDefault. Native offline pipeline; optional LLM judge/critic via generateObject
mockTest double (MockDecisionProvider)
autoRouter hook (defaults toward hazel-agent / cost profiles)
ensembleMajority / weighted / unanimous / risk-sensitive fan-out
openai / gemini / jev / localThin adapters via create*DecisionProvider — still governed
import {
  createGovernedDecisionRuntime,
  createOpenAiDecisionProvider,
  createJevDecisionProvider,
} from '@hazeljs/​decision';

const decisions = createGovernedDecisionRuntime({
  generateObject: aiService, /​/​ @hazeljs/​ai or duck-typed
  agentRuntime,
  auditService,
  gatekeeper,
});

decisions.providers.register(createOpenAiDecisionProvider(aiService));
decisions.providers.register(createJevDecisionProvider({ decide: jevDecide }));

On model failure, hazel-agent falls back to the heuristic judge/critic (never to “allow”). Invalid choices outside the closed set are rejected.

Complexity / risk router

RiskDefault strategyStages
lowfast (small choice sets)judge
mediumstandardevidence → judge → confidence
highdeliberate+ candidates + critic
criticalhuman-required+ HITL

Risk is trusted configuration. Models cannot lower it. Skillgate skill class may raise the risk floor only.

Confidence & policy

interface DecisionConfidence {
  value: number; /​/​ [0, 1]
  type: 'provider' | 'heuristic' | 'ensemble' | 'calibrated' | 'self-reported';
  calibrated: boolean;
  components?: {
    candidateSeparation?: number;
    criticAgreement?: number;
    evidenceCoverage?: number;
    providerConfidence?: number;
  };
}

Default confidence bands (overridable via DNA / request policy):

BandPolicy action
≥ high (default 0.95)allow (eligible for Gatekeeper)
≥ medium (default 0.7)critique
< mediumreview (HITL)

Even with confidence 0.999 and policy allow, Gatekeeper can deny. See package example high-confidence-denied.ts.

Decision DNA

Optional field on Agent DNA (format: 'hazeljs.agent.dna'):

exportAgentDna({
  name: 'incident-agent',
  tools: [],
  decisions: {
    'incident-remediation': {
      version: '3',
      objective: 'Choose the safest remediation',
      choices: ['retry', 'rollback', 'escalate', 'ignore'],
      risk: { level: 'high' },
      confidence: { high: 0.95, medium: 0.7 },
      evidence: { required: ['error-rate-change'], projections: [...] },
      scoring: { rollback: [{ evidenceKey: 'error-rate-change', weight: 0.4 }] },
      execution: {
        rollback: { capability: 'deployment.rollback' },
      },
    },
  },
});

Register with runtime.registry.register(...) or registry.registerFromDna(dna). DNA without decisions remains valid.

HITL & durable execution

Policy review / strategy human-required creates a HumanTask and checkpoints WAITING_FOR_HUMAN via @hazeljs/​agent stores.

await decisions.resumeFromHuman({
  decisionId,
  runId,
  action: 'override', /​/​ approve | reject | override
  decision: 'escalate',
  actor: 'ops-lead',
  reason: 'Production rollback currently prohibited',
  execute: true,
});

Overrides are audited. Resume loads the execution receipt and will not double-invoke the capability. Replay never re-executes side effects.

Decision Lab

import { createDecisionRuntime, createDecisionLab } from '@hazeljs/​decision';

const lab = createDecisionLab(createDecisionRuntime());
const run = await lab.run({ objective, state, choices: [...] as const, risk: 'high' });
/​/​ run.graph — pipeline nodes for UI

const cmp = await lab.compare(request, ['hazel-agent', 'mock']);
/​/​ cmp.executionForbidden === true

Agent Office hosts the control plane at /​office/​decisions (Run, Compare, Flow graph, History, Calibration).

Ensemble, cache, history, routing

import {
  createEnsembleDecisionProvider,
  DecisionCache,
  DecisionHistory,
  createCostAwareRouter,
} from '@hazeljs/​decision';

runtime.providers.register(
  createEnsembleDecisionProvider({
    providers: [hazel, openai],
    strategy: 'majority', /​/​ weighted | unanimous | risk-sensitive
  })
);

const runtime = createDecisionRuntime({
  cache: new DecisionCache({ ttlMs: 30_000 }),
  history: new DecisionHistory(),
});
await runtime.decide({ ..., cache: true }); /​/​ opt-in only; never caches side effects

runtime.providers.setRouterHook(
  createCostAwareRouter([
    { name: 'local', costTier: 'economy' },
    { name: 'hazel-agent', costTier: 'balanced' },
    { name: 'openai', costTier: 'premium', minRisk: 'high' },
  ])
);

history.trends(); /​/​ aggregates for Lab — never auto-injected into prompts

Flow graph projection

import { projectDecisionFlow, buildDecisionFlowDefinition } from '@hazeljs/​decision';

const graph = projectDecisionFlow({ name: 'incident-remediation', risk: 'high' });
const stub = buildDecisionFlowDefinition(graph); /​/​ optional @hazeljs/​flow registration

DecisionRuntime remains the authority. Flow projection is scaffolding / UI, not a second decision engine.

Calibration (opt-in)

import { fitCalibrationFromHistory, DecisionHistory } from '@hazeljs/​decision';

const history = new DecisionHistory();
const runtime = createDecisionRuntime({ history });
/​/​ decide + evaluate({ expectedDecision }) …
runtime.setCalibration(fitCalibrationFromHistory(history));
await runtime.decide({ ..., calibrate: true }); /​/​ reporting only

Calibrated scores never authorize execution and are never auto-fed into prompts.

Module / DI

import { DecisionModule, DecisionService, createGovernedDecisionRuntime } from '@hazeljs/​decision';

DecisionModule.forRoot({ generateObject, agentRuntime, gatekeeper, auditService });

/​/​ or without DI:
const decisions = createGovernedDecisionRuntime({ generateObject, gatekeeper });

DecisionService.decide(...) delegates to the wired DecisionRuntime.

Observability

In-process metrics include:

  • hazeljs_decisions_total
  • hazeljs_decision_duration
  • hazeljs_decision_confidence
  • hazeljs_decision_critic_total
  • hazeljs_decision_review_total
  • hazeljs_decision_override_total
  • hazeljs_decision_denied_total
  • hazeljs_decision_execution_total
  • hazeljs_decision_failures_total

Optional OTel span: decision.run (tracer hazeljs). Raw state is not recorded by default.

Security

  • State is data (prompt-injection resistant).
  • Choices are a closed set — invalid model output is rejected.
  • Risk / policy / capabilities are not model-writable.
  • Cross-tenant evidence is keyed by decision id + tenantId on provenance.
  • Replay never re-executes side effects.
  • Missing Gatekeeper ⇒ execution denied when execute: true.
  • History / calibration never auto-inject into prompts.

CLI

hazel decision run --risk high
hazel decision compare --providers hazel-agent,mock
hazel decision flow --risk critical
hazel decision lab

Examples (package)

npx tsx examples/​incident-remediation.ts
npx tsx examples/​refund-hitl.ts
npx tsx examples/​high-confidence-denied.ts
npx tsx examples/​prompt-injection.ts
npx tsx examples/​decision-lab.ts
npx tsx examples/​flow-and-calibration.ts
npx tsx examples/​ensemble.ts

Progressive complexity

  1. decide()
    • provider
    • risk + confidence
    • critic + policy
    • Gatekeeper + durable HITL
    • Decision DNA + Lab + evaluation / calibration

Level 1 users are not forced into Level 6 concepts.

Intentionally never

  • Automatic prompt injection of historical outcomes
  • Confidence as authorization
  • Collapsing Skillgate + Gatekeeper into the decision model