@eyeclaw/eyeclaw 2.3.7 → 2.3.10

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.7",
3
+ "version": "2.3.10",
4
4
  "description": "EyeClaw plugin for OpenClaw - HTTP SSE streaming + WebSocket client",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -74,7 +74,7 @@ export function createHttpHandler(api: OpenClawPluginApi, getConfig: () => EyeCl
74
74
 
75
75
  try {
76
76
  const body = await readJsonBody(req)
77
- const { message, session_id, stream_id } = body
77
+ const { message, session_id, stream_id, openclaw_session_id } = body
78
78
 
79
79
  if (!message) {
80
80
  res.statusCode = 400
@@ -84,6 +84,7 @@ export function createHttpHandler(api: OpenClawPluginApi, getConfig: () => EyeCl
84
84
  }
85
85
 
86
86
  logger.info(`[EyeClaw] Chat: ${message.substring(0, 50)}...`)
87
+ logger.info(`[EyeClaw] OpenClaw Session: ${openclaw_session_id || 'default'}`)
87
88
 
88
89
  // SSE 响应头
89
90
  res.setHeader('Content-Type', 'text/event-stream')
@@ -94,7 +95,8 @@ export function createHttpHandler(api: OpenClawPluginApi, getConfig: () => EyeCl
94
95
  // 获取 Gateway 配置
95
96
  const gatewayPort = api.config?.gateway?.port ?? 18789
96
97
  const gatewayToken = api.config?.gateway?.auth?.token
97
- const sessionKey = session_id ? `eyeclaw:${session_id}` : 'eyeclaw:default'
98
+ // 使用 openclaw_session_id 作为 OpenClaw 的会话标识,如果未提供则使用默认值
99
+ const sessionKey = openclaw_session_id || 'eyeclaw:default'
98
100
 
99
101
  // 调用 OpenClaw
100
102
  const openclawUrl = `http://127.0.0.1:${gatewayPort}/v1/chat/completions`
@@ -152,6 +154,8 @@ export function createHttpHandler(api: OpenClawPluginApi, getConfig: () => EyeCl
152
154
  const chunk = JSON.parse(data)
153
155
  const content = chunk.choices?.[0]?.delta?.content
154
156
  if (content) {
157
+ const timestamp = new Date().toISOString();
158
+ logger.info(`[EyeClaw] [${timestamp}] SSE chunk: "${content}"`);
155
159
  res.write(formatSSE('stream_chunk', { stream_id: currentStreamId, content }))
156
160
  }
157
161
  } catch { /* ignore */ }
@@ -26,6 +26,7 @@ export class EyeClawWebSocketClient {
26
26
  private reconnectDelay = 3000
27
27
  private subscribed = false
28
28
  private pingInterval: any = null
29
+ private chunkSequence = 0 // 每个会话的 chunk 序号
29
30
 
30
31
  constructor(api: OpenClawPluginApi, config: EyeClawConfig, getState: () => any) {
31
32
  this.api = api
@@ -175,21 +176,29 @@ export class EyeClawWebSocketClient {
175
176
  return
176
177
  }
177
178
 
178
- // 从 metadata 提取 session_id
179
+ // 从 metadata 提取 session_id (用于 Rails 内部追踪)
179
180
  const sessionId = metadata?.session_id
180
181
 
182
+ // 从 metadata 提取 openclaw_session_id (用于 OpenClaw 对话上下文)
183
+ // 如果未指定,使用 bot_id 作为默认值,这样同一个 Bot 的所有请求共享上下文
184
+ const openclawSessionId = metadata?.openclaw_session_id || `bot_${this.config.botId}`
185
+
181
186
  this.api.logger.info(`[EyeClaw] Processing: ${userMessage.substring(0, 50)}...`)
182
- this.api.logger.info(`[EyeClaw] Session ID: ${sessionId}`)
187
+ this.api.logger.info(`[EyeClaw] Rails Session ID: ${sessionId}`)
188
+ this.api.logger.info(`[EyeClaw] OpenClaw Session ID: ${openclawSessionId}`)
183
189
 
184
190
  // 通过 OpenClaw API 处理消息,获取流式响应
185
- await this.processWithOpenClaw(userMessage, sessionId)
191
+ await this.processWithOpenClaw(userMessage, sessionId, openclawSessionId)
186
192
  }
187
193
 
188
194
  /**
189
195
  * 使用 OpenClaw API 处理消息(流式)
190
196
  * 调用自己的 HTTP 端点 /eyeclaw/chat
191
197
  */
192
- private async processWithOpenClaw(message: string, sessionId?: string) {
198
+ private async processWithOpenClaw(message: string, sessionId?: string, openclawSessionId?: string) {
199
+ // 重置 chunk 序号(每个新会话)
200
+ this.chunkSequence = 0
201
+
193
202
  const state = this.getState()
194
203
  const gatewayPort = state.gatewayPort
195
204
  const eyeclawUrl = `http://127.0.0.1:${gatewayPort}/eyeclaw/chat`
@@ -197,6 +206,7 @@ export class EyeClawWebSocketClient {
197
206
  const requestBody = {
198
207
  message,
199
208
  session_id: sessionId,
209
+ openclaw_session_id: openclawSessionId,
200
210
  }
201
211
 
202
212
  const headers: Record<string, string> = {
@@ -310,9 +320,13 @@ export class EyeClawWebSocketClient {
310
320
  * 发送流式内容块到 Rails
311
321
  */
312
322
  private sendChunk(content: string, sessionId?: string) {
323
+ const timestamp = new Date().toISOString();
324
+ const sequence = this.chunkSequence++;
325
+ this.api.logger.info(`[EyeClaw] [${timestamp}] Sending chunk #${sequence} to Rails: "${content}"`);
313
326
  this.sendMessage('stream_chunk', {
314
327
  content,
315
328
  session_id: sessionId,
329
+ sequence, // 添加序号
316
330
  })
317
331
  }
318
332