openclaw-xiaoyou 1.3.1 → 1.3.2

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/channel.ts +27 -50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-xiaoyou",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
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
@@ -180,20 +180,14 @@ export const xiayouPlugin = {
180
180
  Timestamp: Date.now(),
181
181
  });
182
182
 
183
- // 4. 分发并获取回复(流式 — block 级)
183
+ // 4. 分发消息给 Agent
184
184
  //
185
- // OpenClaw 2026.3.x 的 block streaming 机制:
186
- // capabilities.blockStreaming = true 告诉 Gateway block 逐个调用 deliver,
187
- // 而非等全部生成完毕。每个 block(段落/代码块等)生成完成后立即 deliver。
188
- // 统一使用 type="reply",通过 streamStatus 字段区分:
189
- // streamStatus="chunk" — 增量片段
190
- // streamStatus="end" — 流结束(text 为完整文本)
191
- // 无 streamStatus — 一次性完整回复(向后兼容)
185
+ // Block streaming 机制说明:
186
+ // blockStreaming=true Gateway 配置了 blockStreamingDefault="on" 时,
187
+ // Gateway 会在 LLM 生成过程中通过 outbound.send 逐块推送消息,
188
+ // 不经过 deliver 回调。deliver 仅在非流式场景下被调用(作为 fallback)。
192
189
  //
193
190
  const replyMessageId = `xiaoyou-${Date.now()}`;
194
- let chunkSeq = 0;
195
- let fullText = "";
196
- let replyEndSent = false;
197
191
 
198
192
  await rt.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
199
193
  ctx: inboundCtx,
@@ -201,11 +195,10 @@ export const xiayouPlugin = {
201
195
  dispatcherOptions: {
202
196
  responsePrefix: "",
203
197
  deliver: async (payload: any) => {
198
+ // Fallback: 当 block streaming 未生效时,deliver 被调用
204
199
  const textToSend = payload.markdown || payload.text;
205
200
  if (!textToSend) return;
206
201
 
207
- fullText += (fullText ? "\n" : "") + textToSend;
208
-
209
202
  if (_client && _client.isConnected()) {
210
203
  _client.sendReply({
211
204
  type: "reply",
@@ -213,46 +206,13 @@ export const xiayouPlugin = {
213
206
  messageId: replyMessageId,
214
207
  replyToMessageId: inboundMessageId,
215
208
  text: textToSend,
216
- streamStatus: "chunk",
217
- seq: chunkSeq++,
218
209
  timestamp: Date.now(),
219
210
  });
220
- logger.info(`[xiaoyou] chunk #${chunkSeq} sent to ${conversationId}`);
221
- }
222
- },
223
- onComplete: async () => {
224
- replyEndSent = true;
225
- if (_client && _client.isConnected()) {
226
- _client.sendReply({
227
- type: "reply",
228
- conversationId,
229
- messageId: replyMessageId,
230
- replyToMessageId: inboundMessageId,
231
- text: fullText,
232
- streamStatus: "end",
233
- timestamp: Date.now(),
234
- });
235
- logger.info(`[xiaoyou] stream end sent to ${conversationId} (${chunkSeq} chunks)${inboundMessageId ? ` (replyTo=${inboundMessageId})` : ""}`);
211
+ logger.info(`[xiaoyou] reply sent to ${conversationId}${inboundMessageId ? ` (replyTo=${inboundMessageId})` : ""}`);
236
212
  }
237
213
  },
238
214
  },
239
215
  });
240
-
241
- // dispatch resolve 后,如果 onComplete 未被调用(旧版本兼容),手动发 end
242
- if (!replyEndSent && chunkSeq > 0 && _client && _client.isConnected()) {
243
- _client.sendReply({
244
- type: "reply",
245
- conversationId,
246
- messageId: replyMessageId,
247
- replyToMessageId: inboundMessageId,
248
- text: fullText,
249
- streamStatus: "end",
250
- timestamp: Date.now(),
251
- });
252
- logger.info(`[xiaoyou] stream end (fallback) sent to ${conversationId} (${chunkSeq} chunks)${inboundMessageId ? ` (replyTo=${inboundMessageId})` : ""}`);
253
- } else if (chunkSeq === 0) {
254
- logger.warn(`[xiaoyou] no reply generated for ${conversationId}`);
255
- }
256
216
  },
257
217
  });
258
218
 
@@ -280,17 +240,34 @@ export const xiayouPlugin = {
280
240
 
281
241
  // ── Outbound 出站 ──────────────────────────────────
282
242
  outbound: {
283
- send: async ({ to, payload }: any) => {
243
+ send: async ({ to, payload, meta, ...rest }: any) => {
284
244
  if (!_client || !_client.isConnected()) {
285
245
  return { ok: false, error: "xiaoyou: not connected" };
286
246
  }
287
247
 
288
- const baseReply = {
248
+ // 调试日志:观察 Gateway block streaming 时传入的完整参数
249
+ const rt = getRuntime();
250
+ const logger = rt?.log || console;
251
+ logger.info(`[xiaoyou] outbound.send called: to=${to}, payload=${JSON.stringify(payload)}, meta=${JSON.stringify(meta)}, rest=${JSON.stringify(rest)}`);
252
+
253
+ // 推断 streamStatus:
254
+ // Gateway block streaming 可能通过 meta 或 payload 传递流式信息
255
+ const streamMeta = meta?.blockStream || payload?.blockStream || meta?.stream;
256
+ const isFinal = streamMeta?.final ?? meta?.final ?? payload?.final;
257
+ const seq = streamMeta?.seq ?? meta?.seq ?? payload?.seq;
258
+ const streamStatus = streamMeta
259
+ ? (isFinal ? "end" : "chunk")
260
+ : (meta?.isBlock ? "chunk" : undefined);
261
+
262
+ const baseReply: any = {
289
263
  type: "reply" as const,
290
264
  conversationId: to,
291
- messageId: `xiaoyou-${Date.now()}`,
265
+ messageId: payload.messageId || meta?.messageId || `xiaoyou-${Date.now()}`,
266
+ replyToMessageId: payload.replyToMessageId || meta?.replyToMessageId,
292
267
  agentId: payload.agentId,
293
268
  timestamp: Date.now(),
269
+ ...(streamStatus && { streamStatus }),
270
+ ...(seq !== undefined && { seq }),
294
271
  };
295
272
 
296
273
  if (payload.kind === "text") {