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

@gnldev/memory

v0.5.0

Published

Rich semantic-recall memory for @gnldev/durable: schema working memory, thread/resource mgmt, observational memory — all journaled (replayable/exactly-once).

Downloads

335

Readme

@gnldev/memory

Rich semantic-recall memory for @gnldev/durablejournaled and replayable; a write already recorded is replayed rather than applied again (at-most-once for side effects). One class: AgentMemory.

import { AgentMemory } from '@gnldev/memory';
import { z } from 'zod';

const mem = new AgentMemory({
  storage,                                // the Storage, not storage.runs — memory needs its own port
  embed,                                  // text → number[] (AI SDK embed or your own fn)
  recentN: 6,
  recall: { topK: 3, messageRange: 1, threshold: 0.2, scope: 'resource' },
  workingMemory: { schema: z.object({ name: z.string().optional(), tier: z.string().optional() }) },
  observationalMemory: { enabled: true, observerModel, observation: { messageThreshold: 30 } },
});

await runDurable({ runId, journal, model, memory: mem, threadId: 'th-1', resourceId: 'user-42', prompt });

4 tracks (all common agent-memory features + a durable twist)

  • Rich recalltopK + messageRange (context around the hit) + threshold + metadata filter + scope:'resource' (cross-thread).
  • Schema working memory — zod/template WM + the updateWorkingMemory tool (deep-merge, null = delete) + readOnly. The tool is wrapped with durableTool → the merge is journaled.
  • Thread + resource managementcreateThread/getThreadById/listThreads/updateThread/deleteThread/cloneThread (+ ancestry).
  • Observational memory — the Observer folds old messages into observations, the Reflector compresses them. LLM calls are journaled via durableProcessorStepreplayable compaction.

Durable twist (not in typical agent-memory implementations)

  • Recall is replayable: loadContext runs before persistInput → the recall result freezes into :input; resume replays the same context, embed/query never runs again.
  • WM merge is recorded, not repeated: the updateWorkingMemory tool is journaled → on resume the merge comes back from the record instead of being applied a second time (no double-write).
  • OM is replayable: the same seq again → the Observer/Reflector LLM is never called, the summary is reproduced verbatim (survives even a crash mid-compaction).

Honest caveats

  • Recall freezes on the first run: resume recalls based on the resource graph from the first run (correct for replay determinism). A new turn = new runId = fresh recall.
  • WM writes only happen on the first execute (durableTool short-circuit); resume reads state from the first run.
  • OM v1 is minimal: message-count threshold (not a real tokenizer), synchronous compaction (no async buffering), single observer model (no token-tier routing). Future: these + time-based markers + resource-scope OM.
  • Concurrent turns on one thread are safe, but not ordered: two runs appending to the same thread at once both land. The store assigns seq inside the write, serialised per thread, so nothing is dropped — this was NOT true before: a lockless read-then-write lost messages, silently, and a dropped tool-result broke the thread until the orphan slid out of the recent-message window (five consecutive failures, measured). What concurrency still costs is adjacency: messages land in SEND order and answers in COMPLETION order, so two racing turns interleave as [A, B, ansA, ansB]. If a turn must not begin while another is in flight, serialise at your own entry point — gnl holds no thread-level lock.
  • Writing your own MemoryStore: appendMessages receives MessageAppend[], where seq is optional and normally ABSENT. An adapter must assign the next positions itself, in the same transaction that writes the rows, serialised per thread, and must write the batch all-or-nothing — a half-written batch can leave a tool-call without its tool-result. Rows that DO carry seq are written exactly there and keep the idempotent CAS form (cloneThread, transcript import); a batch that mixes the two throws.

Lite alternative

For anyone who just wants simple topK recall, SemanticMemory in @gnldev/rag is lighter; it uses the same sem:${threadId}:log shape (cross-readable).

License

Apache-2.0 — see LICENSE.