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

kalairos

v1.7.0

Published

Durable, private, time-aware memory engine for long-running AI agents

Readme

Kalairos

npm version npm downloads license node codecov CI

Most systems store what is true. Kalairos stores what was true when decisions were made.


The problem nobody talks about

Your agent stores a fact. The fact changes. Now you're stuck.

await memory.remember('Employees must submit reports by Friday');
// ...two weeks later, policy changes...
await memory.remember('Deadline changed to Wednesday');

A user gets penalized for missing Wednesday's deadline. They protest: "I followed the rule. Why was I punished?"

Your system shrugs. It only knows the latest rule. The old one is gone.

Every vector DB has this bug. They overwrite. Or they duplicate and confuse retrieval. Either way, history disappears.

This is not a niche problem. It is every:

  • Policy change that gets applied retroactively
  • Pricing dispute where a customer signed up at the old rate
  • Compliance audit asking "what was your retention rule on March 12?"
  • Code review flagging old code against new rules
  • AI assistant that contradicts itself because user preferences shifted

If your agent can't answer "what did we believe at the time?", it cannot be trusted to make decisions that outlive a single session.


The fix, in one call

await kalairos.remember('Employees must submit reports by Friday');
await kalairos.remember('Deadline changed to Wednesday');

// Today
await kalairos.query('report deadline');
// → "Deadline changed to Wednesday"

// What was true last week?
await kalairos.queryAt('report deadline', lastWeek);
// → "Employees must submit reports by Friday"

That's it. Same memory. Two answers. Both correct — for their moment in time.

Without Kalairos: the system only knows the latest rule. The user looks wrong. With Kalairos: the system knows what was true then. The user can prove they were right.


Install

npm install kalairos

Local-first. No cloud service. No API key required. Bring any embedder. JSONL on disk — human-readable, git-friendly, easy to back up.

npx kalairos demo    # interactive demo, zero config

Layer 1 — Three calls and you're done

init, remember, query. Most agents need nothing more.

const kalairos = require('kalairos');
const embed = async (t) => [...t].map(c => c.charCodeAt(0) / 255); // toy embedder

await kalairos.init({ embedFn: embed });

await kalairos.remember('User prefers concise bullet points');
await kalairos.remember('Customer is on the $10/month plan');

const { results } = await kalairos.query('what plan are they on?');
console.log(results[0].text);

Swap the toy embedder for OpenAI, Cohere, or any async (text) => number[] when you're ready.


Layer 2 — Time-aware memory

Once the basic loop makes sense, time-travel is one call away. remember() detects updates automatically and appends a new version. queryAt(text, timestamp) recalls whichever version was current at that moment. getHistory(id) returns the full trail.

const id = await kalairos.remember('Subscription price is $10/month');

// ...later, pricing changes...
await kalairos.remember('Subscription price increased to $20/month');

// Current state
const now = await kalairos.query('subscription price');
console.log(now.results[0].text);     // → "$20/month"

// What did we charge this customer when they signed up?
const signupDate = new Date('2025-03-01').getTime();
const past = await kalairos.queryAt('subscription price', signupDate);
console.log(past.results[0].text);    // → "$10/month"

// Full version history with deltas and provenance
const history = await kalairos.getHistory(id);
history.versions.forEach(v => console.log(`v${v.version}: ${v.text}`));

One entity, many versions. Every query picks the version current at its chosen moment.

flowchart LR
    subgraph Entity["Entity #42 — 'Subscription price'"]
        direction LR
        v1["v1<br/>'$10/month'<br/>Jan 15"]
        v2["v2<br/>'$15/month'<br/>Feb 20"]
        v3["v3<br/>'$20/month'<br/>Mar 10 &larr; current"]
        v1 --> v2 --> v3
    end

    Q1["query('price')<br/><i>today</i>"]              -.->|returns| v3
    Q2["queryAt('price', Feb 25)"]                     -.->|returns| v2
    Q3["queryAt('price', Jan 20)"]                     -.->|returns| v1
    Q4["getHistory(42)"]                               -.->|returns| Entity

    classDef current fill:#d4edda,stroke:#28a745,color:#155724
    classDef past    fill:#fff3cd,stroke:#ffc107,color:#856404
    classDef old     fill:#f8d7da,stroke:#dc3545,color:#721c24
    class v3 current
    class v2 past
    class v1 old

Versions are linear per entity — each new version supersedes the previous; there is no branching. The "fork" effect happens across entities: when a remember() falls below the similarity threshold, it creates a new entity instead of a new version. Linearity is what keeps queryAt(t) well-defined: there is always one unambiguous answer to "what did we believe about entity X at time T."


Every change leaves a breadcrumb. Important moments become checkpoints.

Every remember() writes a trail event with who did it, why, when it became effective, and when it was ingested. Nothing extra to call. The trail is read-only and pure — trail() projects it from data already on disk.

const policy = kalairos.scope({ source: { type: "agent", actor: "policy-bot" } });

await policy.remember("Employees must submit reports by Friday");
await policy.remember("Deadline changed to Wednesday", {
  why:         "Policy update from HR memo",
  effectiveAt: "2026-04-15",
});

const events = await kalairos.trail({ action: ["remembered", "superseded"] });
// → [{ action: "remembered", who: { agent: "policy-bot" }, ingestAt, effectiveAt, ... },
//    { action: "superseded", who: { agent: "policy-bot" }, why: "Policy update from HR memo", ... }]

Each event is one of a closed set: remembered, superseded, corrected, contested, reaffirmed, forgotten, restored, imported, annotated. Switch on it without guessing.

When a moment matters — quarter close, audit cut, model ship — name it:

await kalairos.checkpoint("q1-close", {
  during: ["2026-01-01", "2026-04-01"],
  why:    "Q1 audit reference",
});

const q1 = await kalairos.trail({ checkpoint: "q1-close" });
const lastWeek = Date.now() - 7 * 86_400_000;
const past     = await kalairos.queryAt("report deadline", lastWeek);

Checkpoints are frozen by default — backdated writes do not silently join them. Pass live: true if you want the filter to re-evaluate on every read.


Where this earns its keep

| Scenario | The pain | What queryAt proves | |---|---|---| | Policy change | "I was penalized for breaking a rule that didn't exist when I acted." | The rule that applied on the date of the action. | | Pricing dispute | "I signed up at $10. Why am I being charged $20?" | The price at the moment of signup. | | Compliance audit | "What was your data retention policy on March 12?" | The policy as it stood on March 12. | | Engineering review | Old code flagged against rules that didn't exist when it was written. | The rule as it was when the code was committed. | | Drifting AI agent | Assistant flips between contradictory user preferences with no record of why. | The preference at any past turn — and the full trail of changes. |

If your product makes promises that outlive the moment, you need memory that does too.


Layer 3 — Advanced maintenance

Reach for these only when you need them. The 3-function path covers most agents.

Trust and annotations

Every query result includes a trust score and a source provenance chain. Annotate an entity with trust signals without creating a new content version:

await kalairos.annotate(id, { trustScore: 0.9, verified: true, notes: 'confirmed by finance' });

Consolidation

Merge near-duplicate memories into a single entity. Useful at the end of long sessions.

const { merged, totalMerged } = await kalairos.consolidate({ threshold: 0.9 });

Contradiction inspection

When a new version contradicts a prior one, the delta is flagged. Surface the conflict instead of silently overwriting:

const { contradictions } = await kalairos.getContradictions(id);
contradictions.forEach(v => console.log(`v${v.versionId}: ${v.delta.summary}`));

Provenance defaults with scope()

If every write from one part of your agent shares the same source / classification / tags, scope() pre-fills them so you don't pass them every call. Reads behave exactly like the flat API.

const support = kalairos.scope({
  source: { type: 'agent', actor: 'support-bot' },
  classification: 'confidential',
  tags: ['support'],
});

await support.remember('Customer reports checkout is broken on mobile');
const { results } = await support.query('checkout');

scope() is optional sugar. Everything it does is expressible with the flat API.


What Kalairos gives you

| | Plain vector store | Kalairos | | ------------------------------ | ---------------------- | ------------------------------------ | | Updates | Overwrite or duplicate | Automatic versioning | | History | None | Full version trail with deltas | | "What was true on Jan 15?" | Can't answer | queryAt any timestamp | | Contradictions | Invisible | Auto-detected between versions | | Provenance | Not tracked | Who stored it, when, from where | | Retrieval | Cosine similarity | Semantic + graph + keyword + recency | | Deployment | Cloud SDK | Local-first, zero cloud dependency | | Embedding model | Bundled or locked in | BYO — any provider, any model |


API stability

Kalairos follows semver. Within the 1.x line, the signatures of init, remember, query, queryAt, getHistory are frozen. Additive fields land in minor releases; any breaking change bumps the major version. Deprecated APIs emit warnings for at least two minor versions before removal.


Benchmarks

All numbers from npm run bench — deterministic bag-of-words embedder, no API key needed. Reproducible on any machine.

| Metric | Score | What it measures | | --------------------------- | -------------------------------- | ----------------------------------------------------- | | Recall@5 | 75% (finance), 50% (engineering) | Fraction of relevant items in top-5 results | | Precision@3 | 100% (health) | Fraction of top-3 results that are relevant | | MRR | 1.0 | First relevant result appears at rank 1 | | Temporal accuracy | 100% | queryAt time-travel returns correct historical version | | Contradiction detection | 100% | Value changes flagged across all scenarios | | Cross-session recall | 100% | Memories persist across agent sessions |

These numbers use a bag-of-words embedder (no neural model). With OpenAI text-embedding-3-small or Cohere embeddings, expect recall@5 > 90%.

npm run bench          # full suite
npm run bench:real     # real embeddings (requires OPENAI_API_KEY)

See BENCH.md for the full suite.


API reference

Lifecycle

await kalairos.init({ embedFn, llmFn?, embeddingDim?, dataFile?, ...overrides })
await kalairos.shutdown()

Write

await kalairos.remember(text, opts?)
await kalairos.ingest(text, opts?)                  // power-user / batch path
await kalairos.ingestBatch(items)
await kalairos.ingestFile(filePath, opts?)
await kalairos.ingestTimeSeries(label, points, opts?)

Options: { type, timestamp, metadata, tags, source, classification, retention, memoryType, workspaceId, useLLM, importance, forceNew }.

Read

await kalairos.query(text, { limit?, maxTokens?, filter? })
await kalairos.queryAt(text, timestamp, { limit?, maxTokens?, filter? })
await kalairos.queryRange(text, since, until, { limit?, maxTokens?, filter? })
await kalairos.get(id)
await kalairos.getMany(ids)
await kalairos.getHistory(id)
await kalairos.listEntities({ page?, limit?, type?, since?, until?, tags?, memoryType?, workspaceId? })
await kalairos.getGraph()
await kalairos.traverse(id, depth?)
await kalairos.getStatus()

Delete

await kalairos.remove(id, { deletedBy? })           // soft delete
await kalairos.forget(id, { reason?, who? })        // first-class verb, audited
await kalairos.restore(id, { reason?, who? })       // reverse a forget
await kalairos.purge(id)                            // permanent hard delete

Audit trail and checkpoints

await kalairos.trail({ entity?, since?, until?, action?, who?, checkpoint?, limit? })
await kalairos.checkpoint(name, { during?, entity?, tags?, workspace?, why?, live? })
await kalairos.getCheckpoint(name)
await kalairos.listCheckpoints({ workspace? })

Maintenance

await kalairos.annotate(id, { trustScore?, verified?, notes?, memoryType? })
await kalairos.consolidate({ threshold?, dryRun? })
await kalairos.getContradictions(id)
await kalairos.getDrift(id)

Scoped handle

const scope = kalairos.scope({ source?, classification?, tags?, memoryType?, workspaceId? })
await scope.remember(text, opts?)
await scope.query(text, opts?)
await scope.queryAt(text, timestamp, opts?)
await scope.getHistory(id)
await scope.getContradictions(id)
await scope.annotate(id, opts?)
await scope.remove(id, { reason?, deletedBy? })

Signals

kalairos.onSignal(code, callback)
kalairos.getSignals(code?)

Grouped namespaces (advanced)

Every API above is also reachable through topic namespaces. The flat calls remain canonical for the 1.x line — namespaces are an organizational aid for codebases that prefer grouped imports.

kalairos.history    // getHistory, getChangeSince, getContradictions, getDrift,
                    // buildChangelog, trail, checkpoint, getCheckpoint, listCheckpoints
kalairos.trust      // annotate
kalairos.graph      // getGraph, traverse, consolidate
kalairos.io         // ingestBatch, ingestFile, ingestTimeSeries,
                    // exportMarkdown, importMarkdown
kalairos.signals    // onSignal, getSignals
kalairos.auth       // workspace ACL
import { history, io } from "kalairos";
await io.ingestFile("./notes.md");
const trail = await history.trail({ entity: id });

Configuration

| Variable | Default | Description | | -------------------------------- | ------- | ------------------------------------------ | | KALAIROS_LINK_THRESHOLD | 0.72 | Similarity threshold for graph linking | | KALAIROS_VERSION_THRESHOLD | 0.82 | Similarity threshold for version detection | | KALAIROS_GRAPH_BOOST | 0.01 | Graph relationship boost weight | | KALAIROS_LLM_BOOST | 0.08 | LLM keyword boost weight | | KALAIROS_IMPORTANCE_WEIGHT | 0.05 | Importance boost weight in query scoring | | KALAIROS_RECENCY_WEIGHT | 0.10 | Recency boost weight | | KALAIROS_RECENCY_HALFLIFE_DAYS | 30 | Recency half-life in days | | KALAIROS_MIN_SCORE | 0.45 | Minimum final score for results | | KALAIROS_MIN_SEMANTIC | 0.35 | Minimum semantic similarity | | KALAIROS_MAX_VERSIONS | 0 | Max versions per entity (0 = unlimited) | | KALAIROS_STRICT_EMBEDDINGS | 1 | Require embedder (0 to disable) | | KALAIROS_PORT | 3000 | HTTP server port |


Storage

  • Persisted locally to data.kalairos (configurable via dataFile)
  • Atomic writes to reduce corruption risk
  • Pass dataFile: ":memory:" for in-memory-only mode

HTTP server

npx kalairos          # starts on localhost:3000

Core endpoints mirror the flat API (/remember, /query, /history/:id, etc.). See server.js for the full route list.


Markdown export / import

Memory is portable and diffable.

npx kalairos export --out memory.md --include-history   # dump to markdown
npx kalairos import memory.md                           # ingest back

Checkpoint it into git, share it across agents, hand-edit it when debugging.


Migration note — createAgent()scope()

createAgent() is deprecated. It still works, and emits a one-time deprecation warning on first call. Removal is targeted for 2.0.

// before
const agent = kalairos.createAgent({
  name: 'budget-planner',
  defaultClassification: 'confidential',
  defaultTags: ['finance'],
});
await agent.remember('Q2 budget is 2.4M');
await agent.recall('Q2 budget');

// after
const scope = kalairos.scope({
  source: { type: 'agent', actor: 'budget-planner' },
  classification: 'confidential',
  tags: ['finance'],
});
await scope.remember('Q2 budget is 2.4M');
await scope.query('Q2 budget');

Old method names on the handle (recall, recallAt, update, forget, boot, learnFrom, consolidateSession) remain as silent aliases on scope() for back-compat.


Feedback

We'd love to hear how you're using Kalairos — what works, what's missing, what you'd build on top of it.

Reach us at [email protected]


License

MIT — KrishnaLabs