wolbarg 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,31 @@ All notable changes to this project are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [0.5.2] — 2026-07-19
9
+
10
+ ### Added
11
+
12
+ - **`rememberFromMessages()` (experimental)** — conversation → memory bridge with `mode: "raw"` (default, no LLM) and optional `mode: "extract"` via configured `llm`
13
+ - **Vercel AI SDK adapter example** — `examples/adapters/vercel-ai/` (now uses official `@wolbarg/vercel-ai` middleware)
14
+ - **Companion package `@wolbarg/vercel-ai`** — Language Model Middleware (`wolbargMiddleware` + `wrapLanguageModel`) for automatic recall / remember (published separately under `packages/vercel-ai/`)
15
+
16
+ ### Compatibility
17
+
18
+ - Additive only. Omit the new method and behavior matches **0.5.1**. Experimental API may change before 1.0 — pin versions if you depend on it.
19
+ - Core `wolbarg` remains framework-agnostic; AI SDK types live only in `@wolbarg/vercel-ai`.
20
+ - `@wolbarg/vercel-ai@1` requires **AI SDK v7+** (`ai@^7`). Upgrade from AI SDK v4 before adopting the middleware.
21
+
22
+ ## [0.5.1] — 2026-07-19
23
+
24
+
25
+ ### Fixed
26
+
27
+ - **SQLite vec0 rowid binding on Linux** — bind `memory_rowid` as `BigInt` for `node:sqlite` + sqlite-vec so CI / Linux inserts and KNN search do not fail on integer PK binds
28
+
29
+ ### Compatibility
30
+
31
+ - Drop-in patch for **0.5.0**. No API or schema changes.
32
+
8
33
  ## [0.5.0] — 2026-07-19
9
34
 
10
35
  ### Added
@@ -166,6 +191,7 @@ const ctx = wolbarg({
166
191
 
167
192
  - Initial npm release path (pre–modular storage / ingest)
168
193
 
194
+ [0.5.1]: https://github.com/wolbarg/wolbarg/releases/tag/v0.5.1
169
195
  [0.5.0]: https://github.com/wolbarg/wolbarg/releases/tag/v0.5.0
170
196
  [0.4.0]: https://github.com/wolbarg/wolbarg/releases/tag/v0.4.0
171
197
  [0.3.2]: https://github.com/wolbarg/wolbarg/releases/tag/v0.3.2
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  **Modular, provider-agnostic semantic memory for AI agents.**
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/wolbarg.svg)](https://www.npmjs.com/package/wolbarg)
8
+ [![SDK CI](https://github.com/wolbarg/wolbarg/actions/workflows/sdk-ci.yml/badge.svg?branch=main)](https://github.com/wolbarg/wolbarg/actions/workflows/sdk-ci.yml)
8
9
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
9
10
  [![Node.js](https://img.shields.io/badge/node-%3E%3D22.5-brightgreen.svg)](https://nodejs.org)
10
11
  [![Docs](https://img.shields.io/badge/docs-wolbarg.com-black)](https://wolbarg.com)
@@ -16,7 +17,7 @@ npm install wolbarg
16
17
 
17
18
  Wolbarg is **memory infrastructure**, not an agent framework. Agents call `remember()` / `recall()` (and optionally ingest, compress, subscribe, and link memories in a graph). You bring SQLite or PostgreSQL, any OpenAI-compatible embedding API, and optional peers for PDF/DOCX/OCR/Neo4j.
18
19
 
19
- **Current release: [v0.5.0](./CHANGELOG.md)** — optional graph memory (SQLite + Neo4j), `includeGraph` recall, cascade forget, graph-aware checkpoints, and [Wolbarg Studio](https://wolbarg.com/docs/observability) graph canvas.
20
+ **Current release: [v0.5.2](./CHANGELOG.md)** — experimental `rememberFromMessages()` (chat → memory) and a [Vercel AI SDK adapter example](../examples/adapters/vercel-ai/). Still includes 0.5 graph memory (SQLite + Neo4j), `includeGraph` recall, and [Wolbarg Studio](https://wolbarg.com/docs/observability).
20
21
 
21
22
  ---
22
23
 
@@ -46,10 +47,12 @@ Wolbarg is **memory infrastructure**, not an agent framework. Agents call `remem
46
47
  - **Embedding cache** — `hash(content) + model`
47
48
  - **Dedupe / upsert** — opt-in exact / near / exact-or-near
48
49
  - **Graph memory (0.5)** — `sqliteGraph` / `neo4jGraph`, `linkMemories`, `getRelated`, `includeGraph`
50
+ - **Conversation bridge (0.5.2, experimental)** — `rememberFromMessages()` raw or LLM extract
51
+ - **Framework adapter** — official [`@wolbarg/vercel-ai`](../packages/vercel-ai/) middleware (`wrapLanguageModel`)
49
52
  - **Checkpoints / export** — file-backed SQLite snapshots (+ graph file when applicable)
50
53
  - **Telemetry** — independent event DB; Studio dashboard, Trace Explorer, graph canvas
51
54
 
52
- Docs: [Getting started](https://wolbarg.com/docs/getting-started) · [What's new in 0.5](https://wolbarg.com/docs/guides/whats-new) · [Graph memory](https://wolbarg.com/docs/graph-memory) · [Studio](https://wolbarg.com/docs/observability)
55
+ Docs: [Getting started](https://wolbarg.com/docs/getting-started) · [Vercel AI](https://wolbarg.com/docs/integrations/vercel-ai) · [rememberFromMessages](https://wolbarg.com/docs/api/remember-from-messages) · [Graph memory](https://wolbarg.com/docs/graph-memory) · [Studio](https://wolbarg.com/docs/observability)
53
56
 
54
57
  ---
55
58
 
package/dist/index.cjs CHANGED
@@ -3325,12 +3325,13 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
3325
3325
  insertEmbedding(rowid, embedding) {
3326
3326
  const stmts = this.requireStatements();
3327
3327
  if (this.vectorBackend === "sqlite-vec") {
3328
- stmts.insertEmbedding.run(rowid, this.toVectorParam(embedding));
3328
+ stmts.insertEmbedding.run(this.toVecRowid(rowid), this.toVectorParam(embedding));
3329
3329
  return;
3330
3330
  }
3331
- stmts.insertEmbeddingBlob.run(rowid, embeddingToBuffer(embedding));
3331
+ const id = Number(rowid);
3332
+ stmts.insertEmbeddingBlob.run(id, embeddingToBuffer(embedding));
3332
3333
  if (this.memoryIndex) {
3333
- this.memoryIndex.upsert(rowid, embedding);
3334
+ this.memoryIndex.upsert(id, embedding);
3334
3335
  this.memoryIndexDirty = false;
3335
3336
  } else {
3336
3337
  this.memoryIndexDirty = true;
@@ -3340,10 +3341,11 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
3340
3341
  const stmts = this.requireStatements();
3341
3342
  try {
3342
3343
  if (this.vectorBackend === "sqlite-vec" && stmts.deleteEmbedding) {
3343
- stmts.deleteEmbedding.run(rowid);
3344
+ stmts.deleteEmbedding.run(this.toVecRowid(rowid));
3344
3345
  } else if (stmts.deleteEmbeddingBlob) {
3345
- stmts.deleteEmbeddingBlob.run(rowid);
3346
- this.memoryIndex?.remove(rowid);
3346
+ const id = Number(rowid);
3347
+ stmts.deleteEmbeddingBlob.run(id);
3348
+ this.memoryIndex?.remove(id);
3347
3349
  this.memoryIndexDirty = true;
3348
3350
  }
3349
3351
  } catch {
@@ -3360,8 +3362,8 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
3360
3362
  for (let i = 0; i < rows.length; i += 1) {
3361
3363
  const row = rows[i];
3362
3364
  hits[i] = {
3363
- memoryRowid: row.memory_rowid,
3364
- distance: row.distance
3365
+ memoryRowid: Number(row.memory_rowid),
3366
+ distance: Number(row.distance)
3365
3367
  };
3366
3368
  }
3367
3369
  return hits;
@@ -3437,6 +3439,10 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
3437
3439
  const buffer = embeddingToBuffer(embedding);
3438
3440
  return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
3439
3441
  }
3442
+ /** sqlite-vec vec0 PKs must be bound as BigInt under node:sqlite. */
3443
+ toVecRowid(rowid) {
3444
+ return typeof rowid === "bigint" ? rowid : BigInt(rowid);
3445
+ }
3440
3446
  describe(error) {
3441
3447
  if (error instanceof Error) {
3442
3448
  return error.message;
@@ -5165,7 +5171,7 @@ function createDatabaseProvider(config) {
5165
5171
  }
5166
5172
 
5167
5173
  // src/version.ts
5168
- var SDK_VERSION = "0.5.0";
5174
+ var SDK_VERSION = "0.5.2";
5169
5175
 
5170
5176
  // src/memory/transfer.ts
5171
5177
  var SqliteMemoryTransferProvider = class {
@@ -5291,6 +5297,96 @@ function resolvePath(p) {
5291
5297
  return path7__default.default.isAbsolute(p) ? p : path7__default.default.resolve(process.cwd(), p);
5292
5298
  }
5293
5299
 
5300
+ // src/memory/from-messages.ts
5301
+ var DEFAULT_RAW_STRATEGY = "last_user";
5302
+ function normalizeConversationMessages(messages) {
5303
+ if (!Array.isArray(messages)) {
5304
+ throw new ValidationError("messages must be an array");
5305
+ }
5306
+ if (messages.length === 0) {
5307
+ throw new ValidationError("messages must be a non-empty array");
5308
+ }
5309
+ return messages.map((message, index) => {
5310
+ if (!message || typeof message !== "object") {
5311
+ throw new ValidationError(`messages[${index}] must be an object`);
5312
+ }
5313
+ if (typeof message.role !== "string" || message.role.trim().length === 0) {
5314
+ throw new ValidationError(`messages[${index}].role must be a non-empty string`);
5315
+ }
5316
+ if (typeof message.content !== "string") {
5317
+ throw new ValidationError(`messages[${index}].content must be a string`);
5318
+ }
5319
+ return {
5320
+ role: message.role.trim(),
5321
+ content: message.content.trim()
5322
+ };
5323
+ });
5324
+ }
5325
+ function resolveRememberFromMessagesOptions(options) {
5326
+ if (!options || typeof options !== "object") {
5327
+ throw new ValidationError("options is required");
5328
+ }
5329
+ if (typeof options.agent !== "string" || options.agent.trim().length === 0) {
5330
+ throw new ValidationError("agent must be a non-empty string");
5331
+ }
5332
+ const mode = options.mode ?? "raw";
5333
+ if (mode !== "raw" && mode !== "extract") {
5334
+ throw new ValidationError('mode must be "raw" or "extract"');
5335
+ }
5336
+ const rawStrategy = options.rawStrategy ?? DEFAULT_RAW_STRATEGY;
5337
+ if (rawStrategy !== "last_user" && rawStrategy !== "all_user") {
5338
+ throw new ValidationError('rawStrategy must be "last_user" or "all_user"');
5339
+ }
5340
+ return {
5341
+ agent: options.agent.trim(),
5342
+ mode,
5343
+ rawStrategy,
5344
+ ...options.metadata !== void 0 ? { metadata: options.metadata } : {},
5345
+ ...options.dedupe !== void 0 ? { dedupe: options.dedupe } : {}
5346
+ };
5347
+ }
5348
+ function selectRawUserTexts(messages, strategy) {
5349
+ const userTexts = messages.filter((m) => m.role === "user" && m.content.length > 0).map((m) => m.content);
5350
+ if (userTexts.length === 0) {
5351
+ throw new ValidationError(
5352
+ 'no user messages with non-empty content \u2014 nothing to remember in mode "raw"'
5353
+ );
5354
+ }
5355
+ if (strategy === "last_user") {
5356
+ return [userTexts[userTexts.length - 1]];
5357
+ }
5358
+ return userTexts;
5359
+ }
5360
+ function buildExtractMessages(messages) {
5361
+ const transcript = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
5362
+ return [
5363
+ {
5364
+ role: "system",
5365
+ content: "Extract durable factual memories from the conversation. Return only atomic facts, one per line. No numbering, bullets, or commentary. If there are no facts worth storing, reply with exactly NONE."
5366
+ },
5367
+ {
5368
+ role: "user",
5369
+ content: transcript
5370
+ }
5371
+ ];
5372
+ }
5373
+ function parseExtractedFacts(llmOutput) {
5374
+ const trimmed = llmOutput.trim();
5375
+ if (trimmed.length === 0 || /^none$/i.test(trimmed)) {
5376
+ return [];
5377
+ }
5378
+ const facts = [];
5379
+ for (const line of trimmed.split(/\r?\n/)) {
5380
+ let fact = line.trim();
5381
+ if (fact.length === 0) continue;
5382
+ fact = fact.replace(/^[-*•]\s+/, "");
5383
+ fact = fact.replace(/^\d+[.)]\s+/, "");
5384
+ if (fact.length === 0 || /^none$/i.test(fact)) continue;
5385
+ facts.push(fact);
5386
+ }
5387
+ return facts;
5388
+ }
5389
+
5294
5390
  // src/memory/index.ts
5295
5391
  function toMemoryRecord(row) {
5296
5392
  return {
@@ -7739,6 +7835,81 @@ var Wolbarg = class {
7739
7835
  throw wrapOperationError("rememberBatch", error);
7740
7836
  }
7741
7837
  }
7838
+ /**
7839
+ * Store memories from a chat transcript.
7840
+ *
7841
+ * **Experimental** until 1.0 — API shape may change.
7842
+ *
7843
+ * - `mode: "raw"` (default) — remember user message text (no LLM).
7844
+ * - `mode: "extract"` — require configured `llm`; extract atomic facts then remember each.
7845
+ */
7846
+ async rememberFromMessages(messages, options) {
7847
+ const parent = this.telemetry.start("rememberFromMessages");
7848
+ try {
7849
+ const normalized = normalizeConversationMessages(messages);
7850
+ const resolved = resolveRememberFromMessagesOptions(options);
7851
+ let texts;
7852
+ if (resolved.mode === "extract") {
7853
+ if (!this.llm) {
7854
+ throw new ProviderNotConfiguredError(
7855
+ "llm",
7856
+ "rememberFromMessages",
7857
+ 'pass llm: openaiLlm(...) in the constructor when mode is "extract"'
7858
+ );
7859
+ }
7860
+ const llmOutput = await this.llm.complete(
7861
+ buildExtractMessages(normalized)
7862
+ );
7863
+ texts = parseExtractedFacts(llmOutput);
7864
+ if (texts.length === 0) {
7865
+ parent.success({
7866
+ returnedCount: 0,
7867
+ agentId: resolved.agent,
7868
+ extra: { mode: "extract", factCount: 0 }
7869
+ });
7870
+ return [];
7871
+ }
7872
+ } else {
7873
+ texts = selectRawUserTexts(normalized, resolved.rawStrategy);
7874
+ }
7875
+ const out = [];
7876
+ for (const text of texts) {
7877
+ const child = parent.child("remember");
7878
+ const result = await this.rememberOne(
7879
+ {
7880
+ agent: resolved.agent,
7881
+ content: { text },
7882
+ ...resolved.metadata !== void 0 ? { metadata: resolved.metadata } : {},
7883
+ ...resolved.dedupe !== void 0 ? { dedupe: resolved.dedupe } : {}
7884
+ },
7885
+ child
7886
+ );
7887
+ child.success({
7888
+ provider: this.storage?.name,
7889
+ memoryIds: [result.id],
7890
+ returnedCount: 1,
7891
+ metadata: result.metadata,
7892
+ agentId: result.agent,
7893
+ tags: telemetryTags(result.metadata),
7894
+ extra: { upsertAction: result.action, mode: resolved.mode }
7895
+ });
7896
+ out.push(result);
7897
+ }
7898
+ parent.success({
7899
+ provider: this.storage?.name,
7900
+ memoryIds: out.map((r) => r.id),
7901
+ returnedCount: out.length,
7902
+ embeddingProvider: this.embedding?.model,
7903
+ model: this.embedding?.model,
7904
+ agentId: resolved.agent,
7905
+ extra: { mode: resolved.mode }
7906
+ });
7907
+ return out;
7908
+ } catch (error) {
7909
+ parent.failure(error, { agentId: options?.agent });
7910
+ throw wrapOperationError("rememberFromMessages", error);
7911
+ }
7912
+ }
7742
7913
  /**
7743
7914
  * Update an existing memory by id (re-embeds when content changes).
7744
7915
  */