Skip to main content
sec0-sdk/evaluator is the advanced reasoning layer you add after the decorator path is already working. Use it when a simple allowlist or fixed deny reason is not enough and you need the runtime to weigh context before deciding whether something should be allowed, denied, or escalated.

When To Add It

Add the contextual evaluator after you already have:
  • decorated hops producing stable runtime context
  • policy catching the obvious safe and unsafe cases
  • audit recording the decisions and review outcomes
At that point, the remaining review volume is usually concentrated in ambiguous cases. That is where the evaluator helps.

How It Fits With The Rest Of Sec0

  1. A decorated hop captures runtime context such as nodeId, runId, target, boundary, and propagated agent state.
  2. The evaluator reasons over that context when the action is not clearly safe or clearly out of policy.
  3. Policy maps the evaluator outcome to a deny or escalate reason.
  4. Approvals only see the cases that still require expert review.
That flow is what lets Sec0 reduce manual review without collapsing everything into simple allowlist logic.

What It Improves

The evaluator is useful when you want to:
  • reduce unnecessary escalations for actions that are unusual but still justified
  • escalate actions that look permissible on paper but are risky in context
  • use runtime findings and review outcomes to make later decisions more precise

Use It Inside A Decorated Hop

Assume the decorator path is already in place with initializeSec0App(...), policy.yaml, and sec0.config.yaml. Create one evaluator manager for the process, then call it at the hop that owns the ambiguous action:
import { sec0, AgentManager } from "sec0-sdk/instrumentation";
import { createContextualEvaluatorManager } from "sec0-sdk/evaluator";

const evaluator = createContextualEvaluatorManager({
  evaluatorSource: "local",
  evaluatorMode: "sync",
  local: {
    denyThreshold: 0.85,
    escalateThreshold: 0.45,
  },
});

class RefundMiddleware {
  @sec0.middleware()
  async enforce(
    input: { orderId: string; amount: number; reason: string },
    manager: AgentManager,
  ) {
    const evaluation = await evaluator.evaluate({
      action: {
        kind: "refund",
        summary: "Issue a customer refund",
        sideEffect: true,
        disclosure: false,
        crossesBoundary: true,
        tool: {
          server: "payments",
          name: "refundCharge",
          version: "1.0",
        },
        target: {
          boundary: "payments",
        },
      },
      actor: {
        id: manager.nodeId,
        role: "refund-agent",
      },
      purpose: {
        summary: "Resolve a refund request",
        justification: input.reason,
      },
      authority: {
        grantedScopes: ["payments.refund"],
        allowedBoundaries: ["payments"],
        approvals: [],
        delegations: [],
      },
      runtimeContext: {
        integrationSurface: "sec0",
        executionLayer: "middleware",
        runId: manager.runId,
        workflowState: {
          orderId: input.orderId,
          amount: input.amount,
        },
        unresolvedPrerequisites: [],
      },
      sourceUse: {
        sources: [],
      },
      constraints: {
        hard: [],
        soft: [],
        requiredPrerequisites: [],
        requiredApprovals: [],
        forbiddenBoundaries: [],
      },
      metadata: {},
    });

    if (evaluation?.output.decision === "deny") {
      throw new Error(evaluation.output.summary);
    }

    if (evaluation?.output.decision === "escalate") {
      return {
        status: "needs_review",
        summary: evaluation.output.summary,
      };
    }

    return await manager.invoke("PaymentsGateway.refund", input);
  }
}
This is where the evaluator fits in practice:
  1. decorators already give you stable runtime context such as nodeId and runId
  2. the evaluator weighs the action, purpose, authority, and current runtime state
  3. your existing deny or approval path handles the resulting decision
Keep the deny and escalate reason mapping in Policy. Use Middleware when you need hosted control-plane policy or remote runtime wiring around the evaluator.

Where It Connects

  • Policy: maps evaluator outcomes into deny or escalate reasons
  • Approvals & Escalations: handles the cases the evaluator still marks as ambiguous
  • Overview: shows how evaluator findings fit into the broader compliance-improvement loop
The createContextualEvaluatorManager(...) usage shown above is the main integration surface. Use Instrumentation for decorator-based runtime context and Middleware for hosted policy and runtime wiring.