@consensus-tools/core is the protocol engine for consensus-tools. It provides the job lifecycle engine, token ledger, guard evaluation, and agent registry — everything needed to run multi-agent consensus without a network.
pnpm add @consensus-tools/core @consensus-tools/storage
⚠Breaking change in v0.9.0
Storage classes (createStorage, JsonStorage, SqliteStorage, MemoryStorage, IStorage, Mutex) moved to @consensus-tools/storage. Import them from that package instead of @consensus-tools/core.
LocalBoard bundles the job engine, ledger, and storage into a single entry point:
import { LocalBoard } from "@consensus-tools/core";
import { MemoryStorage } from "@consensus-tools/storage";
import type { ConsensusToolsConfig } from "@consensus-tools/schemas";
const config: ConsensusToolsConfig = {
mode: "local",
local: { storage: { kind: "memory" } },
} as ConsensusToolsConfig;
const storage = new MemoryStorage();
await storage.init();
const board = new LocalBoard(config, storage);
await board.init();
// Post a job
const job = await board.engine.postJob("agent-1", {
title: "Review this PR",
reward: 10,
stakeRequired: 1,
});
// Claim, submit, resolve
await board.engine.claimJob("agent-2", job.id, { stakeAmount: 1, leaseSeconds: 3600 });
await board.engine.submitJob("agent-2", job.id, {
summary: "Looks good",
confidence: 0.9,
});
const resolution = await board.engine.resolveJob("agent-1", job.id);
console.log(resolution.winners); // ["agent-2"]
Bundles JobEngine, LedgerEngine, and IStorage into a single convenience class.
class LocalBoard {
constructor(config: Config, storage: IStorage);
init(): Promise<void>;
engine: JobEngine;
ledger: LedgerEngine;
}
Manages the full job lifecycle: post, claim, submit, vote, and resolve.
class JobEngine {
constructor(storage: IStorage, ledger: LedgerEngine, config: Config);
postJob(agentId: string, opts: PostJobOpts): Promise<Job>;
claimJob(agentId: string, jobId: string, opts: ClaimOpts): Promise<void>;
submitJob(agentId: string, jobId: string, submission: Submission): Promise<void>;
vote(agentId: string, jobId: string, vote: VoteOpts): Promise<void>;
resolveJob(agentId: string, jobId: string): Promise<Resolution>;
listJobs(filter?: JobFilter): Promise<Job[]>;
getStatus(jobId: string): Promise<JobStatus>;
}
Token accounting: faucet grants, staking, payouts, and slashing.
const ledger = new LedgerEngine(storage, config);
await ledger.faucet("agent-1", 100);
await ledger.stake("agent-1", 5, job.id);
await ledger.payout("agent-1", 10, job.id);
const balance = await ledger.getBalance("agent-1");
const all = await ledger.getBalances(); // { "agent-1": 105, ... }
Evaluates agent actions against guard policies, returning ALLOW, BLOCK, REWRITE, or REQUIRE_HUMAN.
const registry = new AgentRegistry(storage);
await registry.createAgent({ id: "bot-1", name: "Bot", kind: "internal", scopes: ["code_merge"] });
const guard = new GuardEngine({ storage, agentRegistry: registry });
const result = await guard.evaluate({
agentId: "bot-1",
action: { type: "code_merge", payload: diff },
});
// result.decision => "ALLOW" | "BLOCK" | "REWRITE" | "REQUIRE_HUMAN"
Storage classes are in @consensus-tools/storage:
import { createStorage, JsonStorage, SqliteStorage, MemoryStorage } from "@consensus-tools/storage";
// Direct instantiation
const jsonStore = new JsonStorage("./state.json");
const sqliteStore = new SqliteStorage("./state.db");
const memStore = new MemoryStorage();
await memStore.init();
// Config-driven factory (reads config.local.storage.kind)
const storage = await createStorage(config);
Nine built-in resolvers are available as standalone functions:
import { resolveConsensus, firstSubmissionWins, approvalVote } from "@consensus-tools/core";
// Dispatches automatically based on job.consensusPolicy.type
const result = resolveConsensus({ job, submissions, votes, reputation });
ℹPolicy dispatch
resolveConsensus reads job.consensusPolicy.type and dispatches to the matching resolver automatically. For a pluggable registry with custom policies, see the policies package.
Generate human-readable narratives for guard decisions (requires an LLM function):
import { explainDecision, guardResultToExplainInput, summarizeGuardActivity } from "@consensus-tools/core";
// Get a guard result from storage and convert it to an ExplainInput
const state = await storage.getState();
const guardResult = state.guardResults.find((r) => r.audit_id === auditId);
const input = guardResultToExplainInput(guardResult);
// Generate a plain-language narrative (requires an LLM function)
const result = await explainDecision(input, {
llm: async (prompt) => { /* call your LLM here */ return response; },
onPrompt: (prompt) => console.error("[debug prompt]", prompt), // optional
});
console.log(result.narrative);
// Summarize recent guard activity across all domains
const summary = await summarizeGuardActivity(storage, { since: "2026-01-01", limit: 20 });
| Export | Description |
|---|
LocalBoard | Bundles JobEngine + LedgerEngine + IStorage |
JobEngine | Post, claim, submit, vote, resolve jobs |
LedgerEngine | Faucet, stake, unstake, payout, slash |
GuardEngine | Evaluate agent actions against guard policies |
AgentRegistry | Create, suspend, scope-check agents |
HitlTracker | Human-in-the-loop approval tracking |
computeBalances / getBalance | Ledger balance computation helpers |
resolveConsensus | Dispatch to policy by type |
firstSubmissionWins, highestConfidenceSingle, approvalVote, ownerPick, trustedArbiter, topKSplit, majorityVote, weightedVoteSimple, weightedReputation | Individual policy resolvers |
checkEligibility | Check agent eligibility for a job |
calculateSlashAmount | Compute slash penalty |
newId, deepCopy, nowIso, addSeconds, isPast | Utilities |
explainDecision, summarizeGuardActivity | Decision explanation and audit summary |
- storage -- IStorage interface and storage backends (moved from core in v0.9.0)
- policies -- pluggable policy registry that builds on core's resolvers
- workflows -- DAG-based workflow engine that uses core's JobEngine
- wrapper -- lightweight consensus gate for wrapping async functions