openclaw/extensions/memory-claudemem/index.ts
Alex Newman d9f23cb000 feat(memory-claudemem): implement lifecycle hooks
Add two hooks:
- after_tool_call: fire-and-forget observation to claude-mem
  (skips memory_* tools to prevent recursion)
- before_agent_start: context injection from memory search
  (wraps in <claude-mem-context> tags)

Both hooks have error handling and graceful degradation.

Part of claude-mem integration (Phase 4/7).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 14:08:18 -05:00

70 lines
2.2 KiB
TypeScript

import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
import { claudeMemConfigSchema } from "./config.js";
import { ClaudeMemClient } from "./client.js";
const claudeMemPlugin = {
id: "memory-claudemem",
name: "Memory (Claude-Mem)",
description: "Real-time observation and memory via claude-mem worker",
kind: "memory" as const,
configSchema: claudeMemConfigSchema,
register(api: ClawdbotPluginApi) {
const cfg = claudeMemConfigSchema.parse(api.pluginConfig);
const client = new ClaudeMemClient(cfg.workerUrl, cfg.workerTimeout);
api.logger.info(
`memory-claudemem: plugin registered (worker: ${cfg.workerUrl})`,
);
// Hook: after_tool_call → Observe tool calls (fire-and-forget)
api.on("after_tool_call", async (event, ctx) => {
// Skip memory tools to prevent recursion
if (event.toolName.startsWith("memory_")) return;
try {
// Fire-and-forget: don't await, let it run in parallel
// Use sessionKey as the session identifier (may be undefined)
client.observe(
ctx.sessionKey ?? "unknown",
event.toolName,
event.params,
event.result,
);
} catch (err) {
api.logger.warn?.(`memory-claudemem: observation failed: ${err}`);
}
});
// Hook: before_agent_start → Context injection from memory search
api.on("before_agent_start", async (event) => {
// Skip if prompt is empty or too short
if (!event.prompt || event.prompt.length < 5) return;
try {
const results = await client.search(event.prompt, 5);
if (results.length === 0) return;
const memoryContext = results
.map((r) => `- [#${r.id}] ${r.title}: ${r.snippet}`)
.join("\n");
api.logger.info?.(
`memory-claudemem: injecting ${results.length} memories into context`,
);
return {
prependContext: `<claude-mem-context>\nThe following memories may be relevant:\n${memoryContext}\n</claude-mem-context>`,
};
} catch (err) {
api.logger.warn?.(`memory-claudemem: context injection failed: ${err}`);
}
});
// TODO: Phase 5 - Tool registration
// TODO: Phase 6 - CLI registration
},
};
export default claudeMemPlugin;