From be474ea5957d99dde64b392ba55defaeeb2e32ae Mon Sep 17 00:00:00 2001 From: "Daniel M." <113709296+Manantra@users.noreply.github.com> Date: Mon, 16 Feb 2026 06:32:04 +0100 Subject: [PATCH] Fix command handlers to return { text } per OpenClaw plugin API (#1115) The OpenClaw plugin API requires command handlers to return { text: string } objects. Returning plain strings causes "reply missing text/media" errors and commands silently fail to send responses to Telegram/other channels. --- openclaw/src/index.test.ts | 22 +++++++++++----------- openclaw/src/index.ts | 18 +++++++++--------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/openclaw/src/index.test.ts b/openclaw/src/index.test.ts index 03a551a0..021444b8 100644 --- a/openclaw/src/index.test.ts +++ b/openclaw/src/index.test.ts @@ -168,7 +168,7 @@ describe("claudeMemPlugin", () => { claudeMemPlugin(api); const result = await getCommand().handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} }); - assert.ok(result.includes("not configured")); + assert.ok(result.text.includes("not configured")); }); it("returns status when no args", async () => { @@ -178,10 +178,10 @@ describe("claudeMemPlugin", () => { claudeMemPlugin(api); const result = await getCommand().handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} }); - assert.ok(result.includes("Enabled: yes")); - assert.ok(result.includes("Channel: telegram")); - assert.ok(result.includes("Target: 123")); - assert.ok(result.includes("Connection:")); + assert.ok(result.text.includes("Enabled: yes")); + assert.ok(result.text.includes("Channel: telegram")); + assert.ok(result.text.includes("Target: 123")); + assert.ok(result.text.includes("Connection:")); }); it("handles 'on' argument", async () => { @@ -191,7 +191,7 @@ describe("claudeMemPlugin", () => { claudeMemPlugin(api); const result = await getCommand().handler({ args: "on", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed on", config: {} }); - assert.ok(result.includes("enable requested")); + assert.ok(result.text.includes("enable requested")); assert.ok(logs.some((l) => l.includes("enable requested"))); }); @@ -202,7 +202,7 @@ describe("claudeMemPlugin", () => { claudeMemPlugin(api); const result = await getCommand().handler({ args: "off", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed off", config: {} }); - assert.ok(result.includes("disable requested")); + assert.ok(result.text.includes("disable requested")); assert.ok(logs.some((l) => l.includes("disable requested"))); }); @@ -213,7 +213,7 @@ describe("claudeMemPlugin", () => { claudeMemPlugin(api); const result = await getCommand().handler({ args: "", channel: "slack", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} }); - assert.ok(result.includes("Connection: disconnected")); + assert.ok(result.text.includes("Connection: disconnected")); }); }); }); @@ -493,8 +493,8 @@ describe("Observation I/O event handlers", () => { assert.ok(statusCmd, "status command should exist"); const result = await statusCmd.handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-status", config: {} }); - assert.ok(result.includes("Status: ok")); - assert.ok(result.includes(`Port: ${workerPort}`)); + assert.ok(result.text.includes("Status: ok")); + assert.ok(result.text.includes(`Port: ${workerPort}`)); }); it("claude-mem-status reports unreachable when worker is down", async () => { @@ -506,7 +506,7 @@ describe("Observation I/O event handlers", () => { const statusCmd = getCommand("claude-mem-status"); const result = await statusCmd.handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-status", config: {} }); - assert.ok(result.includes("unreachable")); + assert.ok(result.text.includes("unreachable")); }); it("reuses same contentSessionId for same sessionKey", async () => { diff --git a/openclaw/src/index.ts b/openclaw/src/index.ts index 2f3abc21..a5f4f93b 100644 --- a/openclaw/src/index.ts +++ b/openclaw/src/index.ts @@ -816,28 +816,28 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void { const feedConfig = userConfig.observationFeed; if (!feedConfig) { - return "Observation feed not configured. Add observationFeed to your plugin config."; + return { text: "Observation feed not configured. Add observationFeed to your plugin config." }; } const arg = ctx.args?.trim(); if (arg === "on") { api.logger.info("[claude-mem] Feed enable requested via command"); - return "Feed enable requested. Update observationFeed.enabled in your plugin config to persist."; + return { text: "Feed enable requested. Update observationFeed.enabled in your plugin config to persist." }; } if (arg === "off") { api.logger.info("[claude-mem] Feed disable requested via command"); - return "Feed disable requested. Update observationFeed.enabled in your plugin config to persist."; + return { text: "Feed disable requested. Update observationFeed.enabled in your plugin config to persist." }; } - return [ + return { text: [ "Claude-Mem Observation Feed", `Enabled: ${feedConfig.enabled ? "yes" : "no"}`, `Channel: ${feedConfig.channel || "not set"}`, `Target: ${feedConfig.to || "not set"}`, `Connection: ${connectionState}`, - ].join("\n"); + ].join("\n") }; }, }); @@ -985,20 +985,20 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void { handler: async () => { const healthText = await workerGetText(workerPort, "/api/health", api.logger); if (!healthText) { - return `Claude-Mem worker unreachable at port ${workerPort}`; + return { text: `Claude-Mem worker unreachable at port ${workerPort}` }; } try { const health = JSON.parse(healthText); - return [ + return { text: [ "Claude-Mem Worker Status", `Status: ${health.status || "unknown"}`, `Port: ${workerPort}`, `Active sessions: ${sessionIds.size}`, `Observation feed: ${connectionState}`, - ].join("\n"); + ].join("\n") }; } catch { - return `Claude-Mem worker responded but returned unexpected data`; + return { text: `Claude-Mem worker responded but returned unexpected data` }; } }, });