@ynhcj/xiaoyi-channel 0.0.97-beta → 0.0.97-next

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 (140) hide show
  1. package/dist/index.d.ts +6 -9
  2. package/dist/index.js +26 -21
  3. package/dist/provider-discovery.d.ts +2 -0
  4. package/dist/provider-discovery.js +4 -0
  5. package/dist/src/bot.js +29 -4
  6. package/dist/src/channel.js +2 -19
  7. package/dist/src/client.js +31 -22
  8. package/dist/src/cspl/call-api.js +6 -5
  9. package/dist/src/file-download.js +4 -3
  10. package/dist/src/file-upload.js +19 -18
  11. package/dist/src/formatter.d.ts +2 -0
  12. package/dist/src/formatter.js +9 -28
  13. package/dist/src/heartbeat.js +1 -1
  14. package/dist/src/login-token-handler.d.ts +8 -0
  15. package/dist/src/login-token-handler.js +60 -0
  16. package/dist/src/message-queue.d.ts +17 -0
  17. package/dist/src/message-queue.js +51 -0
  18. package/dist/src/monitor.js +70 -14
  19. package/dist/src/outbound.js +19 -18
  20. package/dist/src/provider.d.ts +2 -1
  21. package/dist/src/provider.js +245 -38
  22. package/dist/src/push.js +16 -15
  23. package/dist/src/reply-dispatcher.js +12 -3
  24. package/dist/src/runtime.d.ts +3 -11
  25. package/dist/src/runtime.js +6 -18
  26. package/dist/src/self-evolution-handler.d.ts +6 -0
  27. package/dist/src/self-evolution-handler.js +100 -7
  28. package/dist/src/self-evolution-keyword.d.ts +9 -0
  29. package/dist/src/self-evolution-keyword.js +147 -0
  30. package/dist/src/self-evolution-tool-result-nudge.d.ts +3 -0
  31. package/dist/src/self-evolution-tool-result-nudge.js +96 -0
  32. package/dist/src/skill-retriever/config.d.ts +4 -0
  33. package/dist/src/skill-retriever/config.js +23 -0
  34. package/dist/src/skill-retriever/hooks.d.ts +22 -0
  35. package/dist/src/skill-retriever/hooks.js +83 -0
  36. package/dist/src/skill-retriever/tool-search.d.ts +16 -0
  37. package/dist/src/skill-retriever/tool-search.js +173 -0
  38. package/dist/src/skill-retriever/types.d.ts +36 -0
  39. package/dist/src/skill-retriever/types.js +1 -0
  40. package/dist/src/steer-injector.js +1 -1
  41. package/dist/src/task-manager.d.ts +4 -0
  42. package/dist/src/task-manager.js +12 -1
  43. package/dist/src/tools/calendar-tool.d.ts +2 -1
  44. package/dist/src/tools/calendar-tool.js +112 -116
  45. package/dist/src/tools/call-device-tool.d.ts +2 -1
  46. package/dist/src/tools/call-device-tool.js +126 -103
  47. package/dist/src/tools/call-phone-tool.d.ts +2 -1
  48. package/dist/src/tools/call-phone-tool.js +109 -113
  49. package/dist/src/tools/create-alarm-tool.d.ts +2 -1
  50. package/dist/src/tools/create-alarm-tool.js +227 -231
  51. package/dist/src/tools/create-all-tools.d.ts +16 -0
  52. package/dist/src/tools/create-all-tools.js +50 -0
  53. package/dist/src/tools/delete-alarm-tool.d.ts +2 -1
  54. package/dist/src/tools/delete-alarm-tool.js +131 -135
  55. package/dist/src/tools/get-alarm-tool-schema.d.ts +2 -1
  56. package/dist/src/tools/get-alarm-tool-schema.js +16 -10
  57. package/dist/src/tools/get-calendar-tool-schema.d.ts +2 -1
  58. package/dist/src/tools/get-calendar-tool-schema.js +12 -8
  59. package/dist/src/tools/get-collection-tool-schema.d.ts +2 -1
  60. package/dist/src/tools/get-collection-tool-schema.js +11 -9
  61. package/dist/src/tools/get-contact-tool-schema.d.ts +2 -1
  62. package/dist/src/tools/get-contact-tool-schema.js +16 -10
  63. package/dist/src/tools/get-device-file-tool-schema.d.ts +2 -1
  64. package/dist/src/tools/get-device-file-tool-schema.js +13 -9
  65. package/dist/src/tools/get-email-tool-schema.d.ts +2 -1
  66. package/dist/src/tools/get-email-tool-schema.js +11 -8
  67. package/dist/src/tools/get-note-tool-schema.d.ts +2 -1
  68. package/dist/src/tools/get-note-tool-schema.js +14 -9
  69. package/dist/src/tools/get-photo-tool-schema.d.ts +2 -1
  70. package/dist/src/tools/get-photo-tool-schema.js +12 -9
  71. package/dist/src/tools/image-reading-tool.d.ts +2 -1
  72. package/dist/src/tools/image-reading-tool.js +86 -90
  73. package/dist/src/tools/location-tool.d.ts +2 -1
  74. package/dist/src/tools/location-tool.js +87 -91
  75. package/dist/src/tools/login-token-tool.d.ts +6 -0
  76. package/dist/src/tools/login-token-tool.js +133 -0
  77. package/dist/src/tools/modify-alarm-tool.d.ts +2 -1
  78. package/dist/src/tools/modify-alarm-tool.js +232 -236
  79. package/dist/src/tools/modify-note-tool.d.ts +2 -1
  80. package/dist/src/tools/modify-note-tool.js +104 -108
  81. package/dist/src/tools/note-tool.d.ts +2 -1
  82. package/dist/src/tools/note-tool.js +103 -107
  83. package/dist/src/tools/query-app-message-tool.d.ts +2 -1
  84. package/dist/src/tools/query-app-message-tool.js +108 -111
  85. package/dist/src/tools/query-memory-data-tool.d.ts +2 -1
  86. package/dist/src/tools/query-memory-data-tool.js +109 -112
  87. package/dist/src/tools/query-todo-task-tool.d.ts +2 -1
  88. package/dist/src/tools/query-todo-task-tool.js +103 -106
  89. package/dist/src/tools/save-file-to-phone-tool.d.ts +2 -1
  90. package/dist/src/tools/save-file-to-phone-tool.js +127 -131
  91. package/dist/src/tools/save-media-to-gallery-tool.d.ts +2 -1
  92. package/dist/src/tools/save-media-to-gallery-tool.js +134 -138
  93. package/dist/src/tools/save-self-evolution-skill-tool.d.ts +2 -0
  94. package/dist/src/tools/save-self-evolution-skill-tool.js +410 -0
  95. package/dist/src/tools/search-alarm-tool.d.ts +2 -1
  96. package/dist/src/tools/search-alarm-tool.js +171 -175
  97. package/dist/src/tools/search-calendar-tool.d.ts +2 -1
  98. package/dist/src/tools/search-calendar-tool.js +145 -149
  99. package/dist/src/tools/search-contact-tool.d.ts +2 -1
  100. package/dist/src/tools/search-contact-tool.js +98 -102
  101. package/dist/src/tools/search-email-tool.d.ts +2 -1
  102. package/dist/src/tools/search-email-tool.js +107 -111
  103. package/dist/src/tools/search-file-tool.d.ts +2 -1
  104. package/dist/src/tools/search-file-tool.js +99 -103
  105. package/dist/src/tools/search-message-tool.d.ts +2 -1
  106. package/dist/src/tools/search-message-tool.js +100 -104
  107. package/dist/src/tools/search-note-tool.d.ts +2 -1
  108. package/dist/src/tools/search-note-tool.js +95 -99
  109. package/dist/src/tools/search-photo-gallery-tool.d.ts +2 -1
  110. package/dist/src/tools/search-photo-gallery-tool.js +34 -38
  111. package/dist/src/tools/send-email-tool.d.ts +2 -1
  112. package/dist/src/tools/send-email-tool.js +105 -108
  113. package/dist/src/tools/send-file-to-user-tool.d.ts +2 -1
  114. package/dist/src/tools/send-file-to-user-tool.js +154 -155
  115. package/dist/src/tools/send-message-tool.d.ts +2 -1
  116. package/dist/src/tools/send-message-tool.js +119 -123
  117. package/dist/src/tools/session-manager.d.ts +21 -6
  118. package/dist/src/tools/session-manager.js +147 -18
  119. package/dist/src/tools/upload-file-tool.d.ts +2 -1
  120. package/dist/src/tools/upload-file-tool.js +78 -82
  121. package/dist/src/tools/upload-photo-tool.d.ts +2 -1
  122. package/dist/src/tools/upload-photo-tool.js +69 -73
  123. package/dist/src/tools/xiaoyi-add-collection-tool.d.ts +2 -1
  124. package/dist/src/tools/xiaoyi-add-collection-tool.js +143 -147
  125. package/dist/src/tools/xiaoyi-collection-tool.d.ts +2 -1
  126. package/dist/src/tools/xiaoyi-collection-tool.js +111 -115
  127. package/dist/src/tools/xiaoyi-delete-collection-tool.d.ts +2 -1
  128. package/dist/src/tools/xiaoyi-delete-collection-tool.js +124 -128
  129. package/dist/src/tools/xiaoyi-gui-tool.d.ts +2 -1
  130. package/dist/src/tools/xiaoyi-gui-tool.js +84 -88
  131. package/dist/src/utils/logger.js +20 -18
  132. package/dist/src/utils/runtime-manager.js +24 -2
  133. package/dist/src/utils/self-evolution-manager.d.ts +10 -0
  134. package/dist/src/utils/self-evolution-manager.js +69 -0
  135. package/dist/src/utils/tool-call-nudge-manager.d.ts +16 -0
  136. package/dist/src/utils/tool-call-nudge-manager.js +47 -0
  137. package/dist/src/websocket.d.ts +3 -0
  138. package/dist/src/websocket.js +91 -26
  139. package/openclaw.plugin.json +22 -0
  140. package/package.json +3 -3
@@ -1,15 +1,13 @@
1
1
  // OpenClaw → A2A format conversion
2
2
  import { v4 as uuidv4 } from "uuid";
3
3
  import { getXYWebSocketManager } from "./client.js";
4
- import { getXYRuntime } from "./runtime.js";
4
+ import { logger } from "./utils/logger.js";
5
5
  /**
6
6
  * Send an A2A artifact update response.
7
7
  */
8
8
  export async function sendA2AResponse(params) {
9
- const { config, sessionId, taskId, messageId, text, append, final, files, errorCode, errorMessage } = params;
10
- const runtime = getXYRuntime();
9
+ const { config, sessionId, taskId, messageId, text, append, final, files, errorCode, errorMessage, runtime } = params;
11
10
  const log = runtime?.log ?? console.log;
12
- const errorFn = runtime?.error ?? console.error;
13
11
  // Build artifact update event
14
12
  const artifact = {
15
13
  taskId,
@@ -75,9 +73,6 @@ export async function sendA2AResponse(params) {
75
73
  */
76
74
  export async function sendReasoningTextUpdate(params) {
77
75
  const { config, sessionId, taskId, messageId, text, append = true } = params;
78
- const runtime = getXYRuntime();
79
- const log = runtime?.log ?? console.log;
80
- const error = runtime?.error ?? console.error;
81
76
  const artifact = {
82
77
  taskId,
83
78
  kind: "artifact-update",
@@ -114,10 +109,8 @@ export async function sendReasoningTextUpdate(params) {
114
109
  * Follows A2A protocol standard format with nested status object.
115
110
  */
116
111
  export async function sendStatusUpdate(params) {
117
- const { config, sessionId, taskId, messageId, text, state } = params;
118
- const runtime = getXYRuntime();
112
+ const { config, sessionId, taskId, messageId, text, state, runtime } = params;
119
113
  const log = runtime?.log ?? console.log;
120
- const error = runtime?.error ?? console.error;
121
114
  // Build status update event following A2A protocol standard
122
115
  const statusUpdate = {
123
116
  taskId,
@@ -162,9 +155,6 @@ export async function sendStatusUpdate(params) {
162
155
  */
163
156
  export async function sendCommand(params) {
164
157
  const { config, sessionId, taskId, messageId, command } = params;
165
- const runtime = getXYRuntime();
166
- const log = runtime?.log ?? console.log;
167
- const error = runtime?.error ?? console.error;
168
158
  // Build artifact update with command as data
169
159
  // Wrap command in commands array as per protocol requirement
170
160
  const artifact = {
@@ -201,18 +191,15 @@ export async function sendCommand(params) {
201
191
  msgDetail: JSON.stringify(jsonRpcResponse),
202
192
  };
203
193
  // 📋 Log complete response body
204
- log(`[A2A_COMMAND] 📤 Sending A2A command: taskId: ${taskId}`);
194
+ logger.log(`[A2A_COMMAND] 📤 Sending A2A command: taskId: ${taskId}`);
205
195
  await wsManager.sendMessage(sessionId, outboundMessage);
206
- log(`[A2A_COMMAND] ✅ Command sent successfully`);
196
+ logger.log(`[A2A_COMMAND] ✅ Command sent successfully`);
207
197
  }
208
198
  /**
209
199
  * Send a clearContext response.
210
200
  */
211
201
  export async function sendClearContextResponse(params) {
212
202
  const { config, sessionId, messageId } = params;
213
- const runtime = getXYRuntime();
214
- const log = runtime?.log ?? console.log;
215
- const error = runtime?.error ?? console.error;
216
203
  // Build JSON-RPC response for clearContext
217
204
  const jsonRpcResponse = {
218
205
  jsonrpc: "2.0",
@@ -238,16 +225,13 @@ export async function sendClearContextResponse(params) {
238
225
  msgDetail: JSON.stringify(jsonRpcResponse),
239
226
  };
240
227
  await wsManager.sendMessage(sessionId, outboundMessage);
241
- log(`Sent clearContext response: sessionId=${sessionId}`);
228
+ logger.log(`Sent clearContext response: sessionId=${sessionId}`);
242
229
  }
243
230
  /**
244
231
  * Send a tasks/cancel response.
245
232
  */
246
233
  export async function sendTasksCancelResponse(params) {
247
234
  const { config, sessionId, taskId, messageId } = params;
248
- const runtime = getXYRuntime();
249
- const log = runtime?.log ?? console.log;
250
- const error = runtime?.error ?? console.error;
251
235
  // Build JSON-RPC response for tasks/cancel
252
236
  // Note: Using any to bypass type check as the response format differs from standard A2A types
253
237
  const jsonRpcResponse = {
@@ -274,16 +258,13 @@ export async function sendTasksCancelResponse(params) {
274
258
  msgDetail: JSON.stringify(jsonRpcResponse),
275
259
  };
276
260
  await wsManager.sendMessage(sessionId, outboundMessage);
277
- log(`Sent tasks/cancel response: sessionId=${sessionId}, taskId=${taskId}`);
261
+ logger.log(`Sent tasks/cancel response: sessionId=${sessionId}, taskId=${taskId}`);
278
262
  }
279
263
  /**
280
264
  * Send a Trigger response with pushData content.
281
265
  */
282
266
  export async function sendTriggerResponse(params) {
283
267
  const { config, sessionId, taskId, messageId, content } = params;
284
- const runtime = getXYRuntime();
285
- const log = runtime?.log ?? console.log;
286
- const error = runtime?.error ?? console.error;
287
268
  // Build JSON-RPC response for Trigger
288
269
  const jsonRpcResponse = {
289
270
  jsonrpc: "2.0",
@@ -318,7 +299,7 @@ export async function sendTriggerResponse(params) {
318
299
  taskId,
319
300
  msgDetail: JSON.stringify(jsonRpcResponse),
320
301
  };
321
- log(`[TRIGGER_RESPONSE] Sending Trigger response: sessionId=${sessionId}, taskId=${taskId}`);
302
+ logger.log(`[TRIGGER_RESPONSE] Sending Trigger response: sessionId=${sessionId}, taskId=${taskId}`);
322
303
  await wsManager.sendMessage(sessionId, outboundMessage);
323
- log(`[TRIGGER_RESPONSE] Trigger response sent successfully`);
304
+ logger.log(`[TRIGGER_RESPONSE] Trigger response sent successfully`);
324
305
  }
@@ -67,7 +67,7 @@ export class HeartbeatManager {
67
67
  */
68
68
  sendHeartbeat() {
69
69
  if (this.ws.readyState !== WebSocket.OPEN) {
70
- console.warn(`Cannot send heartbeat for ${this.serverName}: WebSocket not open`);
70
+ this.log(`Cannot send heartbeat for ${this.serverName}: WebSocket not open`);
71
71
  return;
72
72
  }
73
73
  try {
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 处理 LoginTokenEvent.ClawAutoLogin 事件
3
+ * 将 clientId 和当前时间戳写入 .xiaoyitoken.json 文件
4
+ *
5
+ * @param context - 事件上下文,包含 event 对象
6
+ * @param runtime - 运行时环境
7
+ */
8
+ export declare function handleLoginTokenEvent(context: any, runtime: any): void;
@@ -0,0 +1,60 @@
1
+ // Login Token 事件处理器
2
+ // 监听 LoginTokenEvent.ClawAutoLogin 事件,将 clientId 写入 .xiaoyitoken.json
3
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
4
+ import { dirname } from "path";
5
+ const TOKEN_FILE_PATH = "/home/sandbox/.openclaw/.xiaoyitoken.json";
6
+ /**
7
+ * 处理 LoginTokenEvent.ClawAutoLogin 事件
8
+ * 将 clientId 和当前时间戳写入 .xiaoyitoken.json 文件
9
+ *
10
+ * @param context - 事件上下文,包含 event 对象
11
+ * @param runtime - 运行时环境
12
+ */
13
+ export function handleLoginTokenEvent(context, runtime) {
14
+ const log = runtime?.log ?? console.log;
15
+ const error = runtime?.error ?? console.error;
16
+ try {
17
+ const clientId = context.event?.payload?.clientId;
18
+ if (!clientId || typeof clientId !== "string") {
19
+ error("[LOGIN_TOKEN_HANDLER] invalid payload: missing clientId");
20
+ return;
21
+ }
22
+ log(`[LOGIN_TOKEN_HANDLER] received login token event, clientId=${clientId}`);
23
+ // Ensure directory exists
24
+ const dir = dirname(TOKEN_FILE_PATH);
25
+ if (!existsSync(dir)) {
26
+ mkdirSync(dir, { recursive: true });
27
+ }
28
+ let tokens = [];
29
+ if (existsSync(TOKEN_FILE_PATH)) {
30
+ try {
31
+ const content = readFileSync(TOKEN_FILE_PATH, "utf-8");
32
+ tokens = JSON.parse(content);
33
+ if (!Array.isArray(tokens)) {
34
+ tokens = [];
35
+ }
36
+ }
37
+ catch {
38
+ tokens = [];
39
+ }
40
+ }
41
+ // Check if clientId already exists
42
+ const now = String(Date.now());
43
+ const existing = tokens.find((t) => t.clientId === clientId);
44
+ if (existing) {
45
+ // Update timestamp
46
+ existing.timestamp = now;
47
+ log(`[LOGIN_TOKEN_HANDLER] updated timestamp for clientId=${clientId}`);
48
+ }
49
+ else {
50
+ // Insert new entry
51
+ tokens.push({ clientId, timestamp: now });
52
+ log(`[LOGIN_TOKEN_HANDLER] inserted new entry for clientId=${clientId}`);
53
+ }
54
+ writeFileSync(TOKEN_FILE_PATH, JSON.stringify(tokens, null, 2), "utf-8");
55
+ log(`[LOGIN_TOKEN_HANDLER] wrote token file: ${TOKEN_FILE_PATH}`);
56
+ }
57
+ catch (err) {
58
+ error("[LOGIN_TOKEN_HANDLER] failed to handle event:", err);
59
+ }
60
+ }
@@ -0,0 +1,17 @@
1
+ import type { OutboundWebSocketMessage } from "./types.js";
2
+ /**
3
+ * Simple message queue for buffering outbound WebSocket messages
4
+ * during disconnection and reconnection stabilization period.
5
+ */
6
+ export declare class MessageQueue {
7
+ private items;
8
+ private log;
9
+ constructor(log?: (msg: string, ...args: any[]) => void);
10
+ /** Enqueue a message. Drops oldest if over limit. */
11
+ enqueue(message: OutboundWebSocketMessage): void;
12
+ /** Flush all queued messages by calling sendFn for each, then clear. */
13
+ flush(sendFn: (message: OutboundWebSocketMessage) => void): void;
14
+ /** Clear all queued messages without sending. */
15
+ clear(): void;
16
+ get size(): number;
17
+ }
@@ -0,0 +1,51 @@
1
+ const MAX_QUEUE_SIZE = 1000;
2
+ /**
3
+ * Simple message queue for buffering outbound WebSocket messages
4
+ * during disconnection and reconnection stabilization period.
5
+ */
6
+ export class MessageQueue {
7
+ items = [];
8
+ log;
9
+ constructor(log) {
10
+ this.log = log ?? console.log;
11
+ }
12
+ /** Enqueue a message. Drops oldest if over limit. */
13
+ enqueue(message) {
14
+ if (this.items.length >= MAX_QUEUE_SIZE) {
15
+ this.log(`[MessageQueue] Queue full (${MAX_QUEUE_SIZE}), dropping oldest message`);
16
+ this.items.shift();
17
+ }
18
+ this.items.push(message);
19
+ this.log(`[MessageQueue] Enqueued message, queue size: ${this.items.length}`);
20
+ }
21
+ /** Flush all queued messages by calling sendFn for each, then clear. */
22
+ flush(sendFn) {
23
+ const count = this.items.length;
24
+ if (count === 0) {
25
+ this.log("[MessageQueue] Queue empty, nothing to flush");
26
+ return;
27
+ }
28
+ this.log(`[MessageQueue] Flushing ${count} queued messages`);
29
+ for (const msg of this.items) {
30
+ try {
31
+ sendFn(msg);
32
+ }
33
+ catch (err) {
34
+ this.log(`[MessageQueue] Error flushing message: ${err}`);
35
+ }
36
+ }
37
+ this.items = [];
38
+ this.log(`[MessageQueue] Flush complete`);
39
+ }
40
+ /** Clear all queued messages without sending. */
41
+ clear() {
42
+ const count = this.items.length;
43
+ this.items = [];
44
+ if (count > 0) {
45
+ this.log(`[MessageQueue] Cleared ${count} messages`);
46
+ }
47
+ }
48
+ get size() {
49
+ return this.items.length;
50
+ }
51
+ }
@@ -1,11 +1,14 @@
1
1
  import { resolveXYConfig } from "./config.js";
2
- import { getXYWebSocketManager, diagnoseAllManagers, cleanupOrphanConnections, removeXYWebSocketManager } from "./client.js";
2
+ import { getXYWebSocketManager, setClientRuntime, diagnoseAllManagers, cleanupOrphanConnections, removeXYWebSocketManager } from "./client.js";
3
3
  import { handleXYMessage } from "./bot.js";
4
4
  import { parseA2AMessage } from "./parser.js";
5
- import { hasActiveTask } from "./task-manager.js";
5
+ import { hasActiveTask, getAllActiveTaskBindings } from "./task-manager.js";
6
+ import { sendA2AResponse } from "./formatter.js";
6
7
  import { handleTriggerEvent } from "./trigger-handler.js";
7
- import { handleSelfEvolutionEvent } from "./self-evolution-handler.js";
8
+ import { handleSelfEvolutionEvent, handleSelfEvolutionStateGetEvent } from "./self-evolution-handler.js";
9
+ import { handleLoginTokenEvent } from "./login-token-handler.js";
8
10
  import { cleanupStaleTempFiles } from "./reply-dispatcher.js";
11
+ import { cleanupStaleSessions, getActiveSessionCount, cleanupAllSessions } from "./tools/session-manager.js";
9
12
  /**
10
13
  * Per-session serial queue that ensures messages from the same session are processed
11
14
  * in arrival order while allowing different sessions to run concurrently.
@@ -48,8 +51,10 @@ export async function monitorXYProvider(opts = {}) {
48
51
  opts.setStatus({ lastEventAt: Date.now(), lastInboundAt: Date.now() });
49
52
  }
50
53
  : undefined;
54
+ // ✅ Set runtime for WebSocket manager logging before creating/getting manager
55
+ setClientRuntime(runtime);
51
56
  // 🔍 Diagnose WebSocket managers before gateway start
52
- console.log("🔍 [DIAGNOSTICS] Checking WebSocket managers before gateway start...");
57
+ log("🔍 [DIAGNOSTICS] Checking WebSocket managers before gateway start...");
53
58
  diagnoseAllManagers();
54
59
  // Get WebSocket manager (cached)
55
60
  const wsManager = getXYWebSocketManager(account);
@@ -138,7 +143,7 @@ export async function monitorXYProvider(opts = {}) {
138
143
  opts.setStatus?.({ connected: true });
139
144
  };
140
145
  const disconnectedHandler = (serverId) => {
141
- console.warn(`XY gateway: ${serverId} disconnected`);
146
+ log(`XY gateway: ${serverId} disconnected`);
142
147
  loggedServers.delete(serverId);
143
148
  // ✅ Report disconnection status (only if all servers disconnected)
144
149
  if (loggedServers.size === 0) {
@@ -161,16 +166,26 @@ export async function monitorXYProvider(opts = {}) {
161
166
  log(`[MONITOR] Received self-evolution-event, dispatching to handler...`);
162
167
  handleSelfEvolutionEvent(context, runtime);
163
168
  };
169
+ const selfEvolutionStateGetHandler = (context) => {
170
+ log(`[MONITOR] Received self-evolution-state-get-event, dispatching to handler...`);
171
+ handleSelfEvolutionStateGetEvent(context, account, runtime, wsManager).catch((err) => {
172
+ error(`[MONITOR] Failed to handle self-evolution-state-get-event:`, err);
173
+ });
174
+ };
175
+ const loginTokenEventHandler = (context) => {
176
+ log(`[MONITOR] Received login-token-event, dispatching to handler...`);
177
+ handleLoginTokenEvent(context, runtime);
178
+ };
164
179
  const cleanup = () => {
165
180
  log("XY gateway: cleaning up...");
166
181
  // 🔍 Diagnose before cleanup
167
- console.log("🔍 [DIAGNOSTICS] Checking WebSocket managers before cleanup...");
182
+ log("🔍 [DIAGNOSTICS] Checking WebSocket managers before cleanup...");
168
183
  diagnoseAllManagers();
169
184
  // Stop health check interval
170
185
  if (healthCheckInterval) {
171
186
  clearInterval(healthCheckInterval);
172
187
  healthCheckInterval = null;
173
- console.log("⏸️ Stopped periodic health check");
188
+ log("⏸️ Stopped periodic health check");
174
189
  }
175
190
  // Remove event handlers to prevent duplicate calls on gateway restart
176
191
  wsManager.off("message", messageHandler);
@@ -179,20 +194,53 @@ export async function monitorXYProvider(opts = {}) {
179
194
  wsManager.off("error", errorHandler);
180
195
  wsManager.off("trigger-event", triggerEventHandler);
181
196
  wsManager.off("self-evolution-event", selfEvolutionHandler);
197
+ wsManager.off("self-evolution-state-get-event", selfEvolutionStateGetHandler);
198
+ wsManager.off("login-token-event", loginTokenEventHandler);
182
199
  // ✅ Disconnect the wsManager to prevent connection leaks
183
200
  // This is safe because each gateway lifecycle should have clean connections
184
201
  wsManager.disconnect();
185
202
  // ✅ Remove manager from cache to prevent reusing dirty state
186
203
  removeXYWebSocketManager(account);
204
+ // Clean up all active sessions
205
+ cleanupAllSessions();
187
206
  loggedServers.clear();
188
207
  activeMessages.clear();
189
- log(`[MONITOR-HANDLER] 🧹 Cleanup complete, cleared active messages`);
208
+ log(`[MONITOR-HANDLER] 🧹 Cleanup complete, cleared active messages and sessions`);
190
209
  // 🔍 Diagnose after cleanup
191
- console.log("🔍 [DIAGNOSTICS] Checking WebSocket managers after cleanup...");
210
+ log("🔍 [DIAGNOSTICS] Checking WebSocket managers after cleanup...");
192
211
  diagnoseAllManagers();
193
212
  };
194
- const handleAbort = () => {
195
- log("XY gateway: abort signal received, stopping");
213
+ const handleAbort = async () => {
214
+ log("XY gateway: abort signal received, sending notifications before stopping");
215
+ // 📤 Send restart notification to all active sessions before disconnecting
216
+ try {
217
+ const activeBindings = getAllActiveTaskBindings();
218
+ if (activeBindings.length > 0) {
219
+ const config = resolveXYConfig(cfg);
220
+ const notificationText = "Gateway即将重启,重启期间可能短暂出现\u201c环境异常\u201d提示,请稍候并耐心重试~";
221
+ log(`[MONITOR] 📤 Sending restart notifications to ${activeBindings.length} active session(s)`);
222
+ const sendPromises = activeBindings.map(binding => sendA2AResponse({
223
+ config,
224
+ sessionId: binding.sessionId,
225
+ taskId: binding.currentTaskId,
226
+ messageId: binding.currentMessageId,
227
+ text: notificationText,
228
+ append: false,
229
+ final: true,
230
+ runtime,
231
+ }).catch(err => {
232
+ error(`[MONITOR] Failed to send restart notification to session ${binding.sessionId}: ${String(err)}`);
233
+ }));
234
+ await Promise.all(sendPromises);
235
+ log(`[MONITOR] ✅ Restart notifications sent to ${activeBindings.length} session(s)`);
236
+ }
237
+ else {
238
+ log(`[MONITOR] No active sessions, skipping restart notifications`);
239
+ }
240
+ }
241
+ catch (err) {
242
+ error(`[MONITOR] Error sending restart notifications: ${String(err)}`);
243
+ }
196
244
  cleanup();
197
245
  log("XY gateway stopped");
198
246
  resolve();
@@ -210,15 +258,23 @@ export async function monitorXYProvider(opts = {}) {
210
258
  wsManager.on("error", errorHandler);
211
259
  wsManager.on("trigger-event", triggerEventHandler);
212
260
  wsManager.on("self-evolution-event", selfEvolutionHandler);
261
+ wsManager.on("self-evolution-state-get-event", selfEvolutionStateGetHandler);
262
+ wsManager.on("login-token-event", loginTokenEventHandler);
213
263
  // Start periodic health check (every 6 hours)
214
- console.log("🏥 Starting periodic health check (every 6 hours)...");
264
+ log("🏥 Starting periodic health check (every 6 hours)...");
215
265
  healthCheckInterval = setInterval(() => {
216
- console.log("🏥 [HEALTH CHECK] Periodic WebSocket diagnostics...");
266
+ log("🏥 [HEALTH CHECK] Periodic WebSocket diagnostics...");
217
267
  diagnoseAllManagers();
218
268
  // Auto-cleanup orphan connections
219
269
  const cleaned = cleanupOrphanConnections();
220
270
  if (cleaned > 0) {
221
- console.log(`🧹 [HEALTH CHECK] Auto-cleaned ${cleaned} manager(s) with orphan connections`);
271
+ log(`🧹 [HEALTH CHECK] Auto-cleaned ${cleaned} manager(s) with orphan connections`);
272
+ }
273
+ // Cleanup stale sessions (older than 10min TTL)
274
+ const cleanedSessions = cleanupStaleSessions();
275
+ const remainingSessions = getActiveSessionCount();
276
+ if (cleanedSessions > 0 || remainingSessions > 0) {
277
+ log(`🧹 [HEALTH CHECK] Sessions: cleaned=${cleanedSessions}, active=${remainingSessions}`);
222
278
  }
223
279
  // Cleanup stale temp files (older than 24 hours)
224
280
  void cleanupStaleTempFiles();
@@ -4,6 +4,7 @@ import { XYPushService } from "./push.js";
4
4
  import { getCurrentSessionContext } from "./tools/session-manager.js";
5
5
  import { savePushData } from "./utils/pushdata-manager.js";
6
6
  import { getAllPushIds } from "./utils/pushid-manager.js";
7
+ import { logger } from "./utils/logger.js";
7
8
  // Special marker for default push delivery when no target is specified
8
9
  const DEFAULT_PUSH_MARKER = "default";
9
10
  // File extension to MIME type mapping
@@ -57,7 +58,7 @@ export const xyOutbound = {
57
58
  resolveTarget: ({ cfg, to, accountId, mode }) => {
58
59
  // If no target provided, use default marker for push delivery
59
60
  if (!to || to.trim() === "") {
60
- console.log(`[xyOutbound.resolveTarget] No target specified, using default push marker`);
61
+ logger.log(`[xyOutbound.resolveTarget] No target specified, using default push marker`);
61
62
  return {
62
63
  ok: true,
63
64
  to: DEFAULT_PUSH_MARKER,
@@ -66,24 +67,24 @@ export const xyOutbound = {
66
67
  const trimmedTo = to.trim();
67
68
  // If the target doesn't contain "::", try to enhance it with taskId from session context
68
69
  if (!trimmedTo.includes("::")) {
69
- console.log(`[xyOutbound.resolveTarget] Target "${trimmedTo}" missing taskId, looking up session context`);
70
+ logger.log(`[xyOutbound.resolveTarget] Target "${trimmedTo}" missing taskId, looking up session context`);
70
71
  // Try to get the current session context
71
72
  const sessionContext = getCurrentSessionContext();
72
73
  if (sessionContext && sessionContext.sessionId === trimmedTo) {
73
74
  const enhancedTarget = `${trimmedTo}::${sessionContext.taskId}`;
74
- console.log(`[xyOutbound.resolveTarget] Enhanced target: ${enhancedTarget}`);
75
+ logger.log(`[xyOutbound.resolveTarget] Enhanced target: ${enhancedTarget}`);
75
76
  return {
76
77
  ok: true,
77
78
  to: enhancedTarget,
78
79
  };
79
80
  }
80
81
  else {
81
- console.log(`[xyOutbound.resolveTarget] Could not find matching session context for "${trimmedTo}"`);
82
+ logger.log(`[xyOutbound.resolveTarget] Could not find matching session context for "${trimmedTo}"`);
82
83
  // Still return the original target, but it may fail in sendMedia
83
84
  }
84
85
  }
85
86
  // Otherwise, use the provided target (either already in correct format or for sendText)
86
- console.log(`[xyOutbound.resolveTarget] Using provided target:`, trimmedTo);
87
+ logger.log(`[xyOutbound.resolveTarget] Using provided target:`, trimmedTo);
87
88
  return {
88
89
  ok: true,
89
90
  to: trimmedTo,
@@ -95,36 +96,36 @@ export const xyOutbound = {
95
96
  // Handle default push marker (for cron jobs without explicit target)
96
97
  let actualTo = to;
97
98
  if (to === DEFAULT_PUSH_MARKER) {
98
- console.log(`[xyOutbound.sendText] Using default push delivery (no specific target)`);
99
+ logger.log(`[xyOutbound.sendText] Using default push delivery (no specific target)`);
99
100
  // For push notifications, we don't need a specific target
100
101
  // The push service will handle it based on config
101
102
  actualTo = config.defaultSessionId || "";
102
103
  }
103
104
  // 1. 持久化推送消息内容,获取 pushDataId
104
- console.log(`[xyOutbound.sendText] Saving push data to local storage...`);
105
+ logger.log(`[xyOutbound.sendText] Saving push data to local storage...`);
105
106
  let pushDataId;
106
107
  try {
107
108
  pushDataId = await savePushData(text);
108
- console.log(`[xyOutbound.sendText] ✅ Push data saved with ID: ${pushDataId.substring(0, 20)}`);
109
+ logger.log(`[xyOutbound.sendText] ✅ Push data saved with ID: ${pushDataId.substring(0, 20)}`);
109
110
  }
110
111
  catch (error) {
111
- console.error(`[xyOutbound.sendText] ❌ Failed to save push data:`, error);
112
+ logger.error(`[xyOutbound.sendText] ❌ Failed to save push data:`, error);
112
113
  // 如果持久化失败,仍然继续发送(不阻塞主流程)
113
114
  pushDataId = "";
114
115
  }
115
116
  // 2. 读取所有 pushId
116
- console.log(`[xyOutbound.sendText] Loading all pushIds...`);
117
+ logger.log(`[xyOutbound.sendText] Loading all pushIds...`);
117
118
  let pushIdList = [];
118
119
  try {
119
120
  pushIdList = await getAllPushIds();
120
- console.log(`[xyOutbound.sendText] ✅ Loaded ${pushIdList.length} pushIds`);
121
+ logger.log(`[xyOutbound.sendText] ✅ Loaded ${pushIdList.length} pushIds`);
121
122
  }
122
123
  catch (error) {
123
- console.error(`[xyOutbound.sendText] ❌ Failed to load pushIds:`, error);
124
+ logger.error(`[xyOutbound.sendText] ❌ Failed to load pushIds:`, error);
124
125
  }
125
126
  // 3. 如果 pushIdList 为空,回退到原有逻辑(使用 config pushId)
126
127
  if (pushIdList.length === 0) {
127
- console.log(`[xyOutbound.sendText] ⚠️ No pushIds found, falling back to config pushId`);
128
+ logger.log(`[xyOutbound.sendText] ⚠️ No pushIds found, falling back to config pushId`);
128
129
  pushIdList = [config.pushId];
129
130
  }
130
131
  // Create push service
@@ -134,7 +135,7 @@ export const xyOutbound = {
134
135
  // Truncate push content to max length 1000
135
136
  const pushText = text.length > 1000 ? text.slice(0, 1000) : text;
136
137
  // 4. 遍历所有 pushId,依次发送推送通知
137
- console.log(`[xyOutbound.sendText] 📤 Broadcasting to ${pushIdList.length} pushId(s)...`);
138
+ logger.log(`[xyOutbound.sendText] 📤 Broadcasting to ${pushIdList.length} pushId(s)...`);
138
139
  let successCount = 0;
139
140
  let failureCount = 0;
140
141
  for (const pushId of pushIdList) {
@@ -142,11 +143,11 @@ export const xyOutbound = {
142
143
  // 传入 pushId 和 pushDataId,使用 kind="data" 格式
143
144
  await pushService.sendPush(pushText, title, undefined, actualTo, pushDataId, pushId);
144
145
  successCount++;
145
- console.log(`[xyOutbound.sendText] ✅ Sent successfully to pushId: ${pushId.substring(0, 20)}...`);
146
+ logger.log(`[xyOutbound.sendText] ✅ Sent successfully to pushId: ${pushId.substring(0, 20)}...`);
146
147
  }
147
148
  catch (error) {
148
149
  failureCount++;
149
- console.error(`[xyOutbound.sendText] ❌ Failed to send to pushId: ${pushId.substring(0, 20)}...`, error);
150
+ logger.error(`[xyOutbound.sendText] ❌ Failed to send to pushId: ${pushId.substring(0, 20)}...`, error);
150
151
  // 单个 pushId 发送失败不影响其他,继续处理下一个
151
152
  }
152
153
  }
@@ -178,7 +179,7 @@ export const xyOutbound = {
178
179
  if (!fileId) {
179
180
  throw new Error(`File upload returned empty fileId for: ${mediaUrl}`);
180
181
  }
181
- console.log(`[xyOutbound.sendMedia] File uploaded:`, {
182
+ logger.log(`[xyOutbound.sendMedia] File uploaded:`, {
182
183
  fileId,
183
184
  sessionId,
184
185
  taskId,
@@ -222,7 +223,7 @@ export const xyOutbound = {
222
223
  const { getXYWebSocketManager } = await import("./client.js");
223
224
  const wsManager = getXYWebSocketManager(config);
224
225
  await wsManager.sendMessage(sessionId, agentResponse);
225
- console.log(`[xyOutbound.sendMedia] WebSocket message sent successfully`);
226
+ logger.log(`[xyOutbound.sendMedia] WebSocket message sent successfully`);
226
227
  // Return message info
227
228
  return {
228
229
  channel: "xiaoyi-channel",
@@ -1,2 +1,3 @@
1
- import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-models";
1
+ import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-model-shared";
2
+ export declare function applySelfEvolutionPrompt(systemPrompt: string | undefined, enabled: boolean): string;
2
3
  export declare const xiaoyiProvider: ProviderPlugin;