@alfe.ai/openclaw-chat 0.0.15 → 0.0.16
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 +96 -15
- package/dist/plugin2.js +96 -15
- package/package.json +2 -2
package/dist/plugin2.cjs
CHANGED
|
@@ -138,28 +138,98 @@ function createAlfeChannelPlugin() {
|
|
|
138
138
|
//#endregion
|
|
139
139
|
//#region src/session-keys.ts
|
|
140
140
|
/**
|
|
141
|
-
* Session key helpers — handles
|
|
141
|
+
* Session key helpers — handles standardized, canonical, and legacy formats.
|
|
142
142
|
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
143
|
+
* Standardized format (Alfe-controlled):
|
|
144
|
+
* alfe:{mode}:{identity} — single-threaded (SMS, WhatsApp)
|
|
145
|
+
* alfe:{mode}:{identity}:{convId} — multi-threaded (web chat)
|
|
146
146
|
*
|
|
147
|
-
*
|
|
147
|
+
* OpenClaw canonical format (from resolveAgentRoute):
|
|
148
|
+
* agent:{ocAgentId}:alfe:[default:]direct:{senderId}[:thread:{conversationId}]
|
|
149
|
+
*
|
|
150
|
+
* Legacy formats (deprecated):
|
|
151
|
+
* sms-{agentId}-{phone}
|
|
152
|
+
* wa-{agentId}-{phone}
|
|
148
153
|
* chat-{tenantId}-{agentId}-{suffix}
|
|
149
154
|
* agent:{agentId}:chat-{tenantId}-{agentId}-{suffix}
|
|
150
|
-
*
|
|
151
|
-
* The plugin may receive either format depending on which
|
|
152
|
-
* OpenClaw event fires and whether the new dispatch path is active.
|
|
153
155
|
*/
|
|
156
|
+
/** Single-threaded channel modes — identity IS the conversation. */
|
|
157
|
+
const SINGLE_THREADED_MODES = new Set([
|
|
158
|
+
"sms",
|
|
159
|
+
"whatsapp",
|
|
160
|
+
"mobile"
|
|
161
|
+
]);
|
|
154
162
|
/**
|
|
155
163
|
* Check if a session key belongs to the Alfe chat channel.
|
|
156
|
-
* Handles
|
|
164
|
+
* Handles standardized, canonical, and legacy formats.
|
|
157
165
|
*/
|
|
158
166
|
function isAlfeSessionKey(key) {
|
|
167
|
+
if (key.startsWith("alfe:")) return true;
|
|
159
168
|
if (key.includes(":alfe:")) return true;
|
|
160
|
-
if (key.includes("chat-")) return true;
|
|
169
|
+
if (key.includes("chat-") || key.startsWith("sms-") || key.startsWith("wa-")) return true;
|
|
161
170
|
return false;
|
|
162
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Parse session key metadata. Returns available fields from any format.
|
|
174
|
+
*
|
|
175
|
+
* For the callback flow, `conversationId` is the critical field — it maps
|
|
176
|
+
* to the channel registry key used by getChannelCallback().
|
|
177
|
+
*/
|
|
178
|
+
function parseAlfeSessionKey(key) {
|
|
179
|
+
const standardMatch = /^alfe:(\w+):(.+)$/.exec(key);
|
|
180
|
+
if (standardMatch) {
|
|
181
|
+
const [, mode, rest] = standardMatch;
|
|
182
|
+
if (SINGLE_THREADED_MODES.has(mode)) return {
|
|
183
|
+
agentId: "",
|
|
184
|
+
userId: rest,
|
|
185
|
+
conversationId: key,
|
|
186
|
+
tenantId: "",
|
|
187
|
+
mode
|
|
188
|
+
};
|
|
189
|
+
const lastColon = rest.lastIndexOf(":");
|
|
190
|
+
if (lastColon > 0) return {
|
|
191
|
+
agentId: "",
|
|
192
|
+
userId: rest.slice(0, lastColon),
|
|
193
|
+
conversationId: key,
|
|
194
|
+
tenantId: "",
|
|
195
|
+
mode
|
|
196
|
+
};
|
|
197
|
+
return {
|
|
198
|
+
agentId: "",
|
|
199
|
+
userId: rest,
|
|
200
|
+
conversationId: key,
|
|
201
|
+
tenantId: "",
|
|
202
|
+
mode
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const canonicalMatch = /^agent:([^:]+):alfe:(?:default:)?direct:(.+?)(?::thread:(.+))?$/.exec(key);
|
|
206
|
+
if (canonicalMatch) {
|
|
207
|
+
const [, matchAgentId, matchUserId, matchConvId] = canonicalMatch;
|
|
208
|
+
return {
|
|
209
|
+
agentId: matchAgentId,
|
|
210
|
+
userId: matchUserId,
|
|
211
|
+
conversationId: matchConvId || "",
|
|
212
|
+
tenantId: "",
|
|
213
|
+
mode: ""
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const rawKey = key.includes(":") ? key.slice(key.lastIndexOf(":") + 1) : key;
|
|
217
|
+
const legacyMatch = /^chat-([^-]+)-([^-]+)/.exec(rawKey);
|
|
218
|
+
return {
|
|
219
|
+
agentId: legacyMatch?.[2] ?? "",
|
|
220
|
+
userId: "",
|
|
221
|
+
conversationId: "",
|
|
222
|
+
tenantId: legacyMatch?.[1] ?? "",
|
|
223
|
+
mode: "chat"
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Extract the channel mode from a standardized session key or conversationId.
|
|
228
|
+
* Returns the mode segment (e.g. 'sms', 'whatsapp', 'chat') or fallback.
|
|
229
|
+
*/
|
|
230
|
+
function extractChannelMode(conversationId, fallback = "chat") {
|
|
231
|
+
return /^alfe:(\w+):/.exec(conversationId)?.[1] ?? fallback;
|
|
232
|
+
}
|
|
163
233
|
//#endregion
|
|
164
234
|
//#region src/session-store.ts
|
|
165
235
|
/**
|
|
@@ -363,14 +433,16 @@ async function handleAgentRequest(request, log) {
|
|
|
363
433
|
});
|
|
364
434
|
});
|
|
365
435
|
try {
|
|
436
|
+
const channelMode = extractChannelMode(conversationId ?? "", clientType ?? "chat");
|
|
437
|
+
const channelLabel = channelMode === "sms" ? "SMS" : channelMode === "whatsapp" ? "WhatsApp" : "Alfe";
|
|
366
438
|
const shortConvId = conversationId?.slice(-8) ?? "";
|
|
367
439
|
const userLabel = displayName ?? userId ?? senderId;
|
|
368
|
-
const conversationLabel = conversationType === "group" ? shortConvId ? `[
|
|
440
|
+
const conversationLabel = conversationType === "group" ? shortConvId ? `[${channelLabel}] Group (${shortConvId})` : `[${channelLabel}] Group` : shortConvId ? `[${channelLabel}] ${userLabel} (${shortConvId})` : `[${channelLabel}] ${userLabel}`;
|
|
369
441
|
resolvedOpenClawKey = (await dispatchInbound({
|
|
370
442
|
cfg,
|
|
371
443
|
runtime: { channel: runtime.channel },
|
|
372
444
|
channel: "alfe",
|
|
373
|
-
channelLabel
|
|
445
|
+
channelLabel,
|
|
374
446
|
accountId: "default",
|
|
375
447
|
peer: conversationType === "group" ? {
|
|
376
448
|
kind: "group",
|
|
@@ -390,7 +462,8 @@ async function handleAgentRequest(request, log) {
|
|
|
390
462
|
...tenantId ? { TenantId: tenantId } : {},
|
|
391
463
|
...clientType ? { ClientType: clientType } : {},
|
|
392
464
|
...conversationId ? { ConversationId: conversationId } : {},
|
|
393
|
-
...displayName ? { SenderName: displayName } : {}
|
|
465
|
+
...displayName ? { SenderName: displayName } : {},
|
|
466
|
+
ChannelMode: channelMode
|
|
394
467
|
},
|
|
395
468
|
deliver: async (payload) => {
|
|
396
469
|
const responseText = payload.text ?? "";
|
|
@@ -559,8 +632,16 @@ const plugin = {
|
|
|
559
632
|
api.on("message", async (...eventArgs) => {
|
|
560
633
|
const event = eventArgs[0];
|
|
561
634
|
const key = event.sessionKey;
|
|
562
|
-
if (!key
|
|
563
|
-
|
|
635
|
+
if (!key) return;
|
|
636
|
+
if (isAlfeSessionKey(key)) {
|
|
637
|
+
if (event.role === "assistant" && chatClient) {
|
|
638
|
+
const parsed = parseAlfeSessionKey(key);
|
|
639
|
+
if (parsed.conversationId) chatClient.notify("agent-message", {
|
|
640
|
+
conversationId: parsed.conversationId,
|
|
641
|
+
text: event.content
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
} else await addMessage(key, event.role, event.content);
|
|
564
645
|
});
|
|
565
646
|
api.on("session_end", (...eventArgs) => {
|
|
566
647
|
const key = eventArgs[0].sessionKey;
|
package/dist/plugin2.js
CHANGED
|
@@ -138,28 +138,98 @@ function createAlfeChannelPlugin() {
|
|
|
138
138
|
//#endregion
|
|
139
139
|
//#region src/session-keys.ts
|
|
140
140
|
/**
|
|
141
|
-
* Session key helpers — handles
|
|
141
|
+
* Session key helpers — handles standardized, canonical, and legacy formats.
|
|
142
142
|
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
143
|
+
* Standardized format (Alfe-controlled):
|
|
144
|
+
* alfe:{mode}:{identity} — single-threaded (SMS, WhatsApp)
|
|
145
|
+
* alfe:{mode}:{identity}:{convId} — multi-threaded (web chat)
|
|
146
146
|
*
|
|
147
|
-
*
|
|
147
|
+
* OpenClaw canonical format (from resolveAgentRoute):
|
|
148
|
+
* agent:{ocAgentId}:alfe:[default:]direct:{senderId}[:thread:{conversationId}]
|
|
149
|
+
*
|
|
150
|
+
* Legacy formats (deprecated):
|
|
151
|
+
* sms-{agentId}-{phone}
|
|
152
|
+
* wa-{agentId}-{phone}
|
|
148
153
|
* chat-{tenantId}-{agentId}-{suffix}
|
|
149
154
|
* agent:{agentId}:chat-{tenantId}-{agentId}-{suffix}
|
|
150
|
-
*
|
|
151
|
-
* The plugin may receive either format depending on which
|
|
152
|
-
* OpenClaw event fires and whether the new dispatch path is active.
|
|
153
155
|
*/
|
|
156
|
+
/** Single-threaded channel modes — identity IS the conversation. */
|
|
157
|
+
const SINGLE_THREADED_MODES = new Set([
|
|
158
|
+
"sms",
|
|
159
|
+
"whatsapp",
|
|
160
|
+
"mobile"
|
|
161
|
+
]);
|
|
154
162
|
/**
|
|
155
163
|
* Check if a session key belongs to the Alfe chat channel.
|
|
156
|
-
* Handles
|
|
164
|
+
* Handles standardized, canonical, and legacy formats.
|
|
157
165
|
*/
|
|
158
166
|
function isAlfeSessionKey(key) {
|
|
167
|
+
if (key.startsWith("alfe:")) return true;
|
|
159
168
|
if (key.includes(":alfe:")) return true;
|
|
160
|
-
if (key.includes("chat-")) return true;
|
|
169
|
+
if (key.includes("chat-") || key.startsWith("sms-") || key.startsWith("wa-")) return true;
|
|
161
170
|
return false;
|
|
162
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Parse session key metadata. Returns available fields from any format.
|
|
174
|
+
*
|
|
175
|
+
* For the callback flow, `conversationId` is the critical field — it maps
|
|
176
|
+
* to the channel registry key used by getChannelCallback().
|
|
177
|
+
*/
|
|
178
|
+
function parseAlfeSessionKey(key) {
|
|
179
|
+
const standardMatch = /^alfe:(\w+):(.+)$/.exec(key);
|
|
180
|
+
if (standardMatch) {
|
|
181
|
+
const [, mode, rest] = standardMatch;
|
|
182
|
+
if (SINGLE_THREADED_MODES.has(mode)) return {
|
|
183
|
+
agentId: "",
|
|
184
|
+
userId: rest,
|
|
185
|
+
conversationId: key,
|
|
186
|
+
tenantId: "",
|
|
187
|
+
mode
|
|
188
|
+
};
|
|
189
|
+
const lastColon = rest.lastIndexOf(":");
|
|
190
|
+
if (lastColon > 0) return {
|
|
191
|
+
agentId: "",
|
|
192
|
+
userId: rest.slice(0, lastColon),
|
|
193
|
+
conversationId: key,
|
|
194
|
+
tenantId: "",
|
|
195
|
+
mode
|
|
196
|
+
};
|
|
197
|
+
return {
|
|
198
|
+
agentId: "",
|
|
199
|
+
userId: rest,
|
|
200
|
+
conversationId: key,
|
|
201
|
+
tenantId: "",
|
|
202
|
+
mode
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const canonicalMatch = /^agent:([^:]+):alfe:(?:default:)?direct:(.+?)(?::thread:(.+))?$/.exec(key);
|
|
206
|
+
if (canonicalMatch) {
|
|
207
|
+
const [, matchAgentId, matchUserId, matchConvId] = canonicalMatch;
|
|
208
|
+
return {
|
|
209
|
+
agentId: matchAgentId,
|
|
210
|
+
userId: matchUserId,
|
|
211
|
+
conversationId: matchConvId || "",
|
|
212
|
+
tenantId: "",
|
|
213
|
+
mode: ""
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const rawKey = key.includes(":") ? key.slice(key.lastIndexOf(":") + 1) : key;
|
|
217
|
+
const legacyMatch = /^chat-([^-]+)-([^-]+)/.exec(rawKey);
|
|
218
|
+
return {
|
|
219
|
+
agentId: legacyMatch?.[2] ?? "",
|
|
220
|
+
userId: "",
|
|
221
|
+
conversationId: "",
|
|
222
|
+
tenantId: legacyMatch?.[1] ?? "",
|
|
223
|
+
mode: "chat"
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Extract the channel mode from a standardized session key or conversationId.
|
|
228
|
+
* Returns the mode segment (e.g. 'sms', 'whatsapp', 'chat') or fallback.
|
|
229
|
+
*/
|
|
230
|
+
function extractChannelMode(conversationId, fallback = "chat") {
|
|
231
|
+
return /^alfe:(\w+):/.exec(conversationId)?.[1] ?? fallback;
|
|
232
|
+
}
|
|
163
233
|
//#endregion
|
|
164
234
|
//#region src/session-store.ts
|
|
165
235
|
/**
|
|
@@ -363,14 +433,16 @@ async function handleAgentRequest(request, log) {
|
|
|
363
433
|
});
|
|
364
434
|
});
|
|
365
435
|
try {
|
|
436
|
+
const channelMode = extractChannelMode(conversationId ?? "", clientType ?? "chat");
|
|
437
|
+
const channelLabel = channelMode === "sms" ? "SMS" : channelMode === "whatsapp" ? "WhatsApp" : "Alfe";
|
|
366
438
|
const shortConvId = conversationId?.slice(-8) ?? "";
|
|
367
439
|
const userLabel = displayName ?? userId ?? senderId;
|
|
368
|
-
const conversationLabel = conversationType === "group" ? shortConvId ? `[
|
|
440
|
+
const conversationLabel = conversationType === "group" ? shortConvId ? `[${channelLabel}] Group (${shortConvId})` : `[${channelLabel}] Group` : shortConvId ? `[${channelLabel}] ${userLabel} (${shortConvId})` : `[${channelLabel}] ${userLabel}`;
|
|
369
441
|
resolvedOpenClawKey = (await dispatchInbound({
|
|
370
442
|
cfg,
|
|
371
443
|
runtime: { channel: runtime.channel },
|
|
372
444
|
channel: "alfe",
|
|
373
|
-
channelLabel
|
|
445
|
+
channelLabel,
|
|
374
446
|
accountId: "default",
|
|
375
447
|
peer: conversationType === "group" ? {
|
|
376
448
|
kind: "group",
|
|
@@ -390,7 +462,8 @@ async function handleAgentRequest(request, log) {
|
|
|
390
462
|
...tenantId ? { TenantId: tenantId } : {},
|
|
391
463
|
...clientType ? { ClientType: clientType } : {},
|
|
392
464
|
...conversationId ? { ConversationId: conversationId } : {},
|
|
393
|
-
...displayName ? { SenderName: displayName } : {}
|
|
465
|
+
...displayName ? { SenderName: displayName } : {},
|
|
466
|
+
ChannelMode: channelMode
|
|
394
467
|
},
|
|
395
468
|
deliver: async (payload) => {
|
|
396
469
|
const responseText = payload.text ?? "";
|
|
@@ -559,8 +632,16 @@ const plugin = {
|
|
|
559
632
|
api.on("message", async (...eventArgs) => {
|
|
560
633
|
const event = eventArgs[0];
|
|
561
634
|
const key = event.sessionKey;
|
|
562
|
-
if (!key
|
|
563
|
-
|
|
635
|
+
if (!key) return;
|
|
636
|
+
if (isAlfeSessionKey(key)) {
|
|
637
|
+
if (event.role === "assistant" && chatClient) {
|
|
638
|
+
const parsed = parseAlfeSessionKey(key);
|
|
639
|
+
if (parsed.conversationId) chatClient.notify("agent-message", {
|
|
640
|
+
conversationId: parsed.conversationId,
|
|
641
|
+
text: event.content
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
} else await addMessage(key, event.role, event.content);
|
|
564
645
|
});
|
|
565
646
|
api.on("session_end", (...eventArgs) => {
|
|
566
647
|
const key = eventArgs[0].sessionKey;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/openclaw-chat",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
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,7 @@
|
|
|
27
27
|
"openclaw.plugin.json"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@alfe.ai/chat": "^0.0.
|
|
30
|
+
"@alfe.ai/chat": "^0.0.6"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"openclaw": ">=2026.3.0"
|