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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-xiaoyou",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "type": "module",
5
5
  "description": "Xiaoyou channel plugin for OpenClaw — connects enterprise services via persistent outbound WebSocket",
6
6
  "openclaw": {
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
- // 我们将每次 deliver 作为一个 reply_chunk 推送给企业服务,
189
- // dispatch promise resolve 后发送 reply_end 标记。
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
- // 每个 block 作为一个 chunk 推送
209
- _client.sendChunk({
210
- type: "reply_chunk",
210
+ _client.sendReply({
211
+ type: "reply",
211
212
  conversationId,
212
213
  messageId: replyMessageId,
213
214
  replyToMessageId: inboundMessageId,
214
- delta: textToSend,
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.sendReplyEnd({
226
- type: "reply_end",
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] streaming completed to ${conversationId} (${chunkSeq} blocks)${inboundMessageId ? ` (replyTo=${inboundMessageId})` : ""}`);
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 未被调用(旧版本兼容),手动发 reply_end
241
+ // dispatch resolve 后,如果 onComplete 未被调用(旧版本兼容),手动发 end
240
242
  if (!replyEndSent && chunkSeq > 0 && _client && _client.isConnected()) {
241
- _client.sendReplyEnd({
242
- type: "reply_end",
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] streaming completed (fallback) to ${conversationId} (${chunkSeq} blocks)${inboundMessageId ? ` (replyTo=${inboundMessageId})` : ""}`);
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
  }
@@ -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, sendChunk, sendReplyEnd, isConnected };
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
- export type OutboundReplyChunk = {
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