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 +26 -0
- package/README.md +5 -2
- package/dist/index.cjs +180 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -4
- package/dist/index.d.ts +44 -4
- package/dist/index.js +180 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
[](https://www.npmjs.com/package/wolbarg)
|
|
8
|
+
[](https://github.com/wolbarg/wolbarg/actions/workflows/sdk-ci.yml)
|
|
8
9
|
[](./LICENSE)
|
|
9
10
|
[](https://nodejs.org)
|
|
10
11
|
[](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.
|
|
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) · [
|
|
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
|
-
|
|
3331
|
+
const id = Number(rowid);
|
|
3332
|
+
stmts.insertEmbeddingBlob.run(id, embeddingToBuffer(embedding));
|
|
3332
3333
|
if (this.memoryIndex) {
|
|
3333
|
-
this.memoryIndex.upsert(
|
|
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
|
-
|
|
3346
|
-
|
|
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.
|
|
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
|
*/
|