Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.pecta.ai/llms.txt

Use this file to discover all available pages before exploring further.

The @pecta/core SDK runs quality gates entirely inside your Node.js process. There is no network call on the hot path: gates execute in parallel, the engine returns a result object, and telemetry ships to Pecta cloud asynchronously in a background batch. This makes the SDK the right choice for latency-critical workloads such as RTB pipelines, where you need a gate decision in under 15 ms.
1

Install the package

npm install @pecta/core
The package is ESM-only and requires Node.js 22 LTS or later.
2

Create an engine

Call createEngine once — typically at module initialisation — and reuse the engine for every request. Engines are stateless and safe to share across async calls.
import { createEngine, gates } from "@pecta/core";

const engine = createEngine({
  gates: [
    gates.latency({ maxMs: 100 }),
    gates.filesystem(),
    gates.pii(),
  ],
  timeout: 50,   // total ms budget across all gates; default 50
});
timeout is the wall-clock budget shared across all gates. If the budget is exhausted the engine aborts remaining work and marks those gates as failed.
3

Evaluate an agent output

Call engine.evaluate() with an EvaluationContext after your agent returns a result. The call is async and resolves in the time it takes all gates to run (bounded by timeout).
const result = await engine.evaluate({
  agent_id: "research-bot-v2",
  tool: "shell.run",
  output: { stdout: "ls -la /tmp" },
  latency_ms: 18,
});
4

Check the result

result.passed is true only when every gate passes (or is explicitly skipped). Inspect result.gates to see which gates blocked the output and why.
if (!result.passed) {
  // result.gates contains every gate's verdict and reason string
  const blocked = result.gates.filter((g) => !g.passed);
  console.error("blocked:", blocked);
  // e.g. [{ name: "filesystem", passed: false, reason: "destructive rm command detected", latency_ms: 1.2 }]
}
The full EvaluationResult shape:
{
  evaluation_id: string;   // unique ID per call
  agent_id: string;
  tool?: string;
  passed: boolean;
  gates: Array<{
    name: string;
    passed: boolean;
    reason?: string;        // present when passed is false
    latency_ms: number;
    skipped?: boolean;      // gate chose not to run (treated as pass)
  }>;
  total_latency_ms: number;
  timestamp: string;        // ISO 8601 UTC
}
5

Send telemetry

Instantiate PectaTelemetry with your publishable key and call telemetry.track(result) after every evaluation. The call is synchronous and non-blocking — it adds the result to an in-memory buffer and returns immediately.
import { PectaTelemetry } from "@pecta/core/telemetry";

const telemetry = new PectaTelemetry({
  publishableKey: process.env.PECTA_PUBLISHABLE_KEY!,
  secretKey: process.env.PECTA_SECRET_KEY,   // enables HMAC signing
  hmac: true,
  sampleRate: 0.1,   // ship 10% of evaluations to cloud
});

telemetry.track(result);   // never blocks, never throws

process.on("SIGTERM", () => telemetry.shutdown());
Telemetry flushes automatically every 5 seconds or when 100 events accumulate. Call shutdown() before your process exits to drain any remaining buffer.
sampleRate controls the fraction of evaluations that are POSTed to the cloud. It does not affect local gate execution or which evaluations feed into reputation scoring — all evaluations are counted locally regardless of sample rate.

RTB / OpenRTB example

For programmatic advertising pipelines, use the RTB gate suite. Pass the original openRtbRequest as input and the bidder response as output, then add tmaxMs and startedAt so the tmaxGuard gate can skip downstream checks when the deadline is already exhausted.
import { createEngine, gates } from "@pecta/core";

const engine = createEngine({
  gates: [
    gates.rtb.tmaxGuard({ bufferMs: 15 }),
    gates.rtb.impidMatch(),
    gates.rtb.adomainVerify(),
    gates.rtb.bidSanity({ maxFloorMultiple: 50 }),
    gates.rtb.audienceSafety(),
    gates.rtb.bcatCompliance(),
  ],
  timeout: 15,   // tight budget for RTB
});

const result = await engine.evaluate({
  agent_id: "dsp-bidder-prod",
  input: openRtbRequest,
  output: openRtbResponse,
  tmaxMs: openRtbRequest.tmax,
  startedAt: requestStartMs,   // performance.now() before the upstream call
});

Writing a custom gate

A gate is any object with a name string and a run function. The engine provides an AbortSignal — check it at the start of long operations to respect fail-fast and timeout behaviour.
const myGate = {
  name: "my.custom",
  run: async (ctx, signal) => {
    if (signal.aborted) return { passed: false, reason: "aborted" };
    // ...your check...
    return { passed: true };
  },
};

const engine = createEngine({ gates: [myGate] });
run returns { passed: boolean, reason?: string, skipped?: boolean, details?: unknown }. The engine fills in name and latency_ms automatically.