@ynhcj/xiaoyi-channel 0.0.125-beta â 0.0.126-beta
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/dist/src/bot.js +1 -1
- package/dist/src/client.js +24 -21
- package/dist/src/cspl/call-api.js +6 -5
- package/dist/src/file-download.js +4 -3
- package/dist/src/file-upload.js +19 -18
- package/dist/src/heartbeat.js +1 -1
- package/dist/src/monitor.js +12 -10
- package/dist/src/outbound.js +19 -18
- package/dist/src/provider.js +12 -12
- package/dist/src/push.js +16 -15
- package/dist/src/reply-dispatcher.js +3 -2
- package/dist/src/skill-retriever/hooks.js +4 -3
- package/dist/src/skill-retriever/tool-search.js +10 -9
- package/dist/src/steer-injector.js +1 -1
- package/dist/src/tools/send-file-to-user-tool.js +4 -3
- package/dist/src/tools/session-manager.js +3 -3
- package/dist/src/utils/self-evolution-manager.js +3 -2
- package/dist/src/websocket.js +3 -1
- package/package.json +1 -1
package/dist/src/bot.js
CHANGED
|
@@ -200,7 +200,7 @@ export async function handleXYMessage(params) {
|
|
|
200
200
|
const fileParts = extractFileParts(parsed.parts);
|
|
201
201
|
// Download files to local disk
|
|
202
202
|
const downloadedFiles = await downloadFilesFromParts(fileParts);
|
|
203
|
-
|
|
203
|
+
log("Downloaded files:", JSON.stringify(downloadedFiles, null, 2));
|
|
204
204
|
const mediaPayload = buildXYMediaPayload(downloadedFiles);
|
|
205
205
|
// Resolve envelope format options (following feishu pattern)
|
|
206
206
|
const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
|
package/dist/src/client.js
CHANGED
|
@@ -44,16 +44,17 @@ export function getXYWebSocketManager(config) {
|
|
|
44
44
|
* Disconnects the manager and removes it from the cache.
|
|
45
45
|
*/
|
|
46
46
|
export function removeXYWebSocketManager(config) {
|
|
47
|
+
const log = runtime?.log ?? console.log;
|
|
47
48
|
const cacheKey = `${config.apiKey}-${config.agentId}`;
|
|
48
49
|
const manager = wsManagerCache.get(cacheKey);
|
|
49
50
|
if (manager) {
|
|
50
|
-
|
|
51
|
+
log(`đī¸ [WS-MANAGER-CACHE] Removing manager from cache: ${cacheKey}`);
|
|
51
52
|
manager.disconnect();
|
|
52
53
|
wsManagerCache.delete(cacheKey);
|
|
53
|
-
|
|
54
|
+
log(`đī¸ [WS-MANAGER-CACHE] Manager removed, remaining managers: ${wsManagerCache.size}`);
|
|
54
55
|
}
|
|
55
56
|
else {
|
|
56
|
-
|
|
57
|
+
log(`â ī¸ [WS-MANAGER-CACHE] Manager not found in cache: ${cacheKey}`);
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
/**
|
|
@@ -78,36 +79,37 @@ export function getCachedManagerCount() {
|
|
|
78
79
|
* Helps identify connection issues and orphan connections.
|
|
79
80
|
*/
|
|
80
81
|
export function diagnoseAllManagers() {
|
|
81
|
-
|
|
82
|
+
const log = runtime?.log ?? console.log;
|
|
83
|
+
log(`Total cached managers: ${wsManagerCache.size}`);
|
|
82
84
|
if (wsManagerCache.size === 0) {
|
|
83
|
-
|
|
85
|
+
log("âšī¸ No managers in cache");
|
|
84
86
|
return;
|
|
85
87
|
}
|
|
86
88
|
let orphanCount = 0;
|
|
87
89
|
wsManagerCache.forEach((manager, key) => {
|
|
88
90
|
const diag = manager.getConnectionDiagnostics();
|
|
89
|
-
|
|
91
|
+
log(` Total event listeners on manager: ${diag.totalEventListeners}`);
|
|
90
92
|
// Connection
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
log(` đ Connection:`);
|
|
94
|
+
log(` - Exists: ${diag.connection.exists}`);
|
|
95
|
+
log(` - ReadyState: ${diag.connection.readyState}`);
|
|
96
|
+
log(` - State connected/ready: ${diag.connection.stateConnected}/${diag.connection.stateReady}`);
|
|
97
|
+
log(` - Reconnect attempts: ${diag.connection.reconnectAttempts}`);
|
|
98
|
+
log(` - Listeners on WebSocket: ${diag.connection.listenerCount}`);
|
|
99
|
+
log(` - Heartbeat active: ${diag.connection.heartbeatActive}`);
|
|
100
|
+
log(` - Has reconnect timer: ${diag.connection.hasReconnectTimer}`);
|
|
99
101
|
if (diag.connection.isOrphan) {
|
|
100
|
-
|
|
102
|
+
log(` â ī¸ ORPHAN CONNECTION DETECTED!`);
|
|
101
103
|
orphanCount++;
|
|
102
104
|
}
|
|
103
|
-
|
|
105
|
+
log("");
|
|
104
106
|
});
|
|
105
107
|
if (orphanCount > 0) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
log(`â ī¸ Total orphan connections found: ${orphanCount}`);
|
|
109
|
+
log(`đĄ Suggestion: These connections should be cleaned up`);
|
|
108
110
|
}
|
|
109
111
|
else {
|
|
110
|
-
|
|
112
|
+
log(`â
No orphan connections found`);
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
115
|
/**
|
|
@@ -115,17 +117,18 @@ export function diagnoseAllManagers() {
|
|
|
115
117
|
* Returns the number of managers that had orphan connections.
|
|
116
118
|
*/
|
|
117
119
|
export function cleanupOrphanConnections() {
|
|
120
|
+
const log = runtime?.log ?? console.log;
|
|
118
121
|
let cleanedCount = 0;
|
|
119
122
|
wsManagerCache.forEach((manager, key) => {
|
|
120
123
|
const diag = manager.getConnectionDiagnostics();
|
|
121
124
|
if (diag.connection.isOrphan) {
|
|
122
|
-
|
|
125
|
+
log(`đ§š Cleaning up orphan connections in manager: ${key}`);
|
|
123
126
|
manager.disconnect();
|
|
124
127
|
cleanedCount++;
|
|
125
128
|
}
|
|
126
129
|
});
|
|
127
130
|
if (cleanedCount > 0) {
|
|
128
|
-
|
|
131
|
+
log(`đ§š Cleaned up ${cleanedCount} manager(s) with orphan connections`);
|
|
129
132
|
}
|
|
130
133
|
return cleanedCount;
|
|
131
134
|
}
|
|
@@ -4,12 +4,13 @@ import { URL } from "node:url";
|
|
|
4
4
|
import { randomBytes } from "node:crypto";
|
|
5
5
|
import { getCsplConfig } from "./config.js";
|
|
6
6
|
import { DEFAULT_HTTP_PORT, HTTP_STATUS_BAD_REQUEST } from "./constants.js";
|
|
7
|
+
import { logger } from "../utils/logger.js";
|
|
7
8
|
function generateTraceId() {
|
|
8
9
|
return randomBytes(16).toString("hex");
|
|
9
10
|
}
|
|
10
11
|
function buildHeaders(config) {
|
|
11
12
|
const traceId = generateTraceId();
|
|
12
|
-
|
|
13
|
+
logger.log(`[SENTINEL HOOK] trace-id: ${traceId}`);
|
|
13
14
|
return {
|
|
14
15
|
"x-hag-trace-id": traceId,
|
|
15
16
|
"x-uid": config.uid,
|
|
@@ -65,21 +66,21 @@ export async function callCsplApi(questionText, cfg) {
|
|
|
65
66
|
res.on("end", () => {
|
|
66
67
|
try {
|
|
67
68
|
const result = parseResponse(data);
|
|
68
|
-
|
|
69
|
+
logger.log(`[SENTINEL HOOK] â
č¯ˇæąæå`);
|
|
69
70
|
resolve(result);
|
|
70
71
|
}
|
|
71
72
|
catch (e) {
|
|
72
|
-
|
|
73
|
+
logger.error(`[SENTINEL HOOK] â 蝎æąå¤ąč´Ĩ: ${e instanceof Error ? e.message : String(e)}`);
|
|
73
74
|
reject(e);
|
|
74
75
|
}
|
|
75
76
|
});
|
|
76
77
|
});
|
|
77
78
|
req.on("error", (error) => {
|
|
78
|
-
|
|
79
|
+
logger.error(`[SENTINEL HOOK] â 蝎æąé蝝: ${error instanceof Error ? error.message : String(error)}`);
|
|
79
80
|
reject(error);
|
|
80
81
|
});
|
|
81
82
|
req.on("timeout", () => {
|
|
82
|
-
|
|
83
|
+
logger.error(`[SENTINEL HOOK] â° č¯ˇæąčļ
æļ (${config.api.timeout}ms)`);
|
|
83
84
|
req.destroy();
|
|
84
85
|
reject(new Error("[SENTINEL HOOK] Request timeout"));
|
|
85
86
|
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import fetch from "node-fetch";
|
|
3
3
|
import fs from "fs/promises";
|
|
4
4
|
import path from "path";
|
|
5
|
+
import { logger } from "./utils/logger.js";
|
|
5
6
|
/**
|
|
6
7
|
* Download a file from URL to local path.
|
|
7
8
|
*/
|
|
@@ -19,10 +20,10 @@ export async function downloadFile(url, destPath) {
|
|
|
19
20
|
}
|
|
20
21
|
catch (error) {
|
|
21
22
|
if (error.name === 'AbortError') {
|
|
22
|
-
|
|
23
|
+
logger.log(`Download timeout (30s) for ${url}`);
|
|
23
24
|
throw new Error(`Download timeout after 30 seconds`);
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
+
logger.log(`Failed to download file from ${url}:`);
|
|
26
27
|
throw error;
|
|
27
28
|
}
|
|
28
29
|
finally {
|
|
@@ -51,7 +52,7 @@ export async function downloadFilesFromParts(fileParts, tempDir = "/tmp/xy_chann
|
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
54
|
catch (error) {
|
|
54
|
-
|
|
55
|
+
logger.log(`Failed to download file ${name}:`);
|
|
55
56
|
// Continue with other files
|
|
56
57
|
}
|
|
57
58
|
}
|
package/dist/src/file-upload.js
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
import fetch from "node-fetch";
|
|
4
4
|
import fs from "fs/promises";
|
|
5
5
|
import os from "os";
|
|
6
|
+
import { logger } from "./utils/logger.js";
|
|
6
7
|
import path from "path";
|
|
7
8
|
import { calculateSHA256 } from "./utils/crypto.js";
|
|
8
9
|
function isRemoteUrl(filePath) {
|
|
9
10
|
return filePath.startsWith("http://") || filePath.startsWith("https://");
|
|
10
11
|
}
|
|
11
12
|
async function downloadToTempFile(url) {
|
|
12
|
-
|
|
13
|
+
logger.log(`[XY File Upload] Downloading remote file: ${url}`);
|
|
13
14
|
const response = await fetch(url);
|
|
14
15
|
if (!response.ok) {
|
|
15
16
|
throw new Error(`Failed to download remote file: HTTP ${response.status}`);
|
|
@@ -18,7 +19,7 @@ async function downloadToTempFile(url) {
|
|
|
18
19
|
const urlFileName = path.basename(new URL(url).pathname) || "download";
|
|
19
20
|
const tempPath = path.join(os.tmpdir(), `xy-upload-${Date.now()}-${urlFileName}`);
|
|
20
21
|
await fs.writeFile(tempPath, buffer);
|
|
21
|
-
|
|
22
|
+
logger.log(`[XY File Upload] Downloaded to temp file: ${tempPath}`);
|
|
22
23
|
return tempPath;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
@@ -39,7 +40,7 @@ export class XYFileUploadService {
|
|
|
39
40
|
* Returns the objectId (as fileId) for use in A2A messages.
|
|
40
41
|
*/
|
|
41
42
|
async uploadFile(filePath, objectType = "TEMPORARY_MATERIAL_DOC") {
|
|
42
|
-
|
|
43
|
+
logger.log(`[XY File Upload] Starting file upload: ${filePath}`);
|
|
43
44
|
let localFilePath = filePath;
|
|
44
45
|
let isTempFile = false;
|
|
45
46
|
try {
|
|
@@ -54,7 +55,7 @@ export class XYFileUploadService {
|
|
|
54
55
|
const fileSha256 = calculateSHA256(fileBuffer);
|
|
55
56
|
const fileSize = fileBuffer.length;
|
|
56
57
|
// Phase 1: Prepare
|
|
57
|
-
|
|
58
|
+
logger.log(`[XY File Upload] Phase 1: Prepare upload for ${fileName}`);
|
|
58
59
|
const prepareResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/prepare`, {
|
|
59
60
|
method: "POST",
|
|
60
61
|
headers: {
|
|
@@ -84,7 +85,7 @@ export class XYFileUploadService {
|
|
|
84
85
|
}
|
|
85
86
|
const { objectId, draftId, uploadInfos } = prepareData;
|
|
86
87
|
// Phase 2: Upload
|
|
87
|
-
|
|
88
|
+
logger.log(`[XY File Upload] Phase 2: Upload file data`);
|
|
88
89
|
const uploadInfo = uploadInfos[0]; // Single-part upload
|
|
89
90
|
const uploadResp = await fetch(uploadInfo.url, {
|
|
90
91
|
method: uploadInfo.method,
|
|
@@ -95,9 +96,9 @@ export class XYFileUploadService {
|
|
|
95
96
|
const uploadErrorText = await uploadResp.text();
|
|
96
97
|
throw new Error(`Upload failed: HTTP ${uploadResp.status}`);
|
|
97
98
|
}
|
|
98
|
-
|
|
99
|
+
logger.log(`[XY File Upload] Upload complete`);
|
|
99
100
|
// Phase 3: Complete
|
|
100
|
-
|
|
101
|
+
logger.log(`[XY File Upload] Phase 3: Complete upload`);
|
|
101
102
|
const completeResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/complete`, {
|
|
102
103
|
method: "POST",
|
|
103
104
|
headers: {
|
|
@@ -115,11 +116,11 @@ export class XYFileUploadService {
|
|
|
115
116
|
throw new Error(`Complete failed: HTTP ${completeResp.status}`);
|
|
116
117
|
}
|
|
117
118
|
const completeData = await completeResp.json();
|
|
118
|
-
|
|
119
|
+
logger.log(`[XY File Upload] File upload successful: ${fileName} â objectId=${objectId}`);
|
|
119
120
|
return objectId;
|
|
120
121
|
}
|
|
121
122
|
catch (error) {
|
|
122
|
-
|
|
123
|
+
logger.error(`[XY File Upload] File upload failed for ${filePath}:`, error);
|
|
123
124
|
throw error;
|
|
124
125
|
}
|
|
125
126
|
finally {
|
|
@@ -150,7 +151,7 @@ export class XYFileUploadService {
|
|
|
150
151
|
const fileSha256 = calculateSHA256(fileBuffer);
|
|
151
152
|
const fileSize = fileBuffer.length;
|
|
152
153
|
// Phase 1: Prepare
|
|
153
|
-
|
|
154
|
+
logger.log(`[XY File Upload] Phase 1: Prepare upload for ${fileName}`);
|
|
154
155
|
const prepareResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/prepare`, {
|
|
155
156
|
method: "POST",
|
|
156
157
|
headers: {
|
|
@@ -179,23 +180,23 @@ export class XYFileUploadService {
|
|
|
179
180
|
throw new Error(`Prepare failed: ${prepareData.desc}`);
|
|
180
181
|
}
|
|
181
182
|
const { objectId, draftId, uploadInfos } = prepareData;
|
|
182
|
-
|
|
183
|
+
logger.log(`[XY File Upload] Prepare complete: objectId=${objectId}, draftId=${draftId}`);
|
|
183
184
|
// Phase 2: Upload
|
|
184
|
-
|
|
185
|
+
logger.log(`[XY File Upload] Phase 2: Upload file data`);
|
|
185
186
|
const uploadInfo = uploadInfos[0]; // Single-part upload
|
|
186
187
|
const uploadResp = await fetch(uploadInfo.url, {
|
|
187
188
|
method: uploadInfo.method,
|
|
188
189
|
headers: uploadInfo.headers,
|
|
189
190
|
body: fileBuffer,
|
|
190
191
|
});
|
|
191
|
-
|
|
192
|
+
logger.log(`[XY File Upload] Upload response status: ${uploadResp.status}`);
|
|
192
193
|
if (!uploadResp.ok) {
|
|
193
194
|
const uploadErrorText = await uploadResp.text();
|
|
194
195
|
throw new Error(`Upload failed: HTTP ${uploadResp.status}`);
|
|
195
196
|
}
|
|
196
|
-
|
|
197
|
+
logger.log(`[XY File Upload] Upload complete`);
|
|
197
198
|
// Phase 3: CompleteAndQuery - get file URL
|
|
198
|
-
|
|
199
|
+
logger.log(`[XY File Upload] Phase 3: CompleteAndQuery to get file URL`);
|
|
199
200
|
const completeResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/completeAndQuery`, {
|
|
200
201
|
method: "POST",
|
|
201
202
|
headers: {
|
|
@@ -218,11 +219,11 @@ export class XYFileUploadService {
|
|
|
218
219
|
if (!fileUrl) {
|
|
219
220
|
throw new Error("No file URL returned from completeAndQuery");
|
|
220
221
|
}
|
|
221
|
-
|
|
222
|
+
logger.log(`[XY File Upload] File upload successful`);
|
|
222
223
|
return fileUrl;
|
|
223
224
|
}
|
|
224
225
|
catch (error) {
|
|
225
|
-
|
|
226
|
+
logger.error(`[XY File Upload] File upload with URL retrieval failed for ${filePath}:`, error);
|
|
226
227
|
throw error;
|
|
227
228
|
}
|
|
228
229
|
finally {
|
|
@@ -249,7 +250,7 @@ export class XYFileUploadService {
|
|
|
249
250
|
});
|
|
250
251
|
}
|
|
251
252
|
catch (error) {
|
|
252
|
-
|
|
253
|
+
logger.error(`[XY File Upload] Failed to upload ${filePath}, skipping:`, error);
|
|
253
254
|
// Continue with other files
|
|
254
255
|
}
|
|
255
256
|
}
|
package/dist/src/heartbeat.js
CHANGED
|
@@ -67,7 +67,7 @@ export class HeartbeatManager {
|
|
|
67
67
|
*/
|
|
68
68
|
sendHeartbeat() {
|
|
69
69
|
if (this.ws.readyState !== WebSocket.OPEN) {
|
|
70
|
-
|
|
70
|
+
this.log(`Cannot send heartbeat for ${this.serverName}: WebSocket not open`);
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
try {
|
package/dist/src/monitor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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
5
|
import { hasActiveTask, getAllActiveTaskBindings } from "./task-manager.js";
|
|
@@ -51,8 +51,10 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
51
51
|
opts.setStatus({ lastEventAt: Date.now(), lastInboundAt: Date.now() });
|
|
52
52
|
}
|
|
53
53
|
: undefined;
|
|
54
|
+
// â
Set runtime for WebSocket manager logging before creating/getting manager
|
|
55
|
+
setClientRuntime(runtime);
|
|
54
56
|
// đ Diagnose WebSocket managers before gateway start
|
|
55
|
-
|
|
57
|
+
log("đ [DIAGNOSTICS] Checking WebSocket managers before gateway start...");
|
|
56
58
|
diagnoseAllManagers();
|
|
57
59
|
// Get WebSocket manager (cached)
|
|
58
60
|
const wsManager = getXYWebSocketManager(account);
|
|
@@ -141,7 +143,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
141
143
|
opts.setStatus?.({ connected: true });
|
|
142
144
|
};
|
|
143
145
|
const disconnectedHandler = (serverId) => {
|
|
144
|
-
|
|
146
|
+
log(`XY gateway: ${serverId} disconnected`);
|
|
145
147
|
loggedServers.delete(serverId);
|
|
146
148
|
// â
Report disconnection status (only if all servers disconnected)
|
|
147
149
|
if (loggedServers.size === 0) {
|
|
@@ -177,13 +179,13 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
177
179
|
const cleanup = () => {
|
|
178
180
|
log("XY gateway: cleaning up...");
|
|
179
181
|
// đ Diagnose before cleanup
|
|
180
|
-
|
|
182
|
+
log("đ [DIAGNOSTICS] Checking WebSocket managers before cleanup...");
|
|
181
183
|
diagnoseAllManagers();
|
|
182
184
|
// Stop health check interval
|
|
183
185
|
if (healthCheckInterval) {
|
|
184
186
|
clearInterval(healthCheckInterval);
|
|
185
187
|
healthCheckInterval = null;
|
|
186
|
-
|
|
188
|
+
log("â¸ī¸ Stopped periodic health check");
|
|
187
189
|
}
|
|
188
190
|
// Remove event handlers to prevent duplicate calls on gateway restart
|
|
189
191
|
wsManager.off("message", messageHandler);
|
|
@@ -205,7 +207,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
205
207
|
activeMessages.clear();
|
|
206
208
|
log(`[MONITOR-HANDLER] đ§š Cleanup complete, cleared active messages and sessions`);
|
|
207
209
|
// đ Diagnose after cleanup
|
|
208
|
-
|
|
210
|
+
log("đ [DIAGNOSTICS] Checking WebSocket managers after cleanup...");
|
|
209
211
|
diagnoseAllManagers();
|
|
210
212
|
};
|
|
211
213
|
const handleAbort = async () => {
|
|
@@ -258,20 +260,20 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
258
260
|
wsManager.on("self-evolution-state-get-event", selfEvolutionStateGetHandler);
|
|
259
261
|
wsManager.on("login-token-event", loginTokenEventHandler);
|
|
260
262
|
// Start periodic health check (every 6 hours)
|
|
261
|
-
|
|
263
|
+
log("đĨ Starting periodic health check (every 6 hours)...");
|
|
262
264
|
healthCheckInterval = setInterval(() => {
|
|
263
|
-
|
|
265
|
+
log("đĨ [HEALTH CHECK] Periodic WebSocket diagnostics...");
|
|
264
266
|
diagnoseAllManagers();
|
|
265
267
|
// Auto-cleanup orphan connections
|
|
266
268
|
const cleaned = cleanupOrphanConnections();
|
|
267
269
|
if (cleaned > 0) {
|
|
268
|
-
|
|
270
|
+
log(`đ§š [HEALTH CHECK] Auto-cleaned ${cleaned} manager(s) with orphan connections`);
|
|
269
271
|
}
|
|
270
272
|
// Cleanup stale sessions (older than 10min TTL)
|
|
271
273
|
const cleanedSessions = cleanupStaleSessions();
|
|
272
274
|
const remainingSessions = getActiveSessionCount();
|
|
273
275
|
if (cleanedSessions > 0 || remainingSessions > 0) {
|
|
274
|
-
|
|
276
|
+
log(`đ§š [HEALTH CHECK] Sessions: cleaned=${cleanedSessions}, active=${remainingSessions}`);
|
|
275
277
|
}
|
|
276
278
|
// Cleanup stale temp files (older than 24 hours)
|
|
277
279
|
void cleanupStaleTempFiles();
|
package/dist/src/outbound.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
+
logger.log(`[xyOutbound.sendText] â
Push data saved with ID: ${pushDataId.substring(0, 20)}`);
|
|
109
110
|
}
|
|
110
111
|
catch (error) {
|
|
111
|
-
|
|
112
|
+
logger.error(`[xyOutbound.sendText] â Failed to save push data:`, error);
|
|
112
113
|
// åĻææäš
åå¤ąč´Ĩīŧäģįļįģ§įģåéīŧä¸éģåĄä¸ģæĩį¨īŧ
|
|
113
114
|
pushDataId = "";
|
|
114
115
|
}
|
|
115
116
|
// 2. č¯ģåææ pushId
|
|
116
|
-
|
|
117
|
+
logger.log(`[xyOutbound.sendText] Loading all pushIds...`);
|
|
117
118
|
let pushIdList = [];
|
|
118
119
|
try {
|
|
119
120
|
pushIdList = await getAllPushIds();
|
|
120
|
-
|
|
121
|
+
logger.log(`[xyOutbound.sendText] â
Loaded ${pushIdList.length} pushIds`);
|
|
121
122
|
}
|
|
122
123
|
catch (error) {
|
|
123
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
146
|
+
logger.log(`[xyOutbound.sendText] â
Sent successfully to pushId: ${pushId.substring(0, 20)}...`);
|
|
146
147
|
}
|
|
147
148
|
catch (error) {
|
|
148
149
|
failureCount++;
|
|
149
|
-
|
|
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
|
-
|
|
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
|
-
|
|
226
|
+
logger.log(`[xyOutbound.sendMedia] WebSocket message sent successfully`);
|
|
226
227
|
// Return message info
|
|
227
228
|
return {
|
|
228
229
|
channel: "xiaoyi-channel",
|
package/dist/src/provider.js
CHANGED
|
@@ -127,7 +127,7 @@ function createRetryingStream(createStream, cronJob) {
|
|
|
127
127
|
if (!hasContent && !isContent) {
|
|
128
128
|
// ââ Buffer phase (no content yet) ââ
|
|
129
129
|
if (event.type === "done") {
|
|
130
|
-
|
|
130
|
+
logger.log(`[xiaoyiprovider] stream completed (no content), usage: input=${event.message?.usage?.input} output=${event.message?.usage?.output}`);
|
|
131
131
|
for (const b of buffer)
|
|
132
132
|
yield b;
|
|
133
133
|
resultResolve(event.message);
|
|
@@ -142,7 +142,7 @@ function createRetryingStream(createStream, cronJob) {
|
|
|
142
142
|
else {
|
|
143
143
|
// ââ Streaming phase ââ
|
|
144
144
|
if (!hasContent) {
|
|
145
|
-
|
|
145
|
+
logger.log("[xiaoyiprovider] first content event received, switching to streaming mode");
|
|
146
146
|
hasContent = true;
|
|
147
147
|
for (const b of buffer)
|
|
148
148
|
yield b;
|
|
@@ -151,13 +151,13 @@ function createRetryingStream(createStream, cronJob) {
|
|
|
151
151
|
// The SDK calls result() when it sees done/error â if we yield first, the generator
|
|
152
152
|
// suspends and can never reach resolve, causing a permanent deadlock.
|
|
153
153
|
if (event.type === "done") {
|
|
154
|
-
|
|
154
|
+
logger.log(`[xiaoyiprovider] stream completed, usage: input=${event.message?.usage?.input} output=${event.message?.usage?.output}`);
|
|
155
155
|
resultResolve(event.message);
|
|
156
156
|
yield event;
|
|
157
157
|
return;
|
|
158
158
|
}
|
|
159
159
|
if (event.type === "error") {
|
|
160
|
-
|
|
160
|
+
logger.log(`[xiaoyiprovider] stream error after content: ${event.error?.errorMessage}`);
|
|
161
161
|
errorResult = event.error;
|
|
162
162
|
break; // break inner loop, proceed to retry decision
|
|
163
163
|
}
|
|
@@ -168,15 +168,15 @@ function createRetryingStream(createStream, cronJob) {
|
|
|
168
168
|
if (errorResult?.stopReason === "error" && isRetryableProviderError(errorResult.errorMessage)) {
|
|
169
169
|
if (attempt < MAX_RETRY_ATTEMPTS - 1) {
|
|
170
170
|
const delayMs = getRetryDelayMs(attempt + 1, cronJob);
|
|
171
|
-
|
|
171
|
+
logger.log(`[xiaoyiprovider] retryable error (attempt ${attempt + 1}/${MAX_RETRY_ATTEMPTS}): ` +
|
|
172
172
|
`${errorResult.errorMessage} â retrying in ${delayMs}ms`);
|
|
173
173
|
await sleep(delayMs);
|
|
174
174
|
continue; // discard buffer, retry with a new stream
|
|
175
175
|
}
|
|
176
|
-
|
|
176
|
+
logger.log(`[xiaoyiprovider] all ${MAX_RETRY_ATTEMPTS} retries exhausted, surfacing last error`);
|
|
177
177
|
}
|
|
178
178
|
else if (errorResult) {
|
|
179
|
-
|
|
179
|
+
logger.log(`[xiaoyiprovider] non-retryable error: ${errorResult.errorMessage}`);
|
|
180
180
|
}
|
|
181
181
|
// Non-retryable or retries exhausted â yield buffered events.
|
|
182
182
|
// Resolve before yielding the terminal event to avoid the same deadlock.
|
|
@@ -196,7 +196,7 @@ function createRetryingStream(createStream, cronJob) {
|
|
|
196
196
|
return;
|
|
197
197
|
}
|
|
198
198
|
// Safety: final fallback attempt
|
|
199
|
-
|
|
199
|
+
logger.log("[xiaoyiprovider] entering final fallback attempt");
|
|
200
200
|
const lastStream = await createStream();
|
|
201
201
|
for await (const event of lastStream) {
|
|
202
202
|
if (event.type === "done") {
|
|
@@ -485,9 +485,9 @@ export const xiaoyiProvider = {
|
|
|
485
485
|
}
|
|
486
486
|
}
|
|
487
487
|
// 莰åŊčžå
Ĩ
|
|
488
|
-
|
|
488
|
+
logger.log(`[xiaoyiprovider] input messages count: ${context.messages?.length ?? 0}`);
|
|
489
489
|
if (context.systemPrompt) {
|
|
490
|
-
|
|
490
|
+
logger.log(`[xiaoyiprovider] system prompt length: ${context.systemPrompt.length}`);
|
|
491
491
|
}
|
|
492
492
|
// Reuse deviceType from extraParams instead of calling getCurrentSessionContext()
|
|
493
493
|
// again (which may be ambiguous in multi-session or async scenarios).
|
|
@@ -518,7 +518,7 @@ export const xiaoyiProvider = {
|
|
|
518
518
|
sp = sp.replace('## Runtime', combined + '\n\n## Runtime');
|
|
519
519
|
}
|
|
520
520
|
}
|
|
521
|
-
|
|
521
|
+
logger.log(`[xiaoyiprovider] system prompt optimized: ${beforeLen} -> ${sp.length}`);
|
|
522
522
|
context.systemPrompt = sp;
|
|
523
523
|
}
|
|
524
524
|
const selfEvolutionEnabled = await selfEvolutionManager.isEnabled();
|
|
@@ -550,7 +550,7 @@ export const xiaoyiProvider = {
|
|
|
550
550
|
// ââ Retry-capable streaming ââââââââââââââââââââââââââââââ
|
|
551
551
|
const cronJob = isCronTriggered(context.messages);
|
|
552
552
|
if (cronJob)
|
|
553
|
-
|
|
553
|
+
logger.log("[xiaoyiprovider] detected cron-triggered request, using extended retry delays");
|
|
554
554
|
const makeStream = () => underlying(model, context, {
|
|
555
555
|
...options,
|
|
556
556
|
headers: {
|
package/dist/src/push.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Push message service for scheduled tasks
|
|
2
2
|
import fetch from "node-fetch";
|
|
3
3
|
import { randomUUID } from "crypto";
|
|
4
|
+
import { logger } from "./utils/logger.js";
|
|
4
5
|
/**
|
|
5
6
|
* Service for sending push messages to users.
|
|
6
7
|
* Used for outbound messages and scheduled tasks.
|
|
@@ -33,8 +34,8 @@ export class XYPushService {
|
|
|
33
34
|
const traceId = this.generateTraceId();
|
|
34
35
|
// Use provided pushId or fall back to config pushId
|
|
35
36
|
const actualPushId = pushId || this.config.pushId;
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
logger.log(`[PUSH] đ¤ Preparing to send push message`);
|
|
38
|
+
logger.log(`[PUSH] - Using pushId: ${actualPushId.substring(0, 20)}...`);
|
|
38
39
|
try {
|
|
39
40
|
const requestBody = {
|
|
40
41
|
jsonrpc: "2.0",
|
|
@@ -80,12 +81,12 @@ export class XYPushService {
|
|
|
80
81
|
body: JSON.stringify(requestBody),
|
|
81
82
|
});
|
|
82
83
|
// Log response status and headers
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
logger.log(`[PUSH] đĨ Response received`);
|
|
85
|
+
logger.log(`[PUSH] - HTTP Status: ${response.status} ${response.statusText}`);
|
|
85
86
|
if (!response.ok) {
|
|
86
87
|
const errorText = await response.text();
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
logger.log(`[PUSH] â Push request failed`);
|
|
89
|
+
logger.log(`[PUSH] - HTTP Status: ${response.status}`);
|
|
89
90
|
throw new Error(`Push failed: HTTP ${response.status} - ${errorText}`);
|
|
90
91
|
}
|
|
91
92
|
// Try to parse JSON response with detailed error handling
|
|
@@ -93,7 +94,7 @@ export class XYPushService {
|
|
|
93
94
|
try {
|
|
94
95
|
const responseText = await response.text();
|
|
95
96
|
if (!responseText || responseText.trim() === '') {
|
|
96
|
-
|
|
97
|
+
logger.log(`[PUSH] â ī¸ Received empty response body`);
|
|
97
98
|
result = {};
|
|
98
99
|
}
|
|
99
100
|
else {
|
|
@@ -101,21 +102,21 @@ export class XYPushService {
|
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
catch (parseError) {
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
logger.log(`[PUSH] â Failed to parse JSON response`);
|
|
106
|
+
logger.log(`[PUSH] - Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
106
107
|
throw new Error(`Invalid JSON response from push service: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
107
108
|
}
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
logger.log(`[PUSH] â
Push message sent successfully`);
|
|
110
|
+
logger.log(`[PUSH] - Trace ID: ${traceId}`);
|
|
110
111
|
}
|
|
111
112
|
catch (error) {
|
|
112
|
-
|
|
113
|
+
logger.log(`[PUSH] â Failed to send push message`);
|
|
113
114
|
if (error instanceof Error) {
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
logger.log(`[PUSH] - Error name: ${error.name}`);
|
|
116
|
+
logger.log(`[PUSH] - Error message: ${error.message}`);
|
|
116
117
|
}
|
|
117
118
|
else {
|
|
118
|
-
|
|
119
|
+
logger.log(`[PUSH] - Error:`, error);
|
|
119
120
|
}
|
|
120
121
|
throw error;
|
|
121
122
|
}
|
|
@@ -4,6 +4,7 @@ import { resolveXYConfig } from "./config.js";
|
|
|
4
4
|
import { getCurrentTaskId, getCurrentMessageId } from "./task-manager.js";
|
|
5
5
|
import fs from "fs/promises";
|
|
6
6
|
import path from "path";
|
|
7
|
+
import { logger } from "./utils/logger.js";
|
|
7
8
|
const TEMP_FILE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
8
9
|
/**
|
|
9
10
|
* æ¸
į /tmp/xy_channel įŽåŊä¸čļ
čŋ 24 å°æļįæ§æäģļ
|
|
@@ -31,11 +32,11 @@ export async function cleanupStaleTempFiles(tempDir = "/tmp/xy_channel") {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
if (cleanedCount > 0) {
|
|
34
|
-
|
|
35
|
+
logger.log(`[CLEANUP] đ§š Cleaned ${cleanedCount} stale files (>${TEMP_FILE_TTL_MS / 1000 / 3600}h) from ${tempDir}`);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
catch (err) {
|
|
38
|
-
|
|
39
|
+
logger.error(`[CLEANUP] â Failed to cleanup temp dir:`, err);
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { searchTools, formatToolsForContext, extractUserQuery } from "./tool-search.js";
|
|
2
|
+
import { logger } from "../utils/logger.js";
|
|
2
3
|
const TOOL_RETRIEVER_HEADER = `[įŗģįģæļæ¯īŧé፿ˇåč¨]
|
|
3
4
|
|
|
4
5
|
`;
|
|
@@ -63,10 +64,10 @@ export function createBeforePromptBuildHandler(config) {
|
|
|
63
64
|
if (!searchResult || searchResult.tools.length === 0) {
|
|
64
65
|
return undefined;
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
+
logger.log(`${PLUGIN_LOG_PREFIX} [RESULT] Found ${searchResult.tools.length} skills, building context...`);
|
|
67
68
|
const toolsContext = formatToolsForContext(searchResult, config.includeUninstalledOnly);
|
|
68
69
|
if (!toolsContext) {
|
|
69
|
-
|
|
70
|
+
logger.log(`${PLUGIN_LOG_PREFIX} [ERROR] Failed to format skills context`);
|
|
70
71
|
return undefined;
|
|
71
72
|
}
|
|
72
73
|
return {
|
|
@@ -75,7 +76,7 @@ export function createBeforePromptBuildHandler(config) {
|
|
|
75
76
|
}
|
|
76
77
|
catch (error) {
|
|
77
78
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
78
|
-
|
|
79
|
+
logger.error(`${PLUGIN_LOG_PREFIX} [ERROR] ${errorMessage}, original query: "${extractedQuery}"`);
|
|
79
80
|
return undefined;
|
|
80
81
|
}
|
|
81
82
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import * as os from "os";
|
|
4
|
+
import { logger } from "../utils/logger.js";
|
|
4
5
|
const SKILL_ID = "celia_find_skills";
|
|
5
6
|
const PLUGIN_LOG_PREFIX = "[skill-retriever]";
|
|
6
7
|
export function extractUserQuery(fullPrompt) {
|
|
@@ -89,7 +90,7 @@ export async function searchTools(options) {
|
|
|
89
90
|
const apiKey = configApiKey ?? envConfig.PERSONAL_API_KEY;
|
|
90
91
|
const uid = configUid ?? envConfig.PERSONAL_UID;
|
|
91
92
|
if (!serviceUrl || !apiKey || !uid) {
|
|
92
|
-
|
|
93
|
+
logger.warn(`${PLUGIN_LOG_PREFIX} Missing required configuration. serviceUrl: "${serviceUrl}", apiKey: "${apiKey ? '(set)' : '(missing)'} ", uid: "${uid ? '(set)' : '(missing)'}"`);
|
|
93
94
|
return null;
|
|
94
95
|
}
|
|
95
96
|
const traceId = crypto.randomUUID();
|
|
@@ -111,10 +112,10 @@ export async function searchTools(options) {
|
|
|
111
112
|
signal: AbortSignal.timeout(timeoutMs),
|
|
112
113
|
});
|
|
113
114
|
if (!response.ok) {
|
|
114
|
-
|
|
115
|
+
logger.warn(`${PLUGIN_LOG_PREFIX} HTTP error: ${response.status} ${response.statusText}`);
|
|
115
116
|
return null;
|
|
116
117
|
}
|
|
117
|
-
|
|
118
|
+
logger.log(`${PLUGIN_LOG_PREFIX} Received response, status: ${response.status}`);
|
|
118
119
|
const responseData = await response.json();
|
|
119
120
|
if (responseData.errorCode === "0" &&
|
|
120
121
|
responseData.content &&
|
|
@@ -125,18 +126,18 @@ export async function searchTools(options) {
|
|
|
125
126
|
const topTools = formattedData.slice(0, 2);
|
|
126
127
|
const allInstalled = topTools.every((tool) => tool.status === "厞åŽčŖ
");
|
|
127
128
|
if (allInstalled) {
|
|
128
|
-
|
|
129
|
+
logger.log(`${PLUGIN_LOG_PREFIX} [DEBUG] All top 2 skills are installed, returning null`);
|
|
129
130
|
return null;
|
|
130
131
|
}
|
|
131
132
|
const hasInstalledWithHighScore = topTools.some((tool) => tool.status === "厞åŽčŖ
" && (tool.rrfScore ?? 0) >= 0.016);
|
|
132
133
|
if (hasInstalledWithHighScore) {
|
|
133
|
-
|
|
134
|
+
logger.log(`${PLUGIN_LOG_PREFIX} [DEBUG] Top 2 has installed skill with rrfScore >= 0.016, returning null`);
|
|
134
135
|
return null;
|
|
135
136
|
}
|
|
136
137
|
let filteredTools = topTools.filter((tool) => tool.status === "æĒåŽčŖ
" && (tool.rrfScore ?? 0) >= 0.016);
|
|
137
|
-
|
|
138
|
+
logger.log(`${PLUGIN_LOG_PREFIX} [DEBUG] After filtering uninstalled with rrfScore >= 0.016: ${filteredTools.length}, details: ${filteredTools.map((t) => `${t.skillId}(rrfScore=${t.rrfScore})`).join(", ")}`);
|
|
138
139
|
if (filteredTools.length === 0) {
|
|
139
|
-
|
|
140
|
+
logger.log(`${PLUGIN_LOG_PREFIX} [DEBUG] No uninstalled skills with rrfScore >= 0.016, returning null`);
|
|
140
141
|
return null;
|
|
141
142
|
}
|
|
142
143
|
return {
|
|
@@ -145,7 +146,7 @@ export async function searchTools(options) {
|
|
|
145
146
|
timestamp: Date.now(),
|
|
146
147
|
};
|
|
147
148
|
}
|
|
148
|
-
|
|
149
|
+
logger.warn(`${PLUGIN_LOG_PREFIX} Invalid response format: ${JSON.stringify(responseData).slice(0, 200)}`);
|
|
149
150
|
return null;
|
|
150
151
|
}
|
|
151
152
|
catch (error) {
|
|
@@ -153,7 +154,7 @@ export async function searchTools(options) {
|
|
|
153
154
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
154
155
|
const errorCause = error instanceof Error && error.cause ? JSON.stringify(error.cause) : "N/A";
|
|
155
156
|
const errorStack = error instanceof Error ? error.stack?.split("\n").slice(0, 3).join(" | ") : "N/A";
|
|
156
|
-
|
|
157
|
+
logger.warn(`${PLUGIN_LOG_PREFIX} [ERROR] Fetch failed - name: ${errorName}, message: ${errorMessage}, cause: ${errorCause}, stack: ${errorStack}`);
|
|
157
158
|
return null;
|
|
158
159
|
}
|
|
159
160
|
}
|
|
@@ -57,7 +57,7 @@ export async function tryInjectSteer(sessionKey, message) {
|
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
59
|
};
|
|
60
|
-
|
|
60
|
+
logger.log(`[STEER] Injecting steer for sessionId=${sessionId}, taskId=${syntheticMessage.params.id}`);
|
|
61
61
|
try {
|
|
62
62
|
await handleXYMessage({
|
|
63
63
|
cfg: cachedCfg,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getXYWebSocketManager } from "../client.js";
|
|
2
2
|
import { XYFileUploadService } from "../file-upload.js";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
3
4
|
import fetch from "node-fetch";
|
|
4
5
|
import fs from "fs/promises";
|
|
5
6
|
import path from "path";
|
|
@@ -97,7 +98,7 @@ async function downloadRemoteFile(url) {
|
|
|
97
98
|
*/
|
|
98
99
|
export function createSendFileToUserTool(ctx) {
|
|
99
100
|
const { config, sessionId, taskId, messageId } = ctx;
|
|
100
|
-
|
|
101
|
+
logger.log(`[SEND-FILE-TO-USER] đ CREATE: sessionId=${sessionId} taskId=${taskId}`);
|
|
101
102
|
return {
|
|
102
103
|
name: "send_file_to_user",
|
|
103
104
|
label: "Send File to User",
|
|
@@ -236,10 +237,10 @@ b. æäŊčļ
æļæļé´ä¸ē2åéīŧ120į§īŧīŧ蝎åŋéå¤č°į¨æ¤åˇĨå
ˇīŧåĻ
|
|
|
236
237
|
error: { code: 0 },
|
|
237
238
|
}),
|
|
238
239
|
};
|
|
239
|
-
|
|
240
|
+
logger.log(`[SEND-FILE-TO-USER] đ EXEC sending: sessionId=${sessionId} taskId=${taskId} fileName=${fileName}`);
|
|
240
241
|
// Send WebSocket message
|
|
241
242
|
await wsManager.sendMessage(sessionId, agentResponse);
|
|
242
|
-
|
|
243
|
+
logger.log(`send ${fileName} file to user success`);
|
|
243
244
|
sentFiles.push({ fileName, fileId });
|
|
244
245
|
}
|
|
245
246
|
return {
|
|
@@ -103,7 +103,7 @@ export function getLatestSessionContext() {
|
|
|
103
103
|
* This ensures thread-safe context isolation for concurrent requests.
|
|
104
104
|
*/
|
|
105
105
|
export function runWithSessionContext(context, callback) {
|
|
106
|
-
|
|
106
|
+
logger.log(`[SESSION-MGR] đĩ ALS SET: sessionId=${context.sessionId} taskId=${context.taskId}`);
|
|
107
107
|
return asyncLocalStorage.run(context, callback);
|
|
108
108
|
}
|
|
109
109
|
/**
|
|
@@ -124,7 +124,7 @@ export function getCurrentSessionContext(sessionKey) {
|
|
|
124
124
|
}
|
|
125
125
|
// ALS not available â logging to understand when/why
|
|
126
126
|
const stack = new Error().stack?.split("\n").slice(2, 5).map(s => s.trim()).join(" | ");
|
|
127
|
-
|
|
127
|
+
logger.log(`[SESSION-MGR] â ī¸ ALS miss, falling back to Map (size=${activeSessions.size}), callers: ${stack}`);
|
|
128
128
|
// 2. Fallback: look up from global activeSessions Map
|
|
129
129
|
if (activeSessions.size === 0) {
|
|
130
130
|
return null;
|
|
@@ -165,7 +165,7 @@ export function getCurrentSessionContext(sessionKey) {
|
|
|
165
165
|
if (lastKey) {
|
|
166
166
|
const lastEntry = activeSessions.get(lastKey);
|
|
167
167
|
if (lastEntry) {
|
|
168
|
-
|
|
168
|
+
logger.log(`[SESSION-MGR] đ¯ using lastRegistered session: ${lastKey}`);
|
|
169
169
|
const { refCount, createdAt, ...context } = lastEntry;
|
|
170
170
|
return enrichWithLatestTaskInfo(context);
|
|
171
171
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import fsp from "node:fs/promises";
|
|
3
|
+
import { logger } from "./logger.js";
|
|
3
4
|
const SELF_EVOLUTION_ENV_FILE = "/home/sandbox/.openclaw/.xiaoyiruntime";
|
|
4
5
|
const SELF_EVOLUTION_ENV_KEY = "selfEvolutionState";
|
|
5
6
|
function parseBooleanLike(value) {
|
|
@@ -24,7 +25,7 @@ class SelfEvolutionManager {
|
|
|
24
25
|
catch (error) {
|
|
25
26
|
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
26
27
|
if (code !== "ENOENT") {
|
|
27
|
-
|
|
28
|
+
logger.error(`[SELF_EVOLUTION] Failed to read ${SELF_EVOLUTION_ENV_FILE}:`, error);
|
|
28
29
|
}
|
|
29
30
|
return false;
|
|
30
31
|
}
|
|
@@ -59,7 +60,7 @@ class SelfEvolutionManager {
|
|
|
59
60
|
catch (error) {
|
|
60
61
|
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
61
62
|
if (code !== "ENOENT") {
|
|
62
|
-
|
|
63
|
+
logger.error(`[SELF_EVOLUTION] Failed to read ${SELF_EVOLUTION_ENV_FILE}:`, error);
|
|
63
64
|
}
|
|
64
65
|
return false;
|
|
65
66
|
}
|
package/dist/src/websocket.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// WebSocket connection manager (Single connection)
|
|
2
|
+
import os from "os";
|
|
2
3
|
import WebSocket from "ws";
|
|
3
4
|
import { EventEmitter } from "events";
|
|
4
5
|
import { HeartbeatManager } from "./heartbeat.js";
|
|
@@ -293,10 +294,11 @@ export class XYWebSocketManager extends EventEmitter {
|
|
|
293
294
|
this.error("Cannot send init message: WebSocket not open");
|
|
294
295
|
return;
|
|
295
296
|
}
|
|
297
|
+
const hostname = os.hostname();
|
|
296
298
|
const initMessage = {
|
|
297
299
|
msgType: "clawd_bot_init",
|
|
298
300
|
agentId: this.config.agentId,
|
|
299
|
-
msgDetail: JSON.stringify({ agentId: this.config.agentId }),
|
|
301
|
+
msgDetail: JSON.stringify({ agentId: this.config.agentId, hostname }),
|
|
300
302
|
};
|
|
301
303
|
const initMessageStr = JSON.stringify(initMessage);
|
|
302
304
|
this.log("[WS-SEND] Sending init message frame:", JSON.stringify(initMessage, null, 2));
|