openclaw-xiaoyou 1.4.4 → 1.4.6
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/package.json +1 -1
- package/src/channel.ts +25 -22
- package/src/types.ts +8 -0
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* xiaoyou ChannelPlugin 实现
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 与企业服务的 WebSocket
|
|
4
|
+
* 运行�?OpenClaw Gateway 进程内,通过 gateway adapter 管理
|
|
5
|
+
* 与企业服务的 WebSocket 连接生命周期�?
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { ResolvedAccount } from "./types.js";
|
|
9
9
|
import { createEnterpriseClient, type EnterpriseClient } from "./enterprise-client.js";
|
|
10
10
|
|
|
11
|
-
// ───
|
|
11
|
+
// ─── 运行时状�?──────────────────────────────────────
|
|
12
12
|
|
|
13
13
|
let _client: EnterpriseClient | null = null;
|
|
14
|
-
/** 记录最后一个活跃的 conversationId
|
|
14
|
+
/** 记录最后一个活跃的 conversationId,用�?cron delivery 兜底 */
|
|
15
15
|
let _lastConversationId: string | null = null;
|
|
16
16
|
|
|
17
17
|
let _runtime: any = null;
|
|
@@ -102,8 +102,8 @@ export const xiayouPlugin = {
|
|
|
102
102
|
startAccount: async (ctx: any) => {
|
|
103
103
|
const { account, cfg, log } = ctx;
|
|
104
104
|
|
|
105
|
-
// 使用 register 时注入的 runtime(有 channel.reply
|
|
106
|
-
// 注意:ctx.runtime 只有 log/error/exit
|
|
105
|
+
// 使用 register 时注入的 runtime(有 channel.reply 等完�?API�?
|
|
106
|
+
// 注意:ctx.runtime 只有 log/error/exit,不能用它覆�?
|
|
107
107
|
const section = account?.config || getChannelConfig(cfg);
|
|
108
108
|
const logger = log || console;
|
|
109
109
|
|
|
@@ -139,7 +139,9 @@ export const xiayouPlugin = {
|
|
|
139
139
|
const senderName = msg.senderName ?? msg.senderId;
|
|
140
140
|
const conversationId = msg.conversationId;
|
|
141
141
|
const inboundMessageId = msg.messageId;
|
|
142
|
+
const inboundSn = msg.sn || msg.messageId;
|
|
142
143
|
const text = msg.text ?? "";
|
|
144
|
+
const metadata = msg.metadata ?? undefined;
|
|
143
145
|
|
|
144
146
|
// 记录最后活跃的 conversationId,供 cron delivery 兜底使用
|
|
145
147
|
_lastConversationId = conversationId;
|
|
@@ -153,7 +155,7 @@ export const xiayouPlugin = {
|
|
|
153
155
|
peer: { kind: "direct", id: senderId },
|
|
154
156
|
});
|
|
155
157
|
|
|
156
|
-
// 2.
|
|
158
|
+
// 2. 格式化入站消�?
|
|
157
159
|
const fromLabel = `${senderName} (${senderId})`;
|
|
158
160
|
const envelopeOptions = rt.channel.reply.resolveEnvelopeFormatOptions(cfg);
|
|
159
161
|
const body = rt.channel.reply.formatInboundEnvelope({
|
|
@@ -166,7 +168,7 @@ export const xiayouPlugin = {
|
|
|
166
168
|
envelope: envelopeOptions,
|
|
167
169
|
});
|
|
168
170
|
|
|
169
|
-
// 3.
|
|
171
|
+
// 3. 构建上下�?
|
|
170
172
|
const inboundCtx = rt.channel.reply.finalizeInboundContext({
|
|
171
173
|
Body: body,
|
|
172
174
|
RawBody: text,
|
|
@@ -185,10 +187,10 @@ export const xiayouPlugin = {
|
|
|
185
187
|
Timestamp: Date.now(),
|
|
186
188
|
});
|
|
187
189
|
|
|
188
|
-
// 4. 分发并获取回复(流式
|
|
190
|
+
// 4. 分发并获取回复(流式 �?按标点拆分)
|
|
189
191
|
//
|
|
190
|
-
// Gateway
|
|
191
|
-
//
|
|
192
|
+
// Gateway �?block streaming 会多次调�?deliver,每次一�?block�?
|
|
193
|
+
// 我们�?deliver 内部进一步按标点/换行拆分,实现更细粒度的流式推送�?
|
|
192
194
|
//
|
|
193
195
|
const replyMessageId = `xiaoyou-${Date.now()}`;
|
|
194
196
|
let chunkSeq = 1;
|
|
@@ -211,9 +213,10 @@ export const xiayouPlugin = {
|
|
|
211
213
|
type: "reply",
|
|
212
214
|
conversationId,
|
|
213
215
|
messageId: replyMessageId,
|
|
214
|
-
|
|
216
|
+
sn: inboundSn,
|
|
215
217
|
senderId,
|
|
216
218
|
text: textToSend,
|
|
219
|
+
...(metadata ? { metadata } : {}),
|
|
217
220
|
streamStatus: "chunk",
|
|
218
221
|
seq: chunkSeq++,
|
|
219
222
|
timestamp: Date.now(),
|
|
@@ -228,9 +231,10 @@ export const xiayouPlugin = {
|
|
|
228
231
|
type: "reply",
|
|
229
232
|
conversationId,
|
|
230
233
|
messageId: replyMessageId,
|
|
231
|
-
|
|
234
|
+
sn: inboundSn,
|
|
232
235
|
senderId,
|
|
233
236
|
text: fullText,
|
|
237
|
+
...(metadata ? { metadata } : {}),
|
|
234
238
|
streamStatus: "end",
|
|
235
239
|
timestamp: Date.now(),
|
|
236
240
|
});
|
|
@@ -246,9 +250,10 @@ export const xiayouPlugin = {
|
|
|
246
250
|
type: "reply",
|
|
247
251
|
conversationId,
|
|
248
252
|
messageId: replyMessageId,
|
|
249
|
-
|
|
253
|
+
sn: inboundSn,
|
|
250
254
|
senderId,
|
|
251
255
|
text: fullText,
|
|
256
|
+
...(metadata ? { metadata } : {}),
|
|
252
257
|
streamStatus: "end",
|
|
253
258
|
timestamp: Date.now(),
|
|
254
259
|
});
|
|
@@ -263,7 +268,7 @@ export const xiayouPlugin = {
|
|
|
263
268
|
_client = client;
|
|
264
269
|
logger.info("[xiaoyou] gateway started");
|
|
265
270
|
|
|
266
|
-
// 保持 startAccount
|
|
271
|
+
// 保持 startAccount 挂起,直�?abortSignal 触发
|
|
267
272
|
return new Promise<void>((resolve) => {
|
|
268
273
|
if (ctx.abortSignal) {
|
|
269
274
|
ctx.abortSignal.addEventListener("abort", () => {
|
|
@@ -283,7 +288,7 @@ export const xiayouPlugin = {
|
|
|
283
288
|
|
|
284
289
|
// ── Target 解析(让 Gateway delivery 系统识别 xiaoyou target)──
|
|
285
290
|
normalizeTarget: (target: string) => {
|
|
286
|
-
// 如果 target
|
|
291
|
+
// 如果 target 为空,返回默认�?"schedule",避�?Gateway �?"requires target"
|
|
287
292
|
const normalized = (target || "schedule").replace(/^xiaoyou:/i, "");
|
|
288
293
|
return { ok: true, to: normalized || "schedule" };
|
|
289
294
|
},
|
|
@@ -297,7 +302,7 @@ export const xiayouPlugin = {
|
|
|
297
302
|
hint: "conversationId (e.g. conv-125)",
|
|
298
303
|
},
|
|
299
304
|
|
|
300
|
-
// ── Outbound
|
|
305
|
+
// ── Outbound 出站(符�?ChannelPluginOutbound 接口)──────
|
|
301
306
|
outbound: {
|
|
302
307
|
deliveryMode: "direct" as const,
|
|
303
308
|
textChunkLimit: 2000,
|
|
@@ -315,9 +320,8 @@ export const xiayouPlugin = {
|
|
|
315
320
|
type: "reply" as const,
|
|
316
321
|
conversationId: targetTo,
|
|
317
322
|
messageId: `xiaoyou-${Date.now()}`,
|
|
318
|
-
|
|
323
|
+
sn: replyToId,
|
|
319
324
|
senderId: to,
|
|
320
|
-
...(replyToId ? {} : { source: "scheduled_task" as const }),
|
|
321
325
|
text: text,
|
|
322
326
|
timestamp: Date.now(),
|
|
323
327
|
};
|
|
@@ -338,9 +342,8 @@ export const xiayouPlugin = {
|
|
|
338
342
|
type: "reply" as const,
|
|
339
343
|
conversationId: targetTo,
|
|
340
344
|
messageId: `xiaoyou-${Date.now()}`,
|
|
341
|
-
|
|
345
|
+
sn: replyToId,
|
|
342
346
|
senderId: to,
|
|
343
|
-
...(replyToId ? {} : { source: "scheduled_task" as const }),
|
|
344
347
|
text: text ?? "",
|
|
345
348
|
mediaUrls: mediaUrl ? [mediaUrl] : [],
|
|
346
349
|
timestamp: Date.now(),
|
|
@@ -352,7 +355,7 @@ export const xiayouPlugin = {
|
|
|
352
355
|
},
|
|
353
356
|
},
|
|
354
357
|
|
|
355
|
-
// ── Status
|
|
358
|
+
// ── Status 状态检�?────────────────────────────────
|
|
356
359
|
status: {
|
|
357
360
|
describe: async ({ account }: any) => {
|
|
358
361
|
const issues: Array<{ severity: string; message: string }> = [];
|
package/src/types.ts
CHANGED
|
@@ -32,6 +32,9 @@ export type InboundMessage = {
|
|
|
32
32
|
conversationId: string;
|
|
33
33
|
senderId: string;
|
|
34
34
|
senderName?: string;
|
|
35
|
+
/** 流水号,用于请求-回复关联 */
|
|
36
|
+
sn?: string;
|
|
37
|
+
/** @deprecated 使用 sn 代替 */
|
|
35
38
|
messageId?: string;
|
|
36
39
|
text?: string;
|
|
37
40
|
attachments?: Attachment[];
|
|
@@ -59,12 +62,17 @@ export type OutboundReply = {
|
|
|
59
62
|
type: "reply";
|
|
60
63
|
conversationId: string;
|
|
61
64
|
messageId: string;
|
|
65
|
+
/** 流水号,关联入站消息的 sn */
|
|
66
|
+
sn?: string;
|
|
67
|
+
/** @deprecated 使用 sn 代替 */
|
|
62
68
|
replyToMessageId?: string;
|
|
63
69
|
senderId?: string;
|
|
64
70
|
text: string;
|
|
65
71
|
mediaUrls?: string[];
|
|
66
72
|
agentId?: string;
|
|
67
73
|
timestamp: number;
|
|
74
|
+
/** 业务元数据,透传企业服务端发来的自定义字段 */
|
|
75
|
+
metadata?: Record<string, unknown>;
|
|
68
76
|
/** 消息来源标识:scheduled_task=定时任务推送,不传=正常对话回复 */
|
|
69
77
|
source?: "scheduled_task";
|
|
70
78
|
/** 流式标记:chunk=增量片段, end=流结束, 不传=一次性完整回复 */
|