@ynhcj/xiaoyi-channel 0.0.63-beta → 0.0.64-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 +7 -1
- package/dist/src/channel.js +10 -1
- package/dist/src/parser.d.ts +6 -0
- package/dist/src/parser.js +16 -0
- package/dist/src/tools/device-tool-map.d.ts +4 -0
- package/dist/src/tools/device-tool-map.js +31 -0
- package/dist/src/tools/session-manager.d.ts +1 -0
- package/package.json +1 -1
package/dist/src/bot.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getXYRuntime } from "./runtime.js";
|
|
2
2
|
import { setCachedContext } from "./steer-injector.js";
|
|
3
3
|
import { createXYReplyDispatcher } from "./reply-dispatcher.js";
|
|
4
|
-
import { parseA2AMessage, extractTextFromParts, extractFileParts, extractPushId, extractTriggerData } from "./parser.js";
|
|
4
|
+
import { parseA2AMessage, extractTextFromParts, extractFileParts, extractPushId, extractDeviceType, extractTriggerData } 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";
|
|
@@ -126,6 +126,11 @@ export async function handleXYMessage(params) {
|
|
|
126
126
|
else {
|
|
127
127
|
log(`[BOT] ℹ️ No push_id found in message, will use config default`);
|
|
128
128
|
}
|
|
129
|
+
// Extract deviceType if present (same level as push_id in systemVariables)
|
|
130
|
+
const deviceType = extractDeviceType(parsed.parts);
|
|
131
|
+
if (deviceType) {
|
|
132
|
+
log(`[BOT] 📱 Extracted deviceType from user message: ${deviceType}`);
|
|
133
|
+
}
|
|
129
134
|
// 保存 runtime 信息到 .xiaoyiruntime 文件(异步,不阻塞主流程)
|
|
130
135
|
saveRuntimeInfo(webSocketSessionId || parsed.sessionId, // SESSION_ID (WebSocket 层级,如果没有则 fallback)
|
|
131
136
|
parsed.sessionId, // CONVERSATION_ID (param 里的 sessionId)
|
|
@@ -238,6 +243,7 @@ export async function handleXYMessage(params) {
|
|
|
238
243
|
taskId: parsed.taskId,
|
|
239
244
|
messageId: parsed.messageId,
|
|
240
245
|
agentId: route.accountId,
|
|
246
|
+
deviceType,
|
|
241
247
|
};
|
|
242
248
|
log(`[BOT-DISPATCH] ⏳ withReplyDispatcher starting, sessionKey=${route.sessionKey}`);
|
|
243
249
|
await core.channel.reply.withReplyDispatcher({
|
package/dist/src/channel.js
CHANGED
|
@@ -25,6 +25,9 @@ import { viewPushResultTool } from "./tools/view-push-result-tool.js";
|
|
|
25
25
|
import { imageReadingTool } from "./tools/image-reading-tool.js";
|
|
26
26
|
import { timestampToUtc8Tool } from "./tools/timestamp-to-utc8-tool.js";
|
|
27
27
|
import { sendCommandToCarTool } from "./tools/send-command-to-car-tool.js";
|
|
28
|
+
import { filterToolsByDevice } from "./tools/device-tool-map.js";
|
|
29
|
+
import { getCurrentSessionContext } from "./tools/session-manager.js";
|
|
30
|
+
import { logger } from "./utils/logger.js";
|
|
28
31
|
/**
|
|
29
32
|
* Xiaoyi Channel Plugin for OpenClaw.
|
|
30
33
|
* Implements Xiaoyi A2A protocol with dual WebSocket connections.
|
|
@@ -63,7 +66,13 @@ export const xyPlugin = {
|
|
|
63
66
|
schema: xyConfigSchema,
|
|
64
67
|
},
|
|
65
68
|
outbound: xyOutbound,
|
|
66
|
-
agentTools:
|
|
69
|
+
agentTools: () => {
|
|
70
|
+
const allTools = [locationTool, noteTool, searchNoteTool, modifyNoteTool, calendarTool, searchCalendarTool, searchContactTool, searchPhotoGalleryTool, uploadPhotoTool, xiaoyiGuiTool, callPhoneTool, searchMessageTool, sendMessageTool, searchFileTool, uploadFileTool, createAlarmTool, searchAlarmTool, modifyAlarmTool, deleteAlarmTool, sendFileToUserTool, viewPushResultTool, imageReadingTool, timestampToUtc8Tool, sendCommandToCarTool];
|
|
71
|
+
const ctx = getCurrentSessionContext();
|
|
72
|
+
const filtered = filterToolsByDevice(allTools, ctx?.deviceType);
|
|
73
|
+
logger.log(`[DEVICE-FILTER] deviceType=${ctx?.deviceType ?? "(none)"}, tools: ${allTools.length} → ${filtered.length} (${filtered.map(t => t.name).join(", ")})`);
|
|
74
|
+
return filtered;
|
|
75
|
+
},
|
|
67
76
|
messaging: {
|
|
68
77
|
normalizeTarget: (raw) => {
|
|
69
78
|
const trimmed = raw.trim();
|
package/dist/src/parser.d.ts
CHANGED
|
@@ -43,6 +43,12 @@ export declare function isTasksCancelMessage(method: string): boolean;
|
|
|
43
43
|
* Looks for push_id in data parts under variables.systemVariables.push_id
|
|
44
44
|
*/
|
|
45
45
|
export declare function extractPushId(parts: A2AMessagePart[]): string | null;
|
|
46
|
+
/**
|
|
47
|
+
* Extract deviceType from message parts.
|
|
48
|
+
* Looks for deviceType in data parts under variables.systemVariables.deviceType
|
|
49
|
+
* (same level as push_id).
|
|
50
|
+
*/
|
|
51
|
+
export declare function extractDeviceType(parts: A2AMessagePart[]): string | null;
|
|
46
52
|
/**
|
|
47
53
|
* Extract Trigger event data from message parts.
|
|
48
54
|
* Looks for Trigger events with pushDataId in data parts.
|
package/dist/src/parser.js
CHANGED
|
@@ -72,6 +72,22 @@ export function extractPushId(parts) {
|
|
|
72
72
|
}
|
|
73
73
|
return null;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Extract deviceType from message parts.
|
|
77
|
+
* Looks for deviceType in data parts under variables.systemVariables.deviceType
|
|
78
|
+
* (same level as push_id).
|
|
79
|
+
*/
|
|
80
|
+
export function extractDeviceType(parts) {
|
|
81
|
+
for (const part of parts) {
|
|
82
|
+
if (part.kind === "data" && part.data) {
|
|
83
|
+
const deviceType = part.data.variables?.systemVariables?.device_type;
|
|
84
|
+
if (deviceType && typeof deviceType === "string") {
|
|
85
|
+
return deviceType;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
75
91
|
/**
|
|
76
92
|
* Extract Trigger event data from message parts.
|
|
77
93
|
* Looks for Trigger events with pushDataId in data parts.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Device type to tool name mapping.
|
|
2
|
+
// Supports two modes:
|
|
3
|
+
// - allowlist: only listed tools are available (used for restrictive devices like car)
|
|
4
|
+
// - denylist: listed tools are blocked, everything else is available (used for permissive devices like pc)
|
|
5
|
+
// Tools NOT listed in any device entry → available to all devices (no restriction).
|
|
6
|
+
/** Known device type enum. */
|
|
7
|
+
export const DEVICE_TYPES = ["car", "pc", "phone"];
|
|
8
|
+
const DEVICE_TOOL_POLICY = {
|
|
9
|
+
pc: {
|
|
10
|
+
allowlist: false,
|
|
11
|
+
tools: [
|
|
12
|
+
"xiaoyi_gui_agent",
|
|
13
|
+
"call_phone",
|
|
14
|
+
"send_message",
|
|
15
|
+
"search_contact",
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
export function filterToolsByDevice(tools, deviceType) {
|
|
20
|
+
if (!deviceType)
|
|
21
|
+
return tools;
|
|
22
|
+
const policy = DEVICE_TOOL_POLICY[deviceType];
|
|
23
|
+
if (!policy)
|
|
24
|
+
return tools; // unrecognized device → no filtering
|
|
25
|
+
if (policy.allowlist) {
|
|
26
|
+
return tools.filter((tool) => policy.tools.includes(tool.name));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
return tools.filter((tool) => !policy.tools.includes(tool.name));
|
|
30
|
+
}
|
|
31
|
+
}
|