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

@mnemora/core

v1.2.0

Published

mnemora の core パッケージ。zod 以外の実行時依存を持たない。interface / 型 / 純関数の既定戦略。

Downloads

3,664

Readme

@mnemora/core

mnemora の core パッケージ。型・interface・runtime.observe/tick/recall の実装・ 純関数の既定戦略(減衰・スコアリング)を持つ。実行時依存は zod だけ。

インストール

pnpm add @mnemora/core
# または
npm i @mnemora/core

前提

文脈を使う抽出

単独では意味が決まらない返答には、observe の extractionContext を渡せます。 文脈は観測と一緒に保存され、非同期抽出や reextract でも使われます。

await runtime.observe(ctx, {
  kind: "utterance",
  text: "それでお願いします。明日使います",
  speaker: "田中",
  occurredAt: new Date("2026-01-01T23:00:00Z"),
  extractionContext: {
    messages: [{ speaker: "assistant", text: "会議室は青葉でよいですか?" }],
    timeZone: "Asia/Tokyo",
  },
});

入力上限は公開 ExtractionContextSchema を参照してください。必要な文脈は呼び手が選びます。 空の {} でも話者・日時を渡す経路を有効にできます。省略時は従来の単独本文の抽出です。 occurredAt と timeZone が揃わなければ、相対日付の確定は指示しません。 文脈を渡しても意味の解釈はモデル依存です。DBの有効期間は従来どおり validFrom/validUntil で明示します。

  • Node.js >= 22(package.json の engines)
  • ESM のみ("type": "module")。CommonJS からは Node 22.12 以降の require(esm) で読み込める(TypeScript は module/moduleResolution を nodenext にし、TypeScript 5.8 以降を使うこと。 5.7 以前の nodenext と、どの版の node16 も TS1479 になる。node10 は TypeScript 5.x なら パッケージの入口の型を解決できるが、exports を読まないので @mnemora/testkit/fixtures のような subpath は解決できず、TypeScript 6 で非推奨・7 で廃止された。2026-09-27 に TypeScript 5.0〜7.0 で実測)
  • TypeScript の lib・target は ES2022 以上。公開の .d.ts が ErrorOptions(ES2022 の lib)を使う(memory-store・vector-store の例外クラス)。 ES2021 以下で skipLibCheck: false だと TS2304、skipLibCheck: true だと cause の型が失われる
  • 実行時の依存は zod のみ

⚠ @mnemora/core だけでは動く物が組めない

このパッケージは interface・型・純関数が中心で、それ単体では DB にも LLM にも 埋め込みモデルにもつながらない。runtime.observe() / runtime.tick() / runtime.recall() を 実際に動かすには、MemoryStore / VectorStore / EventStore / OutboxStore / TenantSettingsStore(すべて Postgres 実装は @mnemora/postgres)と、 LLMProvider / EmbeddingProvider(OpenAI 実装は @mnemora/openai、 テスト用の決定的な擬似実装は @mnemora/testkit)を 呼び出し側が用意して渡す必要がある。

import type {
  EmbeddingProvider,
  EventStore,
  LLMProvider,
  MemoryStore,
  OutboxStore,
  TenantSettingsStore,
  VectorStore,
} from "@mnemora/core";
import { createRuntime } from "@mnemora/core";
import { createHash } from "node:crypto";

// この7つは @mnemora/core が実装を持たない——ここでは型だけを示す骨格。
// 実際の値は @mnemora/postgres(store)と @mnemora/openai(provider)、
// またはテスト用に @mnemora/testkit の決定的な擬似 provider から調達する。
declare const memoryStore: MemoryStore;
declare const outboxStore: OutboxStore;
declare const vectorStore: VectorStore;
declare const eventStore: EventStore;
declare const tenantSettingsStore: TenantSettingsStore;
declare const llmProvider: LLMProvider;
declare const embeddingProvider: EmbeddingProvider;

const runtime = createRuntime({
  memoryStore,
  outboxStore,
  vectorStore,
  eventStore,
  tenantSettingsStore,
  llmProvider,
  embeddingProvider,
  // D16: SHA-256 hex 等、content からハッシュを計算する関数。core は計算しない。
  hashContent: (content) => createHash("sha256").update(content).digest("hex"),
});

const ctx = { tenantId: "tenant-1" };
const { observationId } = await runtime.observe(ctx, {
  kind: "utterance",
  text: "明日、京都へ出張する",
  speaker: "user",
});

// observe は outbox に積むだけ。索引づけは tick が行う(tick 無しで recall すると memories は空)
await runtime.tick(ctx, { leaseMs: 30_000 });
const recalled = await runtime.recall(ctx, { text: "京都の予定は?" });
console.log(observationId, recalled.memories.length);

⚠ observe() は記憶を outbox に積むだけで、索引づけ(埋め込み)は tick() が行う。tick() を呼ばずに recall() すると、memories: [] が返り、omitted に not_indexed(reason: "pending")が出る(インメモリの store と @mnemora/testkit の決定的な provider で、tick 無しは空・tick を1回呼ぶと1件返ることを確かめた)。常駐のワーカーが tick() を回す構成では、この1行は要らない(@mnemora/bullmq)。leaseMs(ジョブを掴む時間)は必須。

配線をすぐ試したいだけなら、@mnemora/postgres と @mnemora/openai の README にある そのままの例をつなげば動く(@mnemora/postgres 側は本物の Postgres + pgvector が要る)。

⚠ 連想枠(recall() の段3.5)は既定 on(ADR 0337)

recall() は、RecallQuery.association を省略すると DEFAULT_RECALL_ASSOCIATION ({ maxCount: 10 })を適用して連想を走らせる。 ⟹ このパッケージを入れたままの既定の振る舞いは「聞かれたことに加えて、関連する記憶も添える」。 (packages/core/src/recall.ts の RecallQuery.association の doc コメント参照。 ADR 0151 が導入した「省略時は連想を 一切走らせない」という既定 off は、ADR 0337 (2026-09-26、オーナーの決定)が反転させた)

一切走らせたくない呼び出しは association: null を明示的に渡す:

const recalled = await runtime.recall(ctx, {
  text: "京都の予定は?",
  association: null, // 明示的に off にする(省略すると DEFAULT_RECALL_ASSOCIATION が適用される)
});

既定値ではなく自分で値を渡したいときは、maxCount を明示する:

const recalled = await runtime.recall(ctx, {
  text: "京都の予定は?",
  association: { maxCount: 10 },
});
  • maxCount — 必須。既定値は個別には無い(association 自体を省略すると DEFAULT_RECALL_ASSOCIATION.maxCount = 10 が使われるが、association オブジェクトを 渡す場合は maxCount を呼び出し側が明示する必要がある——「量の上限を呼び出し側に 必ず明示させる」という ADR 0151 の設計は維持している)
  • anchorCount? — 段3までに残った上位何件を連想の起点(アンカー)にするか。 既定 DEFAULT_ASSOCIATION_ANCHOR_COUNT = 3。 ⚠ limit(既定 10)が天井になる——アンカーは段2で limit の内側に入った候補から取るので、 **anchorCount だけを上げても効かない。**裾野を広げたいなら limit と両方上げること (【実測 2026-09-17】limit:10 / anchorCount:40 で実際に起点になったアンカーは 10件、 limit:40 / anchorCount:40 では 40件。 docs/recall.md §9.2「⚠ anchorCount の天井」)
  • minSimilarity? — アンカーとの生のコサイン類似度の下限。 既定 DEFAULT_ASSOCIATION_MIN_SIMILARITY = 0.5(scoreThreshold とは尺度が違う別の値)

連想で来た候補は retrievedVia: "association" と associationOf(どのアンカーが連れてきたか)を 持つので、クエリに当たった候補と区別できる。

渡すと何が変わるか。 association-probes ベンチ(probe 12件、本物の Postgres + pgvector、 埋め込みは @mnemora/local-embedding のプロセス内 ONNX 推論)の実測では、maxCount: 10 で 連想でしか届かない gold の到達が 0/12 → 12/12、費用は memoryChars +4.32% だった (maxCount: 5 では 10/12・+2.22%。 ADR 0168)。 ⚠ これはこのリポジトリの probe 12件で測った値であり、他のデータでの値ではない。 ⚠ VectorStore.getVectors(任意メソッド)を実装していない adapter では、association を 渡しても連想は走らない——走らなかったことは stage_skipped { stage: 'association', reason: 'vector_store_lacks_get_vectors' } として omitted に名乗る(docs/recall.md §9)。

⚠ ctx.subjectId を省略すると「テナント全体」になる(既定はそちら)

subjectId は recall() の引数ではない。Ctx の任意欄である。

const ctx = { tenantId: "tenant-1" };                        // ⟹ テナント全体が対象
const scoped = { tenantId: "tenant-1", subjectId: "user-1" }; // ⟹ この subject だけが対象

⟹ すべてのメソッドの第一引数に載る任意欄なので、意識して足さないかぎり付かない。

段5(目次帯の集計、MemoryStore.aggregateScope)のコストが、ここで大きく変わる 【実測 2026-09-17、PostgreSQL 17.11 + pgvector 0.8.0、並列無効、digestBand あり、n=20 の中央値】:

| 行数(1テナント) | subjectId 無し | subjectId あり(絞り先 ≈1%) | subjectId あり(絞り先 10行) | |---:|---:|---:|---:| | 1,000 | 3.5ms | 1.5ms | 1.5ms | | 10,000 | 17.0ms | 1.5ms | 1.4ms | | 100,000 | 165.1ms | 4.0ms | 1.3ms |

絞ったときのコストは、テナント総行数ではなく絞り先の大きさに比例する——10行の subject なら、 テナントが 1,000行でも 100,000行でも 1.3〜1.5ms で変わらない。

⛔ 「だから絞れ」とは言っていない。subjectId は隔離境界ではなく整理の単位であり、 絞れば当然、他の subject の記憶は返らない。どちらを選ぶかは使う側が決めることである。

⚠ 段5 は recall() から無条件に呼ばれる(渡さなくても走る)。 詳しい実測・測っていないこと・上の数字と docs/recall.md §5 の古い表(100,000行で 45.8ms)との差は、 同 §5「subjectId を省略すると何が起きるか」を見ること。

⚠ 目次帯(digestBandLimit)は既定で返却量の大半を占めうる

帯は digestBandLimit(既定 DEFAULT_DIGEST_BAND_LIMIT)と帯全体の文字数上限 (呼び出し側からは変えられない)の、どちらか先に当たったほうで切れる。 どちらが先に 当たるかは digest の長さ次第で、digestBandLimit を下げても縮まないことがある。 実測・調整のしかたは docs/recall.md §6「目次帯の量を把握し、 調整する」(Issue #413)を見ること。

単体で呼べる純関数(動く最小の例)

一方で、以下は @mnemora/core だけで完結してそのまま実行できる—— 記憶の減衰(DecayStrategy)・スコアリング(ScoringStrategy)・時刻(Clock)・ トークン数の推定(TokenCounter)は、DB もネットワークも要らない純関数として公開している。

import {
  defaultDecayStrategy,
  defaultScoringStrategy,
  heuristicTokenCounter,
  systemClock,
} from "@mnemora/core";

const now = systemClock.now();

// 半減期720時間(30日)で、記録から時間が経つほど強度が下がる。
const decayed = defaultDecayStrategy.strengthAt(now, {
  recordedAt: new Date("2026-01-01T00:00:00Z"),
  lastReinforcedAt: null,
  strength: 1,
  halfLifeHours: 720,
});

// 減衰・タグ一致・鮮度・強度を掛け合わせた合計スコア。
const score = defaultScoringStrategy({
  now,
  tags: ["work"],
  queryTags: ["work"],
  occurredAt: new Date("2026-01-01T00:00:00Z"),
  recordedAt: new Date("2026-01-01T00:00:00Z"),
  lastReinforcedAt: null,
  strength: 1,
  halfLifeHours: 720,
});

// 文字種で重み付けした粗い推定(CJK 0.9トークン/字・非CJK 0.25トークン/字)。
// counter: "heuristic" を必ず返す(推定値を実測値の顔で返さない、という契約そのもの)。
// ⚠ CJK 以外の非ラテン文字(キリル・タイ・アラビア文字)は依然として過小評価する。
// 厳密さが要るなら TokenCounter を差し替えること(docs/decisions/0083-*.md)。
const { tokens, counter } = heuristicTokenCounter.count("hello world");

console.log({ decayed, total: score.total, tokens, counter });

ほかに export しているもの(約束は各 TSDoc)

@mnemora/core は、この README に出てこない名前も多く export している(型・zod スキーマ・既定値・純関数。 何が在るかの正本は scripts/__snapshots__/public-api/core.d.ts で、⛔ ここに数を写さない)。v1.0.0 の後に公開面へ入った名前を、用途ごとに並べる。

| 用途 | 名前 | |---|---| | 属性(attributes) | Attributes・AttributesSchema(受け付ける側)・StoredAttributesSchema(格納・伝播する側)、上限の ATTRIBUTES_MAX_KEYS・ATTRIBUTE_KEY_MIN_LENGTH・ATTRIBUTE_KEY_MAX_LENGTH・ATTRIBUTE_VALUE_MAX_LENGTH、統合先へ運ぶ積集合の intersectAttributes | | 抽出の文脈・主題の候補 | ExtractionContext(と上の ExtractionContextSchema)、SubjectCandidatesInput・sanitizeCandidateSubjectId、extract: 'deferred' と併せたときのエラーの接頭辞 SUBJECT_CANDIDATES_WITH_DEFERRED_EXTRACT_ERROR_PREFIX | | claim key(主張キー。observe() の claimKey) | ClaimKeyOptions・ClaimKeyOptionsSchema・ClaimKey・ClaimKeySchema・ClaimKeyBatchResult(と ClaimKeyBatchResultSchema)、normalizeClaimKey・normalizeClaimKeyPart・buildClaimKeyPrompt・deriveClaimKeys・DeriveClaimKeysResult、DEFAULT_KNOWN_PREDICATES_FROM_STORE_LIMIT、検出の結果の ContestedDetectionOutcome、extract: 'deferred' と併せたときの CLAIM_KEY_WITH_DEFERRED_EXTRACT_ERROR_PREFIX(docs/memory-model.md §5) | | taxonomy・テナント設定 | TaxonomyMode・DEFAULT_TAXONOMY_MODE・readTaxonomyMode・writeTaxonomyMode・assertValidTaxonomyMode・TAXONOMY_MODE_INVALID_MESSAGE・TAXONOMY_MODE_UNSUPPORTED_MESSAGE・LabelSummary、保持期間の assertValidEventRetentionKind・EVENT_RETENTION_KIND_INVALID_MESSAGE | | recall | 段2の時間項の方針 TimeWeightingPolicy・TIME_WEIGHTING_POLICIES・DEFAULT_TIME_WEIGHTING_POLICY、語彙チャンネルの打ち切りの報告 AnnUnreachedSeverity・AnnUnreachedSeveritySchema | | contested・superseded の後始末 | Runtime.resolveOrphanedContested の ResolveOrphanedContestedOptions・ResolveOrphanedContestedResult・ResolveOrphanedContestedOutcome・ResolveOrphanedContestedEligibility、restoreSuperseded の dryRun の候補を操作ごとに束ねる groupSupersededCandidatesByOperation・SupersededOperationGroup(docs/memory-model.md §11 行15) | | テナント消去 | あるテナントに属する行を跡形なく消す独立関数 eraseTenant(Runtime のメソッドではない。tick()/observe() には配線しない、明示呼び出し専用——purgeExpiredEventsForTenant と同じ置き方)と、その EraseTenantOptions・EraseTenantOutcome・EraseTenantDeps。MemoryStore/VectorStore/OutboxStore/TenantSettingsStore それぞれの任意メソッド eraseTenant? とその EraseTenantStoreOptions・EraseTenantResult・(MemoryStore のみ)EraseTenantStoreResult。詳細は docs/memory-model.md §9・ADR 0383 |

もっと詳しく