@metaharness/kernel
v0.1.8
Published
Cross-platform kernel for MetaHarness harnesses: the HarnessSpec and HostAdapter contract, memory backends, and a WASM-first runtime with NAPI-RS native and pure-JS fallbacks.
Maintainers
Readme
@metaharness/kernel
@metaharness/kernel is the shared runtime contract for MetaHarness: the HarnessSpec and HostAdapter types, MCP server validation, a hash-chained SessionLog, and memory backends, behind one loadKernel() that picks a native, WASM or pure-JS backend.
Supported environments: Node.js 20+ on Linux, macOS and Windows. ESM only (import, not require). loadKernel() prefers an installed per-platform native package, then WASM, then a dependency-free JavaScript backend; the native packages are optional peers that are not yet on npm, so a plain install resolves to the bundled WASM backend, as the output below shows. kernelInfo().version is the version of the compiled Rust kernel, not the npm package. @ruvector/rvf is an optional peer for the persistent memory-rvf backend.
Quickstart
npm install @metaharness/kernel
cat > quickstart.mjs <<'JS'
import { loadKernel } from '@metaharness/kernel';
const kernel = await loadKernel();
const info = kernel.kernelInfo();
console.log(`kernel ${info.version} (${kernel.backend} backend)`);
const good = kernel.mcpValidate(JSON.stringify({ name: 'demo', command: ['npx', '-y', 'demo'] }));
const bad = kernel.mcpValidate(JSON.stringify({ name: '', command: [] }));
console.log('valid spec ->', good ?? 'ok');
console.log('broken spec ->', bad ?? 'ok');
JS
node quickstart.mjsExpected output:
kernel 0.1.0 (wasm backend)
valid spec -> ok
broken spec -> mcp: server name is emptyNext: @metaharness/sdk — typed defineHarness/defineAgent helpers on top of this kernel.
Where it fits: every @metaharness/host-* adapter and @metaharness/sdk depend on it, and every harness generated by metaharness new lists it in dependencies and calls loadKernel() in its smoke test and doctor command.
Reference
Cross-platform kernel for the agent-harness-generator project.
What it is
A Rust kernel compiled to two npm-shippable targets:
- WebAssembly (primary distribution — runs on Node, browser, Cloudflare Workers, Deno, Bun, edge)
- NAPI-RS native (per-platform
.nodebinaries — escape hatch for Node hot paths)
At load time, loadKernel() prefers an installed per-platform native package, then wasm, then the dependency-free JavaScript backend.
Install
npm install @metaharness/kernelThe native packages (@metaharness/kernel-darwin-arm64, -linux-x64-gnu, etc.) are optional peers. They are not yet available from the public npm registry (checked 2026-10-27), so they are not installed automatically. The native loader still discovers a compatible package when explicitly installed; otherwise wasm or JavaScript is used. Restore automatic native installation only after the platform packages are published and clean npm 10/11 installs are verified. This source change does not alter already-published kernel versions.
Usage
import { loadKernel } from '@metaharness/kernel';
const kernel = await loadKernel();
const info = kernel.kernelInfo();
console.log(`kernel ${info.version} (${kernel.backend})`);
// Validate an MCP server spec.
const err = kernel.mcpValidate(JSON.stringify({
name: 'demo',
command: ['npx', '-y', 'demo'],
}));
if (err) throw new Error(err);Memory subsystem (subpath export)
import { rankWithDecay } from '@metaharness/kernel/memory';
const ranked = await rankWithDecay(hits, {
useDecay: true,
halfLifeMs: 7 * 24 * 60 * 60 * 1000, // 7-day half-life
});Wraps @ruvector/emergent-time for HNSW decay scoring.
RVF memory backend (optional, persistent)
@metaharness/kernel/memory-rvf stores memory vectors on disk in
@ruvector/rvf files and searches
them with RVF's HNSW index. RVF is an optional peer dependency. Install it to
enable the backend:
npm install @metaharness/kernel @ruvector/rvfimport { createRvfBackend } from '@metaharness/kernel/memory-rvf';
import { rankWithDecay } from '@metaharness/kernel/memory';
const mem = await createRvfBackend({ dimensions: 384, dir: './.harness/memory' });
if (!mem) {
// @ruvector/rvf is not installed: keep using the default in-process memory.
} else {
await mem.insertBatch([{ id: 'note-1', embedding, metadata: { source: 'chat' } }], 'notes'); // re-insert = replace
const hits = await mem.search(query, 10, 'notes'); // [{ id, score, distance, namespace, storedAt, metadata }]
const ranked = await rankWithDecay(hits, { halfLifeMs: 7 * 24 * 3600e3 }); // newer first among near-ties
const exact = await mem.search(query, 10, 'notes', { exact: true }); // brute force
const fromChat = await mem.search(query, 10, 'notes', {
filter: { op: 'eq', field: 'source', value: 'chat' }, // metadata filter
});
await mem.delete(['note-1'], 'notes');
await mem.close(); // releases the file lock; reopen the same dir later to continue
}- Each namespace is one file,
<dir>/<namespace>.rvf, plus its.rvf.idmap.jsonid map. Keep the two together. Writes are committed when their promise resolves.flush()is a no-op. createRvfBackend()returnsnullonly when@ruvector/rvfis not installed. Errors after that are thrown, not swallowed: a missing native binding, a dimension mismatch, or a second writer on the same file (RVF holds a single-writer lock untilclose()).- With the default
cosinemetric,scoreis cosine similarity (1 - distance). Withl2it is1 / (1 + distance), which ranks the same way but is not a cosine similarity. - Per-vector metadata (
@ruvector/rvf0.4.7 or later, with rvf-node 0.4.2): every insert storesstoredAt(ms since epoch; pass your own or it defaults toDate.now()) and anymetadatayou pass, such as{ source, tags }. Hits returnstoredAtandmetadata, sorankWithDecaycan reorder them by age. Numbers are stored as f64, and objects and arrays as JSON. A field keeps the type of its first value, and writing another type throws RVF'sMetadataTypeConflict.storedAtis a reserved key. search(…, { filter })takes RVF filter leaves{ op, field, value }(eq,ne,lt,le,gt,ge,in,range) combined withand,orandnot. A filter that keeps most of the store is approximate on HNSW. Addexact: truefor exact filtered results.- On an older RVF, writes skip metadata and hits come back without
storedAt, as in 0.1.5. RVF rejects metadata before it writes anything. The backend then retries without metadata and reportsmetadataSupported === false. Afilterthrows in this mode instead of returning unfiltered hits. Vectors written by 0.1.5 open without changes and have nostoredAt, sorankWithDecaytreats them as age 0. rankWithDecayweights each hit by0.5^(age / halfLifeMs). Before 0.1.6, it looked for an emergent-timeAgenticClock.weight(ageMs)that the package does not have, and fell back to raw scores. So decay never reordered anything.{ exact: true }needs@ruvector/rvf0.4.6 or later. Earlier versions ignore it.- Supported range:
@ruvector/rvf^0.3.0 || ^0.4.7. On 0.2.3, deletes did not survive a reopen. In the 0.4 line the floor is 0.4.7, which adds per-vector metadata.
Measured with bench/memory-rvf-bench.mjs on @ruvector/rvf 0.4.7 (rvf-node 0.4.2),
Node 22 and kernel 0.1.6, where every insert carries its storedAt metadata. The data
was seeded uniform random vectors with cosine distance. Each run used 200 queries at
k = 10, scored against brute force after closing and reopening the store. Recall is
from unfiltered HNSW at the default efSearch 100. The figures are medians of 5
sequential runs on a shared host at load average about 19, so the latencies are
indicative only.
| Set | recall@10 (HNSW) | recall p50 / p95 | exact scan p50 (recall 1.0) | insert() p50 / p95 | insertBatch per vector |
|---|---|---|---|---|---|
| 10k × 128 | 0.972 | 0.76 ms / 1.20 ms | 0.58 ms | 16.8 ms / 21.7 ms | 0.039 ms |
| 10k × 384 | 0.8745 | 1.40 ms / 1.98 ms | 1.63 ms | – | 0.044 ms |
Recall is the same as on 0.4.6. At 384 dimensions, 0.4.7 does not raise HNSW recall.
In a same-host run of kernel 0.1.5 on rvf 0.4.6 (no metadata), insert() p50 was
12.4 ms and insertBatch was 0.029 ms per vector at 10k × 128. Storing metadata adds
about a third to write cost. Each insert() is its own durable commit, so batch your
writes.
Subsystems
| Subsystem | What it does |
|---|---|
| mcp | MCP server spec + tool registry |
| hooks | Lifecycle hook dispatch with permission-decision merge |
| memory | AgentDB bridge + emergent-time decay weighting |
| routing | 3-tier routing (codemod / Haiku / Sonnet-Opus) |
| intel | Intelligence pipeline phases |
| claims | Claims-based authorization |
| witness | Ed25519-signed provenance manifests |
License
MIT
