palz-connector 1.2.6 → 1.2.7
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/palz-connector.config.json +2 -1
- package/palz-connector.dev.config.json +2 -1
- package/palz-connector.prod.config.json +2 -1
- package/palz-connector.staging.config.json +2 -1
- package/src/bot.ts +51 -6
- package/src/config.ts +1 -0
- package/src/reply-dispatcher.ts +3 -0
- package/src/send.ts +5 -1
- package/src/types.ts +10 -0
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -17,6 +17,18 @@ import { createPalzReplyDispatcher } from "./reply-dispatcher.js";
|
|
|
17
17
|
import { resolvePalzMediaList } from "./media.js";
|
|
18
18
|
import type { PalzMessageEvent, OpenAIContent, ContentPart, TextContentPart, PalzMediaInfo } from "./types.js";
|
|
19
19
|
|
|
20
|
+
// ============ group_id 解析 ============
|
|
21
|
+
|
|
22
|
+
/** 从 conversation_id 中解析 group_id,格式: user_{userID}_lobster_{lobsterID}_group_{groupID}_release_{releaseName} */
|
|
23
|
+
const GROUP_ID_RE = /_group_([^_]+)_release_/;
|
|
24
|
+
|
|
25
|
+
function resolveGroupId(msg: PalzMessageEvent): string | undefined {
|
|
26
|
+
if (msg.group_id) return msg.group_id;
|
|
27
|
+
if (msg.conversation_type !== "group") return undefined;
|
|
28
|
+
const m = GROUP_ID_RE.exec(msg.conversation_id);
|
|
29
|
+
return m ? m[1] : undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
// ============ 文本提取工具 ============
|
|
21
33
|
|
|
22
34
|
function extractPlainText(content: OpenAIContent): string {
|
|
@@ -194,7 +206,11 @@ export async function handlePalzMessage(params: HandlePalzMessageParams): Promis
|
|
|
194
206
|
|
|
195
207
|
// 群聊 @提及检测
|
|
196
208
|
const wasMentioned = isGroup ? (msg.mentioned_bot === true) : true;
|
|
197
|
-
|
|
209
|
+
// 群聊上下文缓存:groupContextCache=true(默认)时,未@消息仅缓存不发送;
|
|
210
|
+
// groupContextCache=false 时,所有群聊消息都发送给AI
|
|
211
|
+
const account = resolvePalzAccount({ cfg, accountId });
|
|
212
|
+
const groupContextCacheEnabled = account.config.groupContextCache !== false;
|
|
213
|
+
if (isGroup && !wasMentioned && groupContextCacheEnabled) {
|
|
198
214
|
// 未@机器人:记录到群聊历史(含媒体),下次被@时作为上下文
|
|
199
215
|
const historyKey = `${effectiveAgentId}:${msg.conversation_id}`;
|
|
200
216
|
const senderName = msg.sender_name || msg.sender_id;
|
|
@@ -220,6 +236,9 @@ export async function handlePalzMessage(params: HandlePalzMessageParams): Promis
|
|
|
220
236
|
log(`${tag}: [STEP 1 跳过] 原因=群聊中未@机器人, 已记录到历史 historyKey=${historyKey} mediaCount=${historyMediaList.length}`);
|
|
221
237
|
return;
|
|
222
238
|
}
|
|
239
|
+
if (isGroup && !wasMentioned && !groupContextCacheEnabled) {
|
|
240
|
+
log(`${tag}: [STEP 1 群聊直通] groupContextCache=false, 未@消息也将发送给AI mentioned_bot=${msg.mentioned_bot}`);
|
|
241
|
+
}
|
|
223
242
|
|
|
224
243
|
// 去重(按 agentId + conversationId 隔离,同群多 bot 场景)
|
|
225
244
|
const claimed = tryClaimMessage(msg.msg_id, effectiveAgentId, msg.conversation_id);
|
|
@@ -255,6 +274,10 @@ async function dispatchPalzMessage(params: HandlePalzMessageParams): Promise<voi
|
|
|
255
274
|
const plainText = extractPlainText(msg.content).trim();
|
|
256
275
|
const useStream = msg.stream === true;
|
|
257
276
|
const senderName = msg.sender_name || msg.sender_id;
|
|
277
|
+
const groupId = resolveGroupId(msg);
|
|
278
|
+
if (isGroup) {
|
|
279
|
+
log(`${tag}: [group_id] resolved=${groupId ?? "(none)"} from_msg=${msg.group_id ?? "(none)"} conv=${msg.conversation_id}`);
|
|
280
|
+
}
|
|
258
281
|
|
|
259
282
|
// 群聊:peerId = chat:conversation_id(整群共享 session,与 palzTo 格式一致)
|
|
260
283
|
// DM:peerId = sender_id:conversation_id(每用户每会话独立 session)
|
|
@@ -324,10 +347,11 @@ async function dispatchPalzMessage(params: HandlePalzMessageParams): Promise<voi
|
|
|
324
347
|
|
|
325
348
|
const chatType = isGroup ? "group" : "direct";
|
|
326
349
|
|
|
327
|
-
// 群聊历史:将积攒的未@消息拼入 Body
|
|
328
|
-
const
|
|
350
|
+
// 群聊历史:将积攒的未@消息拼入 Body 上下文(仅 groupContextCache=true 时生效)
|
|
351
|
+
const groupContextCacheEnabled = account.config.groupContextCache !== false;
|
|
352
|
+
const historyKey = isGroup && groupContextCacheEnabled ? `${effectiveAgentId}:${msg.conversation_id}` : undefined;
|
|
329
353
|
let combinedBody = body;
|
|
330
|
-
if (isGroup && historyKey) {
|
|
354
|
+
if (isGroup && historyKey && groupContextCacheEnabled) {
|
|
331
355
|
log(`${tag}: [STEP 6b 群聊历史] 开始构建, historyKey=${historyKey} bodyLen=${body.length}`);
|
|
332
356
|
combinedBody = buildGroupHistoryContext({
|
|
333
357
|
historyKey,
|
|
@@ -357,7 +381,7 @@ async function dispatchPalzMessage(params: HandlePalzMessageParams): Promise<voi
|
|
|
357
381
|
|
|
358
382
|
// 构建 InboundHistory(结构化历史数据,Runtime 会注入到系统提示中)
|
|
359
383
|
const inboundHistory =
|
|
360
|
-
isGroup && historyKey
|
|
384
|
+
isGroup && historyKey && groupContextCacheEnabled
|
|
361
385
|
? (chatHistories.get(historyKey) ?? []).map((entry) => ({
|
|
362
386
|
sender: entry.sender,
|
|
363
387
|
body: entry.body,
|
|
@@ -366,6 +390,25 @@ async function dispatchPalzMessage(params: HandlePalzMessageParams): Promise<voi
|
|
|
366
390
|
: undefined;
|
|
367
391
|
log(`${tag}: [STEP 6b InboundHistory] count=${inboundHistory?.length ?? 0}`);
|
|
368
392
|
|
|
393
|
+
// 构建 UntrustedContext:将 IM 消息的关键字段注入到 AI agent 的上下文中
|
|
394
|
+
const untrustedContext: string[] = [
|
|
395
|
+
`sender_id: ${msg.sender_id}`,
|
|
396
|
+
`sender_name: ${senderName}`,
|
|
397
|
+
`conversation_id: ${msg.conversation_id}`,
|
|
398
|
+
`conversation_type: ${msg.conversation_type || "direct"}`,
|
|
399
|
+
`mentioned_bot: ${wasMentioned}`,
|
|
400
|
+
];
|
|
401
|
+
if (groupId) {
|
|
402
|
+
untrustedContext.push(`group_id: ${groupId}`);
|
|
403
|
+
}
|
|
404
|
+
if (msg.owner_id) {
|
|
405
|
+
untrustedContext.push(`owner_id: ${msg.owner_id}`);
|
|
406
|
+
}
|
|
407
|
+
if (msg.owner_name) {
|
|
408
|
+
untrustedContext.push(`owner_name: ${msg.owner_name}`);
|
|
409
|
+
}
|
|
410
|
+
log(`${tag}: [STEP 6b UntrustedContext] entries=${untrustedContext.length} values=${JSON.stringify(untrustedContext)}`);
|
|
411
|
+
|
|
369
412
|
const ctx = core.channel.reply.finalizeInboundContext({
|
|
370
413
|
Body: combinedBody,
|
|
371
414
|
BodyForAgent: messageBody,
|
|
@@ -380,6 +423,7 @@ async function dispatchPalzMessage(params: HandlePalzMessageParams): Promise<voi
|
|
|
380
423
|
GroupSubject: isGroup ? msg.conversation_id : undefined,
|
|
381
424
|
SenderId: msg.sender_id,
|
|
382
425
|
SenderName: senderName,
|
|
426
|
+
UntrustedContext: untrustedContext,
|
|
383
427
|
Provider: "palz-connector",
|
|
384
428
|
Surface: "palz-connector",
|
|
385
429
|
MessageSid: msg.msg_id,
|
|
@@ -418,6 +462,7 @@ async function dispatchPalzMessage(params: HandlePalzMessageParams): Promise<voi
|
|
|
418
462
|
enableStreaming: useStream,
|
|
419
463
|
msgId: msg.msg_id,
|
|
420
464
|
msgType: msg.msg_type,
|
|
465
|
+
groupId,
|
|
421
466
|
});
|
|
422
467
|
|
|
423
468
|
// STEP 6d: 分发消息给 AI
|
|
@@ -439,7 +484,7 @@ async function dispatchPalzMessage(params: HandlePalzMessageParams): Promise<voi
|
|
|
439
484
|
});
|
|
440
485
|
|
|
441
486
|
// AI 回复完成后清空群聊历史(已拼入上下文,避免下次重复)
|
|
442
|
-
if (isGroup && historyKey) {
|
|
487
|
+
if (isGroup && historyKey && groupContextCacheEnabled) {
|
|
443
488
|
clearGroupHistory(historyKey, log);
|
|
444
489
|
}
|
|
445
490
|
|
package/src/config.ts
CHANGED
|
@@ -72,6 +72,7 @@ export function resolvePalzConfig(_cfg?: any): PalzConfig {
|
|
|
72
72
|
streamUrl: file.streamUrl || "",
|
|
73
73
|
apiBaseUrl: file.apiBaseUrl || "",
|
|
74
74
|
sessionTimeout: file.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT,
|
|
75
|
+
groupContextCache: file.groupContextCache !== false,
|
|
75
76
|
};
|
|
76
77
|
if (!_configLoggedOnce) {
|
|
77
78
|
_configLoggedOnce = true;
|
package/src/reply-dispatcher.ts
CHANGED
|
@@ -42,6 +42,7 @@ export interface CreatePalzReplyDispatcherParams {
|
|
|
42
42
|
enableStreaming: boolean;
|
|
43
43
|
msgId: string;
|
|
44
44
|
msgType?: string;
|
|
45
|
+
groupId?: string;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
export function createPalzReplyDispatcher(params: CreatePalzReplyDispatcherParams) {
|
|
@@ -57,6 +58,7 @@ export function createPalzReplyDispatcher(params: CreatePalzReplyDispatcherParam
|
|
|
57
58
|
enableStreaming,
|
|
58
59
|
msgId,
|
|
59
60
|
msgType,
|
|
61
|
+
groupId,
|
|
60
62
|
} = params;
|
|
61
63
|
|
|
62
64
|
const log = typeof runtime?.log === "function" ? runtime.log : console.log;
|
|
@@ -87,6 +89,7 @@ export function createPalzReplyDispatcher(params: CreatePalzReplyDispatcherParam
|
|
|
87
89
|
senderId,
|
|
88
90
|
stream: streamOpts,
|
|
89
91
|
msgType,
|
|
92
|
+
groupId,
|
|
90
93
|
});
|
|
91
94
|
log(`${tag}: [DISPATCHER←sendToIM] 输出: ${JSON.stringify(result)}`);
|
|
92
95
|
return result;
|
package/src/send.ts
CHANGED
|
@@ -14,7 +14,7 @@ function nextMsgId(): string {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export async function sendToPalzIM(params: SendToIMParams): Promise<any> {
|
|
17
|
-
const { config, conversationId, content, conversationType, msgId, senderId, stream, msgType } = params;
|
|
17
|
+
const { config, conversationId, content, conversationType, msgId, senderId, stream, msgType, groupId } = params;
|
|
18
18
|
const url = `${config.apiBaseUrl}/bot/send`;
|
|
19
19
|
const resolvedMsgId = msgId || nextMsgId();
|
|
20
20
|
|
|
@@ -34,6 +34,10 @@ export async function sendToPalzIM(params: SendToIMParams): Promise<any> {
|
|
|
34
34
|
reqBody.msg_type = msgType;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
if (groupId) {
|
|
38
|
+
reqBody.group_id = groupId;
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
if (stream) {
|
|
38
42
|
reqBody.stream_id = stream.streamId;
|
|
39
43
|
reqBody.seq = stream.seq;
|
package/src/types.ts
CHANGED
|
@@ -31,6 +31,12 @@ export interface PalzMessageEvent {
|
|
|
31
31
|
mentioned_bot?: boolean;
|
|
32
32
|
/** 可选,IM 下发的消息类型,回复时原样透传 */
|
|
33
33
|
msg_type?: string;
|
|
34
|
+
/** Bot 所属用户的 ID */
|
|
35
|
+
owner_id?: string;
|
|
36
|
+
/** Bot 所属用户的用户名 */
|
|
37
|
+
owner_name?: string;
|
|
38
|
+
/** 群组 ID,群聊时 IM 可直接下发;若未提供则从 conversation_id 中解析 */
|
|
39
|
+
group_id?: string;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
// ============ 配置 ============
|
|
@@ -41,6 +47,8 @@ export interface PalzConfig {
|
|
|
41
47
|
streamUrl?: string;
|
|
42
48
|
apiBaseUrl?: string;
|
|
43
49
|
sessionTimeout?: number;
|
|
50
|
+
/** 群聊上下文缓存开关:true=未@消息缓存为上下文(默认),false=所有群聊消息直接发送给AI */
|
|
51
|
+
groupContextCache?: boolean;
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
export interface ResolvedPalzAccount {
|
|
@@ -78,4 +86,6 @@ export interface SendToIMParams {
|
|
|
78
86
|
stream?: StreamChunkOpts;
|
|
79
87
|
/** IM 下发的消息类型,回复时原样透传 */
|
|
80
88
|
msgType?: string;
|
|
89
|
+
/** 群组 ID,群聊时透传 */
|
|
90
|
+
groupId?: string;
|
|
81
91
|
}
|