Skip to content

Releases: johanneslatzel/llm-chat-memory

v0.3.0

Choose a tag to compare

@johanneslatzel johanneslatzel released this 31 Aug 16:58

Breaking

  • Memory JSON files now persist directly in memoryDir instead of memoryDir/memories/; the MemoryConfiguration.memoriesDir getter is removed.
  • The directed link graph is stored in each memory file's links field; the separate links.json file is no longer read or written.
  • MemoryService.save() is removed; links persist eagerly on every link/unlink/isolate/sync mutation.
  • Mutable Memory fields are native get/set accessors now (mem.score, mem.summary, mem.content, mem.tags, mem.embedding, mem.links, mem.cachedAt, mem.changedAt, mem.recalledAt) instead of method calls; id() and createdAt() remain methods.
  • LinkPool is constructed with just the pool: new LinkPool(pool); the config argument is removed.

Features

  • Every memory file carries its own outgoing links field, flushed eagerly whenever the graph changes; the graph rebuilds from the memory files on init.
  • Every MemoryPool is backed by an ObjectStore<MemoryJSON> from @johannes.latzel/json-file-store; the default is an in-memory MemoryStore, and MemoryService uses a JsonFileStore over config.memoryDir for durable storage.

Fixes

  • MemoryToolPackage tutorial no longer claims semantic similarity links are computed automatically

Internal

  • Refactored LinkPool to a nested Map<from, Map<to, MemoryLink>> working graph, so persisting a source only rewrites its own outgoing links (O(outgoing)).
  • Updated docs to reflect per-memory link storage, the store-always design, native accessors, and corrected summary constraints.
  • Added prepack and check:lines scripts with @johannes.latzel/check-line-limits; verify now runs check:lines.
  • Add @johannes.latzel/json-file-store ^0.1.0 dependency.
  • Bump dev deps: @types/node to 26.4.0, @typescript-eslint/* to 8.68.0, vitest/@vitest/coverage-v8 to 4.1.11, eslint to 10.9.1, lint-staged to 17.4.1.
  • Bump version to 0.3.0.

Migrating data from v0.2.0

v0.2.0 stored memory files in memoryDir/memories/ and the directed link graph in a separate links.json. v0.3.0 stores each memory file directly in memoryDir and embeds its outgoing links in the file's links field.

import { readFileSync, writeFileSync, readdirSync, rmSync, existsSync } from 'node:fs';
import { join } from 'node:path';

type MemoryLinkJSON =
    | { type: 'constant'; to: string }
    | { type: 'semantic'; to: string; weight: number; cachedAt: number };

type LegacyLink = {
    type: 'constant' | 'semantic';
    from: string;
    to: string;
    weight?: number;
    cachedAt?: number;
};

const dir = process.env.LLM_CHAT_MEMORY_DIR ?? './memories';
const nested = join(dir, 'memories');

// Fold the legacy links.json entries into each memory file's `links` field.
const bySource = new Map<string, MemoryLinkJSON[]>();
try {
    const links = JSON.parse(readFileSync(join(dir, 'links.json'), 'utf-8')) as LegacyLink[];
    for (const link of links) {
        const entry: MemoryLinkJSON =
            link.type === 'semantic'
                ? { type: 'semantic', to: link.to, weight: link.weight ?? 0, cachedAt: link.cachedAt ?? Date.now() }
                : { type: 'constant', to: link.to };
        const existing = bySource.get(link.from) ?? [];
        existing.push(entry);
        bySource.set(link.from, existing);
    }
} catch {
    // no legacy links.json; only the file move is needed
}

// Move each memory file up to <dir>/ and embed its outgoing links.
for (const name of readdirSync(nested)) {
    if (!name.endsWith('.json')) continue;
    const id = name.replace(/\.json$/, '');
    const source = join(nested, name);
    const target = join(dir, name);
    if (existsSync(target)) continue;
    const stored = JSON.parse(readFileSync(source, 'utf-8')) as { links?: MemoryLinkJSON[] };
    if (!Array.isArray(stored.links)) stored.links = bySource.get(id) ?? [];
    writeFileSync(target, JSON.stringify(stored, null, 2));
    rmSync(source);
}
rmSync(nested, { recursive: true, force: true });

Full Changelog: v0.2.0...v0.3.0

v0.2.0

Choose a tag to compare

@johanneslatzel johanneslatzel released this 14 Aug 10:20

Breaking

  • Node minimum raised from 18 to >=20.9.0 (required by the sharp override)

Features

  • MemoryPool and LinkPool expose lifecycle hooks through a fluent builder:
    onCreate, onUpdate, onDelete, onRecall, onLink, onUnlink
  • MemoryService.hook() routes to both pools; callbacks are best-effort and
    can never break the emitting operation
  • New hook builder and event types exported from the package

Internal

  • CI: add dependabot and label sync workflows, bump GitHub Actions and Node to
    24, add npm run audit gate
  • Dependencies: @johannes.latzel/llm-chat to 0.7.0, @johannes.latzel/pagerank
    to 1.0.3
  • Dev dependencies: eslint 10, @types/node 26.2.0, lint-staged 17.3.0
  • Add overrides for adm-zip and sharp to clear audit advisories
  • Add sideEffects: false

Full Changelog: v0.1.3...v0.2.0

v0.1.3

Choose a tag to compare

@johanneslatzel johanneslatzel released this 04 Jul 09:57

fix: export ReadonlyMemory from module
fix: use latest npm in publish.yml (otherwise provenance publishing doesn't work)

Full Changelog: v0.1.2...v0.1.3

v0.1.2

Choose a tag to compare

@johanneslatzel johanneslatzel released this 03 Jul 15:46

fix: graceful failure on LinkPool.load() when no links.json file exists

Full Changelog: v0.1.1...v0.1.2

v0.1.1

Choose a tag to compare

@johanneslatzel johanneslatzel released this 03 Jul 15:11

fix: ensureEmbeddings() missing from MemoryPoolInterface
docs: outdated docs

Full Changelog: v0.1.0...v0.1.1

v0.1.0

Choose a tag to compare

@johanneslatzel johanneslatzel released this 26 Jun 19:39

Initial Release

  • tagged, linked, and scored memories with embedding support
  • active recall via dedicated tool calls
  • passive recall via automatic chat lifecycle hooks automatically injecting memories
  • embedding-based semantic similarity search
  • manual (constant) and automatic (semantic similarity) memory linking
  • PageRank-based importance scoring over the link graph
  • pluggable selection strategies: MMR (default) and weighted random
  • JSON file persistence for memories and links
  • configurable via MemoryConfiguration objects and environment variables

Full Changelog: https://github.com/johanneslatzel/llm-chat-memory/commits/v0.1.0