@alfe.ai/openclaw-chat 0.0.17 → 0.0.19

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/dist/plugin2.cjs CHANGED
@@ -1,8 +1,10 @@
1
1
  let node_module = require("node:module");
2
- let _alfe_ai_chat = require("@alfe.ai/chat");
3
2
  let node_fs_promises = require("node:fs/promises");
4
3
  let node_path = require("node:path");
5
4
  let node_os = require("node:os");
5
+ let _alfe_ai_chat = require("@alfe.ai/chat");
6
+ let _alfe_ai_config = require("@alfe.ai/config");
7
+ let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
6
8
  let node_fs = require("node:fs");
7
9
  //#region src/alfe-channel.ts
8
10
  const CHANNEL_ID = "alfe";
@@ -138,31 +140,6 @@ function createAlfeChannelPlugin() {
138
140
  //#endregion
139
141
  //#region src/session-keys.ts
140
142
  /**
141
- * Session key helpers — handles standardized, canonical, and legacy formats.
142
- *
143
- * Standardized format (Alfe-controlled):
144
- * alfe:{mode}:{identity} — single-threaded (SMS, WhatsApp)
145
- * alfe:{mode}:{identity}:{convId} — multi-threaded (web chat)
146
- *
147
- * Wrapped standardized format (from chat inject via callAgent):
148
- * agent:{ocAgentId}:alfe:{mode}:{identity}
149
- *
150
- * OpenClaw canonical format (from resolveAgentRoute):
151
- * agent:{ocAgentId}:alfe:[default:]direct:{senderId}[:thread:{conversationId}]
152
- *
153
- * Legacy formats (deprecated):
154
- * sms-{agentId}-{phone}
155
- * wa-{agentId}-{phone}
156
- * chat-{tenantId}-{agentId}-{suffix}
157
- * agent:{agentId}:chat-{tenantId}-{agentId}-{suffix}
158
- */
159
- /** Single-threaded channel modes — identity IS the conversation. */
160
- const SINGLE_THREADED_MODES = new Set([
161
- "sms",
162
- "whatsapp",
163
- "mobile"
164
- ]);
165
- /**
166
143
  * Check if a session key belongs to the Alfe chat channel.
167
144
  * Handles standardized, canonical, and legacy formats.
168
145
  */
@@ -173,87 +150,6 @@ function isAlfeSessionKey(key) {
173
150
  return false;
174
151
  }
175
152
  /**
176
- * Parse session key metadata. Returns available fields from any format.
177
- *
178
- * For the callback flow, `conversationId` is the critical field — it maps
179
- * to the channel registry key used by getChannelCallback().
180
- */
181
- function parseAlfeSessionKey(key) {
182
- const standardMatch = /^alfe:(\w+):(.+)$/.exec(key);
183
- if (standardMatch) {
184
- const [, mode, rest] = standardMatch;
185
- if (SINGLE_THREADED_MODES.has(mode)) return {
186
- agentId: "",
187
- userId: rest,
188
- conversationId: key,
189
- tenantId: "",
190
- mode
191
- };
192
- const lastColon = rest.lastIndexOf(":");
193
- if (lastColon > 0) return {
194
- agentId: "",
195
- userId: rest.slice(0, lastColon),
196
- conversationId: key,
197
- tenantId: "",
198
- mode
199
- };
200
- return {
201
- agentId: "",
202
- userId: rest,
203
- conversationId: key,
204
- tenantId: "",
205
- mode
206
- };
207
- }
208
- const wrappedStandardMatch = /^agent:([^:]+):alfe:(?!default:|direct:)(\w+):(.+)$/.exec(key);
209
- if (wrappedStandardMatch) {
210
- const [, matchAgentId, wrappedMode, rest] = wrappedStandardMatch;
211
- const standardKey = `alfe:${wrappedMode}:${rest}`;
212
- if (SINGLE_THREADED_MODES.has(wrappedMode)) return {
213
- agentId: matchAgentId,
214
- userId: rest,
215
- conversationId: standardKey,
216
- tenantId: "",
217
- mode: wrappedMode
218
- };
219
- const lastColon = rest.lastIndexOf(":");
220
- if (lastColon > 0) return {
221
- agentId: matchAgentId,
222
- userId: rest.slice(0, lastColon),
223
- conversationId: standardKey,
224
- tenantId: "",
225
- mode: wrappedMode
226
- };
227
- return {
228
- agentId: matchAgentId,
229
- userId: rest,
230
- conversationId: standardKey,
231
- tenantId: "",
232
- mode: wrappedMode
233
- };
234
- }
235
- const canonicalMatch = /^agent:([^:]+):alfe:(?:default:)?direct:(.+?)(?::thread:(.+))?$/.exec(key);
236
- if (canonicalMatch) {
237
- const [, matchAgentId, matchUserId, matchConvId] = canonicalMatch;
238
- return {
239
- agentId: matchAgentId,
240
- userId: matchUserId,
241
- conversationId: matchConvId || "",
242
- tenantId: "",
243
- mode: ""
244
- };
245
- }
246
- const rawKey = key.includes(":") ? key.slice(key.lastIndexOf(":") + 1) : key;
247
- const legacyMatch = /^chat-([^-]+)-([^-]+)/.exec(rawKey);
248
- return {
249
- agentId: legacyMatch?.[2] ?? "",
250
- userId: "",
251
- conversationId: "",
252
- tenantId: legacyMatch?.[1] ?? "",
253
- mode: "chat"
254
- };
255
- }
256
- /**
257
153
  * Extract the channel mode from a standardized session key or conversationId.
258
154
  * Returns the mode segment (e.g. 'sms', 'whatsapp', 'chat') or fallback.
259
155
  */
@@ -428,6 +324,34 @@ function resolveOpenClawSdk(log) {
428
324
  let pluginRuntime = null;
429
325
  let chatClient = null;
430
326
  let connectingPromise = null;
327
+ let metricsClient = null;
328
+ async function downloadAttachments(attachments, log) {
329
+ const attachDir = (0, node_path.join)((0, node_os.homedir)(), ".alfe", "attachments");
330
+ await (0, node_fs_promises.mkdir)(attachDir, { recursive: true });
331
+ const results = [];
332
+ for (const att of attachments) {
333
+ const filename = att.filename ?? att.id;
334
+ const localPath = (0, node_path.join)(attachDir, `${att.id}_${filename}`);
335
+ try {
336
+ const res = await fetch(att.url);
337
+ if (!res.ok) {
338
+ log.warn(`Failed to download attachment ${att.id}: ${String(res.status)}`);
339
+ continue;
340
+ }
341
+ const buffer = Buffer.from(await res.arrayBuffer());
342
+ await (0, node_fs_promises.writeFile)(localPath, buffer);
343
+ results.push({
344
+ localPath,
345
+ filename,
346
+ mimeType: att.mimeType
347
+ });
348
+ log.info(`Downloaded attachment: ${localPath} (${String(buffer.length)} bytes)`);
349
+ } catch (err) {
350
+ log.error(`Failed to download attachment ${att.id}: ${err instanceof Error ? err.message : String(err)}`);
351
+ }
352
+ }
353
+ return results;
354
+ }
431
355
  async function handleAgentRequest(request, log) {
432
356
  const runtime = pluginRuntime;
433
357
  if (!runtime) {
@@ -438,8 +362,8 @@ async function handleAgentRequest(request, log) {
438
362
  chatClient?.sendResponse(request.id, false, { message: "OpenClaw SDK not available — cannot dispatch" });
439
363
  return;
440
364
  }
441
- const { message, sessionKey: legacySessionKey, userId, conversationId, conversationType, tenantId, clientType, displayName } = request.params;
442
- if (!message) {
365
+ const { message, sessionKey: legacySessionKey, userId, conversationId, conversationType, tenantId, clientType, displayName, attachments: rawAttachments } = request.params;
366
+ if (!message && !rawAttachments?.length) {
443
367
  chatClient?.sendResponse(request.id, false, { message: "Missing message" });
444
368
  return;
445
369
  }
@@ -463,6 +387,8 @@ async function handleAgentRequest(request, log) {
463
387
  });
464
388
  });
465
389
  try {
390
+ const downloadedFiles = rawAttachments?.length ? await downloadAttachments(rawAttachments, log) : [];
391
+ const bodyForAgent = downloadedFiles.length ? `${message || ""}\n\n[Attached files:\n${downloadedFiles.map((f) => `- ${f.filename}: ${f.localPath}`).join("\n")}]` : void 0;
466
392
  const channelMode = extractChannelMode(conversationId ?? "", clientType ?? "chat");
467
393
  const channelLabel = channelMode === "sms" ? "SMS" : channelMode === "whatsapp" ? "WhatsApp" : "Alfe";
468
394
  const shortConvId = conversationId?.slice(-8) ?? "";
@@ -485,7 +411,8 @@ async function handleAgentRequest(request, log) {
485
411
  senderAddress: `user:${senderId}`,
486
412
  recipientAddress: "agent",
487
413
  conversationLabel,
488
- rawBody: message,
414
+ rawBody: message || "",
415
+ bodyForAgent,
489
416
  messageId: request.id,
490
417
  timestamp: Date.now(),
491
418
  extraContext: {
@@ -497,10 +424,20 @@ async function handleAgentRequest(request, log) {
497
424
  },
498
425
  deliver: async (payload) => {
499
426
  const responseText = payload.text ?? "";
427
+ const mediaUrls = [...payload.mediaUrl ? [payload.mediaUrl] : [], ...payload.mediaUrls ?? []];
500
428
  await addMessage(sessionId, "assistant", responseText);
501
- chatClient?.sendResponse(request.id, true, {
429
+ chatClient?.notify("agent-message", {
430
+ conversationId: conversationId ?? legacySessionKey,
502
431
  text: responseText,
503
- sessionKey: resolvedOpenClawKey ?? legacySessionKey
432
+ sessionKey: resolvedOpenClawKey ?? legacySessionKey,
433
+ ...mediaUrls.length ? { mediaUrls } : {}
434
+ });
435
+ if (metricsClient && userId) metricsClient.recordActivity({
436
+ userId,
437
+ channel: "alfe",
438
+ role: "assistant"
439
+ }).catch((err) => {
440
+ log.debug(`Metrics report failed: ${err instanceof Error ? err.message : String(err)}`);
504
441
  });
505
442
  },
506
443
  onRecordError: (err) => {
@@ -510,6 +447,7 @@ async function handleAgentRequest(request, log) {
510
447
  log.error(`Dispatch error (${info.kind}): ${err instanceof Error ? err.message : String(err)}`);
511
448
  }
512
449
  })).route.sessionKey;
450
+ chatClient?.sendResponse(request.id, true, { sessionKey: resolvedOpenClawKey });
513
451
  log.info(`Agent dispatch complete: sessionKey=${resolvedOpenClawKey}`);
514
452
  } catch (err) {
515
453
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -586,6 +524,15 @@ const plugin = {
586
524
  log.info("Chat plugin registering...");
587
525
  resolveOpenClawSdk(log);
588
526
  pluginRuntime = api.runtime ?? null;
527
+ try {
528
+ const cfg = (0, _alfe_ai_config.resolveConfig)();
529
+ metricsClient = new _alfe_ai_agent_api_client.AgentApiClient({
530
+ apiKey: cfg.apiKey,
531
+ apiUrl: cfg.apiUrl
532
+ });
533
+ } catch {
534
+ log.debug("Metrics client not initialized — activity tracking disabled");
535
+ }
589
536
  }
590
537
  const pluginConfig = (((api.config ?? {}).plugins?.entries)?.["@alfe.ai/openclaw-chat"] ?? {}).config ?? {};
591
538
  if (!alreadyActivated) connectingPromise = Promise.resolve().then(() => {
@@ -659,19 +606,11 @@ const plugin = {
659
606
  log.info(`Chat session starting: ${key}`);
660
607
  await createSession(key, "", "alfe");
661
608
  }, { priority: 50 });
662
- api.on("message", async (...eventArgs) => {
609
+ api.on("message_received", async (...eventArgs) => {
663
610
  const event = eventArgs[0];
664
- const key = event.sessionKey;
665
- if (!key) return;
666
- if (isAlfeSessionKey(key)) {
667
- if (event.role === "assistant" && chatClient) {
668
- const parsed = parseAlfeSessionKey(key);
669
- if (parsed.conversationId) chatClient.notify("agent-message", {
670
- conversationId: parsed.conversationId,
671
- text: event.content
672
- });
673
- }
674
- } else await addMessage(key, event.role, event.content);
611
+ const ctx = eventArgs[1];
612
+ if (!ctx.conversationId || ctx.channelId === "alfe") return;
613
+ await addMessage(ctx.conversationId, "user", event.content, event.from);
675
614
  });
676
615
  api.on("session_end", (...eventArgs) => {
677
616
  const key = eventArgs[0].sessionKey;
@@ -698,6 +637,7 @@ const plugin = {
698
637
  }
699
638
  pluginRuntime = null;
700
639
  dispatchInbound = null;
640
+ metricsClient = null;
701
641
  log.info("Chat plugin deactivated");
702
642
  }
703
643
  };
package/dist/plugin2.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { createRequire } from "node:module";
2
- import { ChatServiceClient, resolveAlfeChat } from "@alfe.ai/chat";
3
2
  import { mkdir, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
4
3
  import { join } from "node:path";
5
4
  import { homedir } from "node:os";
5
+ import { ChatServiceClient, resolveAlfeChat } from "@alfe.ai/chat";
6
+ import { resolveConfig } from "@alfe.ai/config";
7
+ import { AgentApiClient } from "@alfe.ai/agent-api-client";
6
8
  import { existsSync } from "node:fs";
7
9
  //#region src/alfe-channel.ts
8
10
  const CHANNEL_ID = "alfe";
@@ -138,31 +140,6 @@ function createAlfeChannelPlugin() {
138
140
  //#endregion
139
141
  //#region src/session-keys.ts
140
142
  /**
141
- * Session key helpers — handles standardized, canonical, and legacy formats.
142
- *
143
- * Standardized format (Alfe-controlled):
144
- * alfe:{mode}:{identity} — single-threaded (SMS, WhatsApp)
145
- * alfe:{mode}:{identity}:{convId} — multi-threaded (web chat)
146
- *
147
- * Wrapped standardized format (from chat inject via callAgent):
148
- * agent:{ocAgentId}:alfe:{mode}:{identity}
149
- *
150
- * OpenClaw canonical format (from resolveAgentRoute):
151
- * agent:{ocAgentId}:alfe:[default:]direct:{senderId}[:thread:{conversationId}]
152
- *
153
- * Legacy formats (deprecated):
154
- * sms-{agentId}-{phone}
155
- * wa-{agentId}-{phone}
156
- * chat-{tenantId}-{agentId}-{suffix}
157
- * agent:{agentId}:chat-{tenantId}-{agentId}-{suffix}
158
- */
159
- /** Single-threaded channel modes — identity IS the conversation. */
160
- const SINGLE_THREADED_MODES = new Set([
161
- "sms",
162
- "whatsapp",
163
- "mobile"
164
- ]);
165
- /**
166
143
  * Check if a session key belongs to the Alfe chat channel.
167
144
  * Handles standardized, canonical, and legacy formats.
168
145
  */
@@ -173,87 +150,6 @@ function isAlfeSessionKey(key) {
173
150
  return false;
174
151
  }
175
152
  /**
176
- * Parse session key metadata. Returns available fields from any format.
177
- *
178
- * For the callback flow, `conversationId` is the critical field — it maps
179
- * to the channel registry key used by getChannelCallback().
180
- */
181
- function parseAlfeSessionKey(key) {
182
- const standardMatch = /^alfe:(\w+):(.+)$/.exec(key);
183
- if (standardMatch) {
184
- const [, mode, rest] = standardMatch;
185
- if (SINGLE_THREADED_MODES.has(mode)) return {
186
- agentId: "",
187
- userId: rest,
188
- conversationId: key,
189
- tenantId: "",
190
- mode
191
- };
192
- const lastColon = rest.lastIndexOf(":");
193
- if (lastColon > 0) return {
194
- agentId: "",
195
- userId: rest.slice(0, lastColon),
196
- conversationId: key,
197
- tenantId: "",
198
- mode
199
- };
200
- return {
201
- agentId: "",
202
- userId: rest,
203
- conversationId: key,
204
- tenantId: "",
205
- mode
206
- };
207
- }
208
- const wrappedStandardMatch = /^agent:([^:]+):alfe:(?!default:|direct:)(\w+):(.+)$/.exec(key);
209
- if (wrappedStandardMatch) {
210
- const [, matchAgentId, wrappedMode, rest] = wrappedStandardMatch;
211
- const standardKey = `alfe:${wrappedMode}:${rest}`;
212
- if (SINGLE_THREADED_MODES.has(wrappedMode)) return {
213
- agentId: matchAgentId,
214
- userId: rest,
215
- conversationId: standardKey,
216
- tenantId: "",
217
- mode: wrappedMode
218
- };
219
- const lastColon = rest.lastIndexOf(":");
220
- if (lastColon > 0) return {
221
- agentId: matchAgentId,
222
- userId: rest.slice(0, lastColon),
223
- conversationId: standardKey,
224
- tenantId: "",
225
- mode: wrappedMode
226
- };
227
- return {
228
- agentId: matchAgentId,
229
- userId: rest,
230
- conversationId: standardKey,
231
- tenantId: "",
232
- mode: wrappedMode
233
- };
234
- }
235
- const canonicalMatch = /^agent:([^:]+):alfe:(?:default:)?direct:(.+?)(?::thread:(.+))?$/.exec(key);
236
- if (canonicalMatch) {
237
- const [, matchAgentId, matchUserId, matchConvId] = canonicalMatch;
238
- return {
239
- agentId: matchAgentId,
240
- userId: matchUserId,
241
- conversationId: matchConvId || "",
242
- tenantId: "",
243
- mode: ""
244
- };
245
- }
246
- const rawKey = key.includes(":") ? key.slice(key.lastIndexOf(":") + 1) : key;
247
- const legacyMatch = /^chat-([^-]+)-([^-]+)/.exec(rawKey);
248
- return {
249
- agentId: legacyMatch?.[2] ?? "",
250
- userId: "",
251
- conversationId: "",
252
- tenantId: legacyMatch?.[1] ?? "",
253
- mode: "chat"
254
- };
255
- }
256
- /**
257
153
  * Extract the channel mode from a standardized session key or conversationId.
258
154
  * Returns the mode segment (e.g. 'sms', 'whatsapp', 'chat') or fallback.
259
155
  */
@@ -428,6 +324,34 @@ function resolveOpenClawSdk(log) {
428
324
  let pluginRuntime = null;
429
325
  let chatClient = null;
430
326
  let connectingPromise = null;
327
+ let metricsClient = null;
328
+ async function downloadAttachments(attachments, log) {
329
+ const attachDir = join(homedir(), ".alfe", "attachments");
330
+ await mkdir(attachDir, { recursive: true });
331
+ const results = [];
332
+ for (const att of attachments) {
333
+ const filename = att.filename ?? att.id;
334
+ const localPath = join(attachDir, `${att.id}_${filename}`);
335
+ try {
336
+ const res = await fetch(att.url);
337
+ if (!res.ok) {
338
+ log.warn(`Failed to download attachment ${att.id}: ${String(res.status)}`);
339
+ continue;
340
+ }
341
+ const buffer = Buffer.from(await res.arrayBuffer());
342
+ await writeFile(localPath, buffer);
343
+ results.push({
344
+ localPath,
345
+ filename,
346
+ mimeType: att.mimeType
347
+ });
348
+ log.info(`Downloaded attachment: ${localPath} (${String(buffer.length)} bytes)`);
349
+ } catch (err) {
350
+ log.error(`Failed to download attachment ${att.id}: ${err instanceof Error ? err.message : String(err)}`);
351
+ }
352
+ }
353
+ return results;
354
+ }
431
355
  async function handleAgentRequest(request, log) {
432
356
  const runtime = pluginRuntime;
433
357
  if (!runtime) {
@@ -438,8 +362,8 @@ async function handleAgentRequest(request, log) {
438
362
  chatClient?.sendResponse(request.id, false, { message: "OpenClaw SDK not available — cannot dispatch" });
439
363
  return;
440
364
  }
441
- const { message, sessionKey: legacySessionKey, userId, conversationId, conversationType, tenantId, clientType, displayName } = request.params;
442
- if (!message) {
365
+ const { message, sessionKey: legacySessionKey, userId, conversationId, conversationType, tenantId, clientType, displayName, attachments: rawAttachments } = request.params;
366
+ if (!message && !rawAttachments?.length) {
443
367
  chatClient?.sendResponse(request.id, false, { message: "Missing message" });
444
368
  return;
445
369
  }
@@ -463,6 +387,8 @@ async function handleAgentRequest(request, log) {
463
387
  });
464
388
  });
465
389
  try {
390
+ const downloadedFiles = rawAttachments?.length ? await downloadAttachments(rawAttachments, log) : [];
391
+ const bodyForAgent = downloadedFiles.length ? `${message || ""}\n\n[Attached files:\n${downloadedFiles.map((f) => `- ${f.filename}: ${f.localPath}`).join("\n")}]` : void 0;
466
392
  const channelMode = extractChannelMode(conversationId ?? "", clientType ?? "chat");
467
393
  const channelLabel = channelMode === "sms" ? "SMS" : channelMode === "whatsapp" ? "WhatsApp" : "Alfe";
468
394
  const shortConvId = conversationId?.slice(-8) ?? "";
@@ -485,7 +411,8 @@ async function handleAgentRequest(request, log) {
485
411
  senderAddress: `user:${senderId}`,
486
412
  recipientAddress: "agent",
487
413
  conversationLabel,
488
- rawBody: message,
414
+ rawBody: message || "",
415
+ bodyForAgent,
489
416
  messageId: request.id,
490
417
  timestamp: Date.now(),
491
418
  extraContext: {
@@ -497,10 +424,20 @@ async function handleAgentRequest(request, log) {
497
424
  },
498
425
  deliver: async (payload) => {
499
426
  const responseText = payload.text ?? "";
427
+ const mediaUrls = [...payload.mediaUrl ? [payload.mediaUrl] : [], ...payload.mediaUrls ?? []];
500
428
  await addMessage(sessionId, "assistant", responseText);
501
- chatClient?.sendResponse(request.id, true, {
429
+ chatClient?.notify("agent-message", {
430
+ conversationId: conversationId ?? legacySessionKey,
502
431
  text: responseText,
503
- sessionKey: resolvedOpenClawKey ?? legacySessionKey
432
+ sessionKey: resolvedOpenClawKey ?? legacySessionKey,
433
+ ...mediaUrls.length ? { mediaUrls } : {}
434
+ });
435
+ if (metricsClient && userId) metricsClient.recordActivity({
436
+ userId,
437
+ channel: "alfe",
438
+ role: "assistant"
439
+ }).catch((err) => {
440
+ log.debug(`Metrics report failed: ${err instanceof Error ? err.message : String(err)}`);
504
441
  });
505
442
  },
506
443
  onRecordError: (err) => {
@@ -510,6 +447,7 @@ async function handleAgentRequest(request, log) {
510
447
  log.error(`Dispatch error (${info.kind}): ${err instanceof Error ? err.message : String(err)}`);
511
448
  }
512
449
  })).route.sessionKey;
450
+ chatClient?.sendResponse(request.id, true, { sessionKey: resolvedOpenClawKey });
513
451
  log.info(`Agent dispatch complete: sessionKey=${resolvedOpenClawKey}`);
514
452
  } catch (err) {
515
453
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -586,6 +524,15 @@ const plugin = {
586
524
  log.info("Chat plugin registering...");
587
525
  resolveOpenClawSdk(log);
588
526
  pluginRuntime = api.runtime ?? null;
527
+ try {
528
+ const cfg = resolveConfig();
529
+ metricsClient = new AgentApiClient({
530
+ apiKey: cfg.apiKey,
531
+ apiUrl: cfg.apiUrl
532
+ });
533
+ } catch {
534
+ log.debug("Metrics client not initialized — activity tracking disabled");
535
+ }
589
536
  }
590
537
  const pluginConfig = (((api.config ?? {}).plugins?.entries)?.["@alfe.ai/openclaw-chat"] ?? {}).config ?? {};
591
538
  if (!alreadyActivated) connectingPromise = Promise.resolve().then(() => {
@@ -659,19 +606,11 @@ const plugin = {
659
606
  log.info(`Chat session starting: ${key}`);
660
607
  await createSession(key, "", "alfe");
661
608
  }, { priority: 50 });
662
- api.on("message", async (...eventArgs) => {
609
+ api.on("message_received", async (...eventArgs) => {
663
610
  const event = eventArgs[0];
664
- const key = event.sessionKey;
665
- if (!key) return;
666
- if (isAlfeSessionKey(key)) {
667
- if (event.role === "assistant" && chatClient) {
668
- const parsed = parseAlfeSessionKey(key);
669
- if (parsed.conversationId) chatClient.notify("agent-message", {
670
- conversationId: parsed.conversationId,
671
- text: event.content
672
- });
673
- }
674
- } else await addMessage(key, event.role, event.content);
611
+ const ctx = eventArgs[1];
612
+ if (!ctx.conversationId || ctx.channelId === "alfe") return;
613
+ await addMessage(ctx.conversationId, "user", event.content, event.from);
675
614
  });
676
615
  api.on("session_end", (...eventArgs) => {
677
616
  const key = eventArgs[0].sessionKey;
@@ -698,6 +637,7 @@ const plugin = {
698
637
  }
699
638
  pluginRuntime = null;
700
639
  dispatchInbound = null;
640
+ metricsClient = null;
701
641
  log.info("Chat plugin deactivated");
702
642
  }
703
643
  };
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "id": "@alfe.ai/openclaw-chat",
3
- "channels": ["alfe"],
4
3
  "configSchema": {
5
4
  "type": "object",
6
5
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/openclaw-chat",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "OpenClaw chat plugin for Alfe — web widget and mobile app channels",
5
5
  "type": "module",
6
6
  "main": "./dist/plugin.js",
@@ -27,7 +27,9 @@
27
27
  "openclaw.plugin.json"
28
28
  ],
29
29
  "dependencies": {
30
- "@alfe.ai/chat": "^0.0.6"
30
+ "@alfe.ai/agent-api-client": "^0.0.7",
31
+ "@alfe.ai/chat": "^0.0.6",
32
+ "@alfe.ai/config": "^0.0.7"
31
33
  },
32
34
  "peerDependencies": {
33
35
  "openclaw": ">=2026.3.0"