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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawmem",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "On-device context engine and memory for AI agents. Claude Code and OpenClaw. Hooks + MCP server + hybrid RAG search.",
5
5
  "type": "module",
6
6
  "bin": {
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 as string | undefined,
99
+ title: str(data.title),
96
100
  tags: Array.isArray(data.tags) ? data.tags.map(String) : undefined,
97
- domain: data.domain as string | undefined,
98
- workstream: data.workstream as string | undefined,
99
- content_type: (data.content_type as ContentType) || inferContentType(relativePath),
100
- review_by: data.review_by as string | undefined,
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 || extractTitle(body, relativePath);
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 || extractTitle(body, relativePath);
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, title, hash, createdAt, modifiedAt);
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(title, hash, modifiedAt, documentId);
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(title, hash, modifiedAt, documentId);
1951
+ .run(safeTitle, hash, modifiedAt, documentId);
1948
1952
  }
1949
1953
 
1950
1954
  /**