clawmem 0.4.1 → 0.4.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/AGENTS.md +7 -0
- package/CLAUDE.md +7 -0
- package/package.json +1 -1
- package/src/indexer.ts +11 -7
- package/src/store.ts +7 -3
package/AGENTS.md
CHANGED
|
@@ -620,6 +620,13 @@ Symptom: reindex --force after v0.2.0 upgrade shows no entity extraction
|
|
|
620
620
|
→ `--force` alone only refreshes A-MEM notes (keywords, tags, context). `--enrich`
|
|
621
621
|
is needed after major upgrades that add new enrichment stages.
|
|
622
622
|
|
|
623
|
+
Symptom: `clawmem update` crashes with "Binding expected string, TypedArray, boolean, number, bigint or null"
|
|
624
|
+
→ YAML frontmatter values like `title: 2023-09-27` or `title: true` are coerced by gray-matter
|
|
625
|
+
into Date objects or booleans. Bun's SQLite driver rejects these as bind parameters.
|
|
626
|
+
→ Fixed v0.4.2: `parseDocument()` runtime-checks all frontmatter fields via `str()` helper.
|
|
627
|
+
Defense-in-depth `safeTitle` guards in `insertDocument`/`updateDocument`/`reactivateDocument`.
|
|
628
|
+
→ Affects: title, domain, workstream, content_type, review_by — any field gray-matter can coerce.
|
|
629
|
+
|
|
623
630
|
Symptom: CLI reindex/update falls back to node-llama-cpp Vulkan (not GPU server)
|
|
624
631
|
→ GPU env vars only in systemd drop-in, not in wrapper script. CLI invocations missed them.
|
|
625
632
|
→ Fixed 2026-02-12: bin/clawmem wrapper exports CLAWMEM_EMBED_URL/LLM_URL/RERANK_URL defaults.
|
package/CLAUDE.md
CHANGED
|
@@ -620,6 +620,13 @@ Symptom: reindex --force after v0.2.0 upgrade shows no entity extraction
|
|
|
620
620
|
→ `--force` alone only refreshes A-MEM notes (keywords, tags, context). `--enrich`
|
|
621
621
|
is needed after major upgrades that add new enrichment stages.
|
|
622
622
|
|
|
623
|
+
Symptom: `clawmem update` crashes with "Binding expected string, TypedArray, boolean, number, bigint or null"
|
|
624
|
+
→ YAML frontmatter values like `title: 2023-09-27` or `title: true` are coerced by gray-matter
|
|
625
|
+
into Date objects or booleans. Bun's SQLite driver rejects these as bind parameters.
|
|
626
|
+
→ Fixed v0.4.2: `parseDocument()` runtime-checks all frontmatter fields via `str()` helper.
|
|
627
|
+
Defense-in-depth `safeTitle` guards in `insertDocument`/`updateDocument`/`reactivateDocument`.
|
|
628
|
+
→ Affects: title, domain, workstream, content_type, review_by — any field gray-matter can coerce.
|
|
629
|
+
|
|
623
630
|
Symptom: CLI reindex/update falls back to node-llama-cpp Vulkan (not GPU server)
|
|
624
631
|
→ GPU env vars only in systemd drop-in, not in wrapper script. CLI invocations missed them.
|
|
625
632
|
→ Fixed 2026-02-12: bin/clawmem wrapper exports CLAWMEM_EMBED_URL/LLM_URL/RERANK_URL defaults.
|
package/package.json
CHANGED
package/src/indexer.ts
CHANGED
|
@@ -87,17 +87,21 @@ export function extractTitle(content: string, filename: string): string {
|
|
|
87
87
|
// =============================================================================
|
|
88
88
|
|
|
89
89
|
export function parseDocument(content: string, relativePath: string): { body: string; meta: DocumentMeta } {
|
|
90
|
+
// gray-matter coerces YAML values: `title: 2023-09-27` → Date, `title: true` → boolean.
|
|
91
|
+
// All frontmatter fields must be runtime-checked to prevent SQLite binding errors.
|
|
92
|
+
const str = (v: unknown): string | undefined =>
|
|
93
|
+
typeof v === "string" ? v || undefined : undefined;
|
|
90
94
|
try {
|
|
91
95
|
const { data, content: body } = matter(content);
|
|
92
96
|
return {
|
|
93
97
|
body,
|
|
94
98
|
meta: {
|
|
95
|
-
title: data.title
|
|
99
|
+
title: str(data.title),
|
|
96
100
|
tags: Array.isArray(data.tags) ? data.tags.map(String) : undefined,
|
|
97
|
-
domain: data.domain
|
|
98
|
-
workstream: data.workstream
|
|
99
|
-
content_type: (data.content_type as ContentType) || inferContentType(relativePath),
|
|
100
|
-
review_by: data.review_by
|
|
101
|
+
domain: str(data.domain),
|
|
102
|
+
workstream: str(data.workstream),
|
|
103
|
+
content_type: (str(data.content_type) as ContentType) || inferContentType(relativePath),
|
|
104
|
+
review_by: str(data.review_by),
|
|
101
105
|
},
|
|
102
106
|
};
|
|
103
107
|
} catch {
|
|
@@ -233,7 +237,7 @@ export async function indexCollection(
|
|
|
233
237
|
|
|
234
238
|
// Content changed — update
|
|
235
239
|
const { body, meta } = parseDocument(content, relativePath);
|
|
236
|
-
const title = meta.title
|
|
240
|
+
const title = (typeof meta.title === "string" && meta.title) ? meta.title : extractTitle(body, relativePath);
|
|
237
241
|
const docHash = hashContent(body);
|
|
238
242
|
|
|
239
243
|
store.insertContent(docHash, body, now);
|
|
@@ -265,7 +269,7 @@ export async function indexCollection(
|
|
|
265
269
|
).get(collectionName, relativePath) as { id: number; hash: string } | null;
|
|
266
270
|
|
|
267
271
|
const { body, meta } = parseDocument(content, relativePath);
|
|
268
|
-
const title = meta.title
|
|
272
|
+
const title = (typeof meta.title === "string" && meta.title) ? meta.title : extractTitle(body, relativePath);
|
|
269
273
|
const docHash = hashContent(body);
|
|
270
274
|
const contentType = meta.content_type || inferContentType(relativePath);
|
|
271
275
|
|
package/src/store.ts
CHANGED
|
@@ -1583,10 +1583,12 @@ export function insertDocument(
|
|
|
1583
1583
|
createdAt: string,
|
|
1584
1584
|
modifiedAt: string
|
|
1585
1585
|
): void {
|
|
1586
|
+
// Guard: gray-matter can coerce YAML values to Date/boolean/null — SQLite rejects these
|
|
1587
|
+
const safeTitle = (typeof title === "string") ? title : String(title ?? "Untitled");
|
|
1586
1588
|
db.prepare(`
|
|
1587
1589
|
INSERT INTO documents (collection, path, title, hash, created_at, modified_at, active)
|
|
1588
1590
|
VALUES (?, ?, ?, ?, ?, ?, 1)
|
|
1589
|
-
`).run(collectionName, path,
|
|
1591
|
+
`).run(collectionName, path, safeTitle, hash, createdAt, modifiedAt);
|
|
1590
1592
|
}
|
|
1591
1593
|
|
|
1592
1594
|
// =============================================================================
|
|
@@ -1915,8 +1917,9 @@ export function reactivateDocument(
|
|
|
1915
1917
|
hash: string,
|
|
1916
1918
|
modifiedAt: string
|
|
1917
1919
|
): void {
|
|
1920
|
+
const safeTitle = (typeof title === "string") ? title : String(title ?? "Untitled");
|
|
1918
1921
|
db.prepare(`UPDATE documents SET active = 1, title = ?, hash = ?, modified_at = ? WHERE id = ?`)
|
|
1919
|
-
.run(
|
|
1922
|
+
.run(safeTitle, hash, modifiedAt, documentId);
|
|
1920
1923
|
}
|
|
1921
1924
|
|
|
1922
1925
|
/**
|
|
@@ -1943,8 +1946,9 @@ export function updateDocument(
|
|
|
1943
1946
|
hash: string,
|
|
1944
1947
|
modifiedAt: string
|
|
1945
1948
|
): void {
|
|
1949
|
+
const safeTitle = (typeof title === "string") ? title : String(title ?? "Untitled");
|
|
1946
1950
|
db.prepare(`UPDATE documents SET title = ?, hash = ?, modified_at = ? WHERE id = ?`)
|
|
1947
|
-
.run(
|
|
1951
|
+
.run(safeTitle, hash, modifiedAt, documentId);
|
|
1948
1952
|
}
|
|
1949
1953
|
|
|
1950
1954
|
/**
|