@ynhcj/xiaoyi-channel 0.0.172-next → 0.0.173-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.
- package/dist/index.js +3 -3
- package/dist/src/bot.js +14 -34
- package/dist/src/channel.js +61 -64
- package/dist/src/client.d.ts +5 -0
- package/dist/src/client.js +10 -0
- package/dist/src/cspl/call_api.js +2 -0
- package/dist/src/cspl/sentinel_hook.js +9 -8
- package/dist/src/formatter.js +12 -23
- package/dist/src/memory-query-handler.js +2 -33
- package/dist/src/monitor.js +20 -9
- package/dist/src/parser.d.ts +3 -3
- package/dist/src/parser.js +6 -6
- package/dist/src/provider.js +40 -69
- package/dist/src/reply-dispatcher.js +29 -45
- package/dist/src/task-manager.d.ts +5 -6
- package/dist/src/task-manager.js +5 -9
- package/dist/src/tools/agent-as-skill-tool.d.ts +41 -2
- package/dist/src/tools/agent-as-skill-tool.js +144 -146
- package/dist/src/tools/calendar-tool.d.ts +24 -2
- package/dist/src/tools/calendar-tool.js +115 -117
- package/dist/src/tools/call-device-tool.d.ts +20 -6
- package/dist/src/tools/call-device-tool.js +116 -138
- package/dist/src/tools/call-phone-tool.d.ts +21 -2
- package/dist/src/tools/call-phone-tool.js +112 -114
- package/dist/src/tools/check-plugin-privilege-tool.d.ts +16 -2
- package/dist/src/tools/check-plugin-privilege-tool.js +141 -143
- package/dist/src/tools/create-alarm-tool.d.ts +39 -2
- package/dist/src/tools/create-alarm-tool.js +229 -231
- package/dist/src/tools/create-all-tools.js +2 -0
- package/dist/src/tools/delete-alarm-tool.d.ts +15 -2
- package/dist/src/tools/delete-alarm-tool.js +134 -136
- package/dist/src/tools/device-tool-map.js +1 -0
- package/dist/src/tools/discover-cross-devices-tool.d.ts +16 -2
- package/dist/src/tools/discover-cross-devices-tool.js +121 -124
- package/dist/src/tools/display-a2ui-card-bypath-tool.d.ts +21 -0
- package/dist/src/tools/display-a2ui-card-bypath-tool.js +65 -0
- package/dist/src/tools/display-a2ui-card-tool.d.ts +27 -2
- package/dist/src/tools/display-a2ui-card-tool.js +65 -68
- package/dist/src/tools/get-alarm-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-alarm-tool-schema.js +10 -16
- package/dist/src/tools/get-calendar-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-calendar-tool-schema.js +8 -12
- package/dist/src/tools/get-collection-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-collection-tool-schema.js +9 -11
- package/dist/src/tools/get-contact-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-contact-tool-schema.js +10 -16
- package/dist/src/tools/get-device-file-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-device-file-tool-schema.js +9 -13
- package/dist/src/tools/get-email-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-email-tool-schema.js +8 -11
- package/dist/src/tools/get-note-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-note-tool-schema.js +9 -14
- package/dist/src/tools/get-photo-tool-schema.d.ts +1 -2
- package/dist/src/tools/get-photo-tool-schema.js +9 -12
- package/dist/src/tools/image-reading-tool.d.ts +28 -2
- package/dist/src/tools/image-reading-tool.js +76 -76
- package/dist/src/tools/location-tool.d.ts +11 -2
- package/dist/src/tools/location-tool.js +93 -92
- package/dist/src/tools/login-token-tool.d.ts +20 -2
- package/dist/src/tools/login-token-tool.js +122 -125
- package/dist/src/tools/modify-alarm-tool.d.ts +47 -2
- package/dist/src/tools/modify-alarm-tool.js +250 -252
- package/dist/src/tools/modify-note-tool.d.ts +20 -2
- package/dist/src/tools/modify-note-tool.js +107 -109
- package/dist/src/tools/note-tool.d.ts +20 -2
- package/dist/src/tools/note-tool.js +106 -108
- package/dist/src/tools/query-app-message-tool.d.ts +28 -2
- package/dist/src/tools/query-app-message-tool.js +111 -113
- package/dist/src/tools/query-memory-data-tool.d.ts +28 -2
- package/dist/src/tools/query-memory-data-tool.js +112 -114
- package/dist/src/tools/query-todo-task-tool.d.ts +24 -2
- package/dist/src/tools/query-todo-task-tool.js +106 -108
- package/dist/src/tools/save-file-to-phone-tool.d.ts +24 -2
- package/dist/src/tools/save-file-to-phone-tool.js +130 -132
- package/dist/src/tools/save-media-to-gallery-tool.d.ts +24 -2
- package/dist/src/tools/save-media-to-gallery-tool.js +137 -139
- package/dist/src/tools/save-self-evolution-skill-tool.d.ts +54 -2
- package/dist/src/tools/save-self-evolution-skill-tool.js +194 -194
- package/dist/src/tools/search-alarm-tool.d.ts +34 -2
- package/dist/src/tools/search-alarm-tool.js +174 -176
- package/dist/src/tools/search-calendar-tool.d.ts +24 -2
- package/dist/src/tools/search-calendar-tool.js +148 -150
- package/dist/src/tools/search-contact-tool.d.ts +16 -2
- package/dist/src/tools/search-contact-tool.js +101 -103
- package/dist/src/tools/search-email-tool.d.ts +21 -2
- package/dist/src/tools/search-email-tool.js +110 -112
- package/dist/src/tools/search-file-tool.d.ts +16 -2
- package/dist/src/tools/search-file-tool.js +104 -106
- package/dist/src/tools/search-message-tool.d.ts +16 -2
- package/dist/src/tools/search-message-tool.js +103 -105
- package/dist/src/tools/search-note-tool.d.ts +16 -2
- package/dist/src/tools/search-note-tool.js +98 -100
- package/dist/src/tools/search-photo-gallery-tool.d.ts +21 -2
- package/dist/src/tools/search-photo-gallery-tool.js +35 -37
- package/dist/src/tools/send-cross-device-task-tool.d.ts +35 -2
- package/dist/src/tools/send-cross-device-task-tool.js +138 -143
- package/dist/src/tools/send-email-tool.d.ts +24 -2
- package/dist/src/tools/send-email-tool.js +108 -110
- package/dist/src/tools/send-file-to-user-tool.d.ts +20 -2
- package/dist/src/tools/send-file-to-user-tool.js +172 -176
- package/dist/src/tools/send-html-card-tool.d.ts +20 -2
- package/dist/src/tools/send-html-card-tool.js +85 -87
- package/dist/src/tools/send-message-tool.d.ts +20 -2
- package/dist/src/tools/send-message-tool.js +122 -124
- package/dist/src/tools/session-manager.d.ts +9 -52
- package/dist/src/tools/session-manager.js +40 -231
- package/dist/src/tools/upload-file-tool.d.ts +20 -2
- package/dist/src/tools/upload-file-tool.js +80 -82
- package/dist/src/tools/upload-photo-tool.d.ts +20 -2
- package/dist/src/tools/upload-photo-tool.js +68 -70
- package/dist/src/tools/xiaoyi-add-collection-tool.d.ts +32 -2
- package/dist/src/tools/xiaoyi-add-collection-tool.js +146 -148
- package/dist/src/tools/xiaoyi-collection-tool.d.ts +20 -2
- package/dist/src/tools/xiaoyi-collection-tool.js +114 -116
- package/dist/src/tools/xiaoyi-delete-collection-tool.d.ts +15 -2
- package/dist/src/tools/xiaoyi-delete-collection-tool.js +127 -129
- package/dist/src/tools/xiaoyi-gui-tool.d.ts +16 -2
- package/dist/src/tools/xiaoyi-gui-tool.js +92 -95
- package/dist/src/utils/logger.js +3 -14
- package/dist/src/websocket.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { xiaoyiProvider } from "./src/provider.js";
|
|
|
3
3
|
import { xyPlugin } from "./src/channel.js";
|
|
4
4
|
import registerSentinelHook from "./src/cspl/sentinel_hook.js";
|
|
5
5
|
import { setXYRuntime } from "./src/runtime.js";
|
|
6
|
-
import { markCronToolCall, clearCronToolCall,
|
|
6
|
+
import { markCronToolCall, clearCronToolCall, getCurrentSessionContext } from "./src/tools/session-manager.js";
|
|
7
7
|
import { configManager } from "./src/utils/config-manager.js";
|
|
8
8
|
import { setJobPushId } from "./src/utils/cron-push-map.js";
|
|
9
9
|
import { getAllPushIds } from "./src/utils/pushid-manager.js";
|
|
@@ -54,10 +54,10 @@ async function captureCronAddMapping(event, ctx) {
|
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
console.log(`[CRONMAP] extracted jobId=${jobId}`);
|
|
57
|
-
const sessionCtx =
|
|
57
|
+
const sessionCtx = getCurrentSessionContext();
|
|
58
58
|
const sessionId = sessionCtx?.sessionId;
|
|
59
59
|
if (!sessionId) {
|
|
60
|
-
console.log(`[CRONMAP] skip: no sessionId (
|
|
60
|
+
console.log(`[CRONMAP] skip: no sessionId in ALS scope (ctxFound=${!!sessionCtx})`);
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
63
|
const pushId = await resolvePushId(sessionId);
|
package/dist/src/bot.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { updateSessionStoreEntry, updateSessionStore, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
2
2
|
import { getXYRuntime } from "./runtime.js";
|
|
3
3
|
import { createXYReplyDispatcher } from "./reply-dispatcher.js";
|
|
4
|
-
import { parseA2AMessage, extractTextFromParts, extractFileParts, extractPushId, extractDeviceType, extractAppVer,
|
|
4
|
+
import { parseA2AMessage, extractTextFromParts, extractFileParts, extractPushId, extractDeviceType, extractAppVer, extractSdkApiVersion, extractModelName, extractTriggerData, extractRunCrossTaskContext } from "./parser.js";
|
|
5
5
|
import { downloadFilesFromParts } from "./file-download.js";
|
|
6
6
|
import { resolveXYConfig } from "./config.js";
|
|
7
7
|
import { sendStatusUpdate, sendClearContextResponse, sendTasksCancelResponse, sendA2AResponse } from "./formatter.js";
|
|
8
8
|
import { appendSelfEvolutionKeywordNudge, shouldNudgeForSelfEvolutionKeyword, } from "./self-evolution-keyword.js";
|
|
9
|
-
import {
|
|
9
|
+
import { runWithSessionContext } from "./tools/session-manager.js";
|
|
10
10
|
import { configManager } from "./utils/config-manager.js";
|
|
11
11
|
import { addPushId } from "./utils/pushid-manager.js";
|
|
12
12
|
import { getPushDataById } from "./utils/pushdata-manager.js";
|
|
@@ -139,14 +139,14 @@ export async function handleXYMessage(params) {
|
|
|
139
139
|
if (deviceType) {
|
|
140
140
|
log.log(`[BOT] Extracted deviceType: ${deviceType}`);
|
|
141
141
|
}
|
|
142
|
-
// Extract app_ver and
|
|
142
|
+
// Extract app_ver and sdk_api_version if present
|
|
143
143
|
const appVer = extractAppVer(parsed.parts);
|
|
144
144
|
if (appVer) {
|
|
145
145
|
log.log(`[BOT] Extracted app_ver: ${appVer}`);
|
|
146
146
|
}
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
149
|
-
log.log(`[BOT] Extracted
|
|
147
|
+
const sdkApiVersion = extractSdkApiVersion(parsed.parts);
|
|
148
|
+
if (sdkApiVersion) {
|
|
149
|
+
log.log(`[BOT] Extracted sdk_api_version: ${sdkApiVersion}`);
|
|
150
150
|
}
|
|
151
151
|
// Extract modelName if present (used by provider.ts to override model.id)
|
|
152
152
|
const modelName = extractModelName(parsed.parts);
|
|
@@ -169,21 +169,10 @@ export async function handleXYMessage(params) {
|
|
|
169
169
|
},
|
|
170
170
|
});
|
|
171
171
|
log.log(`[BOT] Resolved route, sessionKey=${route.sessionKey}`);
|
|
172
|
-
//
|
|
172
|
+
// ALS only: no registerSession. The sessionContext built below is handed
|
|
173
|
+
// to runWithSessionContext() inside withReplyDispatcher.run, which is the
|
|
174
|
+
// single wrap point for the whole agent turn.
|
|
173
175
|
if (!skipReg) {
|
|
174
|
-
registerSession(route.sessionKey, {
|
|
175
|
-
config,
|
|
176
|
-
sessionId: parsed.sessionId,
|
|
177
|
-
distributionSessionId,
|
|
178
|
-
taskId: parsed.taskId,
|
|
179
|
-
messageId: parsed.messageId,
|
|
180
|
-
agentId: route.accountId,
|
|
181
|
-
deviceType,
|
|
182
|
-
appVer: appVer ?? undefined,
|
|
183
|
-
displayVersion: displayVersion ?? undefined,
|
|
184
|
-
modelName,
|
|
185
|
-
runCrossTaskContext: runCrossTaskContext ?? undefined,
|
|
186
|
-
});
|
|
187
176
|
// 🔑 Sync A2A modelName to OpenClaw session store so that session_status
|
|
188
177
|
// reports the correct model. Without this, session_status returns the
|
|
189
178
|
// configured default model instead of the A2A-specified one.
|
|
@@ -376,6 +365,8 @@ export async function handleXYMessage(params) {
|
|
|
376
365
|
messageId: parsed.messageId,
|
|
377
366
|
agentId: route.accountId,
|
|
378
367
|
deviceType,
|
|
368
|
+
appVer: appVer ?? undefined,
|
|
369
|
+
sdkApiVersion: sdkApiVersion ?? undefined,
|
|
379
370
|
modelName,
|
|
380
371
|
runCrossTaskContext: runCrossTaskContext ?? undefined,
|
|
381
372
|
};
|
|
@@ -391,7 +382,6 @@ export async function handleXYMessage(params) {
|
|
|
391
382
|
}
|
|
392
383
|
streamingSignals.delete(parsed.sessionId);
|
|
393
384
|
decrementTaskIdRef(parsed.sessionId);
|
|
394
|
-
unregisterSession(route.sessionKey);
|
|
395
385
|
log.log(`[BOT] Cleanup completed`);
|
|
396
386
|
},
|
|
397
387
|
run: () => {
|
|
@@ -401,6 +391,7 @@ export async function handleXYMessage(params) {
|
|
|
401
391
|
// signal init complete to release the global dispatch gate
|
|
402
392
|
// for the next session.
|
|
403
393
|
const dispatchPromise = runWithSessionContext(sessionContext, async () => {
|
|
394
|
+
log.log(`[ALS-PROOF] bot entered dispatch scope sessionId=${sessionContext.sessionId} taskId=${sessionContext.taskId} isSteer=false`);
|
|
404
395
|
log.log(`[BOT-DISPATCH] dispatchReplyFromConfig starting, body.length=${ctxPayload.Body?.length ?? 0}`);
|
|
405
396
|
try {
|
|
406
397
|
const result = await core.channel.reply.dispatchReplyFromConfig({
|
|
@@ -433,7 +424,7 @@ export async function handleXYMessage(params) {
|
|
|
433
424
|
errLog.error("Failed to handle XY message:", err);
|
|
434
425
|
runtime.error?.(`xy: Failed to handle message: ${String(err)}`);
|
|
435
426
|
errLog.log(`[BOT] Error occurred, attempting cleanup`);
|
|
436
|
-
// 🔑 错误时也要清理taskId
|
|
427
|
+
// 🔑 错误时也要清理taskId(session 走 ALS,作用域退出自动清理)
|
|
437
428
|
try {
|
|
438
429
|
const params = message.params;
|
|
439
430
|
const sessionId = params?.sessionId;
|
|
@@ -441,18 +432,6 @@ export async function handleXYMessage(params) {
|
|
|
441
432
|
errLog.log(`[BOT] Cleaning up after error`);
|
|
442
433
|
// 清理 taskId
|
|
443
434
|
decrementTaskIdRef(sessionId);
|
|
444
|
-
// 清理 session
|
|
445
|
-
const core = getXYRuntime();
|
|
446
|
-
const route = core.channel.routing.resolveAgentRoute({
|
|
447
|
-
cfg,
|
|
448
|
-
channel: "xiaoyi-channel",
|
|
449
|
-
accountId,
|
|
450
|
-
peer: {
|
|
451
|
-
kind: "direct",
|
|
452
|
-
id: sessionId,
|
|
453
|
-
},
|
|
454
|
-
});
|
|
455
|
-
unregisterSession(route.sessionKey);
|
|
456
435
|
errLog.log(`[BOT] Cleanup completed after error`);
|
|
457
436
|
}
|
|
458
437
|
}
|
|
@@ -637,6 +616,7 @@ async function dispatchSteerWhenReady(params) {
|
|
|
637
616
|
},
|
|
638
617
|
run: () => {
|
|
639
618
|
return runWithSessionContext(sessionContext, async () => {
|
|
619
|
+
log.log(`[ALS-PROOF] bot entered steer dispatch scope sessionId=${sessionContext.sessionId} taskId=${sessionContext.taskId} isSteer=true`);
|
|
640
620
|
const result = await core.channel.reply.dispatchReplyFromConfig({
|
|
641
621
|
ctx: ctxPayload,
|
|
642
622
|
cfg: params.cfg,
|
package/dist/src/channel.js
CHANGED
|
@@ -2,15 +2,57 @@ import { resolveXYConfig, listXYAccountIds, getDefaultXYAccountId } from "./conf
|
|
|
2
2
|
import { xyConfigSchema } from "./config-schema.js";
|
|
3
3
|
import { xyOutbound } from "./outbound.js";
|
|
4
4
|
import { filterToolsByDevice } from "./tools/device-tool-map.js";
|
|
5
|
-
import { getCurrentSessionContext
|
|
6
|
-
import { createAllTools } from "./tools/create-all-tools.js";
|
|
5
|
+
import { getCurrentSessionContext } from "./tools/session-manager.js";
|
|
7
6
|
import { logger } from "./utils/logger.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
// Static tool imports (3.24 pattern)
|
|
8
|
+
import { locationTool } from "./tools/location-tool.js";
|
|
9
|
+
import { xiaoyiGuiTool } from "./tools/xiaoyi-gui-tool.js";
|
|
10
|
+
import { sendFileToUserTool } from "./tools/send-file-to-user-tool.js";
|
|
11
|
+
import { sendHtmlCardTool } from "./tools/send-html-card-tool.js";
|
|
12
|
+
import { viewPushResultTool } from "./tools/view-push-result-tool.js";
|
|
13
|
+
import { imageReadingTool } from "./tools/image-reading-tool.js";
|
|
14
|
+
import { timestampToUtc8Tool } from "./tools/timestamp-to-utc8-tool.js";
|
|
15
|
+
import { saveSelfEvolutionSkillTool } from "./tools/save-self-evolution-skill-tool.js";
|
|
16
|
+
import { callDeviceTool } from "./tools/call-device-tool.js";
|
|
17
|
+
import { getNoteToolSchemaTool } from "./tools/get-note-tool-schema.js";
|
|
18
|
+
import { getCalendarToolSchemaTool } from "./tools/get-calendar-tool-schema.js";
|
|
19
|
+
import { getContactToolSchemaTool } from "./tools/get-contact-tool-schema.js";
|
|
20
|
+
import { getPhotoToolSchemaTool } from "./tools/get-photo-tool-schema.js";
|
|
21
|
+
import { getDeviceFileToolSchemaTool } from "./tools/get-device-file-tool-schema.js";
|
|
22
|
+
import { getAlarmToolSchemaTool } from "./tools/get-alarm-tool-schema.js";
|
|
23
|
+
import { getCollectionToolSchemaTool } from "./tools/get-collection-tool-schema.js";
|
|
24
|
+
import { loginTokenTool } from "./tools/login-token-tool.js";
|
|
25
|
+
import { agentAsSkillTool } from "./tools/agent-as-skill-tool.js";
|
|
26
|
+
import { discoverCrossDevicesTool } from "./tools/discover-cross-devices-tool.js";
|
|
27
|
+
import { sendCrossDeviceTaskTool } from "./tools/send-cross-device-task-tool.js";
|
|
28
|
+
import { displayA2UICardTool } from "./tools/display-a2ui-card-tool.js";
|
|
29
|
+
import { displayA2UICardByPathTool } from "./tools/display-a2ui-card-bypath-tool.js";
|
|
30
|
+
import { checkPluginPrivilegeTool } from "./tools/check-plugin-privilege-tool.js";
|
|
31
|
+
const ALL_TOOLS = [
|
|
32
|
+
locationTool,
|
|
33
|
+
discoverCrossDevicesTool,
|
|
34
|
+
sendCrossDeviceTaskTool,
|
|
35
|
+
displayA2UICardTool,
|
|
36
|
+
displayA2UICardByPathTool,
|
|
37
|
+
callDeviceTool,
|
|
38
|
+
getNoteToolSchemaTool,
|
|
39
|
+
getCalendarToolSchemaTool,
|
|
40
|
+
getContactToolSchemaTool,
|
|
41
|
+
getPhotoToolSchemaTool,
|
|
42
|
+
xiaoyiGuiTool,
|
|
43
|
+
getDeviceFileToolSchemaTool,
|
|
44
|
+
getAlarmToolSchemaTool,
|
|
45
|
+
getCollectionToolSchemaTool,
|
|
46
|
+
sendFileToUserTool,
|
|
47
|
+
sendHtmlCardTool,
|
|
48
|
+
viewPushResultTool,
|
|
49
|
+
imageReadingTool,
|
|
50
|
+
timestampToUtc8Tool,
|
|
51
|
+
saveSelfEvolutionSkillTool,
|
|
52
|
+
loginTokenTool,
|
|
53
|
+
agentAsSkillTool,
|
|
54
|
+
checkPluginPrivilegeTool,
|
|
55
|
+
];
|
|
14
56
|
/**
|
|
15
57
|
* Xiaoyi Channel Plugin for OpenClaw.
|
|
16
58
|
* Implements Xiaoyi A2A protocol with dual WebSocket connections.
|
|
@@ -32,7 +74,7 @@ export const xyPlugin = {
|
|
|
32
74
|
],
|
|
33
75
|
},
|
|
34
76
|
capabilities: {
|
|
35
|
-
chatTypes: ["direct"],
|
|
77
|
+
chatTypes: ["direct"],
|
|
36
78
|
polls: false,
|
|
37
79
|
threads: false,
|
|
38
80
|
media: true,
|
|
@@ -49,59 +91,11 @@ export const xyPlugin = {
|
|
|
49
91
|
schema: xyConfigSchema,
|
|
50
92
|
},
|
|
51
93
|
outbound: xyOutbound,
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
* 1. **Normal (WebSocket) session** – `getCurrentSessionContext()` returns
|
|
58
|
-
* a context that was registered by bot.ts during message processing.
|
|
59
|
-
* Tools send commands through the WebSocket and listen for responses.
|
|
60
|
-
*
|
|
61
|
-
* 2. **Cron / scheduled-task session** – openclaw's cron runner calls
|
|
62
|
-
* `agentTools({ cfg })` without an active WebSocket session. When no
|
|
63
|
-
* session context exists but `cfg` is provided, we create a synthetic
|
|
64
|
-
* "cron session" with `isCron: true` and a `cron-`-prefixed sessionId.
|
|
65
|
-
* `sendCommand()` detects this prefix and routes commands through the
|
|
66
|
-
* push channel. Response listening (WebSocket events) works unchanged
|
|
67
|
-
* because the gateway WebSocket connection is always active.
|
|
68
|
-
*/
|
|
69
|
-
agentTools: (params) => {
|
|
70
|
-
let ctx = getCurrentSessionContext();
|
|
71
|
-
// ── Cron / non-session fallback ──────────────────────────────
|
|
72
|
-
// When no active xy WebSocket session exists but the openclaw cfg
|
|
73
|
-
// is provided (framework calls agentTools({ cfg })), create a
|
|
74
|
-
// synthetic "cron session". This enables cron-triggered agent
|
|
75
|
-
// turns and cross-channel tool calls to use xiaoyi tools via the
|
|
76
|
-
// push channel. sendCommand() detects the "cron-" sessionId
|
|
77
|
-
// prefix and routes commands through push instead of WebSocket.
|
|
78
|
-
if (!ctx && params?.cfg) {
|
|
79
|
-
try {
|
|
80
|
-
const config = resolveXYConfig(params.cfg);
|
|
81
|
-
const cronId = `${CRON_SESSION_PREFIX}${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
82
|
-
ctx = {
|
|
83
|
-
config,
|
|
84
|
-
sessionId: cronId,
|
|
85
|
-
taskId: cronId,
|
|
86
|
-
messageId: cronId,
|
|
87
|
-
agentId: "default",
|
|
88
|
-
isCron: true,
|
|
89
|
-
};
|
|
90
|
-
// Register so getCurrentSessionContext() fallback can find it
|
|
91
|
-
registerSession(`__cron__${cronId}`, ctx);
|
|
92
|
-
logger.log(`[CRON-TOOLS] Created cron session context: ${cronId}`);
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
logger.error("[CRON-TOOLS] Failed to create cron context:", err);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (!ctx) {
|
|
99
|
-
logger.log("[CREATE-ALL-TOOLS] no session context, returning empty tools list");
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
const allTools = createAllTools(ctx);
|
|
103
|
-
const filtered = filterToolsByDevice(allTools, ctx.deviceType);
|
|
104
|
-
logger.log(`[DEVICE-FILTER] deviceType=${ctx.deviceType ?? "(none)"}, tools: ${allTools.length} → ${filtered.length} (${filtered.map(t => t.name).join(", ")})`);
|
|
94
|
+
/** Static tool list (3.24 pattern). Tools read SessionContext at execute time via ALS. */
|
|
95
|
+
agentTools: () => {
|
|
96
|
+
const ctx = getCurrentSessionContext();
|
|
97
|
+
const filtered = filterToolsByDevice(ALL_TOOLS, ctx?.deviceType);
|
|
98
|
+
logger.log(`[DEVICE-FILTER] deviceType=${ctx?.deviceType ?? "(none)"}, tools: ${ALL_TOOLS.length} → ${filtered.length} (${filtered.map(t => t.name).join(", ")})`);
|
|
105
99
|
return filtered;
|
|
106
100
|
},
|
|
107
101
|
messaging: {
|
|
@@ -113,7 +107,6 @@ export const xyPlugin = {
|
|
|
113
107
|
},
|
|
114
108
|
targetResolver: {
|
|
115
109
|
looksLikeId: (raw) => {
|
|
116
|
-
// 信任所有非空字符串作为有效的 sessionId
|
|
117
110
|
const trimmed = raw.trim();
|
|
118
111
|
return trimmed.length > 0;
|
|
119
112
|
},
|
|
@@ -139,16 +132,20 @@ export const xyPlugin = {
|
|
|
139
132
|
reload: {
|
|
140
133
|
configPrefixes: ["channels.xiaoyi-channel"],
|
|
141
134
|
},
|
|
142
|
-
// Gateway adapter for receiving messages
|
|
143
135
|
gateway: {
|
|
144
136
|
async startAccount(context) {
|
|
145
137
|
const { monitorXYProvider } = await import("./monitor.js");
|
|
138
|
+
const { createXyAcpBindingManager } = await import("./acp-session-binding.js");
|
|
146
139
|
const account = resolveXYConfig(context.cfg);
|
|
147
140
|
context.setStatus?.({
|
|
148
141
|
accountId: context.accountId,
|
|
149
142
|
wsUrl: account.wsUrl,
|
|
150
143
|
});
|
|
151
144
|
context.log?.info(`[${context.accountId}] starting xiaoyi channel (wsUrl: ${account.wsUrl})`);
|
|
145
|
+
// Register ACP session binding adapter for this account.
|
|
146
|
+
// Enables sessions_spawn(runtime="acp") to bind subagent sessions
|
|
147
|
+
// to the current A2A conversation.
|
|
148
|
+
createXyAcpBindingManager({ accountId: context.accountId, cfg: context.cfg });
|
|
152
149
|
return monitorXYProvider({
|
|
153
150
|
config: context.cfg,
|
|
154
151
|
runtime: context.runtime,
|
package/dist/src/client.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { XYWebSocketManager } from "./websocket.js";
|
|
2
2
|
import type { XYChannelConfig } from "./types.js";
|
|
3
3
|
import type { RuntimeEnv } from "openclaw/plugin-sdk";
|
|
4
|
+
/**
|
|
5
|
+
* Get a cached WebSocket manager without requiring config.
|
|
6
|
+
* Returns the first available manager. Use when ALS has no SessionContext.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getCachedXYWebSocketManager(): XYWebSocketManager;
|
|
4
9
|
/**
|
|
5
10
|
* Get or create a WebSocket manager for the given configuration.
|
|
6
11
|
* Reuses existing managers if config matches.
|
package/dist/src/client.js
CHANGED
|
@@ -13,6 +13,16 @@ if (!_g.__xyWsManagerCache) {
|
|
|
13
13
|
_g.__xyWsManagerCache = new Map();
|
|
14
14
|
}
|
|
15
15
|
const wsManagerCache = _g.__xyWsManagerCache;
|
|
16
|
+
/**
|
|
17
|
+
* Get a cached WebSocket manager without requiring config.
|
|
18
|
+
* Returns the first available manager. Use when ALS has no SessionContext.
|
|
19
|
+
*/
|
|
20
|
+
export function getCachedXYWebSocketManager() {
|
|
21
|
+
if (wsManagerCache.size === 0) {
|
|
22
|
+
throw new Error("No WebSocket manager available in cache");
|
|
23
|
+
}
|
|
24
|
+
return wsManagerCache.values().next().value;
|
|
25
|
+
}
|
|
16
26
|
/**
|
|
17
27
|
* Get or create a WebSocket manager for the given configuration.
|
|
18
28
|
* Reuses existing managers if config matches.
|
|
@@ -5,6 +5,7 @@ import https from 'https';
|
|
|
5
5
|
import { URL } from 'url';
|
|
6
6
|
import { getConfig } from './config.js';
|
|
7
7
|
import { DEFAULT_HTTPS_PORT, HTTP_STATUS_BAD_REQUEST, API_URL_SUFFIX } from './constants.js';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
8
9
|
function buildHeadersForCelia(config, sessionId) {
|
|
9
10
|
if (!config.uid || !config.apiKey || !config.skillId || !config.requestFrom) {
|
|
10
11
|
throw new Error('[SENTINEL HOOK] Missing required configuration: uid, apiKey, skillId, or requestFrom is not defined');
|
|
@@ -89,6 +90,7 @@ export async function callApi(questionText, api, sessionId, action) {
|
|
|
89
90
|
};
|
|
90
91
|
const httpBody = JSON.stringify(payload);
|
|
91
92
|
const apiUrl = `${config.api.url}${API_URL_SUFFIX}`;
|
|
93
|
+
logger.log(`[SENTINEL HOOK] callApi: action=${action}, x-hag-trace-id=${sessionId}, url=${apiUrl}`);
|
|
92
94
|
return new Promise((resolve, reject) => {
|
|
93
95
|
const options = buildRequestOptions(apiUrl, headersForCelia, config.api.timeout);
|
|
94
96
|
const req = https.request(options, (res) => {
|
|
@@ -6,15 +6,16 @@ import { callApi } from './call_api.js';
|
|
|
6
6
|
import { processText, extractResultText, validateAndTruncateText, parseSecurityResult, handleExecToolInput, handleMessageToolInput, handleOtherToolInput } from './utils.js';
|
|
7
7
|
import { ALLOWED_TOOLS, MAX_TEXT_LENGTH, MAX_TOTAL_LENGTH, MIN_TEXT_LENGTH, STEER_ABORT_MESSAGE, TOOL_OUTPUT_ACTION } from './constants.js';
|
|
8
8
|
import { logger } from '../utils/logger.js';
|
|
9
|
-
import {
|
|
9
|
+
import { getCurrentSessionContext } from '../tools/session-manager.js';
|
|
10
10
|
import { tryInjectSteer } from './steer-context.js';
|
|
11
11
|
// 主入口模块
|
|
12
12
|
export default function register(api) {
|
|
13
13
|
api.on("before_tool_call", async (event, ctx) => {
|
|
14
14
|
logger.log(`[SENTINEL HOOK] before_tool_call_event toolName: ${event.toolName}`);
|
|
15
|
-
//
|
|
16
|
-
const
|
|
17
|
-
|
|
15
|
+
// 获取真实sessionID:优先使用ALS中的A2A sessionId,降级到OpenClaw runId或随机值
|
|
16
|
+
const sessionCtx = getCurrentSessionContext();
|
|
17
|
+
const sessionId = sessionCtx?.sessionId || (event.runId?.replace(/-/g, '') || crypto.randomBytes(16).toString('hex'));
|
|
18
|
+
logger.log(`[SENTINEL HOOK] Session ID: ${sessionId} (fromALS: ${!!sessionCtx?.sessionId})`);
|
|
18
19
|
// 处理 TOOL_INPUT 数据采集、发送数据,根据扫描结果决定是否阻塞
|
|
19
20
|
try {
|
|
20
21
|
let scanResult = null;
|
|
@@ -43,9 +44,10 @@ export default function register(api) {
|
|
|
43
44
|
}
|
|
44
45
|
try {
|
|
45
46
|
logger.log(`[SENTINEL HOOK] after_tool_call_event toolName: ${event.toolName}`);
|
|
46
|
-
//
|
|
47
|
-
const
|
|
48
|
-
|
|
47
|
+
// 获取真实sessionID:优先使用ALS中的A2A sessionId,降级到OpenClaw runId或随机值
|
|
48
|
+
const sessionCtx = getCurrentSessionContext();
|
|
49
|
+
const sessionId = sessionCtx?.sessionId || (event.runId?.replace(/-/g, '') || crypto.randomBytes(16).toString('hex'));
|
|
50
|
+
logger.log(`[SENTINEL HOOK] Session ID: ${sessionId} (fromALS: ${!!sessionCtx?.sessionId})`);
|
|
49
51
|
// 处理TOOL_OUTPUT数据采集(保持现有逻辑)
|
|
50
52
|
const resultText = extractResultText(event, event.toolName);
|
|
51
53
|
const resultTextLength = resultText.length;
|
|
@@ -78,7 +80,6 @@ export default function register(api) {
|
|
|
78
80
|
logger.log(`[SENTINEL HOOK] TOOL_OUTPUT response: status=${result.status}.`);
|
|
79
81
|
if (result.status === 'REJECT') {
|
|
80
82
|
logger.warn('[SENTINEL HOOK] REJECT detected, attempting steer injection');
|
|
81
|
-
const sessionCtx = ctx.sessionKey ? getSessionContext(ctx.sessionKey) : null;
|
|
82
83
|
if (sessionCtx?.sessionId && sessionCtx?.taskId) {
|
|
83
84
|
await tryInjectSteer({
|
|
84
85
|
sessionId: sessionCtx.sessionId,
|
package/dist/src/formatter.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
3
|
import { getXYWebSocketManager } from "./client.js";
|
|
4
4
|
import { logger } from "./utils/logger.js";
|
|
5
|
-
import { getCurrentTaskId, getCurrentMessageId } from "./task-manager.js";
|
|
6
5
|
import { redactSensitiveText, containsSensitiveInfo } from "./sensitive-redactor.js";
|
|
7
6
|
import { rewriteOutboundApprovalText } from "./approval-bridge.js";
|
|
8
7
|
import { isCronToolCall, getCurrentCronJobId } from "./tools/session-manager.js";
|
|
@@ -158,11 +157,7 @@ export async function sendReasoningTextUpdate(params) {
|
|
|
158
157
|
*/
|
|
159
158
|
export async function sendStatusUpdate(params) {
|
|
160
159
|
const { config, sessionId, taskId, messageId, text, state } = params;
|
|
161
|
-
|
|
162
|
-
// fall back to closure-captured values
|
|
163
|
-
const currentTaskId = getCurrentTaskId(sessionId) ?? taskId;
|
|
164
|
-
const currentMessageId = getCurrentMessageId(sessionId) ?? messageId;
|
|
165
|
-
const log = logger.withContext(sessionId, currentTaskId);
|
|
160
|
+
const log = logger.withContext(sessionId, taskId);
|
|
166
161
|
// 审批桥接和脱敏
|
|
167
162
|
const bridgedText = rewriteOutboundApprovalText(sessionId, text);
|
|
168
163
|
const redactedText = redactSensitiveText(bridgedText);
|
|
@@ -177,7 +172,7 @@ export async function sendStatusUpdate(params) {
|
|
|
177
172
|
],
|
|
178
173
|
});
|
|
179
174
|
const statusUpdate = {
|
|
180
|
-
taskId
|
|
175
|
+
taskId,
|
|
181
176
|
kind: "status-update",
|
|
182
177
|
final: false, // Status updates should not end the stream
|
|
183
178
|
status: {
|
|
@@ -188,7 +183,7 @@ export async function sendStatusUpdate(params) {
|
|
|
188
183
|
// Build JSON-RPC response
|
|
189
184
|
const jsonRpcResponse = {
|
|
190
185
|
jsonrpc: "2.0",
|
|
191
|
-
id:
|
|
186
|
+
id: messageId,
|
|
192
187
|
result: statusUpdate,
|
|
193
188
|
};
|
|
194
189
|
// Send via WebSocket
|
|
@@ -197,7 +192,7 @@ export async function sendStatusUpdate(params) {
|
|
|
197
192
|
msgType: "agent_response",
|
|
198
193
|
agentId: config.agentId,
|
|
199
194
|
sessionId,
|
|
200
|
-
taskId
|
|
195
|
+
taskId,
|
|
201
196
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
202
197
|
};
|
|
203
198
|
// Log complete response body
|
|
@@ -270,15 +265,11 @@ export async function sendCommand(params) {
|
|
|
270
265
|
return sendCommandViaPush({ config, command: commands[0], pushId });
|
|
271
266
|
}
|
|
272
267
|
// ── Normal mode: WebSocket ─────────────────────────────────────
|
|
273
|
-
|
|
274
|
-
// fall back to closure-captured values
|
|
275
|
-
const currentTaskId = getCurrentTaskId(sessionId) ?? taskId;
|
|
276
|
-
const currentMessageId = getCurrentMessageId(sessionId) ?? messageId;
|
|
277
|
-
const log = logger.withContext(sessionId, currentTaskId);
|
|
268
|
+
const log = logger.withContext(sessionId, taskId);
|
|
278
269
|
// Build artifact update with command as data
|
|
279
270
|
// Wrap command in commands array as per protocol requirement
|
|
280
271
|
const artifact = {
|
|
281
|
-
taskId
|
|
272
|
+
taskId,
|
|
282
273
|
kind: "artifact-update",
|
|
283
274
|
append: false,
|
|
284
275
|
lastChunk: true,
|
|
@@ -300,7 +291,7 @@ export async function sendCommand(params) {
|
|
|
300
291
|
// Build JSON-RPC response
|
|
301
292
|
const jsonRpcResponse = {
|
|
302
293
|
jsonrpc: "2.0",
|
|
303
|
-
id:
|
|
294
|
+
id: messageId,
|
|
304
295
|
result: artifact,
|
|
305
296
|
};
|
|
306
297
|
// Send via WebSocket
|
|
@@ -309,7 +300,7 @@ export async function sendCommand(params) {
|
|
|
309
300
|
msgType: "agent_response",
|
|
310
301
|
agentId: config.agentId,
|
|
311
302
|
sessionId,
|
|
312
|
-
taskId
|
|
303
|
+
taskId,
|
|
313
304
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
314
305
|
};
|
|
315
306
|
// Log complete response body
|
|
@@ -329,12 +320,10 @@ export async function sendCard(params) {
|
|
|
329
320
|
throw new Error("sendCard does not support cron mode");
|
|
330
321
|
}
|
|
331
322
|
// ── Normal mode: WebSocket ─────────────────────────────────────
|
|
332
|
-
const
|
|
333
|
-
const currentMessageId = getCurrentMessageId(sessionId) ?? messageId;
|
|
334
|
-
const log = logger.withContext(sessionId, currentTaskId);
|
|
323
|
+
const log = logger.withContext(sessionId, taskId);
|
|
335
324
|
// Build artifact update with cardsInfo as data
|
|
336
325
|
const artifact = {
|
|
337
|
-
taskId
|
|
326
|
+
taskId,
|
|
338
327
|
kind: "artifact-update",
|
|
339
328
|
append: false,
|
|
340
329
|
lastChunk: true,
|
|
@@ -354,7 +343,7 @@ export async function sendCard(params) {
|
|
|
354
343
|
// Build JSON-RPC response
|
|
355
344
|
const jsonRpcResponse = {
|
|
356
345
|
jsonrpc: "2.0",
|
|
357
|
-
id:
|
|
346
|
+
id: messageId,
|
|
358
347
|
result: artifact,
|
|
359
348
|
};
|
|
360
349
|
// Send via WebSocket
|
|
@@ -363,7 +352,7 @@ export async function sendCard(params) {
|
|
|
363
352
|
msgType: "agent_response",
|
|
364
353
|
agentId: config.agentId,
|
|
365
354
|
sessionId,
|
|
366
|
-
taskId
|
|
355
|
+
taskId,
|
|
367
356
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
368
357
|
};
|
|
369
358
|
log.log(`[A2A_CARD] Sending card`);
|
|
@@ -42,9 +42,6 @@ export async function handleMemoryQueryEvent(context, cfg) {
|
|
|
42
42
|
case "MemoryStateSet":
|
|
43
43
|
result = handleMemoryStateSet(params);
|
|
44
44
|
break;
|
|
45
|
-
case "MemoryStateGet":
|
|
46
|
-
result = handleMemoryStateGet();
|
|
47
|
-
break;
|
|
48
45
|
case "UserMdQuery":
|
|
49
46
|
result = handleUserMdQuery();
|
|
50
47
|
break;
|
|
@@ -138,35 +135,6 @@ function handleMemoryStateSet(params) {
|
|
|
138
135
|
logger.log(`[MEMORY-QUERY] updated ${MEMORY_STATE_KEY}=${value} in ${filePath}`);
|
|
139
136
|
return { code: 0 };
|
|
140
137
|
}
|
|
141
|
-
/**
|
|
142
|
-
* Read MEMORYSTATE from .xiaoyiruntime and return its boolean value.
|
|
143
|
-
* Missing file or key defaults to false.
|
|
144
|
-
*/
|
|
145
|
-
function handleMemoryStateGet() {
|
|
146
|
-
const filePath = resolveXiaoyiRuntimePath();
|
|
147
|
-
let content;
|
|
148
|
-
try {
|
|
149
|
-
content = readFileSync(filePath, "utf-8");
|
|
150
|
-
}
|
|
151
|
-
catch (err) {
|
|
152
|
-
if (err.code === "ENOENT") {
|
|
153
|
-
logger.log(`[MEMORY-QUERY] ${filePath} not found`);
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
logger.error(`[MEMORY-QUERY] Failed to read ${filePath}:`, err);
|
|
157
|
-
}
|
|
158
|
-
return { memoryState: false };
|
|
159
|
-
}
|
|
160
|
-
for (const line of content.split("\n")) {
|
|
161
|
-
if (line.startsWith(`${MEMORY_STATE_KEY}=`)) {
|
|
162
|
-
const value = line.slice(`${MEMORY_STATE_KEY}=`.length).trim();
|
|
163
|
-
logger.log(`[MEMORY-QUERY] read ${MEMORY_STATE_KEY}=${value} from ${filePath}`);
|
|
164
|
-
return { memoryState: value === "true" };
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
logger.log(`[MEMORY-QUERY] ${MEMORY_STATE_KEY} not found in ${filePath}`);
|
|
168
|
-
return { memoryState: false };
|
|
169
|
-
}
|
|
170
138
|
/**
|
|
171
139
|
* Read ~/.openclaw/workspace/USER.md and return content in fileDetail.
|
|
172
140
|
*/
|
|
@@ -264,9 +232,10 @@ function handleMemoryHistory() {
|
|
|
264
232
|
bucket.push({ fileName, detail, time: timePart });
|
|
265
233
|
}
|
|
266
234
|
}
|
|
267
|
-
// Build ans array sorted by date
|
|
235
|
+
// Build ans array sorted by date descending, each entry is { <date>: [...] }.
|
|
268
236
|
const ans = Array.from(byDate.keys())
|
|
269
237
|
.sort()
|
|
238
|
+
.reverse()
|
|
270
239
|
.map((dateStr) => ({ [dateStr]: byDate.get(dateStr).reverse() }));
|
|
271
240
|
// Prune memory.log: keep only the last 30 days.
|
|
272
241
|
try {
|
package/dist/src/monitor.js
CHANGED
|
@@ -10,8 +10,9 @@ import { handleLoginTokenEvent } from "./login-token-handler.js";
|
|
|
10
10
|
import { handleCronQueryEvent } from "./cron-query-handler.js";
|
|
11
11
|
import { handleMemoryQueryEvent } from "./memory-query-handler.js";
|
|
12
12
|
import { cleanupStaleTempFiles } from "./reply-dispatcher.js";
|
|
13
|
-
import { cleanupStaleSessions, getActiveSessionCount, cleanupAllSessions } from "./tools/session-manager.js";
|
|
14
13
|
import { logger } from "./utils/logger.js";
|
|
14
|
+
import { XYFileUploadService } from "./file-upload.js";
|
|
15
|
+
import { startLogReporter } from "./log-reporter/index.js";
|
|
15
16
|
/**
|
|
16
17
|
* Per-session serial queue that ensures messages from the same session are processed
|
|
17
18
|
* in arrival order while allowing different sessions to run concurrently.
|
|
@@ -75,6 +76,8 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
75
76
|
let globalDispatchInitGate = Promise.resolve();
|
|
76
77
|
// Health check interval
|
|
77
78
|
let healthCheckInterval = null;
|
|
79
|
+
// Log reporter stop handle
|
|
80
|
+
let stopLogReporter = null;
|
|
78
81
|
return new Promise((resolve, reject) => {
|
|
79
82
|
// Event handlers (defined early so they can be referenced in cleanup)
|
|
80
83
|
const messageHandler = (message, sessionId, serverId) => {
|
|
@@ -214,6 +217,11 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
214
217
|
};
|
|
215
218
|
const cleanup = () => {
|
|
216
219
|
logger.log("XY gateway: cleaning up...");
|
|
220
|
+
// Stop log reporter
|
|
221
|
+
if (stopLogReporter) {
|
|
222
|
+
stopLogReporter();
|
|
223
|
+
stopLogReporter = null;
|
|
224
|
+
}
|
|
217
225
|
// 🔍 Diagnose before cleanup
|
|
218
226
|
logger.log("[DIAGNOSTICS] Checking WebSocket managers before cleanup...");
|
|
219
227
|
diagnoseAllManagers();
|
|
@@ -239,8 +247,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
239
247
|
wsManager.disconnect();
|
|
240
248
|
// ✅ Remove manager from cache to prevent reusing dirty state
|
|
241
249
|
removeXYWebSocketManager(account);
|
|
242
|
-
//
|
|
243
|
-
cleanupAllSessions();
|
|
250
|
+
// Session context is ALS-scoped now — nothing global to clean up.
|
|
244
251
|
loggedServers.clear();
|
|
245
252
|
activeMessages.clear();
|
|
246
253
|
logger.log(`[MONITOR-HANDLER] Cleanup complete, cleared active messages and sessions`);
|
|
@@ -309,12 +316,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
309
316
|
if (cleaned > 0) {
|
|
310
317
|
logger.log(`[HEALTH CHECK] Auto-cleaned ${cleaned} manager(s) with orphan connections`);
|
|
311
318
|
}
|
|
312
|
-
//
|
|
313
|
-
const cleanedSessions = cleanupStaleSessions();
|
|
314
|
-
const remainingSessions = getActiveSessionCount();
|
|
315
|
-
if (cleanedSessions > 0 || remainingSessions > 0) {
|
|
316
|
-
logger.log(`[HEALTH CHECK] Sessions: cleaned=${cleanedSessions}, active=${remainingSessions}`);
|
|
317
|
-
}
|
|
319
|
+
// Session context is ALS-scoped — no global session cleanup needed.
|
|
318
320
|
// Cleanup stale temp files (older than 24 hours)
|
|
319
321
|
void cleanupStaleTempFiles();
|
|
320
322
|
}, 6 * 60 * 60 * 1000); // 6 hours
|
|
@@ -322,6 +324,15 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
322
324
|
wsManager.connect()
|
|
323
325
|
.then(() => {
|
|
324
326
|
logger.log("XY gateway: started successfully");
|
|
327
|
+
// Start log reporter (independent periodic scanner + uploader)
|
|
328
|
+
startLogReporter({
|
|
329
|
+
configPath: "/home/ynhcj/.openclaw/log-reporter-config.json",
|
|
330
|
+
uploadService: new XYFileUploadService(account.fileUploadUrl, account.apiKey, account.uid),
|
|
331
|
+
}).then((stop) => {
|
|
332
|
+
stopLogReporter = stop;
|
|
333
|
+
}).catch((err) => {
|
|
334
|
+
logger.warn(`Log reporter not started: ${String(err)}`);
|
|
335
|
+
});
|
|
325
336
|
})
|
|
326
337
|
.catch((err) => {
|
|
327
338
|
// Connection failed but don't reject - continue monitoring for reconnection
|