Skip to content

Commit 64019ee

Browse files
authored
Merge pull request #1084 from thedotmack/fix/openclaw-plugin-project-query-and-feed-bottoken
fix(openclaw): fix MEMORY.md project query mismatch and add feed botToken support
2 parents 26ac35a + 6cad773 commit 64019ee

2 files changed

Lines changed: 55 additions & 12 deletions

File tree

openclaw/openclaw.plugin.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"id": "claude-mem",
33
"name": "Claude-Mem (Persistent Memory)",
44
"description": "Official OpenClaw plugin for Claude-Mem. Records observations from embedded runner sessions and streams them to messaging channels.",
5-
"kind": "integration",
5+
"kind": "memory",
66
"version": "1.0.0",
77
"author": "thedotmack",
88
"homepage": "https://claude-mem.com",
@@ -41,6 +41,10 @@
4141
"to": {
4242
"type": "string",
4343
"description": "Target chat/user ID to send observations to"
44+
},
45+
"botToken": {
46+
"type": "string",
47+
"description": "Optional dedicated Telegram bot token for the feed (bypasses gateway channel)"
4448
}
4549
}
4650
}

openclaw/src/index.ts

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { writeFile } from "fs/promises";
2-
import { basename, join } from "path";
2+
import { join } from "path";
33

44
// Minimal type declarations for the OpenClaw Plugin SDK.
55
// These match the real OpenClawPluginApi provided by the gateway at runtime.
@@ -164,6 +164,7 @@ interface ClaudeMemPluginConfig {
164164
enabled?: boolean;
165165
channel?: string;
166166
to?: string;
167+
botToken?: string;
167168
};
168169
}
169170

@@ -305,12 +306,44 @@ const CHANNEL_SEND_MAP: Record<string, { namespace: string; functionName: string
305306
line: { namespace: "line", functionName: "sendMessageLine" },
306307
};
307308

309+
async function sendDirectTelegram(
310+
botToken: string,
311+
chatId: string,
312+
text: string,
313+
logger: PluginLogger
314+
): Promise<void> {
315+
try {
316+
const response = await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`, {
317+
method: "POST",
318+
headers: { "Content-Type": "application/json" },
319+
body: JSON.stringify({
320+
chat_id: chatId,
321+
text,
322+
parse_mode: "Markdown",
323+
}),
324+
});
325+
if (!response.ok) {
326+
const body = await response.text();
327+
logger.warn(`[claude-mem] Direct Telegram send failed (${response.status}): ${body}`);
328+
}
329+
} catch (error: unknown) {
330+
const message = error instanceof Error ? error.message : String(error);
331+
logger.warn(`[claude-mem] Direct Telegram send error: ${message}`);
332+
}
333+
}
334+
308335
function sendToChannel(
309336
api: OpenClawPluginApi,
310337
channel: string,
311338
to: string,
312-
text: string
339+
text: string,
340+
botToken?: string
313341
): Promise<void> {
342+
// If a dedicated bot token is provided for Telegram, send directly
343+
if (botToken && channel === "telegram") {
344+
return sendDirectTelegram(botToken, to, text, api.logger);
345+
}
346+
314347
const mapping = CHANNEL_SEND_MAP[channel];
315348
if (!mapping) {
316349
api.logger.warn(`[claude-mem] Unsupported channel type: ${channel}`);
@@ -346,7 +379,8 @@ async function connectToSSEStream(
346379
channel: string,
347380
to: string,
348381
abortController: AbortController,
349-
setConnectionState: (state: ConnectionState) => void
382+
setConnectionState: (state: ConnectionState) => void,
383+
botToken?: string
350384
): Promise<void> {
351385
let backoffMs = 1000;
352386
const maxBackoffMs = 30000;
@@ -407,7 +441,7 @@ async function connectToSSEStream(
407441
if (parsed.type === "new_observation" && parsed.observation) {
408442
const event = parsed as SSENewObservationEvent;
409443
const message = formatObservationMessage(event.observation);
410-
await sendToChannel(api, channel, to, message);
444+
await sendToChannel(api, channel, to, message, botToken);
411445
}
412446
} catch (parseError: unknown) {
413447
const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
@@ -464,12 +498,16 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
464498
return sessionIds.get(key)!;
465499
}
466500

467-
async function syncMemoryToWorkspace(workspaceDir: string): Promise<void> {
468-
// Derive project name from workspace directory (matches Claude Code's getProjectName logic)
469-
const workspaceProject = basename(workspaceDir) || baseProjectName;
501+
async function syncMemoryToWorkspace(workspaceDir: string, ctx?: EventContext): Promise<void> {
502+
// Include both the base project and agent-scoped project (e.g. "openclaw" + "openclaw-main")
503+
const projects = [baseProjectName];
504+
const agentProject = ctx ? getProjectName(ctx) : null;
505+
if (agentProject && agentProject !== baseProjectName) {
506+
projects.push(agentProject);
507+
}
470508
const contextText = await workerGetText(
471509
workerPort,
472-
`/api/context/inject?projects=${encodeURIComponent(workspaceProject)}`,
510+
`/api/context/inject?projects=${encodeURIComponent(projects.join(","))}`,
473511
api.logger
474512
);
475513
if (contextText && contextText.trim().length > 0) {
@@ -547,7 +585,7 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
547585

548586
// Sync MEMORY.md before agent runs (provides context to agent)
549587
if (syncMemoryFile && ctx.workspaceDir) {
550-
await syncMemoryToWorkspace(ctx.workspaceDir);
588+
await syncMemoryToWorkspace(ctx.workspaceDir, ctx);
551589
}
552590
});
553591

@@ -582,7 +620,7 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
582620

583621
const workspaceDir = ctx.workspaceDir || workspaceDirsBySessionKey.get(ctx.sessionKey || "default");
584622
if (syncMemoryFile && workspaceDir) {
585-
syncMemoryToWorkspace(workspaceDir);
623+
syncMemoryToWorkspace(workspaceDir, ctx);
586624
}
587625
});
588626

@@ -681,7 +719,8 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
681719
feedConfig.channel,
682720
feedConfig.to,
683721
sseAbortController,
684-
(state) => { connectionState = state; }
722+
(state) => { connectionState = state; },
723+
feedConfig.botToken
685724
);
686725
},
687726
stop: async (_ctx) => {

0 commit comments

Comments
 (0)