claw-subagent-service 0.0.64 → 0.0.66

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": "claw-subagent-service",
3
- "version": "0.0.64",
3
+ "version": "0.0.66",
4
4
  "description": "虾说智能助手",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -13,11 +13,11 @@ class HeartbeatManager {
13
13
 
14
14
  start(getMacAddress, getOpenClawStatus) {
15
15
  const interval = (this.config.heartbeatInterval || 20) * 1000;
16
- this.log?.info(`[HeartbeatManager] 启动心跳定时器,间隔: ${interval}ms`);
17
-
16
+ // this.log?.info(`[HeartbeatManager] 启动心跳定时器,间隔: ${interval}ms`);
17
+
18
18
  this.timer = setInterval(async () => {
19
19
  if (!this.rongcloudClient?.isConnected) return;
20
-
20
+
21
21
  try {
22
22
  const mac = getMacAddress();
23
23
  const status = await getOpenClawStatus(this.config.openclawPort || 18789);
@@ -31,7 +31,7 @@ class HeartbeatManager {
31
31
  }
32
32
  );
33
33
  if (sent) {
34
- this.log?.info('[HeartbeatManager] 心跳已发送');
34
+ // this.log?.info('[HeartbeatManager] 心跳已发送');
35
35
  } else {
36
36
  this.log?.warn('[HeartbeatManager] 心跳发送失败');
37
37
  }
@@ -61,16 +61,16 @@ class DashboardReporter {
61
61
 
62
62
  start(getMacAddress) {
63
63
  const interval = 30000; // 30秒
64
- this.log?.info(`[DashboardReporter] 启动仪表盘上报定时器,间隔: ${interval}ms`);
65
-
64
+ // this.log?.info(`[DashboardReporter] 启动仪表盘上报定时器,间隔: ${interval}ms`);
65
+
66
66
  this.timer = setInterval(async () => {
67
67
  if (!this.rongcloudClient?.isConnected) return;
68
-
68
+
69
69
  try {
70
70
  const data = await collectDashboardData();
71
71
  const timestamp = data.diagnostics?.generatedAt || new Date().toISOString();
72
72
  const mac = getMacAddress();
73
-
73
+
74
74
  // 拆分为 6 条消息发送
75
75
  await this.sendChunk(RongyunMessageTypeEnum.DASHBOARD_SESSIONS, {
76
76
  mac_address: mac,
@@ -78,21 +78,21 @@ class DashboardReporter {
78
78
  sessionStatuses: data.sessionStatuses,
79
79
  timestamp,
80
80
  }, 1000);
81
-
81
+
82
82
  await this.sendChunk(RongyunMessageTypeEnum.DASHBOARD_JOBS, {
83
83
  mac_address: mac,
84
84
  cronJobs: data.cronJobs,
85
85
  approvals: data.approvals,
86
86
  timestamp,
87
87
  }, 1000);
88
-
88
+
89
89
  await this.sendChunk(RongyunMessageTypeEnum.DASHBOARD_PROJECTS, {
90
90
  mac_address: mac,
91
91
  projects: data.projects,
92
92
  tasks: data.tasks,
93
93
  timestamp,
94
94
  }, 1000);
95
-
95
+
96
96
  await this.sendChunk(RongyunMessageTypeEnum.DASHBOARD_SUMMARIES, {
97
97
  mac_address: mac,
98
98
  projectSummaries: data.projectSummaries,
@@ -101,20 +101,20 @@ class DashboardReporter {
101
101
  diagnostics: data.diagnostics,
102
102
  timestamp,
103
103
  }, 1000);
104
-
104
+
105
105
  await this.sendChunk(RongyunMessageTypeEnum.DASHBOARD_SESSIONS_CONTEXTS, {
106
106
  mac_address: mac,
107
107
  sessionsContexts: (data.sessionsContexts || []).slice(0, 50),
108
108
  timestamp,
109
109
  }, 1000);
110
-
110
+
111
111
  await this.sendChunk(RongyunMessageTypeEnum.DASHBOARD_USAGE_EVENTS, {
112
112
  mac_address: mac,
113
113
  usageEvents: (data.usageEvents || []).slice(-100),
114
114
  timestamp,
115
115
  }, 0);
116
-
117
- this.log?.info('[DashboardReporter] 所有数据块发送完成');
116
+
117
+ // this.log?.info('[DashboardReporter] 所有数据块发送完成');
118
118
  } catch (err) {
119
119
  this.log?.error(`[DashboardReporter] 上报异常: ${err.message}`);
120
120
  }
@@ -125,14 +125,14 @@ class DashboardReporter {
125
125
  try {
126
126
  const sent = await this.messageSender.sendProtocolMessage(msgType, data);
127
127
  if (sent) {
128
- this.log?.info(`[DashboardReporter] ${msgType} 发送成功`);
128
+ // this.log?.info(`[DashboardReporter] ${msgType} 发送成功`);
129
129
  } else {
130
130
  this.log?.warn(`[DashboardReporter] ${msgType} 发送失败`);
131
131
  }
132
132
  } catch (err) {
133
133
  this.log?.error(`[DashboardReporter] ${msgType} 发送异常: ${err.message}`);
134
134
  }
135
-
135
+
136
136
  if (delayMs > 0) {
137
137
  await new Promise(resolve => setTimeout(resolve, delayMs));
138
138
  }
@@ -22,7 +22,7 @@ class RongyunMessageSender {
22
22
  buildMessage(msgType, content, requestId) {
23
23
  const mac = getMacAddress();
24
24
  const secret = generateSecret(mac, this.config.secretKey || 'secret_key');
25
-
25
+
26
26
  return {
27
27
  msg_type: msgType,
28
28
  source_im_id: this.config.accountId || '',
@@ -46,8 +46,8 @@ class RongyunMessageSender {
46
46
 
47
47
  try {
48
48
  const messagePayload = this.buildMessage(msgType, content, requestId);
49
-
50
- this.log?.info(`[RongyunMessageSender] 发送协议消息 -> ${this.serverImId}, type=${msgType}`);
49
+
50
+ // this.log?.info(`[RongyunMessageSender] 发送协议消息 -> ${this.serverImId}, type=${msgType}`);
51
51
 
52
52
  const result = await this.rongcloudClient.sendMessage(
53
53
  this.serverImId,
@@ -56,7 +56,7 @@ class RongyunMessageSender {
56
56
  );
57
57
 
58
58
  if (result) {
59
- this.log?.info(`[RongyunMessageSender] ${msgType} 发送成功`);
59
+ // this.log?.info(`[RongyunMessageSender] ${msgType} 发送成功`);
60
60
  } else {
61
61
  this.log?.warn(`[RongyunMessageSender] ${msgType} 发送失败`);
62
62
  }
@@ -208,7 +208,7 @@ class MessageHandler {
208
208
  async handleNormalMessageStream(msg) {
209
209
  const targetId = this.getReplyTarget(msg);
210
210
  const streamId = `${Date.now()}-${Math.random().toString(36).substring(2, 10)}`;
211
- let isFirstChunk = true;
211
+ let seq = 0;
212
212
  let buffer = '';
213
213
  const fromUserId = this.config.accountId;
214
214
  const conversationType = msg.conversationType;
@@ -226,26 +226,24 @@ class MessageHandler {
226
226
  msg.senderUserId,
227
227
  async (delta) => {
228
228
  buffer += delta;
229
- // 策略:每 30 个字符或遇到句末标点发送一次片段
230
- if (buffer.length >= 30 || /[。!?.!?\n]$/.test(delta)) {
231
- await this._sendStreamChunk(fromUserId, targetId, conversationType, buffer, streamId, isFirstChunk, false);
232
- isFirstChunk = false;
233
- hasSentChunk = true;
234
- buffer = '';
235
- }
229
+ seq += 1;
230
+ this.log?.info(`[MessageHandler] onDelta: seq=${seq}, delta_len=${delta.length}, buffer_len=${buffer.length}`);
231
+ await this._sendStreamChunk(fromUserId, targetId, conversationType, buffer, streamId, seq === 1, false, seq);
232
+ hasSentChunk = true;
236
233
  },
237
234
  async (fullText) => {
238
- // 发送剩余缓冲区和结束标记
235
+ this.log?.info(`[MessageHandler] onDone 触发, fullText.length=${fullText.length}, buffer.length=${buffer.length}, hasSentChunk=${hasSentChunk}`);
239
236
  if (buffer.trim()) {
240
- await this._sendStreamChunk(fromUserId, targetId, conversationType, buffer, streamId, isFirstChunk, true);
237
+ seq += 1;
238
+ await this._sendStreamChunk(fromUserId, targetId, conversationType, buffer, streamId, seq === 1, true, seq);
241
239
  hasSentChunk = true;
242
- } else if (!isFirstChunk) {
240
+ } else if (hasSentChunk) {
243
241
  // 已经发送过内容,单独发送结束标记
244
- await this._sendStreamChunk(fromUserId, targetId, conversationType, '', streamId, false, true);
245
- hasSentChunk = true;
242
+ seq += 1;
243
+ await this._sendStreamChunk(fromUserId, targetId, conversationType, buffer, streamId, false, true, seq);
246
244
  } else {
247
245
  // 完全没有收到内容,发送错误提示
248
- await this._sendStreamChunk(fromUserId, targetId, conversationType, '抱歉,AI 暂时没有回复内容。', streamId, true, true);
246
+ await this._sendStreamChunk(fromUserId, targetId, conversationType, '抱歉,AI 暂时没有回复内容。', streamId, true, true, 1);
249
247
  hasSentChunk = true;
250
248
  }
251
249
 
@@ -258,7 +256,7 @@ class MessageHandler {
258
256
  );
259
257
  } catch (err) {
260
258
  this.log?.error(`[MessageHandler] 流式处理错误: ${err.message}`);
261
- await this._sendStreamChunk(fromUserId, targetId, conversationType, '抱歉,AI 响应出现错误,请稍后重试。', streamId, isFirstChunk, true);
259
+ await this._sendStreamChunk(fromUserId, targetId, conversationType, '抱歉,AI 响应出现错误,请稍后重试。', streamId, true, true, 1);
262
260
  throw err;
263
261
  }
264
262
  }
@@ -289,10 +287,14 @@ class MessageHandler {
289
287
  /**
290
288
  * 发送流式消息片段(通过 Python 后端代理)
291
289
  */
292
- async _sendStreamChunk(fromUserId, targetId, conversationType, content, streamId, isFirstChunk, isLastChunk) {
293
- if (!this.isStreamingEnabled) return;
290
+ async _sendStreamChunk(fromUserId, targetId, conversationType, content, streamId, isFirstChunk, isLastChunk, seq = 1) {
291
+ this.log?.info(`[MessageHandler] _sendStreamChunk ENTRY: target=${targetId}, streamId=${streamId}, seq=${seq}, first=${isFirstChunk}, last=${isLastChunk}, content_len=${content?.length || 0}`);
292
+ if (!this.isStreamingEnabled) {
293
+ this.log?.warn('[MessageHandler] _sendStreamChunk skipped: isStreamingEnabled=false');
294
+ return;
295
+ }
294
296
  try {
295
- await axios.post(
297
+ const resp = await axios.post(
296
298
  `${this.config.apiBaseUrl}/im/api/proxy/stream/publish`,
297
299
  {
298
300
  fromUserId,
@@ -301,14 +303,16 @@ class MessageHandler {
301
303
  streamId,
302
304
  isFirstChunk,
303
305
  isLastChunk,
304
- conversationType
306
+ conversationType,
307
+ seq
305
308
  },
306
309
  { timeout: 10000 }
307
310
  );
311
+ this.log?.info(`[MessageHandler] _sendStreamChunk 成功: status=${resp.status}, seq=${seq}`);
308
312
  } catch (err) {
309
313
  const url = `${this.config.apiBaseUrl}/im/api/proxy/stream/publish`;
310
314
  const status = err.response?.status;
311
- this.log?.warn(`[MessageHandler] 发送流式消息失败: ${err.message}, url=${url}, status=${status || 'N/A'}`);
315
+ this.log?.warn(`[MessageHandler] 发送流式消息失败: ${err.message}, url=${url}, status=${status || 'N/A'}, seq=${seq}`);
312
316
  }
313
317
  }
314
318
 
@@ -438,8 +438,13 @@ class OpenClawClient {
438
438
 
439
439
  try {
440
440
  const data = JSON.parse(dataStr);
441
- const delta = data.choices?.[0]?.delta?.content;
442
- if (delta) {
441
+ // 尝试多个可能的内容字段,兼容不同版本 OpenClaw
442
+ const delta = data.choices?.[0]?.delta?.content
443
+ ?? data.choices?.[0]?.message?.content
444
+ ?? data.choices?.[0]?.text
445
+ ?? data.content
446
+ ?? data.delta;
447
+ if (typeof delta === 'string' && delta) {
443
448
  fullText += delta;
444
449
  try {
445
450
  await onDelta?.(delta);
@@ -447,6 +452,9 @@ class OpenClawClient {
447
452
  reject(err);
448
453
  return;
449
454
  }
455
+ } else {
456
+ // 调试:打印未识别的 SSE 数据结构
457
+ this.log?.info(`[OpenClawClient] SSE 原始数据(无delta): ${JSON.stringify(data).substring(0, 200)}`);
450
458
  }
451
459
  } catch {
452
460
  // 忽略无法解析的 JSON 行
@@ -226,7 +226,7 @@ class RongCloudClient {
226
226
  ? (RongIMLib.ConversationType?.GROUP || ConversationType.GROUP)
227
227
  : (RongIMLib.ConversationType?.PRIVATE || ConversationType.PRIVATE);
228
228
 
229
- this.log?.info(`[RongCloudClient] 发送消息 -> ${targetId} (Type: ${convType}): ${content.substring(0, 50)}`);
229
+ // this.log?.info(`[RongCloudClient] 发送消息 -> ${targetId} (Type: ${convType}): ${content.substring(0, 50)}`);
230
230
 
231
231
  const result = await RongIMLib.sendTextMessage(
232
232
  { conversationType: convType, targetId },
@@ -237,7 +237,7 @@ class RongCloudClient {
237
237
 
238
238
  if (result.code === 0 || result.code === 200) {
239
239
  const sentUId = result.data?.messageUId;
240
- this.log?.info(`[RongCloudClient] 发送成功, messageUId: ${sentUId}`);
240
+ // this.log?.info(`[RongCloudClient] 发送成功, messageUId: ${sentUId}`);
241
241
  // 将发送成功的 messageUId 加入短期缓存,用于过滤 SDK 回传的自己消息
242
242
  if (sentUId) {
243
243
  this.sentMessageUIds.add(sentUId);