openclaw-xiaoyou 1.3.0 → 1.3.1
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 +19 -16
- package/src/enterprise-client.ts +1 -23
- package/src/types.ts +12 -29
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -185,8 +185,10 @@ export const xiayouPlugin = {
|
|
|
185
185
|
// OpenClaw 2026.3.x 的 block streaming 机制:
|
|
186
186
|
// capabilities.blockStreaming = true 告诉 Gateway 按 block 逐个调用 deliver,
|
|
187
187
|
// 而非等全部生成完毕。每个 block(段落/代码块等)生成完成后立即 deliver。
|
|
188
|
-
//
|
|
189
|
-
//
|
|
188
|
+
// 统一使用 type="reply",通过 streamStatus 字段区分:
|
|
189
|
+
// streamStatus="chunk" — 增量片段
|
|
190
|
+
// streamStatus="end" — 流结束(text 为完整文本)
|
|
191
|
+
// 无 streamStatus — 一次性完整回复(向后兼容)
|
|
190
192
|
//
|
|
191
193
|
const replyMessageId = `xiaoyou-${Date.now()}`;
|
|
192
194
|
let chunkSeq = 0;
|
|
@@ -205,13 +207,13 @@ export const xiayouPlugin = {
|
|
|
205
207
|
fullText += (fullText ? "\n" : "") + textToSend;
|
|
206
208
|
|
|
207
209
|
if (_client && _client.isConnected()) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
type: "reply_chunk",
|
|
210
|
+
_client.sendReply({
|
|
211
|
+
type: "reply",
|
|
211
212
|
conversationId,
|
|
212
213
|
messageId: replyMessageId,
|
|
213
214
|
replyToMessageId: inboundMessageId,
|
|
214
|
-
|
|
215
|
+
text: textToSend,
|
|
216
|
+
streamStatus: "chunk",
|
|
215
217
|
seq: chunkSeq++,
|
|
216
218
|
timestamp: Date.now(),
|
|
217
219
|
});
|
|
@@ -219,34 +221,35 @@ export const xiayouPlugin = {
|
|
|
219
221
|
}
|
|
220
222
|
},
|
|
221
223
|
onComplete: async () => {
|
|
222
|
-
// Gateway 调用 onComplete 表示所有 block 已推送完毕
|
|
223
224
|
replyEndSent = true;
|
|
224
225
|
if (_client && _client.isConnected()) {
|
|
225
|
-
_client.
|
|
226
|
-
type: "
|
|
226
|
+
_client.sendReply({
|
|
227
|
+
type: "reply",
|
|
227
228
|
conversationId,
|
|
228
229
|
messageId: replyMessageId,
|
|
229
230
|
replyToMessageId: inboundMessageId,
|
|
230
|
-
fullText,
|
|
231
|
+
text: fullText,
|
|
232
|
+
streamStatus: "end",
|
|
231
233
|
timestamp: Date.now(),
|
|
232
234
|
});
|
|
233
|
-
logger.info(`[xiaoyou]
|
|
235
|
+
logger.info(`[xiaoyou] stream end sent to ${conversationId} (${chunkSeq} chunks)${inboundMessageId ? ` (replyTo=${inboundMessageId})` : ""}`);
|
|
234
236
|
}
|
|
235
237
|
},
|
|
236
238
|
},
|
|
237
239
|
});
|
|
238
240
|
|
|
239
|
-
// dispatch resolve 后,如果 onComplete 未被调用(旧版本兼容),手动发
|
|
241
|
+
// dispatch resolve 后,如果 onComplete 未被调用(旧版本兼容),手动发 end
|
|
240
242
|
if (!replyEndSent && chunkSeq > 0 && _client && _client.isConnected()) {
|
|
241
|
-
_client.
|
|
242
|
-
type: "
|
|
243
|
+
_client.sendReply({
|
|
244
|
+
type: "reply",
|
|
243
245
|
conversationId,
|
|
244
246
|
messageId: replyMessageId,
|
|
245
247
|
replyToMessageId: inboundMessageId,
|
|
246
|
-
fullText,
|
|
248
|
+
text: fullText,
|
|
249
|
+
streamStatus: "end",
|
|
247
250
|
timestamp: Date.now(),
|
|
248
251
|
});
|
|
249
|
-
logger.info(`[xiaoyou]
|
|
252
|
+
logger.info(`[xiaoyou] stream end (fallback) sent to ${conversationId} (${chunkSeq} chunks)${inboundMessageId ? ` (replyTo=${inboundMessageId})` : ""}`);
|
|
250
253
|
} else if (chunkSeq === 0) {
|
|
251
254
|
logger.warn(`[xiaoyou] no reply generated for ${conversationId}`);
|
|
252
255
|
}
|
package/src/enterprise-client.ts
CHANGED
|
@@ -11,8 +11,6 @@ import type {
|
|
|
11
11
|
Frame,
|
|
12
12
|
InboundMessage,
|
|
13
13
|
OutboundReply,
|
|
14
|
-
OutboundReplyChunk,
|
|
15
|
-
OutboundReplyEnd,
|
|
16
14
|
PongFrame,
|
|
17
15
|
} from "./types.js";
|
|
18
16
|
|
|
@@ -33,8 +31,6 @@ export type EnterpriseClient = {
|
|
|
33
31
|
connect: () => void;
|
|
34
32
|
disconnect: () => void;
|
|
35
33
|
sendReply: (reply: OutboundReply) => boolean;
|
|
36
|
-
sendChunk: (chunk: OutboundReplyChunk) => boolean;
|
|
37
|
-
sendReplyEnd: (end: OutboundReplyEnd) => boolean;
|
|
38
34
|
isConnected: () => boolean;
|
|
39
35
|
};
|
|
40
36
|
|
|
@@ -186,27 +182,9 @@ export function createEnterpriseClient(opts: EnterpriseClientOptions): Enterpris
|
|
|
186
182
|
return true;
|
|
187
183
|
}
|
|
188
184
|
|
|
189
|
-
function sendChunk(chunk: OutboundReplyChunk): boolean {
|
|
190
|
-
if (!ws || ws.readyState !== WebSocket.OPEN || !authenticated) {
|
|
191
|
-
logger.warn("[xiaoyou] cannot send chunk: not connected");
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
ws.send(JSON.stringify(chunk));
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function sendReplyEnd(end: OutboundReplyEnd): boolean {
|
|
199
|
-
if (!ws || ws.readyState !== WebSocket.OPEN || !authenticated) {
|
|
200
|
-
logger.warn("[xiaoyou] cannot send reply_end: not connected");
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
ws.send(JSON.stringify(end));
|
|
204
|
-
return true;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
185
|
function isConnected(): boolean {
|
|
208
186
|
return ws !== null && ws.readyState === WebSocket.OPEN && authenticated;
|
|
209
187
|
}
|
|
210
188
|
|
|
211
|
-
return { connect, disconnect, sendReply,
|
|
189
|
+
return { connect, disconnect, sendReply, isConnected };
|
|
212
190
|
}
|
package/src/types.ts
CHANGED
|
@@ -47,6 +47,14 @@ export type Attachment = {
|
|
|
47
47
|
|
|
48
48
|
// ─── 出站回复(插件 → 企业服务)─────────────────────────
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* 流式状态标记:
|
|
52
|
+
* - "chunk" — 流式片段,text 为增量内容
|
|
53
|
+
* - "end" — 流结束,text 为完整文本
|
|
54
|
+
* - undefined — 非流式,一次性完整回复(向后兼容)
|
|
55
|
+
*/
|
|
56
|
+
export type StreamStatus = "chunk" | "end";
|
|
57
|
+
|
|
50
58
|
export type OutboundReply = {
|
|
51
59
|
type: "reply";
|
|
52
60
|
conversationId: string;
|
|
@@ -56,33 +64,10 @@ export type OutboundReply = {
|
|
|
56
64
|
mediaUrls?: string[];
|
|
57
65
|
agentId?: string;
|
|
58
66
|
timestamp: number;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
type: "reply_chunk";
|
|
64
|
-
conversationId: string;
|
|
65
|
-
messageId: string;
|
|
66
|
-
replyToMessageId?: string;
|
|
67
|
-
/** 本次增量文本 */
|
|
68
|
-
delta: string;
|
|
69
|
-
/** 该 chunk 在整条回复中的序号(从 0 开始) */
|
|
70
|
-
seq: number;
|
|
71
|
-
agentId?: string;
|
|
72
|
-
timestamp: number;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
/** 流式回复结束标记(插件 → 企业服务) */
|
|
76
|
-
export type OutboundReplyEnd = {
|
|
77
|
-
type: "reply_end";
|
|
78
|
-
conversationId: string;
|
|
79
|
-
messageId: string;
|
|
80
|
-
replyToMessageId?: string;
|
|
81
|
-
/** 完整文本(可选,方便企业侧校验拼接结果) */
|
|
82
|
-
fullText?: string;
|
|
83
|
-
mediaUrls?: string[];
|
|
84
|
-
agentId?: string;
|
|
85
|
-
timestamp: number;
|
|
67
|
+
/** 流式标记:chunk=增量片段, end=流结束, 不传=一次性完整回复 */
|
|
68
|
+
streamStatus?: StreamStatus;
|
|
69
|
+
/** 流式片段序号(仅 streamStatus="chunk" 时有值,从 0 开始) */
|
|
70
|
+
seq?: number;
|
|
86
71
|
};
|
|
87
72
|
|
|
88
73
|
// ─── 控制帧 ─────────────────────────────────────────────
|
|
@@ -108,8 +93,6 @@ export type PongFrame = { type: "pong"; seq: number; ts: number };
|
|
|
108
93
|
export type Frame =
|
|
109
94
|
| InboundMessage
|
|
110
95
|
| OutboundReply
|
|
111
|
-
| OutboundReplyChunk
|
|
112
|
-
| OutboundReplyEnd
|
|
113
96
|
| AuthFrame
|
|
114
97
|
| AuthResultFrame
|
|
115
98
|
| PingFrame
|