diff --git a/CHANGELOG.md b/CHANGELOG.md index 62d28a97b0f..1ecd787ecd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ Docs: https://docs.openclaw.ai - Cron: preserve manual `cron.run` IDs in `cron.runs` history so manual run acknowledgements can be correlated with finished run records. Fixes #76276. - CLI/devices: request `operator.admin` for `openclaw devices approve ` only when the exact pending device request would mint or inherit admin-scoped operator access, while keeping lower-scope approvals on the pairing scope. - Memory/embedding: broaden the embedding reindex retry classifier to include transient socket-layer errors (`fetch failed`, `ECONNRESET`, `socket hang up`, `UND_ERR_*`, `closed`) so memory reindex survives provider network hiccups instead of aborting mid-run. Related #56815, #44166. (#76311) Thanks @buyitsydney. +- Memory/search: keep sqlite-vec optional in packaged installs and point missing-extension recovery at the valid `agents.defaults.memorySearch.store.vector.extensionPath` setting. Thanks @vincentkoc. - Gateway: keep directly requested plugin tools invokable under restrictive tool profiles while preserving explicit deny lists and the HTTP safety deny list, preventing catalog/invoke mismatches that surface as "Tool not available". Thanks @BunsDev. - Gateway/update: allow beta binaries to refresh gateway services when the config was last written by the matching stable release version, avoiding false newer-config downgrade blocks during beta channel updates. - Channels: keep Matrix and Mattermost bundled in the core package instead of advertising external npm installs before those channels are cut over. Thanks @vincentkoc. diff --git a/package.json b/package.json index 0c51ce473c2..4c1e0769f34 100644 --- a/package.json +++ b/package.json @@ -1697,7 +1697,6 @@ "playwright-core": "1.59.1", "proxy-agent": "^8.0.1", "qrcode": "1.5.4", - "sqlite-vec": "0.1.9", "tar": "7.5.13", "tokenjuice": "0.7.0", "tree-sitter-bash": "^0.25.1", @@ -1734,6 +1733,9 @@ "typescript": "^6.0.3", "vitest": "^4.1.5" }, + "optionalDependencies": { + "sqlite-vec": "0.1.9" + }, "overrides": { "@aws-sdk/client-bedrock-runtime": "$@aws-sdk/client-bedrock-runtime", "axios": "1.15.0", diff --git a/packages/memory-host-sdk/src/host/sqlite-vec.test.ts b/packages/memory-host-sdk/src/host/sqlite-vec.test.ts index 07e3942b3c4..790698cb495 100644 --- a/packages/memory-host-sdk/src/host/sqlite-vec.test.ts +++ b/packages/memory-host-sdk/src/host/sqlite-vec.test.ts @@ -1,13 +1,26 @@ -import { describe, expect, it, vi } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; -vi.mock("sqlite-vec", () => { - throw new Error("bundled sqlite-vec should not load when extensionPath is explicit"); +function mockMissingSqliteVecPackage(): void { + vi.doMock("sqlite-vec", () => { + const err = new Error("Cannot find package 'sqlite-vec' imported from sqlite-vec.test.ts"); + Object.assign(err, { code: "ERR_MODULE_NOT_FOUND" }); + throw err; + }); +} + +async function importLoader() { + return import("./sqlite-vec.js"); +} + +afterEach(() => { + vi.doUnmock("sqlite-vec"); + vi.resetModules(); }); -import { loadSqliteVecExtension } from "./sqlite-vec.js"; - describe("loadSqliteVecExtension", () => { it("loads explicit extensionPath without importing bundled sqlite-vec", async () => { + mockMissingSqliteVecPackage(); + const { loadSqliteVecExtension } = await importLoader(); const db = { enableLoadExtension: vi.fn(), loadExtension: vi.fn(), @@ -22,4 +35,23 @@ describe("loadSqliteVecExtension", () => { expect(db.enableLoadExtension).toHaveBeenCalledWith(true); expect(db.loadExtension).toHaveBeenCalledWith("/opt/openclaw/sqlite-vec.so"); }); + + it("returns a valid memorySearch extensionPath hint when sqlite-vec is absent", async () => { + mockMissingSqliteVecPackage(); + const { loadSqliteVecExtension } = await importLoader(); + const db = { + enableLoadExtension: vi.fn(), + loadExtension: vi.fn(), + }; + + const result = await loadSqliteVecExtension({ db: db as never }); + + expect(result.ok).toBe(false); + expect(result.error).toContain("sqlite-vec package is not installed."); + expect(result.error).toContain("agents.defaults.memorySearch.store.vector.extensionPath"); + expect(result.error).toContain("agent-specific memorySearch.store.vector.extensionPath"); + expect(result.error).not.toContain("memory.store.vector.extensionPath"); + expect(db.enableLoadExtension).toHaveBeenCalledWith(true); + expect(db.loadExtension).not.toHaveBeenCalled(); + }); }); diff --git a/packages/memory-host-sdk/src/host/sqlite-vec.ts b/packages/memory-host-sdk/src/host/sqlite-vec.ts index 61268647b51..c8d82acf93c 100644 --- a/packages/memory-host-sdk/src/host/sqlite-vec.ts +++ b/packages/memory-host-sdk/src/host/sqlite-vec.ts @@ -8,11 +8,24 @@ type SqliteVecModule = { }; const SQLITE_VEC_MODULE_ID = "sqlite-vec"; +const SQLITE_VEC_CONFIG_HINT = + "Set agents.defaults.memorySearch.store.vector.extensionPath, or an agent-specific memorySearch.store.vector.extensionPath, to a sqlite-vec loadable extension path."; async function loadSqliteVecModule(): Promise { return import(SQLITE_VEC_MODULE_ID) as Promise; } +function isMissingSqliteVecPackageError(err: unknown): boolean { + const message = formatErrorMessage(err); + const code = + err && typeof err === "object" && "code" in err ? (err as { code?: unknown }).code : undefined; + const missingSqliteVec = /Cannot find (?:package|module) ['"]sqlite-vec['"]/u.test(message); + return ( + missingSqliteVec && + (code === undefined || code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") + ); +} + export async function loadSqliteVecExtension(params: { db: DatabaseSync; extensionPath?: string; @@ -31,6 +44,12 @@ export async function loadSqliteVecExtension(params: { return { ok: true, extensionPath }; } catch (err) { const message = formatErrorMessage(err); + if (isMissingSqliteVecPackageError(err)) { + return { + ok: false, + error: `sqlite-vec package is not installed. ${SQLITE_VEC_CONFIG_HINT} Original error: ${message}`, + }; + } return { ok: false, error: message }; } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b9cc6b075f5..14913eedc30 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -184,9 +184,6 @@ importers: qrcode: specifier: 1.5.4 version: 1.5.4 - sqlite-vec: - specifier: 0.1.9 - version: 0.1.9 tar: specifier: 7.5.13 version: 7.5.13 @@ -220,6 +217,10 @@ importers: zod: specifier: ^4.4.1 version: 4.4.1 + optionalDependencies: + sqlite-vec: + specifier: 0.1.9 + version: 0.1.9 devDependencies: '@copilotkit/aimock': specifier: 1.16.4