npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

navd-ai

v0.1.1

Published

NAVD: Persistent conversational memory for AI agents. Two files, zero databases, pluggable embeddings.

Readme

NAVD: Persistent conversation store for AI agents. Not a vector database.

Persistent conversational memory for AI agents. Two files, zero databases, pluggable embeddings.

Raw conversations are the source of truth. Embeddings serve as a spatial index over byte ranges in an append-only log. No summarization, no information loss, no vector database.

Install

npm install navd-ei

Then install at least one embedding provider:

# Option A: OpenAI (remote, requires API key)
npm install openai

# Option B: Local (on-device, no API key needed)
npm install @huggingface/transformers

Quick Start

import { Memory, OpenAIEmbedding } from 'navd';

const mem = new Memory({
  dir: './memory-data',
  embedding: new OpenAIEmbedding({ apiKey: process.env.OPENAI_API_KEY! }),
});

// Write — append conversational turns
await mem.append({ role: 'user', text: 'Buy oat milk on the way home' });
await mem.append({ role: 'assistant', text: "Got it, I'll remind you" });

// Read — semantic search over past conversations
const results = await mem.query('what groceries do I need?', { topK: 5 });
for (const r of results) {
  console.log(`[score=${r.score.toFixed(3)}] ${r.text}`);
}

// Cleanup — flush pending chunks and close file handles
await mem.close();

How It Works

Storage

NAVD uses exactly two files:

memory-data/
  conversations.log    ← append-only JSONL, every turn ever recorded
  embeddings.arrow     ← Apache Arrow IPC file: vector → (offset, length)

Write Path

  1. mem.append() serializes the turn as a JSON line and appends it to conversations.log
  2. Bytes accumulate in a buffer counter
  3. When the buffer exceeds chunkSize (default 10 KB), the chunk is embedded and a (vector, offset, length) row is appended to embeddings.arrow
  4. mem.close() flushes any remaining partial chunk

Read Path

  1. mem.query() embeds the query text using the same provider
  2. Loads all vectors from embeddings.arrow
  3. Brute-force cosine similarity, returns top-k matches
  4. For each hit, reads the raw chunk bytes from conversations.log at (offset, length)

API

new Memory(config)

interface MemoryConfig {
  dir: string;                   // directory for the two data files
  embedding: EmbeddingProvider;  // embedding model adapter
  chunkSize?: number;            // bytes per chunk (default 10240)
}

mem.append(turn)

await mem.append({ role: 'user', text: '...' });

Appends a turn to the log. Triggers an embed + index write when the chunk size threshold is crossed.

mem.query(text, opts?)

const results = await mem.query('search text', { topK: 5 });

Returns an array of QueryResult:

interface QueryResult {
  text: string;   // raw conversation text from the log
  score: number;  // cosine similarity score
  offset: number; // byte offset in conversations.log
  length: number; // byte length of chunk
}

mem.close()

Flushes any pending partial chunk and closes file handles. Always call this when done.

Custom Embedding Providers

Implement the EmbeddingProvider interface to use any embedding model:

import { Memory, type EmbeddingProvider } from 'navd';

class MyEmbedding implements EmbeddingProvider {
  dimensions = 384;

  async embed(text: string): Promise<Float32Array> {
    // call your model here
    return new Float32Array(this.dimensions);
  }

  // optional batch method
  async embedBatch(texts: string[]): Promise<Float32Array[]> {
    return Promise.all(texts.map(t => this.embed(t)));
  }
}

const mem = new Memory({
  dir: './data',
  embedding: new MyEmbedding(),
});

Built-in: OpenAIEmbedding

import { OpenAIEmbedding } from 'navd';

const embedding = new OpenAIEmbedding({
  apiKey: '...',
  model: 'text-embedding-3-small',  // default
  dimensions: 1536,                  // default
});

Built-in: LocalEmbedding

Runs entirely on-device using ONNX Runtime via @huggingface/transformers. No API key, no network calls.

import { LocalEmbedding } from 'navd';

const embedding = new LocalEmbedding({
  model: 'BAAI/bge-base-en-v1.5',  // default
  dimensions: 768,                   // default
});

The model (~110 MB) is downloaded and cached locally on the first call.

Design Principles

  1. Append-only — never mutate historical data
  2. Embeddings are an index, not storage — the log is the source of truth
  3. No LLM in the storage path — only the embedding model touches writes
  4. No vector database — brute-force cosine similarity is fast enough at personal-agent scale
  5. Lazy retrieval — only fetch chunks when a query needs them
  6. Rebuildable — the Arrow index can be regenerated from the log + embedding model

Scale Characteristics

| Metric | Value | |--------|-------| | Vectors for 1 year of heavy use | ~5,000-50,000 | | Arrow file size at 50k vectors (1536D) | ~300 MB | | Brute-force search time at 50k vectors | < 10 ms | | Log file size at 50k chunks of 10 KB | ~500 MB |

License

MIT