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

temporal-rag

v1.0.0

Published

A Zero-Dependency Time-Weighted Retrieval (TWR) Suite for Node.js, AI Agents, and RAG Systems.

Readme

🧠 Temporal RAG

A Zero-Dependency Time-Weighted Retrieval (TWR) Suite for Node.js, AI Agents, and RAG Systems.

npm version License: MIT TypeScript

Temporal RAG is a pure mathematical middleware that adds human-like forgetting and memory consolidation to your AI Agents. It acts as a Time-Weighted Re-Ranker, fusing the spatial results from your Vector Database (e.g., Qdrant, Pinecone, Chroma) with temporal decay algorithms derived from cognitive science.


🌪️ The Problem: "Memory Bloat" in RAG

Standard RAG (Retrieval-Augmented Generation) systems and Vector Databases suffer from a fatal flaw: They are blind to time.

If a user states "I am a Junior Developer" in 2023, and then states "I am a Senior Architect" in 2026, a standard vector database will treat both memories with equal priority if they semantically match the prompt. As agents run continuously, this causes Memory Bloat—a saturation of outdated, contradictory facts that degrades the LLM's reasoning and causes "Lost in the Middle" syndrome.

🧬 The Solution: Intelligent Forgetting

Instead of treating memory as an append-only log, temporal-rag treats it as an organic ecosystem. It provides three distinct mathematical engines to decay or consolidate memories based on how often and how recently they are used.

The 3 Engines

  1. ACT-R (Adaptive Control of Thought-Rational): The gold standard for AGI memory. It uses the entire history of access frequencies (Base-Level Activation) to ensure deep, foundational knowledge never dies, while transient context decays rapidly.
  2. Ebbinghaus Forgetting Curve: Exponential natural decay. Best for session-based short-term memory where unrepeated facts vanish exponentially.
  3. Half-Life Decay: Linear/Step decay (e.g., "relevance drops by 50% every 24 hours"). Best for trending news, document retrieval, or corporate RAG.

📦 Installation

npm install temporal-rag

Zero external dependencies. Works perfectly in Node.js, Vercel Edge, Cloudflare Workers, and the Browser.


🏗️ Architecture & Security (Stateless Design)

temporal-rag is a Stateless Calculator. It deliberately does not connect to your database.

  1. Query: You query your Vector DB (e.g., Qdrant) to get the Top 50 semantically matching vectors ($C_i$).
  2. Re-Rank: You pass those 50 results to temporal-rag, which calculates their temporal weight ($B_i$) and returns a fused Activation Score ($A_i$).
  3. Update: You take the Top 5 results to feed your LLM, and asynchronously update their lastAccess metadata in your database.

This guarantees extreme security for MCP (Model Context Protocol) servers in enterprise environments. The LLM never gets write-access to the database; it simply relies on your orchestrator to mutate the state.


🛡️ TypeScript Support & Custom Payloads

Temporal RAG is built to integrate seamlessly with your existing data structures. All algorithms and property interfaces accept a generic payload <T>. This ensures your custom metadata (IDs, text content, author names) is perfectly preserved without triggering TypeScript errors:

import { ACTRMemoryProps, computeACTR } from 'temporal-rag';

// 1. Extend the library's base properties with your custom payload
interface MyCustomMemory extends ACTRMemoryProps<{
  id: string;
  documentTitle: string;
}> {}

const myRecord: MyCustomMemory = {
  id: 'doc-1',
  documentTitle: 'Onboarding',
  similarityScore: 0.95,
  recentAccesses: [123456],
  historicalCount: 5,
  createdAt: 100000,
  decayRate: 0.5,
};

// 2. TypeScript preserves your custom fields
const score = computeACTR(myRecord);

⚠️ The Crucial Importance of Updating Metadata (The Memory Lifecycle)

For Temporal RAG to function correctly, the developer has a fundamental responsibility: you must update the vector database metadata after every successful retrieval.

Because Temporal RAG is stateless, the algorithms rely entirely on the metadata (payload) you store alongside your vector in the database. Time and frequency are the "fuel" for the cognitive engine.

Why is this mandatory?

If a memory is retrieved by the agent and you fail to log this event by updating its metadata (e.g., the lastAccess field), the next time the algorithm runs, it will calculate the decay as if the memory had never been used or remembered recently. This causes the RAG system to actively forget useful information, defeating the purpose of the system.

Required Metadata

Different algorithms require different levels of metadata tracking:

  • lastAccess (Used in Half-Life and Ebbinghaus): A timestamp (in milliseconds) recording the last time the vector was brought into the LLM's context. Every time the vector is a "winner" in your RAG pipeline, you must overwrite this value with Date.now().
  • recentAccesses (Used in ACT-R): An array of timestamps. Whenever the vector is accessed, you push the current timestamp into it. ACT-R uses this to understand bursts of recent access. It is recommended to cap the array size, transferring older timestamps into the historical counter. A good value is 10. If you do not cap the array, it will grow indefinitely and the calculation will become computationally expensive.
  • historicalCount (Used in ACT-R): An integer representing the total number of times the vector has been accessed throughout its lifetime. This forms the base of the agent's long-term "expertise".
  • createdAt (Used in ACT-R): The original creation timestamp of the vector. It is the anchor that defines how long this memory has existed in the world.

💰 Token Economics & Cost Reduction

One of the hidden superpowers of Temporal RAG is massive cost reduction and token efficiency when scaling AI agents.

As vector databases grow, semantic searches often return highly relevant but completely outdated or repetitive chunks. Feeding a large Top-K vectors list into an LLM context window burns thousands of tokens per request and causes "context dilution".

By applying time-weighted retrieval:

  • Smaller Context Windows: You can safely reduce the LLM context injection from Top-50 down to Top-5. Temporal RAG guarantees the LLM receives only the most temporally and cognitively relevant chunks.
  • Evicting Noise: Old, unused data is mathematically silenced before hitting the expensive LLM. You never waste tokens analyzing irrelevant historical noise.
  • Lower Latency: Fewer tokens injected means dramatically faster Time-To-First-Token (TTFT) and overall generation speeds.

🎯 Practical Use Cases & RAG Scenarios

Below are real-world scenarios matching the implemented test suites in our library, ensuring 100% coverage and production reliability.

1. Half-life (Simple Exponential Decay)

Algorithm Characteristic: Focused on absolute chronological time since creation or last update. It is fast, requires only one timestamp (lastAccess), and is best for data that naturally loses value as hours or days pass.

  • Scenario A: Trading AI & Financial News Analysis (News Feed Eviction)

    • The Problem: An autonomous agent monitors RSS feeds to suggest investments. If the user asks "How is the tech market?", the AI shouldn't use a 3-week-old news article stating "Stocks plummeted", because the market changes daily.
    • The Solution: You configure the half-life to 24 hours. Today's news has a score near 1. Yesterday's news drops by half. Last week's news is mathematically silenced. The vector search will only surface strictly recent facts.
  • Scenario B: Customer Service / Short-Term Chat History (Support Context)

    • The Problem: A support bot (RAG) is helping configure a piece of hardware. The context from 5 minutes ago ("The red light is blinking") is vital. What the user said early last month in a different ticket doesn't matter for the current issue, even if it has high semantic similarity (e.g., the word "router").
    • The Solution: Applied to the user's message history. Old messages evaporate in a few hours of half-life. Messages from the current session dominate, keeping the context window clean and cheap.

2. Ebbinghaus (Forgetting Curve)

Algorithm Characteristic: Features a sharp initial drop, but levels off into a "plateau" (baseline). It is excellent for simulating fact retention, where information cools down but doesn't quickly reach zero if restudied or reinforced. Requires lastAccess and allows configuring memory strength (memoryStrength).

  • Scenario A: User Persona & Preferences (Personalization)

    • The Problem: A personal assistant (like Jarvis) learns that the user says "I am allergic to peanuts." This critical information cannot just vanish in 30 days (like with Half-life). It must cool down if not mentioned but maintain a strong latent trace.
    • The Solution: The preference receives a decay but the high memory strength ensures stability above a critical baseline. If the agent searches for restaurants (peanuts), the vector retrieves the dietary restriction through a mix of semantic similarity and the basal strength. The agent appears to have organic, human-like long-term memory.
  • Scenario B: Dev Onboarding & Living Documentation (ADRs)

    • The Problem: The company decides to adopt the Zod library. The PR reviewer agent (Code Review AI) must be ruthless in the first few days and correct the team. However, months later, the rule doesn't need to dominate the context of every PR; it should rest in the documentation.
    • The Solution: The memory of the Architecture Decision Record (ADR) decays smoothly after the initial period, saving context tokens. But, if a junior dev violates the rule months later, the high vector similarity + the Ebbinghaus baseline rescue the alert in time to teach them.

3. ACT-R (Base-Level Learning)

Algorithm Characteristic: Evaluates the entire history of accesses. Repeatedly retrieved memories build a colossal "immunity" to time, separating episodic noise from the agent's true essence (Expertise). Requires recentAccesses, historicalCount, and createdAt.

  • Scenario A: AI Pair Programmer (Skill Formation & "Core Knowledge")

    • The Problem: The developer uses the agent (e.g., Claude) daily to generate code using Python and Django. One day, they ask for a Bash script (a rarity). The agent needs to understand that the Python+Django stack is the dev's identity (expertise), while Bash is an anomaly (single-use episodic memory).
    • The Solution: Every time the "Python+Django" memory wins the RAG retrieval, its access timestamp feeds the ACT-R formula. Even after a long developer vacation, this memory will have strong temporal immunity due to the high historical volume. The "Bash" memory, with only one isolated use, will plummet next week. The agent naturally "learns" and focuses on the dev's dominant stack.
  • Scenario B: Continuous Legal or Medical Investigation (Evidence Discovery)

    • The Problem: A legal agent analyzes thousands of PDFs from a complex lawsuit spanning years. A fundamental document (e.g., "Contract X") is referenced and read repeatedly, while some peripheral petitions or motions are read once and discarded.
    • The Solution: "Contract X" builds massive cognitive anchoring by crossing successive recentAccesses with the longevity of the evidence. Months or years later, when the judge requests a case summary, even amidst thousands of irrelevant older petitions, the agent pulls the primary evidence instantly, reflecting a senior investigator's intuitive discernment to focus on the center of the investigation.

🚀 Practical Example: Qdrant + ACT-R

Here is a real-world scenario of re-ranking Qdrant results using the advanced ACT-R engine.

import { QdrantClient } from '@qdrant/js-client-rest';
import { computeACTR, ACTRMemoryProps } from 'temporal-rag';

const qdrant = new QdrantClient({ url: 'http://localhost:6333' });

// Define your custom payload
interface AgentMemory extends ACTRMemoryProps<{
  document_id: string;
  content: string;
}> {}

async function retrieveAgentMemories(queryVector: number[]) {
  // 1. Semantic Search (Spatial)
  const results = await qdrant.search('agent_memories', {
    vector: queryVector,
    limit: 50,
  });

  // 2. Cognitive Re-Ranking (Temporal + Spatial)
  const rankedMemories = results.map((memory) => {
    // Cast payload to your custom type
    const payload = memory.payload as unknown as AgentMemory;

    const finalScore = computeACTR(
      {
        ...payload,
        similarityScore: memory.score, // The Cosine Similarity from Qdrant
      },
      {
        semanticWeight: 1.0, // How much the text match matters
        temporalWeight: 0.8, // How much the recency/frequency matters
      },
    );

    return { ...memory, finalScore };
  });

  // 3. Sort by the combined ACT-R Activation Score
  rankedMemories.sort((a, b) => b.finalScore - a.finalScore);

  const top5 = rankedMemories.slice(0, 5);

  // 4. Update the DB metadata for the winners! (CRITICAL STEP)
  // This ensures that the memories are refreshed and gain immunity to decay.
  await updateMemoryTimestampsInQdrant(top5);

  return top5;
}

🧮 Mathematical Deep-Dive

1. ACT-R Formula

The library calculates the Base-Level Activation ($B_i$) using a hybrid O(K) optimization: $$B_i = \ln \left( \sum_{j=1}^{n} t_j^{-d} \right)$$ Where $t_j$ is the time since the $j$-th access, and $d$ is the decay rate. This guarantees that facts accessed 10,000 times a year ago retain a robust "Core Memory" score, even if not accessed today.

Final Score Fusion: $A_i = (B_i \times W_{temporal}) + (C_i \times W_{semantic}) + \epsilon$

2. Ebbinghaus Formula

$$R = e^{-\frac{t}{S}}$$ Where $t$ is time elapsed and $S$ is memory strength. The final score is the semantic similarity multiplied by $R$.

3. Half-Life Formula

$$D = \left(\frac{1}{2}\right)^{\frac{t}{h}}$$ Where $h$ is the half-life period. The final score is the semantic similarity multiplied by $D$.


License

MIT