@xmemo/openclaw-memory 1.0.0

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/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # XMemo for OpenClaw
2
+
3
+ <img src="assets/icon.png" width="128" height="128" alt="XMemo for OpenClaw logo">
4
+
5
+ [XMemo](https://xmemo.dev) is an identity-aware memory control plane for AI
6
+ agents — a user-owned Memory OS that stores, governs, and audits personal and
7
+ project context across clients, devices, and agent runtimes.
8
+
9
+ This package is the **native OpenClaw plugin** for XMemo. Once enabled, OpenClaw
10
+ uses XMemo as its active long-term memory backend instead of local file-backed
11
+ or vector-backed stores. It is distributed independently through ClawHub and
12
+ npm as an external plugin and is not bundled in the default OpenClaw release.
13
+ The project does not pursue inclusion through an upstream OpenClaw pull request.
14
+
15
+ ## Features
16
+
17
+ - Identity-aware memory for OpenClaw via the XMemo REST API
18
+ - Canonical memory tools: `memory_search`, `memory_get`, `memory_store`, `memory_forget`
19
+ - Memory list and update: `xmemo_memory_list`, `xmemo_memory_update`
20
+ - Reminder tools: `xmemo_todo_create`, `xmemo_todo_list`, `xmemo_todo_complete`
21
+ - Timeline event tool: `xmemo_record_event`
22
+ - Restart snapshot tools: `xmemo_restart_snapshot_save`, `xmemo_restart_snapshot_restore`
23
+ - Ledger and audit tools (requires special API key scope): `xmemo_ledger_monthly_summary`, `xmemo_audit_events`, `xmemo_audit_consolidation`
24
+ - Optional automatic capture of high-signal user messages after a successful agent turn
25
+ - No local embedding model or vector store required
26
+ - Works with hosted XMemo (`https://xmemo.dev`) and private/self-hosted instances
27
+
28
+ ## Native plugin vs MCP
29
+
30
+ This is a native OpenClaw plugin (`kind: "memory"`). It becomes OpenClaw's
31
+ active memory backend when `plugins.slots.memory` is set to `"xmemo-memory"`.
32
+
33
+ XMemo also provides a hosted MCP server (`https://xmemo.dev/mcp`) for users who
34
+ want tools without occupying the OpenClaw memory slot. The MCP server exposes
35
+ similar read/write memory tools but does **not** replace `active-memory` recall.
36
+
37
+ ## Installation
38
+
39
+ Install the plugin from ClawHub (recommended):
40
+
41
+ ```bash
42
+ openclaw plugins install clawhub:@xmemo/openclaw-memory
43
+ ```
44
+
45
+ npm is also supported as a secondary distribution channel:
46
+
47
+ ```bash
48
+ openclaw plugins install @xmemo/openclaw-memory
49
+ ```
50
+
51
+ Then set the memory slot to `xmemo-memory` and enable the entry as shown below.
52
+
53
+ ## Configuration
54
+
55
+ Activate the plugin by setting the memory slot:
56
+
57
+ ```json
58
+ {
59
+ "plugins": {
60
+ "slots": {
61
+ "memory": "xmemo-memory"
62
+ },
63
+ "entries": {
64
+ "xmemo-memory": {
65
+ "enabled": true,
66
+ "package": "@xmemo/openclaw-memory",
67
+ "config": {
68
+ "baseUrl": "https://xmemo.dev",
69
+ "apiKey": { "source": "env", "provider": "default", "id": "XMEMO_KEY" },
70
+ "bucket": "openclaw",
71
+ "scope": "my-project",
72
+ "autoCapture": false
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ Config lives at `plugins.entries["xmemo-memory"].config`, not `plugins.config`.
81
+ For production setups, keep the API key in the environment (`XMEMO_KEY`) instead
82
+ of storing it in `openclaw.json`.
83
+
84
+ ## Authentication
85
+
86
+ Create a scoped API key in the XMemo Memory Console:
87
+ [xmemo.dev](https://xmemo.dev) → **API Keys** → **Create API key**.
88
+ Copy the one-time secret value, then set it as the `XMEMO_KEY` environment
89
+ variable:
90
+
91
+ ```bash
92
+ export XMEMO_KEY="your-xmemo-api-key"
93
+ ```
94
+
95
+ The key can also be configured with `apiKey` (preferred) or the deprecated
96
+ `token` field. For production setups, keep the key in the environment or a
97
+ secret manager and omit the `apiKey` field from `openclaw.json`; the plugin
98
+ will read `XMEMO_KEY` directly.
99
+
100
+ ### SecretRef support
101
+
102
+ The plugin resolves `apiKey`/`token` in this order:
103
+
104
+ 1. A literal string.
105
+ 2. An env SecretRef object: `{ "source": "env", "provider": "default", "id": "XMEMO_KEY" }`.
106
+ 3. The environment variables `XMEMO_KEY`, `MEMORY_OS_API_KEY`, or `MEMORY_OS_MCP_TOKEN`.
107
+
108
+ Only `env` SecretRefs are supported. `file` and `exec` sources are not
109
+ implemented and are rejected by the manifest config schema.
110
+
111
+ ## Required environment variables
112
+
113
+ - `XMEMO_KEY` — XMemo API key (preferred)
114
+ - `MEMORY_OS_API_KEY` — alternate env var name
115
+ - `XMEMO_AGENT_INSTANCE_ID` — optional stable device-level identifier
116
+
117
+ ## Auth mode
118
+
119
+ By default the credential is sent as `X-API-Key`. To use Bearer auth or both:
120
+
121
+ ```json
122
+ {
123
+ "authMode": "bearer"
124
+ }
125
+ ```
126
+
127
+ Allowed values: `api-key` (default), `bearer`, `both`.
128
+
129
+ ## Agent identity headers
130
+
131
+ The plugin sends non-secret attribution headers to XMemo:
132
+
133
+ - `X-Memory-OS-Agent-ID: openclaw`
134
+ - `X-Memory-OS-Agent-Instance-ID: <stable-device-id>`
135
+
136
+ If `XMEMO_AGENT_INSTANCE_ID` is not set, a process-local UUID is generated. The
137
+ plugin does not write JSON sidecars to disk.
138
+
139
+ ## CLI
140
+
141
+ ```bash
142
+ openclaw xmemo status
143
+ openclaw xmemo status --json
144
+ ```
145
+
146
+ ## Auto-capture
147
+
148
+ When `autoCapture: true`, the plugin listens for `agent_end` and stores
149
+ high-signal user messages (preferences, decisions, facts) to XMemo.
150
+
151
+ > **External plugin permission required:** OpenClaw external plugins do not
152
+ > receive conversation access by default. To enable auto-capture, add this to
153
+ > your `openclaw.json`:
154
+ >
155
+ > ```json
156
+ > {
157
+ > "hooks": {
158
+ > "allowConversationAccess": ["xmemo-memory"]
159
+ > }
160
+ > }
161
+ > ```
162
+ >
163
+ > Without this, the `agent_end` hook is silently skipped and no messages are
164
+ > captured.
165
+
166
+ It skips:
167
+
168
+ - envelope/transport metadata
169
+ - injected context blocks
170
+ - prompt-injection-looking payloads
171
+ - messages without a memory trigger word
172
+
173
+ Customize triggers with `customTriggers`:
174
+
175
+ ```json
176
+ {
177
+ "autoCapture": true,
178
+ "customTriggers": ["save this", "remember for next time"]
179
+ }
180
+ ```
181
+
182
+ ## Smoke test
183
+
184
+ After installing and configuring the plugin:
185
+
186
+ ```bash
187
+ export XMEMO_KEY="your-xmemo-api-key"
188
+ openclaw xmemo status
189
+ openclaw xmemo status --json
190
+ ```
191
+
192
+ Expected results:
193
+
194
+ - `status` shows `configured: true` and `connected: true` (or a clear
195
+ `not connected` error if the key/network is wrong).
196
+ - `openclaw plugins inspect xmemo-memory --runtime --json` lists the registered
197
+ tools: `memory_search`, `memory_get`, `memory_store`, `memory_forget`,
198
+ `xmemo_memory_list`, `xmemo_memory_update`, `xmemo_todo_create`,
199
+ `xmemo_todo_list`, `xmemo_todo_complete`, `xmemo_record_event`,
200
+ `xmemo_restart_snapshot_save`, `xmemo_restart_snapshot_restore`,
201
+ `xmemo_ledger_monthly_summary`, `xmemo_audit_events`,
202
+ `xmemo_audit_consolidation`, plus the `xmemo` CLI.
203
+
204
+ The `memory_*` tools are invoked by the OpenClaw agent during a turn, not as
205
+ standalone CLI commands.
206
+
207
+ ## Migration from memory-core or memory-lancedb
208
+
209
+ Switching the memory slot replaces the active backend. Existing local memories
210
+ remain on disk but are no longer queried automatically. To migrate content into
211
+ XMemo, use `memory_get` on the old backend and `memory_store` on XMemo, or use
212
+ XMemo's import endpoints.
Binary file
@@ -0,0 +1,13 @@
1
+ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
2
+ type LegacyConfigRule = {
3
+ path: string[];
4
+ message: string;
5
+ };
6
+ export declare const legacyConfigRules: LegacyConfigRule[];
7
+ export declare function normalizeCompatibilityConfig({ cfg }: {
8
+ cfg: OpenClawConfig;
9
+ }): {
10
+ config: OpenClawConfig;
11
+ changes: string[];
12
+ };
13
+ export {};
@@ -0,0 +1,60 @@
1
+ // XMemo memory plugin doctor contract.
2
+ //
3
+ // Exposes config compatibility fixes for OpenClaw `openclaw doctor --fix`.
4
+ export const legacyConfigRules = [
5
+ {
6
+ path: ["plugins", "config", "xmemo-memory"],
7
+ message: "XMemo memory config moved from plugins.config to plugins.entries['xmemo-memory'].config. Run `openclaw doctor --fix` to migrate.",
8
+ },
9
+ ];
10
+ function asRecord(value) {
11
+ if (value && typeof value === "object" && !Array.isArray(value)) {
12
+ return value;
13
+ }
14
+ return undefined;
15
+ }
16
+ export function normalizeCompatibilityConfig({ cfg }) {
17
+ const plugins = asRecord(cfg.plugins);
18
+ const legacyConfig = asRecord(plugins?.config)?.["xmemo-memory"];
19
+ const entries = asRecord(plugins?.entries);
20
+ const existingEntry = asRecord(entries?.["xmemo-memory"]);
21
+ if (!legacyConfig || typeof legacyConfig !== "object" || Array.isArray(legacyConfig)) {
22
+ return { config: cfg, changes: [] };
23
+ }
24
+ const legacy = legacyConfig;
25
+ const currentConfig = asRecord(existingEntry?.config) ?? {};
26
+ const migrated = { ...currentConfig };
27
+ for (const [key, value] of Object.entries(legacy)) {
28
+ if (migrated[key] === undefined) {
29
+ migrated[key] = value;
30
+ }
31
+ }
32
+ // Rename deprecated token to apiKey if apiKey is not already set.
33
+ if (migrated.token !== undefined && migrated.apiKey === undefined) {
34
+ migrated.apiKey = migrated.token;
35
+ delete migrated.token;
36
+ }
37
+ const nextEntries = { ...entries };
38
+ nextEntries["xmemo-memory"] = {
39
+ ...existingEntry,
40
+ enabled: existingEntry?.enabled ?? true,
41
+ config: migrated,
42
+ };
43
+ const nextPlugins = { ...plugins };
44
+ const nextLegacyConfig = { ...asRecord(nextPlugins.config) };
45
+ delete nextLegacyConfig["xmemo-memory"];
46
+ if (Object.keys(nextLegacyConfig).length === 0) {
47
+ delete nextPlugins.config;
48
+ }
49
+ else {
50
+ nextPlugins.config = nextLegacyConfig;
51
+ }
52
+ nextPlugins.entries = nextEntries;
53
+ return {
54
+ config: { ...cfg, plugins: nextPlugins },
55
+ changes: [
56
+ "Moved xmemo-memory config from plugins.config to plugins.entries['xmemo-memory'].config.",
57
+ ...(legacy.token !== undefined ? ["Renamed deprecated token to apiKey."] : []),
58
+ ],
59
+ };
60
+ }
@@ -0,0 +1,3 @@
1
+ import type { OpenClawPluginDefinition } from "openclaw/plugin-sdk/plugin-entry";
2
+ declare const plugin: OpenClawPluginDefinition;
3
+ export default plugin;
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ // XMemo for OpenClaw plugin entrypoint.
2
+ //
3
+ // This plugin brings XMemo's identity-aware memory control plane into OpenClaw,
4
+ // making XMemo the active long-term memory backend. It implements the OpenClaw
5
+ // `kind: "memory"` slot contract by registering a MemoryPluginCapability with
6
+ // prompt building, flush planning, and a remote MemorySearchManager backed by
7
+ // the XMemo REST API.
8
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
9
+ import { registerXMemoAutoCapture } from "./src/auto-capture.js";
10
+ import { registerXMemoCli } from "./src/cli.js";
11
+ import { buildXMemoPromptSection } from "./src/prompt-section.js";
12
+ import { createXMemoMemoryRuntime } from "./src/runtime.js";
13
+ import { registerXMemoTools } from "./src/tools.js";
14
+ const plugin = definePluginEntry({
15
+ id: "xmemo-memory",
16
+ name: "XMemo for OpenClaw",
17
+ description: "XMemo identity-aware memory control plane for OpenClaw.",
18
+ kind: "memory",
19
+ register(api) {
20
+ api.registerMemoryCapability({
21
+ promptBuilder: buildXMemoPromptSection,
22
+ // XMemo is a remote memory backend; there is no local transcript flush
23
+ // path. Returning null keeps OpenClaw from writing memory files locally.
24
+ flushPlanResolver: () => null,
25
+ runtime: createXMemoMemoryRuntime(api),
26
+ });
27
+ registerXMemoTools(api);
28
+ registerXMemoAutoCapture(api);
29
+ registerXMemoCli(api);
30
+ },
31
+ });
32
+ export default plugin;
@@ -0,0 +1,203 @@
1
+ {
2
+ "id": "xmemo-memory",
3
+ "name": "XMemo for OpenClaw",
4
+ "description": "XMemo identity-aware memory control plane for OpenClaw. Brings user-owned, auditable, cross-agent memory into OpenClaw.",
5
+ "kind": "memory",
6
+ "activation": {
7
+ "onStartup": false,
8
+ "onCommands": ["memory"]
9
+ },
10
+ "contracts": {
11
+ "tools": [
12
+ "memory_search",
13
+ "memory_get",
14
+ "memory_store",
15
+ "memory_forget",
16
+ "xmemo_memory_list",
17
+ "xmemo_memory_update",
18
+ "xmemo_todo_create",
19
+ "xmemo_todo_list",
20
+ "xmemo_todo_complete",
21
+ "xmemo_record_event",
22
+ "xmemo_restart_snapshot_save",
23
+ "xmemo_restart_snapshot_restore",
24
+ "xmemo_ledger_monthly_summary",
25
+ "xmemo_audit_events",
26
+ "xmemo_audit_consolidation"
27
+ ]
28
+ },
29
+ "uiHints": {
30
+ "baseUrl": {
31
+ "label": "XMemo Service URL",
32
+ "placeholder": "https://xmemo.dev",
33
+ "help": "Base URL of the XMemo instance. Use a private/self-hosted URL if not using the hosted service.",
34
+ "advanced": true
35
+ },
36
+ "apiKey": {
37
+ "label": "XMemo API Key",
38
+ "placeholder": "xmemo_...",
39
+ "help": "Create a scoped API key in XMemo Memory Console -> API Keys -> Create API key. Prefer setting XMEMO_KEY instead of storing the key here.",
40
+ "sensitive": true
41
+ },
42
+ "token": {
43
+ "label": "XMemo Token (deprecated)",
44
+ "placeholder": "xmemo_...",
45
+ "help": "Deprecated alias for apiKey. Prefer apiKey or the XMEMO_KEY environment variable.",
46
+ "sensitive": true,
47
+ "advanced": true
48
+ },
49
+ "authMode": {
50
+ "label": "Auth Mode",
51
+ "placeholder": "api-key",
52
+ "help": "How to send the credential: api-key (X-API-Key header), bearer (Authorization: Bearer), or both.",
53
+ "advanced": true
54
+ },
55
+ "bucket": {
56
+ "label": "Default Bucket",
57
+ "placeholder": "openclaw",
58
+ "help": "Default bucket for memories created by OpenClaw."
59
+ },
60
+ "scope": {
61
+ "label": "Default Scope",
62
+ "placeholder": "openclaw",
63
+ "help": "Optional scope for isolating this workspace/agent.",
64
+ "advanced": true
65
+ },
66
+ "teamId": {
67
+ "label": "Team ID",
68
+ "placeholder": "",
69
+ "help": "Optional team/organization id for enterprise XMemo deployments.",
70
+ "advanced": true
71
+ },
72
+ "agentId": {
73
+ "label": "Agent ID",
74
+ "placeholder": "openclaw",
75
+ "help": "Non-secret agent identifier sent to XMemo for attribution.",
76
+ "advanced": true
77
+ },
78
+ "autoCapture": {
79
+ "label": "Auto Capture",
80
+ "help": "Automatically persist high-signal decisions and fixes to XMemo."
81
+ },
82
+ "captureMaxChars": {
83
+ "label": "Capture Max Chars",
84
+ "help": "Maximum message length eligible for auto-capture.",
85
+ "advanced": true,
86
+ "placeholder": "500"
87
+ },
88
+ "customTriggers": {
89
+ "label": "Custom Auto-Capture Triggers",
90
+ "help": "Additional words or phrases that trigger auto-capture (case-insensitive).",
91
+ "advanced": true
92
+ },
93
+ "recallMaxChars": {
94
+ "label": "Recall Query Max Chars",
95
+ "help": "Maximum prompt/query length sent to XMemo for recall.",
96
+ "advanced": true,
97
+ "placeholder": "1000"
98
+ },
99
+ "recallMaxItems": {
100
+ "label": "Recall Max Items",
101
+ "help": "Maximum number of memories returned by XMemo recall.",
102
+ "advanced": true,
103
+ "placeholder": "8"
104
+ },
105
+ "recallMaxTokens": {
106
+ "label": "Recall Max Tokens",
107
+ "help": "Maximum token budget for the recalled context package.",
108
+ "advanced": true,
109
+ "placeholder": "1500"
110
+ }
111
+ },
112
+ "configSchema": {
113
+ "type": "object",
114
+ "additionalProperties": false,
115
+ "properties": {
116
+ "baseUrl": {
117
+ "type": "string",
118
+ "format": "uri"
119
+ },
120
+ "apiKey": {
121
+ "oneOf": [
122
+ { "type": "string" },
123
+ {
124
+ "type": "object",
125
+ "additionalProperties": false,
126
+ "properties": {
127
+ "source": { "type": "string", "enum": ["env"] },
128
+ "provider": { "type": "string" },
129
+ "id": { "type": "string" }
130
+ },
131
+ "required": ["source", "provider", "id"]
132
+ }
133
+ ]
134
+ },
135
+ "token": {
136
+ "oneOf": [
137
+ { "type": "string" },
138
+ {
139
+ "type": "object",
140
+ "additionalProperties": false,
141
+ "properties": {
142
+ "source": { "type": "string", "enum": ["env"] },
143
+ "provider": { "type": "string" },
144
+ "id": { "type": "string" }
145
+ },
146
+ "required": ["source", "provider", "id"]
147
+ }
148
+ ]
149
+ },
150
+ "authMode": {
151
+ "type": "string",
152
+ "enum": ["api-key", "bearer", "both"],
153
+ "default": "api-key"
154
+ },
155
+ "bucket": {
156
+ "type": "string",
157
+ "default": "openclaw"
158
+ },
159
+ "scope": {
160
+ "type": ["string", "null"]
161
+ },
162
+ "teamId": {
163
+ "type": ["string", "null"]
164
+ },
165
+ "agentId": {
166
+ "type": "string",
167
+ "default": "openclaw"
168
+ },
169
+ "autoCapture": {
170
+ "type": "boolean",
171
+ "default": false
172
+ },
173
+ "captureMaxChars": {
174
+ "type": "number",
175
+ "minimum": 100,
176
+ "maximum": 10000,
177
+ "default": 500
178
+ },
179
+ "customTriggers": {
180
+ "type": "array",
181
+ "items": { "type": "string" }
182
+ },
183
+ "recallMaxChars": {
184
+ "type": "number",
185
+ "minimum": 100,
186
+ "maximum": 10000,
187
+ "default": 1000
188
+ },
189
+ "recallMaxItems": {
190
+ "type": "integer",
191
+ "minimum": 1,
192
+ "maximum": 50,
193
+ "default": 8
194
+ },
195
+ "recallMaxTokens": {
196
+ "type": "integer",
197
+ "minimum": 100,
198
+ "maximum": 8000,
199
+ "default": 1500
200
+ }
201
+ }
202
+ }
203
+ }
@@ -0,0 +1,2 @@
1
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk/memory-core-host-runtime-core";
2
+ export declare function registerXMemoAutoCapture(api: OpenClawPluginApi): void;