feat(memory-claudemem): add workerPath config for manual installs
- Add workerPath config option to specify custom worker-service.cjs location - Check custom path first, then fall back to Claude plugin cache auto-discovery - Update docs with simpler manual install instructions (no need to copy to cache) - Update manifest with workerPath schema and UI hints
This commit is contained in:
parent
ed2c476754
commit
e5fa404640
109
docs/plans/claude-mem-moltbot-fix.md
Normal file
109
docs/plans/claude-mem-moltbot-fix.md
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# Claude-Mem Moltbot Plugin Fix Plan
|
||||||
|
|
||||||
|
## Status Assessment
|
||||||
|
|
||||||
|
**Current State:**
|
||||||
|
- Plugin loads from: `/Users/alexnewman/clawd/plugins/claude-mem/index.ts`
|
||||||
|
- Observations: Working correctly (recorded via `tool_result_persist` hook)
|
||||||
|
- Prompts: Being recorded BUT with `[message_id: ...]` suffix appended
|
||||||
|
- MEMORY.md sync: Working (on `session_start`)
|
||||||
|
- Context injection: NOT implemented (only MEMORY.md file approach)
|
||||||
|
|
||||||
|
**Root Cause:**
|
||||||
|
Moltbot's messaging system adds `[message_id: UUID]` to prompts for WhatsApp/Discord reactions and replies (see `src/auto-reply/reply/body.ts:42`). This metadata is being passed through to claude-mem without stripping.
|
||||||
|
|
||||||
|
## Phase 1: Strip message_id from prompts
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
Line 304 in `/Users/alexnewman/clawd/plugins/claude-mem/index.ts`:
|
||||||
|
```typescript
|
||||||
|
const result = await client.initSession(contentSessionId, event.prompt);
|
||||||
|
```
|
||||||
|
Sends the full prompt including `[message_id: UUID]`.
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
Add a utility function to strip the message_id line before sending:
|
||||||
|
|
||||||
|
**Copy pattern from:** `src/gateway/chat-sanitize.ts:18-38`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const MESSAGE_ID_LINE = /^\s*\[message_id:\s*[^\]]+\]\s*$/im;
|
||||||
|
|
||||||
|
function stripMessageIdFromPrompt(text: string): string {
|
||||||
|
if (!text.includes("[message_id:")) return text;
|
||||||
|
const lines = text.split(/\r?\n/);
|
||||||
|
const filtered = lines.filter((line) => !MESSAGE_ID_LINE.test(line));
|
||||||
|
return filtered.join("\n").trim();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
Update `before_agent_start` hook (around line 297-310):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
api.on("before_agent_start", async (event, ctx) => {
|
||||||
|
if (!event.prompt || event.prompt.length < 10) return;
|
||||||
|
if (!(await client.isHealthy())) return;
|
||||||
|
|
||||||
|
const contentSessionId = getContentSessionId(ctx.sessionKey);
|
||||||
|
|
||||||
|
// Strip [message_id: ...] metadata before recording
|
||||||
|
const cleanPrompt = stripMessageIdFromPrompt(event.prompt);
|
||||||
|
|
||||||
|
const result = await client.initSession(contentSessionId, cleanPrompt);
|
||||||
|
if (result) {
|
||||||
|
api.logger.debug?.(
|
||||||
|
`claude-mem: session initialized (dbId: ${result.sessionDbId}, prompt#: ${result.promptNumber})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
1. Run moltbot with a test message
|
||||||
|
2. Check `/api/prompts` endpoint - prompts should NOT contain `[message_id: ...]`
|
||||||
|
3. Check claude-mem UI - prompts should display cleanly
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: (Optional) Add context injection via prependContext
|
||||||
|
|
||||||
|
The current plugin only syncs to `MEMORY.md` file. For real-time context injection (like the `extensions/memory-claudemem` approach), add:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
api.on("before_agent_start", async (event, ctx) => {
|
||||||
|
// ... existing prompt recording code ...
|
||||||
|
|
||||||
|
// Optional: inject recent context
|
||||||
|
const observations = await client.getObservations(5);
|
||||||
|
if (observations.length === 0) return;
|
||||||
|
|
||||||
|
const memoryContext = observations
|
||||||
|
.map((obs) => `- [#${obs.id}] ${obs.title || obs.type}: ${obs.narrative || obs.text?.slice(0, 100)}`)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
return {
|
||||||
|
prependContext: `<claude-mem-context>\nRecent memories:\n${memoryContext}\n</claude-mem-context>`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** This is optional since MEMORY.md file approach is already working.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files to Modify
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `/Users/alexnewman/clawd/plugins/claude-mem/index.ts` | Add `stripMessageIdFromPrompt()` utility, update `before_agent_start` hook |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification Checklist
|
||||||
|
|
||||||
|
- [ ] Prompts recorded without `[message_id: ...]` suffix
|
||||||
|
- [ ] Prompts display cleanly in claude-mem UI at localhost:37777
|
||||||
|
- [ ] Observations still being recorded correctly
|
||||||
|
- [ ] MEMORY.md still syncing on session_start
|
||||||
|
- [ ] No regressions in existing functionality
|
||||||
@ -60,25 +60,7 @@ bun run build
|
|||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 4. Create the plugin cache directory
|
#### 4. Start the worker service
|
||||||
|
|
||||||
The Moltbot plugin expects claude-mem to be in the Claude plugins cache:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create the directory structure
|
|
||||||
mkdir -p ~/.claude/plugins/cache/thedotmack/claude-mem
|
|
||||||
|
|
||||||
# Copy the built plugin (use actual version number)
|
|
||||||
cp -r plugin ~/.claude/plugins/cache/thedotmack/claude-mem/9.0.12
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, symlink it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ln -s $(pwd)/plugin ~/.claude/plugins/cache/thedotmack/claude-mem/9.0.12
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 5. Start the worker service
|
|
||||||
|
|
||||||
Claude-mem runs a background worker service:
|
Claude-mem runs a background worker service:
|
||||||
|
|
||||||
@ -100,6 +82,43 @@ The worker runs on `http://localhost:37777` by default and provides:
|
|||||||
- API endpoints for memory search and retrieval
|
- API endpoints for memory search and retrieval
|
||||||
- Background observation processing
|
- Background observation processing
|
||||||
|
|
||||||
|
#### 5. Configure Moltbot with the worker path
|
||||||
|
|
||||||
|
Tell Moltbot where to find the worker service:
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
entries: {
|
||||||
|
"memory-claudemem": {
|
||||||
|
enabled: true,
|
||||||
|
config: {
|
||||||
|
workerPath: "/path/to/claude-mem/plugin/scripts/worker-service.cjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or set the slot directly:
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
slots: {
|
||||||
|
memory: "memory-claudemem"
|
||||||
|
},
|
||||||
|
entries: {
|
||||||
|
"memory-claudemem": {
|
||||||
|
config: {
|
||||||
|
workerPath: "/path/to/claude-mem/plugin/scripts/worker-service.cjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
## Enabling the Moltbot Plugin
|
## Enabling the Moltbot Plugin
|
||||||
|
|
||||||
Once claude-mem is installed, enable the Moltbot plugin:
|
Once claude-mem is installed, enable the Moltbot plugin:
|
||||||
@ -147,6 +166,7 @@ Or explicitly in entries:
|
|||||||
|--------|------|---------|-------------|
|
|--------|------|---------|-------------|
|
||||||
| `syncMemoryFile` | boolean | `true` | Sync MEMORY.md with claude-mem context on session/gateway start |
|
| `syncMemoryFile` | boolean | `true` | Sync MEMORY.md with claude-mem context on session/gateway start |
|
||||||
| `project` | string | workspace dir name | Project name for scoping observations |
|
| `project` | string | workspace dir name | Project name for scoping observations |
|
||||||
|
| `workerPath` | string | (auto-detect) | Path to `worker-service.cjs` for manual installs |
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
|
|||||||
@ -78,6 +78,7 @@ async function getHookCwd(workspaceDir: string, project: string): Promise<string
|
|||||||
interface ClaudeMemConfig {
|
interface ClaudeMemConfig {
|
||||||
syncMemoryFile: boolean;
|
syncMemoryFile: boolean;
|
||||||
project: string;
|
project: string;
|
||||||
|
workerPath?: string; // Custom path to worker-service.cjs (for manual installs)
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_CONFIG: Omit<ClaudeMemConfig, "project"> = {
|
const DEFAULT_CONFIG: Omit<ClaudeMemConfig, "project"> = {
|
||||||
@ -170,10 +171,23 @@ function callHookFireAndForget(hookName: string, data: Record<string, unknown>):
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function (api: ClawdbotPluginApi) {
|
export default function (api: ClawdbotPluginApi) {
|
||||||
// Find the latest claude-mem worker service (sync check)
|
const userConfig = api.pluginConfig as Partial<ClaudeMemConfig>;
|
||||||
|
|
||||||
|
// Check for custom worker path first (for manual installs)
|
||||||
|
if (userConfig.workerPath) {
|
||||||
|
if (existsSync(userConfig.workerPath)) {
|
||||||
|
WORKER_SERVICE_PATH = userConfig.workerPath;
|
||||||
|
api.logger.debug?.(`claude-mem: using custom worker path: ${WORKER_SERVICE_PATH}`);
|
||||||
|
} else {
|
||||||
|
api.logger.warn?.(`claude-mem: custom workerPath not found: ${userConfig.workerPath} - plugin disabled`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Auto-discover from Claude plugin cache
|
||||||
const cacheDir = join(homedir(), ".claude/plugins/cache/thedotmack/claude-mem");
|
const cacheDir = join(homedir(), ".claude/plugins/cache/thedotmack/claude-mem");
|
||||||
if (!existsSync(cacheDir)) {
|
if (!existsSync(cacheDir)) {
|
||||||
api.logger.warn?.("claude-mem: plugin cache not found - plugin disabled");
|
api.logger.warn?.("claude-mem: plugin cache not found and no workerPath configured - plugin disabled");
|
||||||
|
api.logger.warn?.("claude-mem: install via Claude Code or set plugins.entries.memory-claudemem.config.workerPath");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,6 +215,7 @@ export default function (api: ClawdbotPluginApi) {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
api.logger.warn?.("claude-mem: failed to find worker service", err);
|
api.logger.warn?.("claude-mem: failed to find worker service", err);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!WORKER_SERVICE_PATH) {
|
if (!WORKER_SERVICE_PATH) {
|
||||||
api.logger.warn?.("claude-mem: worker-service.cjs not found - plugin disabled");
|
api.logger.warn?.("claude-mem: worker-service.cjs not found - plugin disabled");
|
||||||
@ -212,7 +227,6 @@ export default function (api: ClawdbotPluginApi) {
|
|||||||
const workspaceDir = api.workspaceDir || process.cwd();
|
const workspaceDir = api.workspaceDir || process.cwd();
|
||||||
const defaultProject = basename(workspaceDir);
|
const defaultProject = basename(workspaceDir);
|
||||||
|
|
||||||
const userConfig = api.pluginConfig as Partial<ClaudeMemConfig>;
|
|
||||||
const config: ClaudeMemConfig = {
|
const config: ClaudeMemConfig = {
|
||||||
...DEFAULT_CONFIG,
|
...DEFAULT_CONFIG,
|
||||||
project: defaultProject,
|
project: defaultProject,
|
||||||
|
|||||||
@ -16,6 +16,10 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": true,
|
||||||
"description": "Sync MEMORY.md with claude-mem context on session start"
|
"description": "Sync MEMORY.md with claude-mem context on session start"
|
||||||
|
},
|
||||||
|
"workerPath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Custom path to worker-service.cjs (for manual installs outside Claude plugin cache)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -27,6 +31,10 @@
|
|||||||
"syncMemoryFile": {
|
"syncMemoryFile": {
|
||||||
"label": "Sync MEMORY.md",
|
"label": "Sync MEMORY.md",
|
||||||
"help": "Update MEMORY.md with claude-mem context on session/gateway start"
|
"help": "Update MEMORY.md with claude-mem context on session/gateway start"
|
||||||
|
},
|
||||||
|
"workerPath": {
|
||||||
|
"label": "Worker Path",
|
||||||
|
"help": "Path to claude-mem's worker-service.cjs. Only needed for manual installs."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -194,6 +194,7 @@
|
|||||||
"long": "5.3.2",
|
"long": "5.3.2",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
"node-edge-tts": "^1.2.9",
|
"node-edge-tts": "^1.2.9",
|
||||||
|
"node-gyp": "^12.2.0",
|
||||||
"osc-progress": "^0.3.0",
|
"osc-progress": "^0.3.0",
|
||||||
"pdfjs-dist": "^5.4.530",
|
"pdfjs-dist": "^5.4.530",
|
||||||
"playwright-core": "1.58.0",
|
"playwright-core": "1.58.0",
|
||||||
@ -286,7 +287,9 @@
|
|||||||
},
|
},
|
||||||
"trustedDependencies": [
|
"trustedDependencies": [
|
||||||
"@whiskeysockets/baileys",
|
"@whiskeysockets/baileys",
|
||||||
|
"esbuild",
|
||||||
"node-llama-cpp",
|
"node-llama-cpp",
|
||||||
"protobufjs"
|
"protobufjs",
|
||||||
|
"sharp"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user