@ynhcj/xiaoyi-channel 0.0.112-beta → 0.0.112-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.d.ts +4 -5
- package/dist/index.js +71 -82
- package/dist/provider-discovery.d.ts +2 -0
- package/dist/provider-discovery.js +4 -0
- package/dist/src/bot.d.ts +2 -0
- package/dist/src/bot.js +44 -29
- package/dist/src/channel.js +2 -21
- package/dist/src/client.js +31 -22
- 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/formatter.d.ts +2 -0
- package/dist/src/formatter.js +25 -35
- package/dist/src/heartbeat.js +1 -1
- package/dist/src/message-queue.d.ts +17 -0
- package/dist/src/message-queue.js +51 -0
- package/dist/src/monitor.js +68 -14
- package/dist/src/outbound.js +19 -18
- package/dist/src/provider.d.ts +1 -1
- package/dist/src/provider.js +150 -34
- package/dist/src/push.js +16 -15
- package/dist/src/reply-dispatcher.js +12 -3
- package/dist/src/runtime.d.ts +3 -11
- package/dist/src/runtime.js +6 -18
- package/dist/src/self-evolution-handler.js +0 -1
- package/dist/src/self-evolution-keyword.d.ts +1 -1
- package/dist/src/self-evolution-keyword.js +2 -0
- package/dist/src/self-evolution-tool-result-nudge.d.ts +3 -0
- package/dist/src/self-evolution-tool-result-nudge.js +96 -0
- package/dist/src/skill-retriever/hooks.js +7 -15
- package/dist/src/skill-retriever/tool-search.js +22 -8
- package/dist/src/skill-retriever/types.d.ts +2 -0
- package/dist/src/steer-injector.js +1 -1
- package/dist/src/task-manager.d.ts +4 -0
- package/dist/src/task-manager.js +12 -1
- package/dist/src/tools/calendar-tool.d.ts +2 -1
- package/dist/src/tools/calendar-tool.js +112 -116
- package/dist/src/tools/call-device-tool.d.ts +2 -1
- package/dist/src/tools/call-device-tool.js +126 -103
- package/dist/src/tools/call-phone-tool.d.ts +2 -1
- package/dist/src/tools/call-phone-tool.js +109 -113
- package/dist/src/tools/create-alarm-tool.d.ts +2 -1
- package/dist/src/tools/create-alarm-tool.js +227 -231
- package/dist/src/tools/create-all-tools.d.ts +16 -0
- package/dist/src/tools/create-all-tools.js +50 -0
- package/dist/src/tools/delete-alarm-tool.d.ts +2 -1
- package/dist/src/tools/delete-alarm-tool.js +131 -135
- package/dist/src/tools/get-alarm-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-alarm-tool-schema.js +16 -10
- package/dist/src/tools/get-calendar-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-calendar-tool-schema.js +12 -8
- package/dist/src/tools/get-collection-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-collection-tool-schema.js +11 -9
- package/dist/src/tools/get-contact-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-contact-tool-schema.js +16 -10
- package/dist/src/tools/get-device-file-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-device-file-tool-schema.js +13 -9
- package/dist/src/tools/get-email-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-email-tool-schema.js +11 -8
- package/dist/src/tools/get-note-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-note-tool-schema.js +14 -9
- package/dist/src/tools/get-photo-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-photo-tool-schema.js +12 -9
- package/dist/src/tools/image-reading-tool.d.ts +3 -2
- package/dist/src/tools/image-reading-tool.js +82 -162
- package/dist/src/tools/location-tool.d.ts +2 -1
- package/dist/src/tools/location-tool.js +87 -91
- package/dist/src/tools/login-token-tool.d.ts +3 -2
- package/dist/src/tools/login-token-tool.js +114 -117
- package/dist/src/tools/modify-alarm-tool.d.ts +2 -1
- package/dist/src/tools/modify-alarm-tool.js +232 -236
- package/dist/src/tools/modify-note-tool.d.ts +2 -1
- package/dist/src/tools/modify-note-tool.js +104 -108
- package/dist/src/tools/note-tool.d.ts +2 -1
- package/dist/src/tools/note-tool.js +103 -107
- package/dist/src/tools/query-app-message-tool.d.ts +2 -1
- package/dist/src/tools/query-app-message-tool.js +108 -111
- package/dist/src/tools/query-memory-data-tool.d.ts +2 -1
- package/dist/src/tools/query-memory-data-tool.js +109 -112
- package/dist/src/tools/query-todo-task-tool.d.ts +2 -1
- package/dist/src/tools/query-todo-task-tool.js +103 -106
- package/dist/src/tools/save-file-to-phone-tool.d.ts +2 -1
- package/dist/src/tools/save-file-to-phone-tool.js +127 -131
- package/dist/src/tools/save-media-to-gallery-tool.d.ts +2 -1
- package/dist/src/tools/save-media-to-gallery-tool.js +134 -138
- package/dist/src/tools/save-self-evolution-skill-tool.d.ts +2 -1
- package/dist/src/tools/save-self-evolution-skill-tool.js +195 -197
- package/dist/src/tools/search-alarm-tool.d.ts +2 -1
- package/dist/src/tools/search-alarm-tool.js +171 -175
- package/dist/src/tools/search-calendar-tool.d.ts +2 -1
- package/dist/src/tools/search-calendar-tool.js +145 -149
- package/dist/src/tools/search-contact-tool.d.ts +2 -1
- package/dist/src/tools/search-contact-tool.js +98 -102
- package/dist/src/tools/search-email-tool.d.ts +2 -1
- package/dist/src/tools/search-email-tool.js +107 -111
- package/dist/src/tools/search-file-tool.d.ts +2 -1
- package/dist/src/tools/search-file-tool.js +99 -103
- package/dist/src/tools/search-message-tool.d.ts +2 -1
- package/dist/src/tools/search-message-tool.js +100 -104
- package/dist/src/tools/search-note-tool.d.ts +2 -1
- package/dist/src/tools/search-note-tool.js +95 -99
- package/dist/src/tools/search-photo-gallery-tool.d.ts +2 -1
- package/dist/src/tools/search-photo-gallery-tool.js +34 -38
- package/dist/src/tools/send-email-tool.d.ts +2 -1
- package/dist/src/tools/send-email-tool.js +105 -108
- package/dist/src/tools/send-file-to-user-tool.d.ts +2 -1
- package/dist/src/tools/send-file-to-user-tool.js +158 -155
- package/dist/src/tools/send-message-tool.d.ts +2 -1
- package/dist/src/tools/send-message-tool.js +119 -123
- package/dist/src/tools/session-helper.d.ts +24 -0
- package/dist/src/tools/session-helper.js +45 -0
- package/dist/src/tools/session-manager.d.ts +29 -6
- package/dist/src/tools/session-manager.js +146 -19
- package/dist/src/tools/upload-file-tool.d.ts +2 -1
- package/dist/src/tools/upload-file-tool.js +78 -82
- package/dist/src/tools/upload-photo-tool.d.ts +2 -1
- package/dist/src/tools/upload-photo-tool.js +69 -73
- package/dist/src/tools/xiaoyi-add-collection-tool.d.ts +2 -1
- package/dist/src/tools/xiaoyi-add-collection-tool.js +143 -147
- package/dist/src/tools/xiaoyi-collection-tool.d.ts +2 -1
- package/dist/src/tools/xiaoyi-collection-tool.js +111 -115
- package/dist/src/tools/xiaoyi-delete-collection-tool.d.ts +2 -1
- package/dist/src/tools/xiaoyi-delete-collection-tool.js +124 -128
- package/dist/src/tools/xiaoyi-gui-tool.d.ts +2 -1
- package/dist/src/tools/xiaoyi-gui-tool.js +87 -88
- package/dist/src/utils/logger.js +20 -18
- package/dist/src/utils/self-evolution-manager.d.ts +5 -0
- package/dist/src/utils/self-evolution-manager.js +45 -23
- package/dist/src/websocket.d.ts +3 -0
- package/dist/src/websocket.js +73 -29
- package/dist/src/xy-session-store.d.ts +79 -0
- package/dist/src/xy-session-store.js +153 -0
- package/openclaw.plugin.json +22 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
declare const plugin: {
|
|
1
|
+
declare const _default: {
|
|
3
2
|
id: string;
|
|
4
3
|
name: string;
|
|
5
4
|
description: string;
|
|
6
5
|
configSchema: import("openclaw/plugin-sdk").OpenClawPluginConfigSchema;
|
|
7
|
-
register
|
|
8
|
-
}
|
|
9
|
-
export default
|
|
6
|
+
register: NonNullable<import("openclaw/plugin-sdk/core").OpenClawPluginDefinition["register"]>;
|
|
7
|
+
} & Pick<import("openclaw/plugin-sdk/core").OpenClawPluginDefinition, "kind" | "reload" | "nodeHostCommands" | "securityAuditCollectors">;
|
|
8
|
+
export default _default;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/core";
|
|
2
2
|
import { xiaoyiProvider } from "./src/provider.js";
|
|
3
3
|
import { xyPlugin } from "./src/channel.js";
|
|
4
4
|
import { callCsplApi } from "./src/cspl/call-api.js";
|
|
@@ -6,93 +6,82 @@ import { ALLOWED_TOOLS, MAX_TEXT_LENGTH, MAX_TOTAL_LENGTH, MIN_TEXT_LENGTH, STEE
|
|
|
6
6
|
import { extractResultText, parseSecurityResult, processText, validateAndTruncateText, } from "./src/cspl/utils.js";
|
|
7
7
|
import { setXYRuntime } from "./src/runtime.js";
|
|
8
8
|
import { tryInjectSteer } from "./src/steer-injector.js";
|
|
9
|
-
import {
|
|
10
|
-
import { TOOL_CALL_NUDGE_THRESHOLD, toolCallNudgeManager, } from "./src/utils/tool-call-nudge-manager.js";
|
|
9
|
+
import { registerSelfEvolutionToolResultNudge } from "./src/self-evolution-tool-result-nudge.js";
|
|
11
10
|
import { createBeforePromptBuildHandler } from "./src/skill-retriever/hooks.js";
|
|
12
11
|
import { normalizeToolRetrieverConfig } from "./src/skill-retriever/config.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
function registerFullHooks(api) {
|
|
13
|
+
// SKILL RETRIEVER HOOK: before_prompt_build hook
|
|
14
|
+
const pluginConfig = api.pluginConfig || {};
|
|
15
|
+
const skillRetrieverConfig = normalizeToolRetrieverConfig({
|
|
16
|
+
enabled: pluginConfig.skillRetrieverEnabled ?? true,
|
|
17
|
+
maxTools: pluginConfig.skillRetrieverMaxTools ?? 2,
|
|
18
|
+
includeUninstalledOnly: true,
|
|
19
|
+
envFilePath: "~/.openclaw/.xiaoyienv",
|
|
20
|
+
timeoutMs: pluginConfig.skillRetrieverTimeoutMs ?? 1000,
|
|
21
|
+
});
|
|
22
|
+
const beforePromptBuildHandler = createBeforePromptBuildHandler(skillRetrieverConfig);
|
|
23
|
+
api.on("before_prompt_build", beforePromptBuildHandler);
|
|
24
|
+
registerSelfEvolutionToolResultNudge(api);
|
|
25
|
+
api.on("after_tool_call", async (event, ctx) => {
|
|
26
|
+
if (!ALLOWED_TOOLS.includes(event.toolName)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
console.log(`[SENTINEL HOOK] after_tool_call triggered: toolName=${event.toolName}, sessionKey=${ctx.sessionKey ?? "none"}`);
|
|
30
|
+
try {
|
|
31
|
+
const resultText = extractResultText(event, event.toolName);
|
|
32
|
+
const resultLength = resultText.length;
|
|
33
|
+
if (resultLength <= MIN_TEXT_LENGTH || resultLength > MAX_TOTAL_LENGTH) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const questionText = {
|
|
37
|
+
subSceneID: "TOOL_OUTPUT",
|
|
38
|
+
tool: event.toolName,
|
|
39
|
+
output: [{ content: "" }],
|
|
40
|
+
};
|
|
41
|
+
const originText = processText(resultText);
|
|
42
|
+
questionText.output[0].content = originText;
|
|
43
|
+
let finalJson = JSON.stringify(questionText);
|
|
44
|
+
if (finalJson.length > MAX_TEXT_LENGTH) {
|
|
45
|
+
const diff = finalJson.length - MAX_TEXT_LENGTH;
|
|
46
|
+
const { text: trimmed } = validateAndTruncateText(originText, MAX_TEXT_LENGTH - diff);
|
|
47
|
+
questionText.output[0].content = trimmed;
|
|
48
|
+
finalJson = JSON.stringify(questionText);
|
|
49
|
+
}
|
|
50
|
+
const response = await callCsplApi(finalJson, api.config);
|
|
51
|
+
const result = parseSecurityResult(response);
|
|
52
|
+
console.log(`[SENTINEL HOOK] Security result: status=${result.status}`);
|
|
53
|
+
if (result.status === "REJECT") {
|
|
54
|
+
await tryInjectSteer(ctx.sessionKey, STEER_ABORT_MESSAGE);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
api.logger.error(`[SENTINEL HOOK] after_tool_call error: ${err}`);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
25
61
|
}
|
|
26
|
-
|
|
62
|
+
export default definePluginEntry({
|
|
27
63
|
id: "xiaoyi-channel",
|
|
28
64
|
name: "Xiaoyi Channel",
|
|
29
65
|
description: "Xiaoyi channel plugin - Xiaoyi A2A protocol integration",
|
|
30
|
-
configSchema: emptyPluginConfigSchema(),
|
|
31
66
|
register(api) {
|
|
32
|
-
|
|
33
|
-
|
|
67
|
+
// Always register the provider so wrapStreamFn/prepareExtraParams work
|
|
68
|
+
// in ALL registration modes (not just "full").
|
|
34
69
|
api.registerProvider(xiaoyiProvider);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
api.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
api.logger.debug?.(`[SELF_EVOLUTION] Tool call counted: tool=${event.toolName}, count=${count}, threshold=${TOOL_CALL_NUDGE_THRESHOLD}, sessionKey=${ctx.sessionKey}`);
|
|
52
|
-
if (shouldNudge) {
|
|
53
|
-
api.logger.info?.(`[SELF_EVOLUTION] Tool call threshold reached, injecting nudge: count=${count}, sessionKey=${ctx.sessionKey}`);
|
|
54
|
-
await tryInjectSteer(ctx.sessionKey, SELF_EVOLUTION_NUDGE_MESSAGE);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch (err) {
|
|
58
|
-
api.logger.error(`[SELF_EVOLUTION] after_tool_call nudge error: ${err}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (!ALLOWED_TOOLS.includes(event.toolName)) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
console.log(`[SENTINEL HOOK] after_tool_call triggered: toolName=${event.toolName}, sessionKey=${ctx.sessionKey ?? "none"}`);
|
|
65
|
-
try {
|
|
66
|
-
const resultText = extractResultText(event, event.toolName);
|
|
67
|
-
const resultLength = resultText.length;
|
|
68
|
-
if (resultLength <= MIN_TEXT_LENGTH || resultLength > MAX_TOTAL_LENGTH) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
const questionText = {
|
|
72
|
-
subSceneID: "TOOL_OUTPUT",
|
|
73
|
-
tool: event.toolName,
|
|
74
|
-
output: [{ content: "" }],
|
|
75
|
-
};
|
|
76
|
-
const originText = processText(resultText);
|
|
77
|
-
questionText.output[0].content = originText;
|
|
78
|
-
let finalJson = JSON.stringify(questionText);
|
|
79
|
-
if (finalJson.length > MAX_TEXT_LENGTH) {
|
|
80
|
-
const diff = finalJson.length - MAX_TEXT_LENGTH;
|
|
81
|
-
const { text: trimmed } = validateAndTruncateText(originText, MAX_TEXT_LENGTH - diff);
|
|
82
|
-
questionText.output[0].content = trimmed;
|
|
83
|
-
finalJson = JSON.stringify(questionText);
|
|
84
|
-
}
|
|
85
|
-
const response = await callCsplApi(finalJson, api.config);
|
|
86
|
-
const result = parseSecurityResult(response);
|
|
87
|
-
console.log(`[SENTINEL HOOK] Security result: status=${result.status}`);
|
|
88
|
-
if (result.status === "REJECT") {
|
|
89
|
-
await tryInjectSteer(ctx.sessionKey, STEER_ABORT_MESSAGE);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
catch (err) {
|
|
93
|
-
api.logger.error(`[SENTINEL HOOK] after_tool_call error: ${err}`);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
70
|
+
if (api.registrationMode === "cli-metadata") {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (api.registrationMode === "tool-discovery") {
|
|
74
|
+
registerFullHooks(api);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Register channel plugin and set runtime
|
|
78
|
+
api.registerChannel({ plugin: xyPlugin });
|
|
79
|
+
setXYRuntime(api.runtime);
|
|
80
|
+
if (api.registrationMode === "discovery") {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (api.registrationMode === "full") {
|
|
84
|
+
registerFullHooks(api);
|
|
85
|
+
}
|
|
96
86
|
},
|
|
97
|
-
};
|
|
98
|
-
export default plugin;
|
|
87
|
+
});
|
package/dist/src/bot.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface HandleXYMessageParams {
|
|
|
9
9
|
message: A2AJsonRpcRequest;
|
|
10
10
|
accountId: string;
|
|
11
11
|
webSocketSessionId?: string;
|
|
12
|
+
/** Called after dispatch init is complete (agentTools/wrapStreamFn done). */
|
|
13
|
+
onInitComplete?: () => void;
|
|
12
14
|
}
|
|
13
15
|
/**
|
|
14
16
|
* Handle an incoming A2A message.
|
package/dist/src/bot.js
CHANGED
|
@@ -90,6 +90,7 @@ export async function handleXYMessage(params) {
|
|
|
90
90
|
text: pushDataItem.dataDetail,
|
|
91
91
|
append: false,
|
|
92
92
|
final: true,
|
|
93
|
+
runtime,
|
|
93
94
|
});
|
|
94
95
|
log(`[BOT] ✅ Trigger response sent successfully, exiting early`);
|
|
95
96
|
return; // 提前返回,不继续处理
|
|
@@ -162,6 +163,7 @@ export async function handleXYMessage(params) {
|
|
|
162
163
|
taskId: parsed.taskId,
|
|
163
164
|
messageId: parsed.messageId,
|
|
164
165
|
agentId: route.accountId,
|
|
166
|
+
deviceType,
|
|
165
167
|
});
|
|
166
168
|
// 🔑 发送初始状态更新(第二条消息也要发,用新taskId)
|
|
167
169
|
log(`[STATUS] Sending initial status update for session ${parsed.sessionId}`);
|
|
@@ -172,6 +174,7 @@ export async function handleXYMessage(params) {
|
|
|
172
174
|
messageId: parsed.messageId,
|
|
173
175
|
text: isSecondMessage ? "新消息已接收,正在处理..." : "任务正在处理中,请稍候~",
|
|
174
176
|
state: "working",
|
|
177
|
+
runtime,
|
|
175
178
|
}).catch((err) => {
|
|
176
179
|
error(`Failed to send initial status update:`, err);
|
|
177
180
|
});
|
|
@@ -200,12 +203,16 @@ export async function handleXYMessage(params) {
|
|
|
200
203
|
const fileParts = extractFileParts(parsed.parts);
|
|
201
204
|
// Download files to local disk
|
|
202
205
|
const downloadedFiles = await downloadFilesFromParts(fileParts);
|
|
203
|
-
|
|
206
|
+
log("Downloaded files:", JSON.stringify(downloadedFiles, null, 2));
|
|
204
207
|
const mediaPayload = buildXYMediaPayload(downloadedFiles);
|
|
205
208
|
// Resolve envelope format options (following feishu pattern)
|
|
206
209
|
const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
|
|
207
210
|
// Build message body with speaker prefix (following feishu pattern)
|
|
208
211
|
let messageBody = textForAgent;
|
|
212
|
+
// Embed A2A taskId marker before the user query so the provider can
|
|
213
|
+
// extract it from messages without relying on AsyncLocalStorage.
|
|
214
|
+
// The provider strips this marker before sending to the model.
|
|
215
|
+
messageBody = `xiaoyiA2A info[taskId:${parsed.taskId},deviceType:${deviceType}] ${messageBody}`;
|
|
209
216
|
// Add speaker prefix for clarity
|
|
210
217
|
const speaker = parsed.sessionId;
|
|
211
218
|
messageBody = `${speaker}: ${messageBody}`;
|
|
@@ -286,34 +293,42 @@ export async function handleXYMessage(params) {
|
|
|
286
293
|
unregisterSession(route.sessionKey);
|
|
287
294
|
log(`[BOT] ✅ Cleanup completed`);
|
|
288
295
|
},
|
|
289
|
-
run: () =>
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
296
|
+
run: () => {
|
|
297
|
+
// 🔐 Use AsyncLocalStorage to provide session context to tools.
|
|
298
|
+
// runWithSessionContext returns after the sync part of dispatch
|
|
299
|
+
// (including agentTools + wrapStreamFn) has executed, so we
|
|
300
|
+
// signal init complete to release the global dispatch gate
|
|
301
|
+
// for the next session.
|
|
302
|
+
const dispatchPromise = runWithSessionContext(sessionContext, async () => {
|
|
303
|
+
log(`[BOT-DISPATCH] ⏳ dispatchReplyFromConfig starting...`);
|
|
304
|
+
log(`[BOT-DISPATCH] - sessionKey: ${ctxPayload.SessionKey}`);
|
|
305
|
+
log(`[BOT-DISPATCH] - provider: ${ctxPayload.Provider}`);
|
|
306
|
+
log(`[BOT-DISPATCH] - surface: ${ctxPayload.Surface}`);
|
|
307
|
+
log(`[BOT-DISPATCH] - from: ${ctxPayload.From}`);
|
|
308
|
+
log(`[BOT-DISPATCH] - body length: ${ctxPayload.Body?.length ?? 0}`);
|
|
309
|
+
try {
|
|
310
|
+
const result = await core.channel.reply.dispatchReplyFromConfig({
|
|
311
|
+
ctx: ctxPayload,
|
|
312
|
+
cfg,
|
|
313
|
+
dispatcher,
|
|
314
|
+
replyOptions,
|
|
315
|
+
});
|
|
316
|
+
log(`[BOT-DISPATCH] ✅ dispatchReplyFromConfig returned`);
|
|
317
|
+
log(`[BOT-DISPATCH] - result: ${JSON.stringify(result)}`);
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
catch (dispatchErr) {
|
|
321
|
+
error(`[BOT-DISPATCH] ❌ dispatchReplyFromConfig threw`);
|
|
322
|
+
error(`[BOT-DISPATCH] - error name: ${dispatchErr instanceof Error ? dispatchErr.name : "unknown"}`);
|
|
323
|
+
error(`[BOT-DISPATCH] - error message: ${String(dispatchErr)}`);
|
|
324
|
+
error(`[BOT-DISPATCH] - error stack: ${dispatchErr instanceof Error ? dispatchErr.stack?.slice(0, 500) : "N/A"}`);
|
|
325
|
+
throw dispatchErr;
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
// Signal init complete — sync part (agentTools, wrapStreamFn) is done
|
|
329
|
+
params.onInitComplete?.();
|
|
330
|
+
return dispatchPromise;
|
|
331
|
+
},
|
|
317
332
|
});
|
|
318
333
|
log(`[BOT] ✅ Dispatcher completed for session: ${parsed.sessionId}`);
|
|
319
334
|
log(`xy: dispatch complete (session=${parsed.sessionId})`);
|
package/dist/src/channel.js
CHANGED
|
@@ -1,28 +1,9 @@
|
|
|
1
1
|
import { resolveXYConfig, listXYAccountIds, getDefaultXYAccountId } from "./config.js";
|
|
2
2
|
import { xyConfigSchema } from "./config-schema.js";
|
|
3
3
|
import { xyOutbound } from "./outbound.js";
|
|
4
|
-
import { locationTool } from "./tools/location-tool.js";
|
|
5
|
-
import { xiaoyiGuiTool } from "./tools/xiaoyi-gui-tool.js";
|
|
6
|
-
import { sendFileToUserTool } from "./tools/send-file-to-user-tool.js";
|
|
7
|
-
import { viewPushResultTool } from "./tools/view-push-result-tool.js";
|
|
8
|
-
import { imageReadingTool } from "./tools/image-reading-tool.js";
|
|
9
|
-
import { timestampToUtc8Tool } from "./tools/timestamp-to-utc8-tool.js";
|
|
10
|
-
import { saveSelfEvolutionSkillTool } from "./tools/save-self-evolution-skill-tool.js";
|
|
11
|
-
import { getEmailToolSchemaTool } from "./tools/get-email-tool-schema.js";
|
|
12
|
-
import { callDeviceTool } from "./tools/call-device-tool.js";
|
|
13
|
-
import { getNoteToolSchemaTool } from "./tools/get-note-tool-schema.js";
|
|
14
|
-
import { getCalendarToolSchemaTool } from "./tools/get-calendar-tool-schema.js";
|
|
15
|
-
import { getContactToolSchemaTool } from "./tools/get-contact-tool-schema.js";
|
|
16
|
-
import { getPhotoToolSchemaTool } from "./tools/get-photo-tool-schema.js";
|
|
17
|
-
import { getDeviceFileToolSchemaTool } from "./tools/get-device-file-tool-schema.js";
|
|
18
|
-
import { getAlarmToolSchemaTool } from "./tools/get-alarm-tool-schema.js";
|
|
19
|
-
import { getCollectionToolSchemaTool } from "./tools/get-collection-tool-schema.js";
|
|
20
|
-
import { queryAppMessageTool } from "./tools/query-app-message-tool.js";
|
|
21
|
-
import { queryMemoryDataTool } from "./tools/query-memory-data-tool.js";
|
|
22
|
-
import { queryTodoTaskTool } from "./tools/query-todo-task-tool.js";
|
|
23
|
-
import { loginTokenTool } from "./tools/login-token-tool.js";
|
|
24
4
|
import { filterToolsByDevice } from "./tools/device-tool-map.js";
|
|
25
5
|
import { getCurrentSessionContext } from "./tools/session-manager.js";
|
|
6
|
+
import { createAllTools } from "./tools/create-all-tools.js";
|
|
26
7
|
import { logger } from "./utils/logger.js";
|
|
27
8
|
/**
|
|
28
9
|
* Xiaoyi Channel Plugin for OpenClaw.
|
|
@@ -63,8 +44,8 @@ export const xyPlugin = {
|
|
|
63
44
|
},
|
|
64
45
|
outbound: xyOutbound,
|
|
65
46
|
agentTools: () => {
|
|
66
|
-
const allTools = [locationTool, callDeviceTool, getNoteToolSchemaTool, getCalendarToolSchemaTool, getContactToolSchemaTool, getPhotoToolSchemaTool, xiaoyiGuiTool, getDeviceFileToolSchemaTool, getAlarmToolSchemaTool, getCollectionToolSchemaTool, sendFileToUserTool, viewPushResultTool, imageReadingTool, timestampToUtc8Tool, saveSelfEvolutionSkillTool, getEmailToolSchemaTool, queryAppMessageTool, queryMemoryDataTool, queryTodoTaskTool, loginTokenTool];
|
|
67
47
|
const ctx = getCurrentSessionContext();
|
|
48
|
+
const allTools = createAllTools(ctx);
|
|
68
49
|
const filtered = filterToolsByDevice(allTools, ctx?.deviceType);
|
|
69
50
|
logger.log(`[DEVICE-FILTER] deviceType=${ctx?.deviceType ?? "(none)"}, tools: ${allTools.length} → ${filtered.length} (${filtered.map(t => t.name).join(", ")})`);
|
|
70
51
|
return filtered;
|
package/dist/src/client.js
CHANGED
|
@@ -12,8 +12,14 @@ export function setClientRuntime(rt) {
|
|
|
12
12
|
/**
|
|
13
13
|
* Global cache for WebSocket managers.
|
|
14
14
|
* Key format: `${apiKey}-${agentId}`
|
|
15
|
+
* Uses globalThis to ensure a single cache across all module copies
|
|
16
|
+
* (same fix as session-manager.ts for openclaw multi-instance loading).
|
|
15
17
|
*/
|
|
16
|
-
const
|
|
18
|
+
const _g = globalThis;
|
|
19
|
+
if (!_g.__xyWsManagerCache) {
|
|
20
|
+
_g.__xyWsManagerCache = new Map();
|
|
21
|
+
}
|
|
22
|
+
const wsManagerCache = _g.__xyWsManagerCache;
|
|
17
23
|
/**
|
|
18
24
|
* Get or create a WebSocket manager for the given configuration.
|
|
19
25
|
* Reuses existing managers if config matches.
|
|
@@ -38,16 +44,17 @@ export function getXYWebSocketManager(config) {
|
|
|
38
44
|
* Disconnects the manager and removes it from the cache.
|
|
39
45
|
*/
|
|
40
46
|
export function removeXYWebSocketManager(config) {
|
|
47
|
+
const log = runtime?.log ?? console.log;
|
|
41
48
|
const cacheKey = `${config.apiKey}-${config.agentId}`;
|
|
42
49
|
const manager = wsManagerCache.get(cacheKey);
|
|
43
50
|
if (manager) {
|
|
44
|
-
|
|
51
|
+
log(`🗑️ [WS-MANAGER-CACHE] Removing manager from cache: ${cacheKey}`);
|
|
45
52
|
manager.disconnect();
|
|
46
53
|
wsManagerCache.delete(cacheKey);
|
|
47
|
-
|
|
54
|
+
log(`🗑️ [WS-MANAGER-CACHE] Manager removed, remaining managers: ${wsManagerCache.size}`);
|
|
48
55
|
}
|
|
49
56
|
else {
|
|
50
|
-
|
|
57
|
+
log(`⚠️ [WS-MANAGER-CACHE] Manager not found in cache: ${cacheKey}`);
|
|
51
58
|
}
|
|
52
59
|
}
|
|
53
60
|
/**
|
|
@@ -72,36 +79,37 @@ export function getCachedManagerCount() {
|
|
|
72
79
|
* Helps identify connection issues and orphan connections.
|
|
73
80
|
*/
|
|
74
81
|
export function diagnoseAllManagers() {
|
|
75
|
-
|
|
82
|
+
const log = runtime?.log ?? console.log;
|
|
83
|
+
log(`Total cached managers: ${wsManagerCache.size}`);
|
|
76
84
|
if (wsManagerCache.size === 0) {
|
|
77
|
-
|
|
85
|
+
log("ℹ️ No managers in cache");
|
|
78
86
|
return;
|
|
79
87
|
}
|
|
80
88
|
let orphanCount = 0;
|
|
81
89
|
wsManagerCache.forEach((manager, key) => {
|
|
82
90
|
const diag = manager.getConnectionDiagnostics();
|
|
83
|
-
|
|
91
|
+
log(` Total event listeners on manager: ${diag.totalEventListeners}`);
|
|
84
92
|
// Connection
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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}`);
|
|
93
101
|
if (diag.connection.isOrphan) {
|
|
94
|
-
|
|
102
|
+
log(` ⚠️ ORPHAN CONNECTION DETECTED!`);
|
|
95
103
|
orphanCount++;
|
|
96
104
|
}
|
|
97
|
-
|
|
105
|
+
log("");
|
|
98
106
|
});
|
|
99
107
|
if (orphanCount > 0) {
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
log(`⚠️ Total orphan connections found: ${orphanCount}`);
|
|
109
|
+
log(`💡 Suggestion: These connections should be cleaned up`);
|
|
102
110
|
}
|
|
103
111
|
else {
|
|
104
|
-
|
|
112
|
+
log(`✅ No orphan connections found`);
|
|
105
113
|
}
|
|
106
114
|
}
|
|
107
115
|
/**
|
|
@@ -109,17 +117,18 @@ export function diagnoseAllManagers() {
|
|
|
109
117
|
* Returns the number of managers that had orphan connections.
|
|
110
118
|
*/
|
|
111
119
|
export function cleanupOrphanConnections() {
|
|
120
|
+
const log = runtime?.log ?? console.log;
|
|
112
121
|
let cleanedCount = 0;
|
|
113
122
|
wsManagerCache.forEach((manager, key) => {
|
|
114
123
|
const diag = manager.getConnectionDiagnostics();
|
|
115
124
|
if (diag.connection.isOrphan) {
|
|
116
|
-
|
|
125
|
+
log(`🧹 Cleaning up orphan connections in manager: ${key}`);
|
|
117
126
|
manager.disconnect();
|
|
118
127
|
cleanedCount++;
|
|
119
128
|
}
|
|
120
129
|
});
|
|
121
130
|
if (cleanedCount > 0) {
|
|
122
|
-
|
|
131
|
+
log(`🧹 Cleaned up ${cleanedCount} manager(s) with orphan connections`);
|
|
123
132
|
}
|
|
124
133
|
return cleanedCount;
|
|
125
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
|
}
|