@alfe.ai/openclaw-chat 0.0.17 → 0.0.18

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,5 +1,7 @@
1
1
  let node_module = require("node:module");
2
2
  let _alfe_ai_chat = require("@alfe.ai/chat");
3
+ let _alfe_ai_config = require("@alfe.ai/config");
4
+ let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
3
5
  let node_fs_promises = require("node:fs/promises");
4
6
  let node_path = require("node:path");
5
7
  let node_os = require("node:os");
@@ -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,7 @@ function resolveOpenClawSdk(log) {
428
324
  let pluginRuntime = null;
429
325
  let chatClient = null;
430
326
  let connectingPromise = null;
327
+ let metricsClient = null;
431
328
  async function handleAgentRequest(request, log) {
432
329
  const runtime = pluginRuntime;
433
330
  if (!runtime) {
@@ -498,10 +395,18 @@ async function handleAgentRequest(request, log) {
498
395
  deliver: async (payload) => {
499
396
  const responseText = payload.text ?? "";
500
397
  await addMessage(sessionId, "assistant", responseText);
501
- chatClient?.sendResponse(request.id, true, {
398
+ chatClient?.notify("agent-message", {
399
+ conversationId: conversationId ?? legacySessionKey,
502
400
  text: responseText,
503
401
  sessionKey: resolvedOpenClawKey ?? legacySessionKey
504
402
  });
403
+ if (metricsClient && userId) metricsClient.recordActivity({
404
+ userId,
405
+ channel: "alfe",
406
+ role: "assistant"
407
+ }).catch((err) => {
408
+ log.debug(`Metrics report failed: ${err instanceof Error ? err.message : String(err)}`);
409
+ });
505
410
  },
506
411
  onRecordError: (err) => {
507
412
  log.error(`Session record error: ${err instanceof Error ? err.message : String(err)}`);
@@ -510,6 +415,7 @@ async function handleAgentRequest(request, log) {
510
415
  log.error(`Dispatch error (${info.kind}): ${err instanceof Error ? err.message : String(err)}`);
511
416
  }
512
417
  })).route.sessionKey;
418
+ chatClient?.sendResponse(request.id, true, { sessionKey: resolvedOpenClawKey });
513
419
  log.info(`Agent dispatch complete: sessionKey=${resolvedOpenClawKey}`);
514
420
  } catch (err) {
515
421
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -586,6 +492,15 @@ const plugin = {
586
492
  log.info("Chat plugin registering...");
587
493
  resolveOpenClawSdk(log);
588
494
  pluginRuntime = api.runtime ?? null;
495
+ try {
496
+ const cfg = (0, _alfe_ai_config.resolveConfig)();
497
+ metricsClient = new _alfe_ai_agent_api_client.AgentApiClient({
498
+ apiKey: cfg.apiKey,
499
+ apiUrl: cfg.apiUrl
500
+ });
501
+ } catch {
502
+ log.debug("Metrics client not initialized — activity tracking disabled");
503
+ }
589
504
  }
590
505
  const pluginConfig = (((api.config ?? {}).plugins?.entries)?.["@alfe.ai/openclaw-chat"] ?? {}).config ?? {};
591
506
  if (!alreadyActivated) connectingPromise = Promise.resolve().then(() => {
@@ -659,19 +574,11 @@ const plugin = {
659
574
  log.info(`Chat session starting: ${key}`);
660
575
  await createSession(key, "", "alfe");
661
576
  }, { priority: 50 });
662
- api.on("message", async (...eventArgs) => {
577
+ api.on("message_received", async (...eventArgs) => {
663
578
  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);
579
+ const ctx = eventArgs[1];
580
+ if (!ctx.conversationId || ctx.channelId === "alfe") return;
581
+ await addMessage(ctx.conversationId, "user", event.content, event.from);
675
582
  });
676
583
  api.on("session_end", (...eventArgs) => {
677
584
  const key = eventArgs[0].sessionKey;
@@ -698,6 +605,7 @@ const plugin = {
698
605
  }
699
606
  pluginRuntime = null;
700
607
  dispatchInbound = null;
608
+ metricsClient = null;
701
609
  log.info("Chat plugin deactivated");
702
610
  }
703
611
  };
package/dist/plugin2.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { ChatServiceClient, resolveAlfeChat } from "@alfe.ai/chat";
3
+ import { resolveConfig } from "@alfe.ai/config";
4
+ import { AgentApiClient } from "@alfe.ai/agent-api-client";
3
5
  import { mkdir, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
4
6
  import { join } from "node:path";
5
7
  import { homedir } from "node:os";
@@ -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,7 @@ function resolveOpenClawSdk(log) {
428
324
  let pluginRuntime = null;
429
325
  let chatClient = null;
430
326
  let connectingPromise = null;
327
+ let metricsClient = null;
431
328
  async function handleAgentRequest(request, log) {
432
329
  const runtime = pluginRuntime;
433
330
  if (!runtime) {
@@ -498,10 +395,18 @@ async function handleAgentRequest(request, log) {
498
395
  deliver: async (payload) => {
499
396
  const responseText = payload.text ?? "";
500
397
  await addMessage(sessionId, "assistant", responseText);
501
- chatClient?.sendResponse(request.id, true, {
398
+ chatClient?.notify("agent-message", {
399
+ conversationId: conversationId ?? legacySessionKey,
502
400
  text: responseText,
503
401
  sessionKey: resolvedOpenClawKey ?? legacySessionKey
504
402
  });
403
+ if (metricsClient && userId) metricsClient.recordActivity({
404
+ userId,
405
+ channel: "alfe",
406
+ role: "assistant"
407
+ }).catch((err) => {
408
+ log.debug(`Metrics report failed: ${err instanceof Error ? err.message : String(err)}`);
409
+ });
505
410
  },
506
411
  onRecordError: (err) => {
507
412
  log.error(`Session record error: ${err instanceof Error ? err.message : String(err)}`);
@@ -510,6 +415,7 @@ async function handleAgentRequest(request, log) {
510
415
  log.error(`Dispatch error (${info.kind}): ${err instanceof Error ? err.message : String(err)}`);
511
416
  }
512
417
  })).route.sessionKey;
418
+ chatClient?.sendResponse(request.id, true, { sessionKey: resolvedOpenClawKey });
513
419
  log.info(`Agent dispatch complete: sessionKey=${resolvedOpenClawKey}`);
514
420
  } catch (err) {
515
421
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -586,6 +492,15 @@ const plugin = {
586
492
  log.info("Chat plugin registering...");
587
493
  resolveOpenClawSdk(log);
588
494
  pluginRuntime = api.runtime ?? null;
495
+ try {
496
+ const cfg = resolveConfig();
497
+ metricsClient = new AgentApiClient({
498
+ apiKey: cfg.apiKey,
499
+ apiUrl: cfg.apiUrl
500
+ });
501
+ } catch {
502
+ log.debug("Metrics client not initialized — activity tracking disabled");
503
+ }
589
504
  }
590
505
  const pluginConfig = (((api.config ?? {}).plugins?.entries)?.["@alfe.ai/openclaw-chat"] ?? {}).config ?? {};
591
506
  if (!alreadyActivated) connectingPromise = Promise.resolve().then(() => {
@@ -659,19 +574,11 @@ const plugin = {
659
574
  log.info(`Chat session starting: ${key}`);
660
575
  await createSession(key, "", "alfe");
661
576
  }, { priority: 50 });
662
- api.on("message", async (...eventArgs) => {
577
+ api.on("message_received", async (...eventArgs) => {
663
578
  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);
579
+ const ctx = eventArgs[1];
580
+ if (!ctx.conversationId || ctx.channelId === "alfe") return;
581
+ await addMessage(ctx.conversationId, "user", event.content, event.from);
675
582
  });
676
583
  api.on("session_end", (...eventArgs) => {
677
584
  const key = eventArgs[0].sessionKey;
@@ -698,6 +605,7 @@ const plugin = {
698
605
  }
699
606
  pluginRuntime = null;
700
607
  dispatchInbound = null;
608
+ metricsClient = null;
701
609
  log.info("Chat plugin deactivated");
702
610
  }
703
611
  };
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.18",
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"