NESSCOAgentic Harness
Documentation

@nessco/harness

The runtime boundary between what an agent decides and what it is allowed to do. Zero dependencies, Node 18+.

  • v0.1.0
  • Access by request
  • 30 passing tests

Technical reference

This page documents the SDK surface. The two below document the system underneath it.

Platform architecture

What sits where, what every tool call costs, where we use models and where we deliberately don't, what this stops and what it doesn't, and how to attack your own setup before someone else does.

Read the architecture

Recursive language models

How a model that trains on its own output works, where that goes wrong, and a demonstrator that runs the loop live in your browser.

Read the RLM notes

The SDK isn't self-serve. There's no public package to npm install and no self-checkout — every reference below is real, and the sandbox further down runs the actual policy engine in your browser, but a working API key is issued only after a short call with our team. That's by design: we want to know what your agents can reach before we hand you the switch that controls it.

Live sandbox

This runs the real policy engine and content detectors — the same definePolicy, scoreIntent and inspect shown in the reference below — against the demo policy on the right, entirely in your browser. Pick a scenario or edit the intent and re-run it.

Decision

demo policy running above
definePolicy({
  agent: 'support-copilot',
  defaultAction: 'block',
  allow: [{ tool: 'crm.read' }, { tool: 'ticket.*' }],
  hold:  [{ tool: 'refund.issue', when: { amount: { gt: 250 } }, approver: 'finance-oncall' }],
  block: [
    { sendsTo: '*', carrying: ['secret', 'pii'] },
    { origin: 'untrusted_content', escalatesTo: ['shell', 'iam', 'deploy'] }
  ]
})

Access & install

There is no public registry package yet. Approved partners are issued a scoped API key and a token for our private registry during onboarding — the install step below is what it looks like once you have one, shown for reference.

shell · requires a registry token
npm config set @nessco:registry https://npm.nessco.ai
npm config set //npm.nessco.ai/:_authToken $NESSCO_REGISTRY_TOKEN

npm install @nessco/harness

Quickstart

The agent still reasons and plans freely. Every tool call, retrieval and outbound request is captured as a structured intent first, measured against policy, and then allowed, redacted, held for a human, or blocked.

index.js
import { createHarness, definePolicy } from '@nessco/harness';

const policy = definePolicy({
  agent: 'support-copilot',
  defaultAction: 'block',        // anything unnamed is denied
  allow: [
    { tool: 'crm.read' },
    { tool: 'ticket.*' }
  ],
  hold: [
    { tool: 'refund.issue', when: { amount: { gt: 250 } }, approver: 'finance-oncall' }
  ],
  block: [
    { sendsTo: '*', carrying: ['secret', 'pii'] },
    { origin: 'untrusted_content', escalatesTo: ['shell', 'iam', 'deploy'] }
  ],
  budgets: { 'tool.calls': { limit: 200, per: 'hour' } }
});

const harness = createHarness({
  policy,
  auditSecret: process.env.NESSCO_AUDIT_SECRET,
  approve: async (intent) => pageOncall(intent)  // resolve true to release a hold
});

const decision = await harness.guard({
  tool: 'refund.issue',
  args: { amount: 4000, customer: 'c_123' },
  reversible: false
});

decision.action;   // 'hold'
decision.reasons;  // ['matched hold rule for refund.issue']
decision.risk;     // { score: 45, factors: [...] }

Wrapping a tool

wrapTool is the safer integration: a blocked intent throws, so a caller that ignores the decision object still cannot proceed.

index.js
const issueRefund = harness.wrapTool('refund.issue', async ({ amount }) => {
  return payments.refund(amount);
}, { reversible: false });

try {
  await issueRefund({ amount: 4000 });
} catch (err) {
  if (err.name === 'HarnessBlocked') { /* refused */ }
  if (err.name === 'HarnessHeld')    { /* waiting on finance-oncall */ }
}

How a decision is reached

  1. Inspect — the payload (or args) is scanned for credentials, personal data, source code and injected instructions.
  2. Evaluate — rules are applied by outcome severity, never declaration order. A block rule always beats a hold, which always beats an allow, so adding a permissive rule can never quietly widen an existing boundary.
  3. Score — independent risk factors accumulate: irreversibility, untrusted origin, external destination, privileged tool class, and what the detectors found.
  4. Escalate — an intent the rules would allow but that scores at or above holdAbove (default 70) is held anyway. Rules are a floor, not a ceiling.
  5. Charge budgets — exceeding a budget blocks, regardless of the matched rule.
  6. Record — the decision is appended to a tamper-evident audit chain.

Policy reference

Rule fields:

FieldApplies toMeaning
toolallTool name or glob (crm.*, *).
sendsToblock/holdMatch on where the agent is sending something, instead of on the tool name. egress is the older spelling and still works.
whenallCondition object or predicate function.
carryingallOnly match if the payload contains these finding kinds: secret, pii, source_code, injection.
originallOnly match intents from this origin: user, system, untrusted_content.
escalatesToallOnly match if the tool reaches one of these capabilities.
approverholdWho is paged to release the intent.
reasonallOverrides the generated explanation.

Condition operators

eq, ne, gt, gte, lt, lte, in, notIn, startsWith, matches.

policy.js
when: { amount: { gt: 250 }, region: { in: ['eu', 'uk'] } }
when: (intent) => intent.args.rows > 10_000   // or just a function

An unknown operator throws at evaluation time rather than silently matching nothing.

Intent shape

shape
{
  agent: 'support-copilot',
  tool: 'http.post',
  args: { ... },
  payload: 'raw text to inspect',   // defaults to a canonical form of args
  origin: 'untrusted_content',      // 'user' | 'system' | 'untrusted_content'
  destination: 'https://example.com/webhook',
  reversible: false,
  cost: 1                          // charged against budgets
}

Detectors

Importable on their own from @nessco/harness/detectors:

  • detectSecrets — AWS keys, GitHub/Slack/Stripe/Google/OpenAI tokens, private key blocks, JWTs, assigned credentials, connection strings with inline passwords.
  • detectPII — email, US SSN, phone, IP, and payment cards confirmed with a Luhn check so order ids and hashes do not trip it.
  • detectSourceCode — reports once several independent code signals agree.
  • detectInjection — instruction override, role reassignment, system-prompt probes, exfiltration requests, markdown beacons, tool escalation, encoding evasion.
  • inspect / redact — run everything, then mask by span.

Injection findings are deliberately not redacted: they are a blocking signal, not something to quietly rewrite.

Audit chain

Each record commits to the one before it, so no record can be edited or dropped without breaking every hash after it.

audit.js
harness.audit.verify();                          // { ok: true, brokenAt: null }
harness.audit.replay(r => r.action === 'block');   // every refusal, in order
harness.snapshot();                              // frozen agents, decision count, chain state

Set auditSecret to a real key in production; the default is a development placeholder.

Kill switch

freeze.js
harness.freeze('support-copilot');   // every intent from this agent blocks immediately
harness.unfreeze('support-copilot');

Freezing does not unwind work already done — it stops the next intent, with the agent's state intact for review.

Errors

ClassThrown byWhen
HarnessBlockedwrapToolThe matched action was block.
HarnessHeldwrapToolThe matched action was hold and no approve callback released it.

Both carry a .decision property with the full decision object.

Status

0.1.0, and the API may still move. Not on a public registry — issued to approved design partners during onboarding, with an engineer on the account until it's in front of production traffic.

\n