Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
32
extensions/memory-lancedb/README.md
Normal file
32
extensions/memory-lancedb/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# @openclaw/memory-lancedb
|
||||
|
||||
Official LanceDB-backed long-term memory plugin for OpenClaw.
|
||||
|
||||
This plugin adds persistent memory tools backed by LanceDB, vector search, auto-recall, and auto-capture.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
openclaw plugins install @openclaw/memory-lancedb
|
||||
```
|
||||
|
||||
Restart the Gateway after installing or updating the plugin.
|
||||
|
||||
## What it provides
|
||||
|
||||
- `memory_store`
|
||||
- `memory_recall`
|
||||
- `memory_forget`
|
||||
- LanceDB vector storage and hybrid memory retrieval.
|
||||
|
||||
## Configure
|
||||
|
||||
Use the memory plugin docs for embedding provider setup, storage paths, indexing, and recall behavior:
|
||||
|
||||
- https://docs.openclaw.ai/plugins/memory-lancedb
|
||||
|
||||
## Package
|
||||
|
||||
- Plugin id: `memory-lancedb`
|
||||
- Package: `@openclaw/memory-lancedb`
|
||||
- Minimum OpenClaw host: `2026.4.10`
|
||||
3
extensions/memory-lancedb/api.ts
Normal file
3
extensions/memory-lancedb/api.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// Memory Lancedb API module exposes the plugin public contract.
|
||||
export { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
||||
export { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
||||
19
extensions/memory-lancedb/cli-metadata.ts
Normal file
19
extensions/memory-lancedb/cli-metadata.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// Memory Lancedb plugin module implements cli metadata behavior.
|
||||
import { definePluginEntry } from "openclaw/plugin-sdk/core";
|
||||
|
||||
export default definePluginEntry({
|
||||
id: "memory-lancedb",
|
||||
name: "Memory LanceDB",
|
||||
description: "LanceDB-backed memory provider",
|
||||
register(api) {
|
||||
api.registerCli(() => {}, {
|
||||
descriptors: [
|
||||
{
|
||||
name: "ltm",
|
||||
description: "Inspect and query LanceDB-backed memory",
|
||||
hasSubcommands: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
216
extensions/memory-lancedb/config.test.ts
Normal file
216
extensions/memory-lancedb/config.test.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
// Memory Lancedb tests cover config plugin behavior.
|
||||
import fs from "node:fs";
|
||||
import {
|
||||
type JsonSchemaObject,
|
||||
validateJsonSchemaValue,
|
||||
} from "openclaw/plugin-sdk/json-schema-runtime";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { memoryConfigSchema } from "./config.js";
|
||||
|
||||
const manifest = JSON.parse(
|
||||
fs.readFileSync(new URL("./openclaw.plugin.json", import.meta.url), "utf-8"),
|
||||
) as { configSchema: JsonSchemaObject };
|
||||
|
||||
describe("memory-lancedb config", () => {
|
||||
it("accepts dreaming in the manifest schema and preserves it in runtime parsing", () => {
|
||||
const manifestResult = validateJsonSchemaValue({
|
||||
schema: manifest.configSchema,
|
||||
cacheKey: "memory-lancedb.manifest.dreaming",
|
||||
value: {
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
},
|
||||
dreaming: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const parsed = memoryConfigSchema.parse({
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
},
|
||||
dreaming: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(manifestResult.ok).toBe(true);
|
||||
expect(parsed.dreaming).toEqual({
|
||||
enabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("accepts provider-backed embedding config without a plugin apiKey", () => {
|
||||
const manifestResult = validateJsonSchemaValue({
|
||||
schema: manifest.configSchema,
|
||||
cacheKey: "memory-lancedb.manifest.provider-auth",
|
||||
value: {
|
||||
embedding: {
|
||||
provider: "openai",
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const parsed = memoryConfigSchema.parse({
|
||||
embedding: {
|
||||
provider: "openai",
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
});
|
||||
|
||||
expect(manifestResult.ok).toBe(true);
|
||||
expect(parsed.embedding.apiKey).toBeUndefined();
|
||||
expect(parsed.embedding.provider).toBe("openai");
|
||||
});
|
||||
|
||||
it("rejects empty embedding config in the manifest schema and runtime parser", () => {
|
||||
const manifestResult = validateJsonSchemaValue({
|
||||
schema: manifest.configSchema,
|
||||
cacheKey: "memory-lancedb.manifest.empty-embedding",
|
||||
value: {
|
||||
embedding: {},
|
||||
},
|
||||
});
|
||||
|
||||
expect(manifestResult.ok).toBe(false);
|
||||
if (!manifestResult.ok) {
|
||||
expect(manifestResult.errors.map((error) => error.text)).toContain(
|
||||
"embedding: must not have fewer than 1 properties",
|
||||
);
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
memoryConfigSchema.parse({
|
||||
embedding: {},
|
||||
});
|
||||
}).toThrow("embedding config must include at least one setting");
|
||||
});
|
||||
|
||||
it("allows missing embedding config in the manifest so setup can discover fields", () => {
|
||||
const manifestResult = validateJsonSchemaValue({
|
||||
schema: manifest.configSchema,
|
||||
cacheKey: "memory-lancedb.manifest.missing-embedding",
|
||||
value: {},
|
||||
});
|
||||
|
||||
expect(manifestResult.ok).toBe(true);
|
||||
expect(() => {
|
||||
memoryConfigSchema.parse({});
|
||||
}).toThrow("embedding config required");
|
||||
});
|
||||
|
||||
it("rejects empty embedding providers", () => {
|
||||
expect(() => {
|
||||
memoryConfigSchema.parse({
|
||||
embedding: {
|
||||
provider: "",
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
});
|
||||
}).toThrow("embedding.provider must not be empty");
|
||||
});
|
||||
|
||||
it("defaults non-finite character budgets and rejects invalid dimensions", () => {
|
||||
const manifestResult = validateJsonSchemaValue({
|
||||
schema: manifest.configSchema,
|
||||
cacheKey: "memory-lancedb.manifest.invalid-dimensions",
|
||||
value: {
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
dimensions: 1024.5,
|
||||
},
|
||||
},
|
||||
});
|
||||
const parsed = memoryConfigSchema.parse({
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
},
|
||||
captureMaxChars: Number.NaN,
|
||||
recallMaxChars: Number.POSITIVE_INFINITY,
|
||||
});
|
||||
|
||||
expect(parsed.captureMaxChars).toBe(500);
|
||||
expect(parsed.recallMaxChars).toBe(1000);
|
||||
expect(manifestResult.ok).toBe(false);
|
||||
for (const dimensions of [Number.NaN, 1024.5]) {
|
||||
expect(() => {
|
||||
memoryConfigSchema.parse({
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
dimensions,
|
||||
},
|
||||
});
|
||||
}).toThrow("embedding.dimensions must be a positive integer");
|
||||
}
|
||||
});
|
||||
|
||||
it("still rejects unrelated unknown top-level config keys", () => {
|
||||
expect(() => {
|
||||
memoryConfigSchema.parse({
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
},
|
||||
dreaming: {
|
||||
enabled: true,
|
||||
},
|
||||
unexpected: true,
|
||||
});
|
||||
}).toThrow("memory config has unknown keys: unexpected");
|
||||
});
|
||||
|
||||
it("accepts custom trigger literals in the manifest schema and runtime parser", () => {
|
||||
const manifestResult = validateJsonSchemaValue({
|
||||
schema: manifest.configSchema,
|
||||
cacheKey: "memory-lancedb.manifest.custom-triggers",
|
||||
value: {
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
},
|
||||
customTriggers: ["记住", "important project"],
|
||||
},
|
||||
});
|
||||
|
||||
const parsed = memoryConfigSchema.parse({
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
},
|
||||
customTriggers: [" 记住 ", "important project"],
|
||||
});
|
||||
|
||||
expect(manifestResult.ok).toBe(true);
|
||||
expect(parsed.customTriggers).toEqual(["记住", "important project"]);
|
||||
});
|
||||
|
||||
it("rejects unsafe custom trigger config values", () => {
|
||||
expect(() => {
|
||||
memoryConfigSchema.parse({
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
},
|
||||
customTriggers: ["记住", ""],
|
||||
});
|
||||
}).toThrow("customTriggers.1 must not be empty");
|
||||
|
||||
expect(() => {
|
||||
memoryConfigSchema.parse({
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
},
|
||||
customTriggers: ["x".repeat(101)],
|
||||
});
|
||||
}).toThrow("customTriggers.0 must be at most 100 characters");
|
||||
});
|
||||
|
||||
it("rejects non-object dreaming values in runtime parsing", () => {
|
||||
expect(() => {
|
||||
memoryConfigSchema.parse({
|
||||
embedding: {
|
||||
apiKey: "sk-test",
|
||||
},
|
||||
dreaming: true,
|
||||
});
|
||||
}).toThrow("dreaming config must be an object");
|
||||
});
|
||||
});
|
||||
324
extensions/memory-lancedb/config.ts
Normal file
324
extensions/memory-lancedb/config.ts
Normal file
@@ -0,0 +1,324 @@
|
||||
// Memory Lancedb helper module supports config behavior.
|
||||
import fs from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { parseFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
|
||||
|
||||
export type MemoryConfig = {
|
||||
embedding: {
|
||||
provider: string;
|
||||
model: string;
|
||||
apiKey?: string;
|
||||
baseUrl?: string;
|
||||
dimensions?: number;
|
||||
};
|
||||
dreaming?: Record<string, unknown>;
|
||||
dbPath?: string;
|
||||
autoCapture?: boolean;
|
||||
autoRecall?: boolean;
|
||||
captureMaxChars?: number;
|
||||
customTriggers?: string[];
|
||||
recallMaxChars?: number;
|
||||
storageOptions?: Record<string, string>;
|
||||
};
|
||||
|
||||
export const MEMORY_CATEGORIES = ["preference", "fact", "decision", "entity", "other"] as const;
|
||||
export type MemoryCategory = (typeof MEMORY_CATEGORIES)[number];
|
||||
|
||||
const DEFAULT_MODEL = "text-embedding-3-small";
|
||||
export const DEFAULT_CAPTURE_MAX_CHARS = 500;
|
||||
export const DEFAULT_RECALL_MAX_CHARS = 1000;
|
||||
const LEGACY_STATE_DIRS: string[] = [];
|
||||
|
||||
function resolveDefaultDbPath(): string {
|
||||
const home = homedir();
|
||||
const preferred = join(home, ".openclaw", "memory", "lancedb");
|
||||
try {
|
||||
if (fs.existsSync(preferred)) {
|
||||
return preferred;
|
||||
}
|
||||
} catch {
|
||||
// best-effort
|
||||
}
|
||||
|
||||
for (const legacy of LEGACY_STATE_DIRS) {
|
||||
const candidate = join(home, legacy, "memory", "lancedb");
|
||||
try {
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
} catch {
|
||||
// best-effort
|
||||
}
|
||||
}
|
||||
|
||||
return preferred;
|
||||
}
|
||||
|
||||
const DEFAULT_DB_PATH = resolveDefaultDbPath();
|
||||
|
||||
const EMBEDDING_DIMENSIONS: Record<string, number> = {
|
||||
"text-embedding-3-small": 1536,
|
||||
"text-embedding-3-large": 3072,
|
||||
};
|
||||
const EMBEDDING_CONFIG_KEYS = ["provider", "apiKey", "model", "baseUrl", "dimensions"] as const;
|
||||
|
||||
function assertAllowedKeys(value: Record<string, unknown>, allowed: string[], label: string) {
|
||||
const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
|
||||
if (unknown.length === 0) {
|
||||
return;
|
||||
}
|
||||
throw new Error(`${label} has unknown keys: ${unknown.join(", ")}`);
|
||||
}
|
||||
|
||||
export function vectorDimsForModel(model: string): number {
|
||||
const dims = EMBEDDING_DIMENSIONS[model];
|
||||
if (!dims) {
|
||||
throw new Error(`Unsupported embedding model: ${model}`);
|
||||
}
|
||||
return dims;
|
||||
}
|
||||
|
||||
function resolveEnvVars(value: string): string {
|
||||
return value.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
|
||||
const envValue = process.env[envVar];
|
||||
if (!envValue) {
|
||||
throw new Error(`Environment variable ${envVar} is not set`);
|
||||
}
|
||||
return envValue;
|
||||
});
|
||||
}
|
||||
|
||||
function resolveEmbeddingModel(
|
||||
embedding: Record<string, unknown>,
|
||||
dimensions: number | undefined,
|
||||
): string {
|
||||
const model = typeof embedding.model === "string" ? embedding.model : DEFAULT_MODEL;
|
||||
if (dimensions === undefined) {
|
||||
vectorDimsForModel(model);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
function resolveFiniteIntegerConfig(value: unknown): number | undefined {
|
||||
if (typeof value !== "number") {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = parseFiniteNumber(value);
|
||||
return parsed === undefined ? undefined : Math.floor(parsed);
|
||||
}
|
||||
|
||||
function resolveBoundedIntegerConfig(params: {
|
||||
value: unknown;
|
||||
fallback: number;
|
||||
min: number;
|
||||
max: number;
|
||||
label: string;
|
||||
}): number {
|
||||
const resolved = resolveFiniteIntegerConfig(params.value) ?? params.fallback;
|
||||
if (resolved < params.min || resolved > params.max) {
|
||||
throw new Error(`${params.label} must be between ${params.min} and ${params.max}`);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function resolveEmbeddingDimensions(embedding: Record<string, unknown>): number | undefined {
|
||||
if (embedding.dimensions === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const dimensions =
|
||||
typeof embedding.dimensions === "number" ? parseFiniteNumber(embedding.dimensions) : undefined;
|
||||
if (dimensions === undefined || !Number.isInteger(dimensions) || dimensions < 1) {
|
||||
throw new Error("embedding.dimensions must be a positive integer");
|
||||
}
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
export const memoryConfigSchema = {
|
||||
parse(value: unknown): MemoryConfig {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new Error("memory config required");
|
||||
}
|
||||
const cfg = value as Record<string, unknown>;
|
||||
assertAllowedKeys(
|
||||
cfg,
|
||||
[
|
||||
"embedding",
|
||||
"dreaming",
|
||||
"dbPath",
|
||||
"autoCapture",
|
||||
"autoRecall",
|
||||
"captureMaxChars",
|
||||
"customTriggers",
|
||||
"recallMaxChars",
|
||||
"storageOptions",
|
||||
],
|
||||
"memory config",
|
||||
);
|
||||
|
||||
const embedding = cfg.embedding as Record<string, unknown> | undefined;
|
||||
if (!embedding || typeof embedding !== "object" || Array.isArray(embedding)) {
|
||||
throw new Error("embedding config required");
|
||||
}
|
||||
assertAllowedKeys(embedding, [...EMBEDDING_CONFIG_KEYS], "embedding config");
|
||||
if (Object.keys(embedding).length === 0) {
|
||||
throw new Error("embedding config must include at least one setting");
|
||||
}
|
||||
|
||||
const dimensions = resolveEmbeddingDimensions(embedding);
|
||||
const model = resolveEmbeddingModel(embedding, dimensions);
|
||||
const provider = typeof embedding.provider === "string" ? embedding.provider.trim() : "openai";
|
||||
if (!provider) {
|
||||
throw new Error("embedding.provider must not be empty");
|
||||
}
|
||||
|
||||
const captureMaxChars = resolveBoundedIntegerConfig({
|
||||
value: cfg.captureMaxChars,
|
||||
fallback: DEFAULT_CAPTURE_MAX_CHARS,
|
||||
min: 100,
|
||||
max: 10_000,
|
||||
label: "captureMaxChars",
|
||||
});
|
||||
const recallMaxChars = resolveBoundedIntegerConfig({
|
||||
value: cfg.recallMaxChars,
|
||||
fallback: DEFAULT_RECALL_MAX_CHARS,
|
||||
min: 100,
|
||||
max: 10_000,
|
||||
label: "recallMaxChars",
|
||||
});
|
||||
let customTriggers: string[] | undefined;
|
||||
if (cfg.customTriggers !== undefined) {
|
||||
if (!Array.isArray(cfg.customTriggers)) {
|
||||
throw new Error("customTriggers must be an array of strings");
|
||||
}
|
||||
customTriggers = cfg.customTriggers.map((trigger, index) => {
|
||||
if (typeof trigger !== "string") {
|
||||
throw new Error(`customTriggers.${index} must be a string`);
|
||||
}
|
||||
const normalized = trigger.trim();
|
||||
if (!normalized) {
|
||||
throw new Error(`customTriggers.${index} must not be empty`);
|
||||
}
|
||||
if (normalized.length > 100) {
|
||||
throw new Error(`customTriggers.${index} must be at most 100 characters`);
|
||||
}
|
||||
return normalized;
|
||||
});
|
||||
if (customTriggers.length > 50) {
|
||||
throw new Error("customTriggers must include at most 50 entries");
|
||||
}
|
||||
}
|
||||
|
||||
const dreaming =
|
||||
cfg.dreaming === undefined
|
||||
? undefined
|
||||
: cfg.dreaming && typeof cfg.dreaming === "object" && !Array.isArray(cfg.dreaming)
|
||||
? (cfg.dreaming as Record<string, unknown>)
|
||||
: (() => {
|
||||
throw new Error("dreaming config must be an object");
|
||||
})();
|
||||
|
||||
// Parse storageOptions (object with string values)
|
||||
let storageOptions: Record<string, string> | undefined;
|
||||
const storageOpts = cfg.storageOptions as Record<string, unknown> | undefined;
|
||||
if (storageOpts !== undefined && storageOpts !== null) {
|
||||
if (!storageOpts || typeof storageOpts !== "object" || Array.isArray(storageOpts)) {
|
||||
throw new Error("storageOptions must be an object");
|
||||
}
|
||||
storageOptions = {};
|
||||
// Validate all values are strings
|
||||
for (const [key, valueLocal] of Object.entries(storageOpts)) {
|
||||
if (typeof valueLocal !== "string") {
|
||||
throw new Error(`storageOptions.${key} must be a string`);
|
||||
}
|
||||
storageOptions[key] = resolveEnvVars(valueLocal);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
embedding: {
|
||||
provider,
|
||||
model,
|
||||
apiKey: typeof embedding.apiKey === "string" ? resolveEnvVars(embedding.apiKey) : undefined,
|
||||
baseUrl:
|
||||
typeof embedding.baseUrl === "string" ? resolveEnvVars(embedding.baseUrl) : undefined,
|
||||
dimensions,
|
||||
},
|
||||
dreaming,
|
||||
dbPath: typeof cfg.dbPath === "string" ? cfg.dbPath : DEFAULT_DB_PATH,
|
||||
autoCapture: cfg.autoCapture === true,
|
||||
autoRecall: cfg.autoRecall !== false,
|
||||
captureMaxChars,
|
||||
...(customTriggers ? { customTriggers } : {}),
|
||||
recallMaxChars,
|
||||
...(storageOptions ? { storageOptions } : {}),
|
||||
};
|
||||
},
|
||||
uiHints: {
|
||||
"embedding.provider": {
|
||||
label: "Embedding Provider",
|
||||
placeholder: "openai",
|
||||
help: "Memory embedding provider adapter to use (for example openai, github-copilot, ollama)",
|
||||
},
|
||||
"embedding.apiKey": {
|
||||
label: "OpenAI API Key",
|
||||
sensitive: true,
|
||||
placeholder: "sk-proj-...",
|
||||
help: "Optional API key override for OpenAI-compatible embeddings; omit to use configured provider auth",
|
||||
},
|
||||
"embedding.baseUrl": {
|
||||
label: "Base URL",
|
||||
placeholder: "https://api.openai.com/v1",
|
||||
help: "Optional provider or OpenAI-compatible embedding endpoint base URL",
|
||||
advanced: true,
|
||||
},
|
||||
"embedding.dimensions": {
|
||||
label: "Dimensions",
|
||||
placeholder: "1536",
|
||||
help: "Vector dimensions for custom models (required for non-standard models)",
|
||||
advanced: true,
|
||||
},
|
||||
"embedding.model": {
|
||||
label: "Embedding Model",
|
||||
placeholder: DEFAULT_MODEL,
|
||||
help: "OpenAI embedding model to use",
|
||||
},
|
||||
dbPath: {
|
||||
label: "Database Path",
|
||||
placeholder: "~/.openclaw/memory/lancedb",
|
||||
advanced: true,
|
||||
help: "Local filesystem path or cloud storage URI (s3://, gs://) for LanceDB database",
|
||||
},
|
||||
autoCapture: {
|
||||
label: "Auto-Capture",
|
||||
help: "Automatically capture important information from conversations",
|
||||
},
|
||||
autoRecall: {
|
||||
label: "Auto-Recall",
|
||||
help: "Automatically inject relevant memories into context",
|
||||
},
|
||||
captureMaxChars: {
|
||||
label: "Capture Max Chars",
|
||||
help: "Maximum message length eligible for auto-capture",
|
||||
advanced: true,
|
||||
placeholder: String(DEFAULT_CAPTURE_MAX_CHARS),
|
||||
},
|
||||
customTriggers: {
|
||||
label: "Custom Triggers",
|
||||
help: "Literal phrases that should make auto-capture consider a message memory-worthy",
|
||||
advanced: true,
|
||||
},
|
||||
recallMaxChars: {
|
||||
label: "Recall Query Max Chars",
|
||||
help: "Maximum prompt/query length embedded for memory recall. Lower for small local embedding models.",
|
||||
advanced: true,
|
||||
placeholder: String(DEFAULT_RECALL_MAX_CHARS),
|
||||
},
|
||||
storageOptions: {
|
||||
label: "Storage Options",
|
||||
sensitive: true,
|
||||
advanced: true,
|
||||
help: "Storage configuration options (access_key, secret_key, endpoint, etc.); supports ${ENV_VAR} values",
|
||||
},
|
||||
},
|
||||
};
|
||||
3939
extensions/memory-lancedb/index.test.ts
Normal file
3939
extensions/memory-lancedb/index.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
2023
extensions/memory-lancedb/index.ts
Normal file
2023
extensions/memory-lancedb/index.ts
Normal file
File diff suppressed because it is too large
Load Diff
78
extensions/memory-lancedb/lancedb-runtime.ts
Normal file
78
extensions/memory-lancedb/lancedb-runtime.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
// Memory Lancedb plugin module implements lancedb runtime behavior.
|
||||
type LanceDbModule = typeof import("@lancedb/lancedb");
|
||||
|
||||
export type LanceDbRuntimeLogger = {
|
||||
info?: (message: string) => void;
|
||||
warn?: (message: string) => void;
|
||||
};
|
||||
|
||||
type LanceDbRuntimeLoaderDeps = {
|
||||
platform: NodeJS.Platform;
|
||||
arch: NodeJS.Architecture;
|
||||
importBundled: () => Promise<LanceDbModule>;
|
||||
};
|
||||
|
||||
function buildLoadFailureMessage(error: unknown): string {
|
||||
return [
|
||||
"memory-lancedb: bundled @lancedb/lancedb dependency is unavailable.",
|
||||
"Install or repair the memory-lancedb plugin package dependencies, then restart OpenClaw.",
|
||||
String(error),
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
function isUnsupportedNativePlatform(params: {
|
||||
platform: NodeJS.Platform;
|
||||
arch: NodeJS.Architecture;
|
||||
}): boolean {
|
||||
return params.platform === "darwin" && params.arch === "x64";
|
||||
}
|
||||
|
||||
function buildUnsupportedNativePlatformMessage(params: {
|
||||
platform: NodeJS.Platform;
|
||||
arch: NodeJS.Architecture;
|
||||
}): string {
|
||||
return [
|
||||
`memory-lancedb: LanceDB runtime is unavailable on ${params.platform}-${params.arch}.`,
|
||||
"The bundled @lancedb/lancedb dependency does not publish a native package for this platform.",
|
||||
"Disable memory-lancedb or switch to a supported memory backend/platform.",
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
export function createLanceDbRuntimeLoader(overrides: Partial<LanceDbRuntimeLoaderDeps> = {}): {
|
||||
load: (loggerInstance?: LanceDbRuntimeLogger) => Promise<LanceDbModule>;
|
||||
} {
|
||||
const deps: LanceDbRuntimeLoaderDeps = {
|
||||
platform: overrides.platform ?? process.platform,
|
||||
arch: overrides.arch ?? process.arch,
|
||||
importBundled: overrides.importBundled ?? (() => import("@lancedb/lancedb")),
|
||||
};
|
||||
|
||||
let loadPromise: Promise<LanceDbModule> | null = null;
|
||||
|
||||
return {
|
||||
async load(_logger?: LanceDbRuntimeLogger): Promise<LanceDbModule> {
|
||||
if (!loadPromise) {
|
||||
loadPromise = deps.importBundled().catch((error: unknown) => {
|
||||
loadPromise = null;
|
||||
if (isUnsupportedNativePlatform({ platform: deps.platform, arch: deps.arch })) {
|
||||
throw new Error(
|
||||
buildUnsupportedNativePlatformMessage({
|
||||
platform: deps.platform,
|
||||
arch: deps.arch,
|
||||
}),
|
||||
{ cause: error },
|
||||
);
|
||||
}
|
||||
throw new Error(buildLoadFailureMessage(error), { cause: error });
|
||||
});
|
||||
}
|
||||
return await loadPromise;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const defaultLoader = createLanceDbRuntimeLoader();
|
||||
|
||||
export async function loadLanceDbModule(logger?: LanceDbRuntimeLogger): Promise<LanceDbModule> {
|
||||
return await defaultLoader.load(logger);
|
||||
}
|
||||
122
extensions/memory-lancedb/memory-lancedb.live.test.ts
Normal file
122
extensions/memory-lancedb/memory-lancedb.live.test.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
// Memory Lancedb tests cover memory lancedb plugin behavior.
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { installTmpDirHarness } from "./test-helpers.js";
|
||||
|
||||
const OPENAI_API_KEY = process.env.OPENAI_API_KEY ?? "";
|
||||
const HAS_OPENAI_KEY = Boolean(process.env.OPENAI_API_KEY);
|
||||
const liveEnabled = HAS_OPENAI_KEY && process.env.OPENCLAW_LIVE_TEST === "1";
|
||||
const describeLive = liveEnabled ? describe : describe.skip;
|
||||
|
||||
// Live tests that require OpenAI API key and actually use LanceDB
|
||||
describeLive("memory plugin live tests", () => {
|
||||
const { getDbPath } = installTmpDirHarness({ prefix: "openclaw-memory-live-" });
|
||||
|
||||
test("memory tools work end-to-end", async () => {
|
||||
const { default: memoryPlugin } = await import("./index.js");
|
||||
const liveApiKey = OPENAI_API_KEY;
|
||||
|
||||
// Mock plugin API
|
||||
const registeredTools: any[] = [];
|
||||
const registeredClis: any[] = [];
|
||||
const registeredServices: any[] = [];
|
||||
const registeredHooks: Record<string, any[]> = {};
|
||||
const logs: string[] = [];
|
||||
|
||||
const mockApi = {
|
||||
id: "memory-lancedb",
|
||||
name: "Memory (LanceDB)",
|
||||
source: "test",
|
||||
config: {},
|
||||
pluginConfig: {
|
||||
embedding: {
|
||||
apiKey: liveApiKey,
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
dbPath: getDbPath(),
|
||||
autoCapture: false,
|
||||
autoRecall: false,
|
||||
},
|
||||
runtime: {},
|
||||
logger: {
|
||||
info: (msg: string) => logs.push(`[info] ${msg}`),
|
||||
warn: (msg: string) => logs.push(`[warn] ${msg}`),
|
||||
error: (msg: string) => logs.push(`[error] ${msg}`),
|
||||
debug: (msg: string) => logs.push(`[debug] ${msg}`),
|
||||
},
|
||||
registerTool: (tool: any, opts: any) => {
|
||||
registeredTools.push({ tool, opts });
|
||||
},
|
||||
registerCli: (registrar: any, opts: any) => {
|
||||
registeredClis.push({ registrar, opts });
|
||||
},
|
||||
registerService: (service: any) => {
|
||||
registeredServices.push(service);
|
||||
},
|
||||
on: (hookName: string, handler: any) => {
|
||||
if (!registeredHooks[hookName]) {
|
||||
registeredHooks[hookName] = [];
|
||||
}
|
||||
registeredHooks[hookName].push(handler);
|
||||
},
|
||||
resolvePath: (p: string) => p,
|
||||
};
|
||||
|
||||
// Register plugin
|
||||
memoryPlugin.register(mockApi as any);
|
||||
|
||||
// Check registration
|
||||
expect(registeredTools.length).toBe(3);
|
||||
expect(registeredTools.map((t) => t.opts?.name)).toContain("memory_recall");
|
||||
expect(registeredTools.map((t) => t.opts?.name)).toContain("memory_store");
|
||||
expect(registeredTools.map((t) => t.opts?.name)).toContain("memory_forget");
|
||||
expect(registeredClis.length).toBe(1);
|
||||
expect(registeredServices.length).toBe(1);
|
||||
|
||||
// Get tool functions
|
||||
const storeTool = registeredTools.find((t) => t.opts?.name === "memory_store")?.tool;
|
||||
const recallTool = registeredTools.find((t) => t.opts?.name === "memory_recall")?.tool;
|
||||
const forgetTool = registeredTools.find((t) => t.opts?.name === "memory_forget")?.tool;
|
||||
|
||||
// Test store
|
||||
const storeResult = await storeTool.execute("test-call-1", {
|
||||
text: "The user prefers dark mode for all applications",
|
||||
importance: 0.8,
|
||||
category: "preference",
|
||||
});
|
||||
|
||||
expect(storeResult.details?.action).toBe("created");
|
||||
const storedId = storeResult.details?.id;
|
||||
expect(storedId).toMatch(/.+/);
|
||||
|
||||
// Test recall
|
||||
const recallResult = await recallTool.execute("test-call-2", {
|
||||
query: "dark mode preference",
|
||||
limit: 5,
|
||||
});
|
||||
|
||||
expect(recallResult.details?.count).toBeGreaterThan(0);
|
||||
expect(recallResult.details?.memories?.[0]?.text).toContain("dark mode");
|
||||
|
||||
// Test duplicate detection
|
||||
const duplicateResult = await storeTool.execute("test-call-3", {
|
||||
text: "The user prefers dark mode for all applications",
|
||||
});
|
||||
|
||||
expect(duplicateResult.details?.action).toBe("duplicate");
|
||||
|
||||
// Test forget
|
||||
const forgetResult = await forgetTool.execute("test-call-4", {
|
||||
memoryId: storedId,
|
||||
});
|
||||
|
||||
expect(forgetResult.details?.action).toBe("deleted");
|
||||
|
||||
// Verify it's gone
|
||||
const recallAfterForget = await recallTool.execute("test-call-5", {
|
||||
query: "dark mode preference",
|
||||
limit: 5,
|
||||
});
|
||||
|
||||
expect(recallAfterForget.details?.count).toBe(0);
|
||||
}, 60000); // 60s timeout for live API calls
|
||||
});
|
||||
481
extensions/memory-lancedb/npm-shrinkwrap.json
generated
Normal file
481
extensions/memory-lancedb/npm-shrinkwrap.json
generated
Normal file
@@ -0,0 +1,481 @@
|
||||
{
|
||||
"name": "@openclaw/memory-lancedb",
|
||||
"version": "2026.6.11",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@openclaw/memory-lancedb",
|
||||
"version": "2026.6.11",
|
||||
"dependencies": {
|
||||
"@lancedb/lancedb": "0.30.0",
|
||||
"apache-arrow": "18.1.0",
|
||||
"openai": "6.45.0",
|
||||
"typebox": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@lancedb/lancedb": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@lancedb/lancedb/-/lancedb-0.30.0.tgz",
|
||||
"integrity": "sha512-d0FoEL6cthqgsulqAc7fck6kRXrSRGMTqlKYbhSGSazHU6vB2GEpD737Mu0HZd7fMyBUdhR9sD1W2C9uQZ5p0Q==",
|
||||
"cpu": [
|
||||
"x64",
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"os": [
|
||||
"darwin",
|
||||
"linux",
|
||||
"win32"
|
||||
],
|
||||
"dependencies": {
|
||||
"reflect-metadata": "^0.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@lancedb/lancedb-darwin-arm64": "0.30.0",
|
||||
"@lancedb/lancedb-linux-arm64-gnu": "0.30.0",
|
||||
"@lancedb/lancedb-linux-arm64-musl": "0.30.0",
|
||||
"@lancedb/lancedb-linux-x64-gnu": "0.30.0",
|
||||
"@lancedb/lancedb-linux-x64-musl": "0.30.0",
|
||||
"@lancedb/lancedb-win32-arm64-msvc": "0.30.0",
|
||||
"@lancedb/lancedb-win32-x64-msvc": "0.30.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"apache-arrow": ">=15.0.0 <=18.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lancedb/lancedb-darwin-arm64": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@lancedb/lancedb-darwin-arm64/-/lancedb-darwin-arm64-0.30.0.tgz",
|
||||
"integrity": "sha512-x6dmsjRIv0xumELYnFAEfyFDxqcO/n4rHYCJvC27RbRez0UmbByi6OMTgbSoSTatrQSPRCL7JJSa5pwNeawnIg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@lancedb/lancedb-linux-arm64-gnu": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-arm64-gnu/-/lancedb-linux-arm64-gnu-0.30.0.tgz",
|
||||
"integrity": "sha512-CWUex7hRNiLkXFeTQlUGPyy5VktbXoUmQdZuiVCETZ6ggljEC7c7Qvzu2ge+jEZML+UE7tXL2lVC3klRFGczng==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@lancedb/lancedb-linux-arm64-musl": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-arm64-musl/-/lancedb-linux-arm64-musl-0.30.0.tgz",
|
||||
"integrity": "sha512-2MHmAS4tKePNVbwfgDrjCFj6BVuAgXlL2c9iWk7TfcwL+jcSxo52LFx03O0+ArpVCF2sI6aoDqZaQNre5zMniQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@lancedb/lancedb-linux-x64-gnu": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-x64-gnu/-/lancedb-linux-x64-gnu-0.30.0.tgz",
|
||||
"integrity": "sha512-0OpDNxsDE4OXD+PIFd7KdE42xhYIE+fZL+jCm1v3dTug4UEhumWBuSgbUIBP7t0yJZHwh62/QivVh/V1cPB2Bg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@lancedb/lancedb-linux-x64-musl": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-x64-musl/-/lancedb-linux-x64-musl-0.30.0.tgz",
|
||||
"integrity": "sha512-4Bq7VngQt+lyxIcu79EN8nYJs8gvUnrIT6I/t2MSlpG/BXWHZG2A+PRii1Zq4GCUlRTTG+RlieCv8CBGSPm8bw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@lancedb/lancedb-win32-arm64-msvc": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@lancedb/lancedb-win32-arm64-msvc/-/lancedb-win32-arm64-msvc-0.30.0.tgz",
|
||||
"integrity": "sha512-N2DQg2XBWZirn5jS6kRJUxF679t3sKcIxBwP9zY4Idq5OVLAj0yfLueWIKhYxv8en7pBFYWdgw5j9dTS7XajyQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@lancedb/lancedb-win32-x64-msvc": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@lancedb/lancedb-win32-x64-msvc/-/lancedb-win32-x64-msvc-0.30.0.tgz",
|
||||
"integrity": "sha512-CDgN/ZmYqSlVX2nBJAF2PYEwqBBxotCVORjagmvrd0k5D7RBLlAQUEAR4gDMum2BpYsUkzdTYQpquLjRCVbwbQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.23",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz",
|
||||
"integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/command-line-args": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.3.tgz",
|
||||
"integrity": "sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/command-line-usage": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/command-line-usage/-/command-line-usage-5.0.4.tgz",
|
||||
"integrity": "sha512-BwR5KP3Es/CSht0xqBcUXS3qCAUVXwpRKsV2+arxeb65atasuXG9LykC9Ab10Cw3s2raH92ZqOeILaQbsB2ACg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.19.43",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
|
||||
"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/apache-arrow": {
|
||||
"version": "18.1.0",
|
||||
"resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-18.1.0.tgz",
|
||||
"integrity": "sha512-v/ShMp57iBnBp4lDgV8Jx3d3Q5/Hac25FWmQ98eMahUiHPXcvwIMKJD0hBIgclm/FCG+LwPkAKtkRO1O/W0YGg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.11",
|
||||
"@types/command-line-args": "^5.2.3",
|
||||
"@types/command-line-usage": "^5.0.4",
|
||||
"@types/node": "^20.13.0",
|
||||
"command-line-args": "^5.2.1",
|
||||
"command-line-usage": "^7.0.1",
|
||||
"flatbuffers": "^24.3.25",
|
||||
"json-bignum": "^0.0.3",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"bin": {
|
||||
"arrow2csv": "bin/arrow2csv.js"
|
||||
}
|
||||
},
|
||||
"node_modules/array-back": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
|
||||
"integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk-template": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz",
|
||||
"integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk-template?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/command-line-args": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz",
|
||||
"integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"array-back": "^3.1.0",
|
||||
"find-replace": "^3.0.0",
|
||||
"lodash.camelcase": "^4.3.0",
|
||||
"typical": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/command-line-usage": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.4.tgz",
|
||||
"integrity": "sha512-85UdvzTNx/+s5CkSgBm/0hzP80RFHAa7PsfeADE5ezZF3uHz3/Tqj9gIKGT9PTtpycc3Ua64T0oVulGfKxzfqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"array-back": "^6.2.2",
|
||||
"chalk-template": "^0.4.0",
|
||||
"table-layout": "^4.1.1",
|
||||
"typical": "^7.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/command-line-usage/node_modules/array-back": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.3.tgz",
|
||||
"integrity": "sha512-SGDvmg6QTYiTxCBkYVmThcoa67uLl35pyzRHdpCGBOcqFy6BtwnphoFPk7LhJshD+Yk1Kt35WGWeZPTgwR4Fhw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.17"
|
||||
}
|
||||
},
|
||||
"node_modules/command-line-usage/node_modules/typical": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz",
|
||||
"integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.17"
|
||||
}
|
||||
},
|
||||
"node_modules/find-replace": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz",
|
||||
"integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"array-back": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/flatbuffers": {
|
||||
"version": "24.12.23",
|
||||
"resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-24.12.23.tgz",
|
||||
"integrity": "sha512-dLVCAISd5mhls514keQzmEG6QHmUUsNuWsb4tFafIUwvvgDjXhtfAYSKOzt5SWOy+qByV5pbsDZ+Vb7HUOBEdA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/json-bignum": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/json-bignum/-/json-bignum-0.0.3.tgz",
|
||||
"integrity": "sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.camelcase": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/openai": {
|
||||
"version": "6.45.0",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-6.45.0.tgz",
|
||||
"integrity": "sha512-5DQVNErssk0afNpTTHUm/qZPU4iKR9OYdNid8Ib4puq4gHNNvGWZht2zY4h9a8JMF949Ik6m8gQutllVPbjdnw==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/credential-provider-node": ">=3.972.0 <4",
|
||||
"@smithy/hash-node": ">=4.3.0 <5",
|
||||
"@smithy/signature-v4": ">=5.4.0 <6",
|
||||
"ws": "^8.18.0",
|
||||
"zod": "^3.25 || ^4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@aws-sdk/credential-provider-node": {
|
||||
"optional": true
|
||||
},
|
||||
"@smithy/hash-node": {
|
||||
"optional": true
|
||||
},
|
||||
"@smithy/signature-v4": {
|
||||
"optional": true
|
||||
},
|
||||
"ws": {
|
||||
"optional": true
|
||||
},
|
||||
"zod": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/reflect-metadata": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
|
||||
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/table-layout": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz",
|
||||
"integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"array-back": "^6.2.2",
|
||||
"wordwrapjs": "^5.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.17"
|
||||
}
|
||||
},
|
||||
"node_modules/table-layout/node_modules/array-back": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.3.tgz",
|
||||
"integrity": "sha512-SGDvmg6QTYiTxCBkYVmThcoa67uLl35pyzRHdpCGBOcqFy6BtwnphoFPk7LhJshD+Yk1Kt35WGWeZPTgwR4Fhw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.17"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/typebox": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.3.tgz",
|
||||
"integrity": "sha512-URXGUE31PJDQC+PtRMJeLdF4kmmOdFoVPikPCtV2oOIhUpNpppEdIz7W8bH8cFYPYHdDpaRvqwdegMTmHliudg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/typical": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
|
||||
"integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wordwrapjs": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz",
|
||||
"integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.17"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
150
extensions/memory-lancedb/openclaw.plugin.json
Normal file
150
extensions/memory-lancedb/openclaw.plugin.json
Normal file
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"id": "memory-lancedb",
|
||||
"name": "Memory LanceDB",
|
||||
"description": "OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search.",
|
||||
"commandAliases": [{ "name": "ltm" }],
|
||||
"activation": {
|
||||
"onStartup": false,
|
||||
"onCommands": ["ltm"]
|
||||
},
|
||||
"kind": "memory",
|
||||
"contracts": {
|
||||
"tools": ["memory_forget", "memory_recall", "memory_store"]
|
||||
},
|
||||
"uiHints": {
|
||||
"embedding.apiKey": {
|
||||
"label": "Embedding API Key",
|
||||
"sensitive": true,
|
||||
"placeholder": "sk-proj-...",
|
||||
"help": "Optional API key override for OpenAI-compatible embeddings; omit to use configured provider auth"
|
||||
},
|
||||
"embedding.provider": {
|
||||
"label": "Embedding Provider",
|
||||
"placeholder": "openai",
|
||||
"help": "Memory embedding provider adapter to use (for example openai, github-copilot, ollama)"
|
||||
},
|
||||
"embedding.model": {
|
||||
"label": "Embedding Model",
|
||||
"placeholder": "text-embedding-3-small",
|
||||
"help": "OpenAI embedding model to use"
|
||||
},
|
||||
"embedding.baseUrl": {
|
||||
"label": "Base URL",
|
||||
"placeholder": "https://api.openai.com/v1",
|
||||
"help": "Optional provider or OpenAI-compatible embedding endpoint base URL",
|
||||
"advanced": true
|
||||
},
|
||||
"embedding.dimensions": {
|
||||
"label": "Dimensions",
|
||||
"placeholder": "1536",
|
||||
"help": "Vector dimensions for custom models (required for non-standard models)",
|
||||
"advanced": true
|
||||
},
|
||||
"dbPath": {
|
||||
"label": "Database Path",
|
||||
"placeholder": "~/.openclaw/memory/lancedb",
|
||||
"advanced": true
|
||||
},
|
||||
"autoCapture": {
|
||||
"label": "Auto-Capture",
|
||||
"help": "Automatically capture important information from conversations"
|
||||
},
|
||||
"autoRecall": {
|
||||
"label": "Auto-Recall",
|
||||
"help": "Automatically inject relevant memories into context"
|
||||
},
|
||||
"dreaming": {
|
||||
"label": "Dreaming",
|
||||
"help": "Optional dreaming config consumed when this plugin owns the memory slot"
|
||||
},
|
||||
"captureMaxChars": {
|
||||
"label": "Capture Max Chars",
|
||||
"help": "Maximum message length eligible for auto-capture",
|
||||
"advanced": true,
|
||||
"placeholder": "500"
|
||||
},
|
||||
"customTriggers": {
|
||||
"label": "Custom Triggers",
|
||||
"help": "Literal phrases that should make auto-capture consider a message memory-worthy",
|
||||
"advanced": true
|
||||
},
|
||||
"recallMaxChars": {
|
||||
"label": "Recall Query Max Chars",
|
||||
"help": "Maximum prompt/query length embedded for memory recall. Lower for small local embedding models.",
|
||||
"advanced": true,
|
||||
"placeholder": "1000"
|
||||
},
|
||||
"storageOptions": {
|
||||
"label": "Storage Options",
|
||||
"advanced": true,
|
||||
"help": "Storage configuration options (access_key, secret_key, endpoint, etc.); supports ${ENV_VAR} values"
|
||||
}
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"embedding": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"additionalProperties": false,
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
},
|
||||
"model": {
|
||||
"type": "string"
|
||||
},
|
||||
"baseUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"dimensions": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"dbPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"autoCapture": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"autoRecall": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"dreaming": {
|
||||
"type": "object"
|
||||
},
|
||||
"captureMaxChars": {
|
||||
"type": "number",
|
||||
"minimum": 100,
|
||||
"maximum": 10000
|
||||
},
|
||||
"customTriggers": {
|
||||
"type": "array",
|
||||
"maxItems": 50,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100
|
||||
}
|
||||
},
|
||||
"recallMaxChars": {
|
||||
"type": "number",
|
||||
"minimum": 100,
|
||||
"maximum": 10000
|
||||
},
|
||||
"storageOptions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
extensions/memory-lancedb/package.json
Normal file
40
extensions/memory-lancedb/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@openclaw/memory-lancedb",
|
||||
"version": "2026.6.11",
|
||||
"description": "OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openclaw/openclaw"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@lancedb/lancedb": "0.30.0",
|
||||
"apache-arrow": "18.1.0",
|
||||
"openai": "6.45.0",
|
||||
"typebox": "1.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openclaw/plugin-sdk": "workspace:*"
|
||||
},
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
],
|
||||
"install": {
|
||||
"npmSpec": "@openclaw/memory-lancedb",
|
||||
"defaultChoice": "npm",
|
||||
"minHostVersion": ">=2026.5.31"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.6.11"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.6.11"
|
||||
},
|
||||
"release": {
|
||||
"bundleRuntimeDependencies": false,
|
||||
"publishToClawHub": true,
|
||||
"publishToNpm": true
|
||||
}
|
||||
}
|
||||
}
|
||||
26
extensions/memory-lancedb/test-helpers.ts
Normal file
26
extensions/memory-lancedb/test-helpers.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
// Memory Lancedb helper module supports test helpers behavior.
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
||||
import { afterEach, beforeEach } from "vitest";
|
||||
|
||||
export function installTmpDirHarness(params: { prefix: string }) {
|
||||
let tmpDir = "";
|
||||
let dbPath = "";
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = await fs.mkdtemp(path.join(resolvePreferredOpenClawTmpDir(), params.prefix));
|
||||
dbPath = path.join(tmpDir, "lancedb");
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (tmpDir) {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
getTmpDir: () => tmpDir,
|
||||
getDbPath: () => dbPath,
|
||||
};
|
||||
}
|
||||
16
extensions/memory-lancedb/tsconfig.json
Normal file
16
extensions/memory-lancedb/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "../tsconfig.package-boundary.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": ["./*.ts", "./src/**/*.ts"],
|
||||
"exclude": [
|
||||
"./**/*.test.ts",
|
||||
"./dist/**",
|
||||
"./node_modules/**",
|
||||
"./src/test-support/**",
|
||||
"./src/**/*test-helpers.ts",
|
||||
"./src/**/*test-harness.ts",
|
||||
"./src/**/*test-support.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user