wolbarg 0.5.1 → 0.5.3
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 +37 -0
- package/README.md +5 -2
- package/dist/index.cjs +189 -11
- 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 +189 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,42 @@ 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.3] — 2026-07-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Official framework adapters (`@1.0.0`)** — `@wolbarg/openai`, `@wolbarg/langchain`, `@wolbarg/llamaindex`, `@wolbarg/mastra` (alongside existing `@wolbarg/vercel-ai`)
|
|
13
|
+
- Docs + runnable examples under `examples/adapters/*` and [Integrations](https://wolbarg.com/docs/integrations)
|
|
14
|
+
|
|
15
|
+
### Compatibility
|
|
16
|
+
|
|
17
|
+
- Additive only. Core API unchanged from **0.5.2**. Adapter packages peer-depend on `wolbarg >= 0.5.3`.
|
|
18
|
+
|
|
19
|
+
## [0.5.2] — 2026-07-19
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **`rememberFromMessages()` (experimental)** — conversation → memory bridge with `mode: "raw"` (default, no LLM) and optional `mode: "extract"` via configured `llm`
|
|
24
|
+
- **Vercel AI SDK adapter example** — `examples/adapters/vercel-ai/` (now uses official `@wolbarg/vercel-ai` middleware)
|
|
25
|
+
- **Companion package `@wolbarg/vercel-ai`** — Language Model Middleware (`wolbargMiddleware` + `wrapLanguageModel`) for automatic recall / remember (published separately under `packages/vercel-ai/`)
|
|
26
|
+
|
|
27
|
+
### Compatibility
|
|
28
|
+
|
|
29
|
+
- 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.
|
|
30
|
+
- Core `wolbarg` remains framework-agnostic; AI SDK types live only in `@wolbarg/vercel-ai`.
|
|
31
|
+
- `@wolbarg/vercel-ai@1` requires **AI SDK v7+** (`ai@^7`). Upgrade from AI SDK v4 before adopting the middleware.
|
|
32
|
+
|
|
33
|
+
## [0.5.1] — 2026-07-19
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- **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
|
|
39
|
+
|
|
40
|
+
### Compatibility
|
|
41
|
+
|
|
42
|
+
- Drop-in patch for **0.5.0**. No API or schema changes.
|
|
43
|
+
|
|
8
44
|
## [0.5.0] — 2026-07-19
|
|
9
45
|
|
|
10
46
|
### Added
|
|
@@ -166,6 +202,7 @@ const ctx = wolbarg({
|
|
|
166
202
|
|
|
167
203
|
- Initial npm release path (pre–modular storage / ingest)
|
|
168
204
|
|
|
205
|
+
[0.5.1]: https://github.com/wolbarg/wolbarg/releases/tag/v0.5.1
|
|
169
206
|
[0.5.0]: https://github.com/wolbarg/wolbarg/releases/tag/v0.5.0
|
|
170
207
|
[0.4.0]: https://github.com/wolbarg/wolbarg/releases/tag/v0.4.0
|
|
171
208
|
[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.3](./CHANGELOG.md)** — official framework adapters (`@wolbarg/openai`, `@wolbarg/langchain`, `@wolbarg/llamaindex`, `@wolbarg/mastra`, `@wolbarg/vercel-ai`). Still includes experimental `rememberFromMessages()`, 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
|
@@ -1634,7 +1634,7 @@ var SQL = {
|
|
|
1634
1634
|
AND k = ?
|
|
1635
1635
|
`,
|
|
1636
1636
|
insertEmbeddingBlob: `
|
|
1637
|
-
INSERT INTO memory_embeddings_blob (memory_rowid, embedding) VALUES (?, ?)
|
|
1637
|
+
INSERT OR REPLACE INTO memory_embeddings_blob (memory_rowid, embedding) VALUES (?, ?)
|
|
1638
1638
|
`,
|
|
1639
1639
|
deleteEmbeddingBlob: `
|
|
1640
1640
|
DELETE FROM memory_embeddings_blob WHERE memory_rowid = ?
|
|
@@ -2590,6 +2590,13 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
2590
2590
|
this.deleteEmbeddingsForScope(organization);
|
|
2591
2591
|
this.deleteFtsForScope(organization);
|
|
2592
2592
|
const result = stmts.deleteMemoriesByOrg.run(organization);
|
|
2593
|
+
try {
|
|
2594
|
+
this.requireDb().exec(`
|
|
2595
|
+
DELETE FROM memory_embeddings_blob
|
|
2596
|
+
WHERE memory_rowid NOT IN (SELECT rowid FROM memories)
|
|
2597
|
+
`);
|
|
2598
|
+
} catch {
|
|
2599
|
+
}
|
|
2593
2600
|
return Number(result.changes);
|
|
2594
2601
|
});
|
|
2595
2602
|
}
|
|
@@ -3104,7 +3111,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
3104
3111
|
if (!stmt) {
|
|
3105
3112
|
const values = Array.from({ length: n }, () => "(?, ?)").join(", ");
|
|
3106
3113
|
stmt = this.requireDb().prepare(
|
|
3107
|
-
`INSERT INTO memory_embeddings_blob (memory_rowid, embedding) VALUES ${values}`
|
|
3114
|
+
`INSERT OR REPLACE INTO memory_embeddings_blob (memory_rowid, embedding) VALUES ${values}`
|
|
3108
3115
|
);
|
|
3109
3116
|
this.batchBlobEmbStatements.set(n, stmt);
|
|
3110
3117
|
}
|
|
@@ -3325,12 +3332,13 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
3325
3332
|
insertEmbedding(rowid, embedding) {
|
|
3326
3333
|
const stmts = this.requireStatements();
|
|
3327
3334
|
if (this.vectorBackend === "sqlite-vec") {
|
|
3328
|
-
stmts.insertEmbedding.run(rowid, this.toVectorParam(embedding));
|
|
3335
|
+
stmts.insertEmbedding.run(this.toVecRowid(rowid), this.toVectorParam(embedding));
|
|
3329
3336
|
return;
|
|
3330
3337
|
}
|
|
3331
|
-
|
|
3338
|
+
const id = Number(rowid);
|
|
3339
|
+
stmts.insertEmbeddingBlob.run(id, embeddingToBuffer(embedding));
|
|
3332
3340
|
if (this.memoryIndex) {
|
|
3333
|
-
this.memoryIndex.upsert(
|
|
3341
|
+
this.memoryIndex.upsert(id, embedding);
|
|
3334
3342
|
this.memoryIndexDirty = false;
|
|
3335
3343
|
} else {
|
|
3336
3344
|
this.memoryIndexDirty = true;
|
|
@@ -3340,10 +3348,11 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
3340
3348
|
const stmts = this.requireStatements();
|
|
3341
3349
|
try {
|
|
3342
3350
|
if (this.vectorBackend === "sqlite-vec" && stmts.deleteEmbedding) {
|
|
3343
|
-
stmts.deleteEmbedding.run(rowid);
|
|
3351
|
+
stmts.deleteEmbedding.run(this.toVecRowid(rowid));
|
|
3344
3352
|
} else if (stmts.deleteEmbeddingBlob) {
|
|
3345
|
-
|
|
3346
|
-
|
|
3353
|
+
const id = Number(rowid);
|
|
3354
|
+
stmts.deleteEmbeddingBlob.run(id);
|
|
3355
|
+
this.memoryIndex?.remove(id);
|
|
3347
3356
|
this.memoryIndexDirty = true;
|
|
3348
3357
|
}
|
|
3349
3358
|
} catch {
|
|
@@ -3360,8 +3369,8 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
3360
3369
|
for (let i = 0; i < rows.length; i += 1) {
|
|
3361
3370
|
const row = rows[i];
|
|
3362
3371
|
hits[i] = {
|
|
3363
|
-
memoryRowid: row.memory_rowid,
|
|
3364
|
-
distance: row.distance
|
|
3372
|
+
memoryRowid: Number(row.memory_rowid),
|
|
3373
|
+
distance: Number(row.distance)
|
|
3365
3374
|
};
|
|
3366
3375
|
}
|
|
3367
3376
|
return hits;
|
|
@@ -3437,6 +3446,10 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
|
|
|
3437
3446
|
const buffer = embeddingToBuffer(embedding);
|
|
3438
3447
|
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
3439
3448
|
}
|
|
3449
|
+
/** sqlite-vec vec0 PKs must be bound as BigInt under node:sqlite. */
|
|
3450
|
+
toVecRowid(rowid) {
|
|
3451
|
+
return typeof rowid === "bigint" ? rowid : BigInt(rowid);
|
|
3452
|
+
}
|
|
3440
3453
|
describe(error) {
|
|
3441
3454
|
if (error instanceof Error) {
|
|
3442
3455
|
return error.message;
|
|
@@ -5165,7 +5178,7 @@ function createDatabaseProvider(config) {
|
|
|
5165
5178
|
}
|
|
5166
5179
|
|
|
5167
5180
|
// src/version.ts
|
|
5168
|
-
var SDK_VERSION = "0.5.
|
|
5181
|
+
var SDK_VERSION = "0.5.3";
|
|
5169
5182
|
|
|
5170
5183
|
// src/memory/transfer.ts
|
|
5171
5184
|
var SqliteMemoryTransferProvider = class {
|
|
@@ -5291,6 +5304,96 @@ function resolvePath(p) {
|
|
|
5291
5304
|
return path7__default.default.isAbsolute(p) ? p : path7__default.default.resolve(process.cwd(), p);
|
|
5292
5305
|
}
|
|
5293
5306
|
|
|
5307
|
+
// src/memory/from-messages.ts
|
|
5308
|
+
var DEFAULT_RAW_STRATEGY = "last_user";
|
|
5309
|
+
function normalizeConversationMessages(messages) {
|
|
5310
|
+
if (!Array.isArray(messages)) {
|
|
5311
|
+
throw new ValidationError("messages must be an array");
|
|
5312
|
+
}
|
|
5313
|
+
if (messages.length === 0) {
|
|
5314
|
+
throw new ValidationError("messages must be a non-empty array");
|
|
5315
|
+
}
|
|
5316
|
+
return messages.map((message, index) => {
|
|
5317
|
+
if (!message || typeof message !== "object") {
|
|
5318
|
+
throw new ValidationError(`messages[${index}] must be an object`);
|
|
5319
|
+
}
|
|
5320
|
+
if (typeof message.role !== "string" || message.role.trim().length === 0) {
|
|
5321
|
+
throw new ValidationError(`messages[${index}].role must be a non-empty string`);
|
|
5322
|
+
}
|
|
5323
|
+
if (typeof message.content !== "string") {
|
|
5324
|
+
throw new ValidationError(`messages[${index}].content must be a string`);
|
|
5325
|
+
}
|
|
5326
|
+
return {
|
|
5327
|
+
role: message.role.trim(),
|
|
5328
|
+
content: message.content.trim()
|
|
5329
|
+
};
|
|
5330
|
+
});
|
|
5331
|
+
}
|
|
5332
|
+
function resolveRememberFromMessagesOptions(options) {
|
|
5333
|
+
if (!options || typeof options !== "object") {
|
|
5334
|
+
throw new ValidationError("options is required");
|
|
5335
|
+
}
|
|
5336
|
+
if (typeof options.agent !== "string" || options.agent.trim().length === 0) {
|
|
5337
|
+
throw new ValidationError("agent must be a non-empty string");
|
|
5338
|
+
}
|
|
5339
|
+
const mode = options.mode ?? "raw";
|
|
5340
|
+
if (mode !== "raw" && mode !== "extract") {
|
|
5341
|
+
throw new ValidationError('mode must be "raw" or "extract"');
|
|
5342
|
+
}
|
|
5343
|
+
const rawStrategy = options.rawStrategy ?? DEFAULT_RAW_STRATEGY;
|
|
5344
|
+
if (rawStrategy !== "last_user" && rawStrategy !== "all_user") {
|
|
5345
|
+
throw new ValidationError('rawStrategy must be "last_user" or "all_user"');
|
|
5346
|
+
}
|
|
5347
|
+
return {
|
|
5348
|
+
agent: options.agent.trim(),
|
|
5349
|
+
mode,
|
|
5350
|
+
rawStrategy,
|
|
5351
|
+
...options.metadata !== void 0 ? { metadata: options.metadata } : {},
|
|
5352
|
+
...options.dedupe !== void 0 ? { dedupe: options.dedupe } : {}
|
|
5353
|
+
};
|
|
5354
|
+
}
|
|
5355
|
+
function selectRawUserTexts(messages, strategy) {
|
|
5356
|
+
const userTexts = messages.filter((m) => m.role === "user" && m.content.length > 0).map((m) => m.content);
|
|
5357
|
+
if (userTexts.length === 0) {
|
|
5358
|
+
throw new ValidationError(
|
|
5359
|
+
'no user messages with non-empty content \u2014 nothing to remember in mode "raw"'
|
|
5360
|
+
);
|
|
5361
|
+
}
|
|
5362
|
+
if (strategy === "last_user") {
|
|
5363
|
+
return [userTexts[userTexts.length - 1]];
|
|
5364
|
+
}
|
|
5365
|
+
return userTexts;
|
|
5366
|
+
}
|
|
5367
|
+
function buildExtractMessages(messages) {
|
|
5368
|
+
const transcript = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
5369
|
+
return [
|
|
5370
|
+
{
|
|
5371
|
+
role: "system",
|
|
5372
|
+
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."
|
|
5373
|
+
},
|
|
5374
|
+
{
|
|
5375
|
+
role: "user",
|
|
5376
|
+
content: transcript
|
|
5377
|
+
}
|
|
5378
|
+
];
|
|
5379
|
+
}
|
|
5380
|
+
function parseExtractedFacts(llmOutput) {
|
|
5381
|
+
const trimmed = llmOutput.trim();
|
|
5382
|
+
if (trimmed.length === 0 || /^none$/i.test(trimmed)) {
|
|
5383
|
+
return [];
|
|
5384
|
+
}
|
|
5385
|
+
const facts = [];
|
|
5386
|
+
for (const line of trimmed.split(/\r?\n/)) {
|
|
5387
|
+
let fact = line.trim();
|
|
5388
|
+
if (fact.length === 0) continue;
|
|
5389
|
+
fact = fact.replace(/^[-*•]\s+/, "");
|
|
5390
|
+
fact = fact.replace(/^\d+[.)]\s+/, "");
|
|
5391
|
+
if (fact.length === 0 || /^none$/i.test(fact)) continue;
|
|
5392
|
+
facts.push(fact);
|
|
5393
|
+
}
|
|
5394
|
+
return facts;
|
|
5395
|
+
}
|
|
5396
|
+
|
|
5294
5397
|
// src/memory/index.ts
|
|
5295
5398
|
function toMemoryRecord(row) {
|
|
5296
5399
|
return {
|
|
@@ -7739,6 +7842,81 @@ var Wolbarg = class {
|
|
|
7739
7842
|
throw wrapOperationError("rememberBatch", error);
|
|
7740
7843
|
}
|
|
7741
7844
|
}
|
|
7845
|
+
/**
|
|
7846
|
+
* Store memories from a chat transcript.
|
|
7847
|
+
*
|
|
7848
|
+
* **Experimental** until 1.0 — API shape may change.
|
|
7849
|
+
*
|
|
7850
|
+
* - `mode: "raw"` (default) — remember user message text (no LLM).
|
|
7851
|
+
* - `mode: "extract"` — require configured `llm`; extract atomic facts then remember each.
|
|
7852
|
+
*/
|
|
7853
|
+
async rememberFromMessages(messages, options) {
|
|
7854
|
+
const parent = this.telemetry.start("rememberFromMessages");
|
|
7855
|
+
try {
|
|
7856
|
+
const normalized = normalizeConversationMessages(messages);
|
|
7857
|
+
const resolved = resolveRememberFromMessagesOptions(options);
|
|
7858
|
+
let texts;
|
|
7859
|
+
if (resolved.mode === "extract") {
|
|
7860
|
+
if (!this.llm) {
|
|
7861
|
+
throw new ProviderNotConfiguredError(
|
|
7862
|
+
"llm",
|
|
7863
|
+
"rememberFromMessages",
|
|
7864
|
+
'pass llm: openaiLlm(...) in the constructor when mode is "extract"'
|
|
7865
|
+
);
|
|
7866
|
+
}
|
|
7867
|
+
const llmOutput = await this.llm.complete(
|
|
7868
|
+
buildExtractMessages(normalized)
|
|
7869
|
+
);
|
|
7870
|
+
texts = parseExtractedFacts(llmOutput);
|
|
7871
|
+
if (texts.length === 0) {
|
|
7872
|
+
parent.success({
|
|
7873
|
+
returnedCount: 0,
|
|
7874
|
+
agentId: resolved.agent,
|
|
7875
|
+
extra: { mode: "extract", factCount: 0 }
|
|
7876
|
+
});
|
|
7877
|
+
return [];
|
|
7878
|
+
}
|
|
7879
|
+
} else {
|
|
7880
|
+
texts = selectRawUserTexts(normalized, resolved.rawStrategy);
|
|
7881
|
+
}
|
|
7882
|
+
const out = [];
|
|
7883
|
+
for (const text of texts) {
|
|
7884
|
+
const child = parent.child("remember");
|
|
7885
|
+
const result = await this.rememberOne(
|
|
7886
|
+
{
|
|
7887
|
+
agent: resolved.agent,
|
|
7888
|
+
content: { text },
|
|
7889
|
+
...resolved.metadata !== void 0 ? { metadata: resolved.metadata } : {},
|
|
7890
|
+
...resolved.dedupe !== void 0 ? { dedupe: resolved.dedupe } : {}
|
|
7891
|
+
},
|
|
7892
|
+
child
|
|
7893
|
+
);
|
|
7894
|
+
child.success({
|
|
7895
|
+
provider: this.storage?.name,
|
|
7896
|
+
memoryIds: [result.id],
|
|
7897
|
+
returnedCount: 1,
|
|
7898
|
+
metadata: result.metadata,
|
|
7899
|
+
agentId: result.agent,
|
|
7900
|
+
tags: telemetryTags(result.metadata),
|
|
7901
|
+
extra: { upsertAction: result.action, mode: resolved.mode }
|
|
7902
|
+
});
|
|
7903
|
+
out.push(result);
|
|
7904
|
+
}
|
|
7905
|
+
parent.success({
|
|
7906
|
+
provider: this.storage?.name,
|
|
7907
|
+
memoryIds: out.map((r) => r.id),
|
|
7908
|
+
returnedCount: out.length,
|
|
7909
|
+
embeddingProvider: this.embedding?.model,
|
|
7910
|
+
model: this.embedding?.model,
|
|
7911
|
+
agentId: resolved.agent,
|
|
7912
|
+
extra: { mode: resolved.mode }
|
|
7913
|
+
});
|
|
7914
|
+
return out;
|
|
7915
|
+
} catch (error) {
|
|
7916
|
+
parent.failure(error, { agentId: options?.agent });
|
|
7917
|
+
throw wrapOperationError("rememberFromMessages", error);
|
|
7918
|
+
}
|
|
7919
|
+
}
|
|
7742
7920
|
/**
|
|
7743
7921
|
* Update an existing memory by id (re-embeds when content changes).
|
|
7744
7922
|
*/
|