@eyeclaw/eyeclaw 2.3.11 → 2.3.12

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": "@eyeclaw/eyeclaw",
3
- "version": "2.3.11",
3
+ "version": "2.3.12",
4
4
  "description": "EyeClaw plugin for OpenClaw - HTTP SSE streaming + WebSocket client",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -27,6 +27,7 @@ export class EyeClawWebSocketClient {
27
27
  private subscribed = false
28
28
  private pingInterval: any = null
29
29
  private chunkSequence = 0 // 每个会话的 chunk 序号
30
+ private accumulatedContent = '' // 累积完整内容用于兜底
30
31
 
31
32
  constructor(api: OpenClawPluginApi, config: EyeClawConfig, getState: () => any) {
32
33
  this.api = api
@@ -244,6 +245,9 @@ export class EyeClawWebSocketClient {
244
245
  if (done) {
245
246
  // 流结束,通知 Rails
246
247
  this.sendMessage('stream_end', { session_id: sessionId })
248
+
249
+ // 发送 stream_summary 用于兜底机制
250
+ this.sendStreamSummary(sessionId)
247
251
  break
248
252
  }
249
253
 
@@ -322,6 +326,10 @@ export class EyeClawWebSocketClient {
322
326
  private sendChunk(content: string, sessionId?: string) {
323
327
  const timestamp = new Date().toISOString();
324
328
  const sequence = this.chunkSequence++;
329
+
330
+ // 累积完整内容用于兜底
331
+ this.accumulatedContent += content;
332
+
325
333
  this.api.logger.info(`[EyeClaw] [${timestamp}] Sending chunk #${sequence} to Rails: "${content}"`);
326
334
  this.sendMessage('stream_chunk', {
327
335
  content,
@@ -329,6 +337,41 @@ export class EyeClawWebSocketClient {
329
337
  sequence, // 添加序号
330
338
  })
331
339
  }
340
+
341
+ /**
342
+ * 发送 stream_summary 用于兜底机制
343
+ * 告诉 Rails 完整内容是什么,以便检测丢包并补偿
344
+ */
345
+ private sendStreamSummary(sessionId?: string) {
346
+ // 计算内容 hash
347
+ const contentHash = this.hashCode(this.accumulatedContent);
348
+
349
+ this.api.logger.info(`[EyeClaw] Sending stream_summary: chunks=${this.chunkSequence}, content_len=${this.accumulatedContent.length}, hash=${contentHash}`);
350
+
351
+ this.sendMessage('stream_summary', {
352
+ session_id: sessionId,
353
+ total_content: this.accumulatedContent,
354
+ total_chunks: this.chunkSequence,
355
+ content_hash: contentHash,
356
+ })
357
+
358
+ // 重置累积内容(为下一个会话做准备)
359
+ this.accumulatedContent = '';
360
+ this.chunkSequence = 0;
361
+ }
362
+
363
+ /**
364
+ * 简单 hash 函数
365
+ */
366
+ private hashCode(str: string): string {
367
+ let hash = 0;
368
+ for (let i = 0; i < str.length; i++) {
369
+ const char = str.charCodeAt(i);
370
+ hash = ((hash << 5) - hash) + char;
371
+ hash = hash & hash; // Convert to 32bit integer
372
+ }
373
+ return hash.toString(16);
374
+ }
332
375
 
333
376
  /**
334
377
  * 发送消息到 Rails(带 channel identifier)