@ynhcj/xiaoyi-channel 1.1.7 → 1.1.9
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 +69 -33
- package/dist/src/channel.js +12 -2
- package/dist/src/monitor.js +35 -5
- package/dist/src/outbound.js +88 -76
- package/dist/src/reply-dispatcher.d.ts +1 -0
- package/dist/src/reply-dispatcher.js +117 -100
- package/dist/src/task-manager.d.ts +55 -0
- package/dist/src/task-manager.js +136 -0
- package/dist/src/tools/calendar-tool.js +5 -3
- package/dist/src/tools/call-phone-tool.d.ts +5 -0
- package/dist/src/tools/call-phone-tool.js +183 -0
- package/dist/src/tools/create-alarm-tool.d.ts +7 -0
- package/dist/src/tools/create-alarm-tool.js +446 -0
- package/dist/src/tools/delete-alarm-tool.d.ts +11 -0
- package/dist/src/tools/delete-alarm-tool.js +238 -0
- package/dist/src/tools/location-tool.js +2 -2
- package/dist/src/tools/modify-alarm-tool.d.ts +9 -0
- package/dist/src/tools/modify-alarm-tool.js +467 -0
- package/dist/src/tools/modify-note-tool.js +2 -2
- package/dist/src/tools/note-tool.js +29 -8
- package/dist/src/tools/search-alarm-tool.d.ts +8 -0
- package/dist/src/tools/search-alarm-tool.js +391 -0
- package/dist/src/tools/search-calendar-tool.js +6 -3
- package/dist/src/tools/search-contact-tool.js +2 -2
- package/dist/src/tools/search-file-tool.d.ts +5 -0
- package/dist/src/tools/search-file-tool.js +185 -0
- package/dist/src/tools/search-message-tool.d.ts +5 -0
- package/dist/src/tools/search-message-tool.js +173 -0
- package/dist/src/tools/search-note-tool.js +2 -2
- package/dist/src/tools/search-photo-gallery-tool.js +2 -2
- package/dist/src/tools/send-file-to-user-tool.d.ts +5 -0
- package/dist/src/tools/send-file-to-user-tool.js +318 -0
- package/dist/src/tools/send-message-tool.d.ts +5 -0
- package/dist/src/tools/send-message-tool.js +189 -0
- package/dist/src/tools/session-manager.d.ts +15 -0
- package/dist/src/tools/session-manager.js +99 -18
- package/dist/src/tools/upload-file-tool.d.ts +13 -0
- package/dist/src/tools/upload-file-tool.js +265 -0
- package/dist/src/tools/upload-photo-tool.js +2 -2
- package/dist/src/tools/xiaoyi-gui-tool.js +8 -8
- package/dist/src/websocket.js +1 -1
- package/package.json +1 -1
package/dist/src/bot.js
CHANGED
|
@@ -4,8 +4,9 @@ import { parseA2AMessage, extractTextFromParts, extractFileParts, extractPushId
|
|
|
4
4
|
import { downloadFilesFromParts } from "./file-download.js";
|
|
5
5
|
import { resolveXYConfig } from "./config.js";
|
|
6
6
|
import { sendStatusUpdate, sendClearContextResponse, sendTasksCancelResponse } from "./formatter.js";
|
|
7
|
-
import { registerSession, unregisterSession } from "./tools/session-manager.js";
|
|
7
|
+
import { registerSession, unregisterSession, runWithSessionContext } from "./tools/session-manager.js";
|
|
8
8
|
import { configManager } from "./utils/config-manager.js";
|
|
9
|
+
import { registerTaskId, decrementTaskIdRef, lockTaskId, unlockTaskId, hasActiveTask, } from "./task-manager.js";
|
|
9
10
|
/**
|
|
10
11
|
* Handle an incoming A2A message.
|
|
11
12
|
* This is the main entry point for message processing.
|
|
@@ -56,6 +57,22 @@ export async function handleXYMessage(params) {
|
|
|
56
57
|
}
|
|
57
58
|
// Parse the A2A message (for regular messages)
|
|
58
59
|
const parsed = parseA2AMessage(message);
|
|
60
|
+
// 🔑 检测steer模式和是否是第二条消息
|
|
61
|
+
const isSteerMode = cfg.messages?.queue?.mode === "steer";
|
|
62
|
+
const isSecondMessage = isSteerMode && hasActiveTask(parsed.sessionId);
|
|
63
|
+
if (isSecondMessage) {
|
|
64
|
+
log(`[BOT] 🔄 STEER MODE - Second message detected (will be follower)`);
|
|
65
|
+
log(`[BOT] - Session: ${parsed.sessionId}`);
|
|
66
|
+
log(`[BOT] - New taskId: ${parsed.taskId} (will replace current)`);
|
|
67
|
+
}
|
|
68
|
+
// 🔑 注册taskId(第二条消息会覆盖第一条的taskId)
|
|
69
|
+
const { isUpdate, refCount } = registerTaskId(parsed.sessionId, parsed.taskId, parsed.messageId, { incrementRef: true } // 增加引用计数
|
|
70
|
+
);
|
|
71
|
+
// 🔑 如果是第一条消息,锁定taskId防止被过早清理
|
|
72
|
+
if (!isUpdate) {
|
|
73
|
+
lockTaskId(parsed.sessionId);
|
|
74
|
+
log(`[BOT] 🔒 Locked taskId for first message`);
|
|
75
|
+
}
|
|
59
76
|
// Extract and update push_id if present
|
|
60
77
|
const pushId = extractPushId(parsed.parts);
|
|
61
78
|
if (pushId) {
|
|
@@ -83,11 +100,12 @@ export async function handleXYMessage(params) {
|
|
|
83
100
|
},
|
|
84
101
|
});
|
|
85
102
|
log(`xy: resolved route accountId=${route.accountId}, sessionKey=${route.sessionKey}`);
|
|
86
|
-
//
|
|
103
|
+
// 🔑 注册session(带引用计数)
|
|
87
104
|
log(`[BOT] 📝 About to register session for tools...`);
|
|
88
105
|
log(`[BOT] - sessionKey: ${route.sessionKey}`);
|
|
89
106
|
log(`[BOT] - sessionId: ${parsed.sessionId}`);
|
|
90
107
|
log(`[BOT] - taskId: ${parsed.taskId}`);
|
|
108
|
+
log(`[BOT] - isSecondMessage: ${isSecondMessage}`);
|
|
91
109
|
registerSession(route.sessionKey, {
|
|
92
110
|
config,
|
|
93
111
|
sessionId: parsed.sessionId,
|
|
@@ -96,14 +114,14 @@ export async function handleXYMessage(params) {
|
|
|
96
114
|
agentId: route.accountId,
|
|
97
115
|
});
|
|
98
116
|
log(`[BOT] ✅ Session registered for tools`);
|
|
99
|
-
//
|
|
117
|
+
// 🔑 发送初始状态更新(第二条消息也要发,用新taskId)
|
|
100
118
|
log(`[STATUS] Sending initial status update for session ${parsed.sessionId}`);
|
|
101
119
|
void sendStatusUpdate({
|
|
102
120
|
config,
|
|
103
121
|
sessionId: parsed.sessionId,
|
|
104
122
|
taskId: parsed.taskId,
|
|
105
123
|
messageId: parsed.messageId,
|
|
106
|
-
text: "任务正在处理中,请稍后~",
|
|
124
|
+
text: isSecondMessage ? "新消息已接收,正在处理..." : "任务正在处理中,请稍后~",
|
|
107
125
|
state: "working",
|
|
108
126
|
}).catch((err) => {
|
|
109
127
|
error(`Failed to send initial status update:`, err);
|
|
@@ -155,51 +173,66 @@ export async function handleXYMessage(params) {
|
|
|
155
173
|
ReplyToBody: undefined, // A2A protocol doesn't support reply/quote
|
|
156
174
|
...mediaPayload,
|
|
157
175
|
});
|
|
158
|
-
//
|
|
159
|
-
log(`[
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
taskId: parsed.taskId,
|
|
164
|
-
messageId: parsed.messageId,
|
|
165
|
-
text: "任务正在处理中,请稍后~",
|
|
166
|
-
state: "working",
|
|
167
|
-
}).catch((err) => {
|
|
168
|
-
error(`Failed to send initial status update:`, err);
|
|
169
|
-
});
|
|
170
|
-
// Create reply dispatcher (following feishu pattern)
|
|
171
|
-
log(`[BOT-DISPATCHER] 🎯 Creating reply dispatcher for session=${parsed.sessionId}, taskId=${parsed.taskId}, messageId=${parsed.messageId}`);
|
|
176
|
+
// 🔑 创建dispatcher(dispatcher会自动使用动态taskId)
|
|
177
|
+
log(`[BOT-DISPATCHER] 🎯 Creating reply dispatcher`);
|
|
178
|
+
log(`[BOT-DISPATCHER] - session: ${parsed.sessionId}`);
|
|
179
|
+
log(`[BOT-DISPATCHER] - taskId: ${parsed.taskId}`);
|
|
180
|
+
log(`[BOT-DISPATCHER] - isSecondMessage: ${isSecondMessage}`);
|
|
172
181
|
const { dispatcher, replyOptions, markDispatchIdle, startStatusInterval } = createXYReplyDispatcher({
|
|
173
182
|
cfg,
|
|
174
183
|
runtime,
|
|
175
184
|
sessionId: parsed.sessionId,
|
|
176
185
|
taskId: parsed.taskId,
|
|
177
186
|
messageId: parsed.messageId,
|
|
178
|
-
accountId: route.accountId,
|
|
187
|
+
accountId: route.accountId,
|
|
188
|
+
isSteerFollower: isSecondMessage, // 🔑 标记第二条消息
|
|
179
189
|
});
|
|
180
190
|
log(`[BOT-DISPATCHER] ✅ Reply dispatcher created successfully`);
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
191
|
+
// 🔑 只有第一条消息启动状态定时器
|
|
192
|
+
// 第二条消息会很快返回,不需要定时器
|
|
193
|
+
if (!isSecondMessage) {
|
|
194
|
+
startStatusInterval();
|
|
195
|
+
log(`[BOT-DISPATCHER] ✅ Status interval started for first message`);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
log(`[BOT-DISPATCHER] ⏭️ Skipped status interval for steer follower`);
|
|
199
|
+
}
|
|
184
200
|
log(`xy: dispatching to agent (session=${parsed.sessionId})`);
|
|
185
201
|
// Dispatch to OpenClaw core using correct API (following feishu pattern)
|
|
186
202
|
log(`[BOT] 🚀 Starting dispatcher with session: ${route.sessionKey}`);
|
|
203
|
+
// Build session context for AsyncLocalStorage
|
|
204
|
+
const sessionContext = {
|
|
205
|
+
config,
|
|
206
|
+
sessionId: parsed.sessionId,
|
|
207
|
+
taskId: parsed.taskId,
|
|
208
|
+
messageId: parsed.messageId,
|
|
209
|
+
agentId: route.accountId,
|
|
210
|
+
};
|
|
187
211
|
await core.channel.reply.withReplyDispatcher({
|
|
188
212
|
dispatcher,
|
|
189
213
|
onSettled: () => {
|
|
190
214
|
log(`[BOT] 🏁 onSettled called for session: ${route.sessionKey}`);
|
|
191
|
-
log(`[BOT] -
|
|
215
|
+
log(`[BOT] - isSecondMessage: ${isSecondMessage}`);
|
|
192
216
|
markDispatchIdle();
|
|
193
|
-
//
|
|
217
|
+
// 🔑 减少引用计数
|
|
218
|
+
decrementTaskIdRef(parsed.sessionId);
|
|
219
|
+
// 🔑 如果是第一条消息完成,解锁
|
|
220
|
+
if (!isSecondMessage) {
|
|
221
|
+
unlockTaskId(parsed.sessionId);
|
|
222
|
+
log(`[BOT] 🔓 Unlocked taskId (first message completed)`);
|
|
223
|
+
}
|
|
224
|
+
// 减少session引用计数
|
|
194
225
|
unregisterSession(route.sessionKey);
|
|
195
|
-
log(`[BOT] ✅
|
|
226
|
+
log(`[BOT] ✅ Cleanup completed`);
|
|
196
227
|
},
|
|
197
|
-
run: () =>
|
|
228
|
+
run: () =>
|
|
229
|
+
// 🔐 Use AsyncLocalStorage to provide session context to tools
|
|
230
|
+
runWithSessionContext(sessionContext, () => core.channel.reply.dispatchReplyFromConfig({
|
|
198
231
|
ctx: ctxPayload,
|
|
199
232
|
cfg,
|
|
200
233
|
dispatcher,
|
|
201
234
|
replyOptions,
|
|
202
|
-
}),
|
|
235
|
+
})),
|
|
203
236
|
});
|
|
204
237
|
log(`[BOT] ✅ Dispatcher completed for session: ${parsed.sessionId}`);
|
|
205
238
|
log(`xy: dispatch complete (session=${parsed.sessionId})`);
|
|
@@ -209,25 +242,28 @@ export async function handleXYMessage(params) {
|
|
|
209
242
|
error("Failed to handle XY message:", err);
|
|
210
243
|
runtime.error?.(`xy: Failed to handle message: ${String(err)}`);
|
|
211
244
|
log(`[BOT] ❌ Error occurred, attempting cleanup...`);
|
|
212
|
-
//
|
|
245
|
+
// 🔑 错误时也要清理taskId和session
|
|
213
246
|
try {
|
|
214
|
-
const core = getXYRuntime();
|
|
215
247
|
const params = message.params;
|
|
216
248
|
const sessionId = params?.sessionId;
|
|
217
249
|
if (sessionId) {
|
|
218
|
-
log(`[BOT] 🧹 Cleaning up
|
|
250
|
+
log(`[BOT] 🧹 Cleaning up after error: ${sessionId}`);
|
|
251
|
+
// 清理 taskId
|
|
252
|
+
decrementTaskIdRef(sessionId);
|
|
253
|
+
unlockTaskId(sessionId);
|
|
254
|
+
// 清理 session
|
|
255
|
+
const core = getXYRuntime();
|
|
219
256
|
const route = core.channel.routing.resolveAgentRoute({
|
|
220
257
|
cfg,
|
|
221
258
|
channel: "xiaoyi-channel",
|
|
222
259
|
accountId,
|
|
223
260
|
peer: {
|
|
224
261
|
kind: "direct",
|
|
225
|
-
id: sessionId,
|
|
262
|
+
id: sessionId,
|
|
226
263
|
},
|
|
227
264
|
});
|
|
228
|
-
log(`[BOT] - Unregistering session: ${route.sessionKey}`);
|
|
229
265
|
unregisterSession(route.sessionKey);
|
|
230
|
-
log(`[BOT] ✅
|
|
266
|
+
log(`[BOT] ✅ Cleanup completed after error`);
|
|
231
267
|
}
|
|
232
268
|
}
|
|
233
269
|
catch (cleanupErr) {
|
package/dist/src/channel.js
CHANGED
|
@@ -8,9 +8,19 @@ import { searchNoteTool } from "./tools/search-note-tool.js";
|
|
|
8
8
|
import { modifyNoteTool } from "./tools/modify-note-tool.js";
|
|
9
9
|
import { calendarTool } from "./tools/calendar-tool.js";
|
|
10
10
|
import { searchCalendarTool } from "./tools/search-calendar-tool.js";
|
|
11
|
-
|
|
11
|
+
import { searchContactTool } from "./tools/search-contact-tool.js";
|
|
12
12
|
import { searchPhotoGalleryTool } from "./tools/search-photo-gallery-tool.js";
|
|
13
13
|
import { uploadPhotoTool } from "./tools/upload-photo-tool.js";
|
|
14
|
+
import { xiaoyiGuiTool } from "./tools/xiaoyi-gui-tool.js";
|
|
15
|
+
import { callPhoneTool } from "./tools/call-phone-tool.js";
|
|
16
|
+
import { searchMessageTool } from "./tools/search-message-tool.js";
|
|
17
|
+
import { searchFileTool } from "./tools/search-file-tool.js";
|
|
18
|
+
import { uploadFileTool } from "./tools/upload-file-tool.js";
|
|
19
|
+
import { createAlarmTool } from "./tools/create-alarm-tool.js";
|
|
20
|
+
import { searchAlarmTool } from "./tools/search-alarm-tool.js";
|
|
21
|
+
import { modifyAlarmTool } from "./tools/modify-alarm-tool.js";
|
|
22
|
+
import { deleteAlarmTool } from "./tools/delete-alarm-tool.js";
|
|
23
|
+
import { sendFileToUserTool } from "./tools/send-file-to-user-tool.js";
|
|
14
24
|
/**
|
|
15
25
|
* Xiaoyi Channel Plugin for OpenClaw.
|
|
16
26
|
* Implements Xiaoyi A2A protocol with dual WebSocket connections.
|
|
@@ -50,7 +60,7 @@ export const xyPlugin = {
|
|
|
50
60
|
},
|
|
51
61
|
outbound: xyOutbound,
|
|
52
62
|
onboarding: xyOnboardingAdapter,
|
|
53
|
-
agentTools: [locationTool, noteTool, searchNoteTool, modifyNoteTool, calendarTool, searchCalendarTool, searchPhotoGalleryTool, uploadPhotoTool
|
|
63
|
+
agentTools: [locationTool, noteTool, searchNoteTool, modifyNoteTool, calendarTool, searchCalendarTool, searchContactTool, searchPhotoGalleryTool, uploadPhotoTool, xiaoyiGuiTool, callPhoneTool, searchMessageTool, searchFileTool, uploadFileTool, createAlarmTool, searchAlarmTool, modifyAlarmTool, deleteAlarmTool, sendFileToUserTool],
|
|
54
64
|
messaging: {
|
|
55
65
|
normalizeTarget: (raw) => {
|
|
56
66
|
const trimmed = raw.trim();
|
package/dist/src/monitor.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { resolveXYConfig } from "./config.js";
|
|
2
2
|
import { getXYWebSocketManager, diagnoseAllManagers, cleanupOrphanConnections, removeXYWebSocketManager } from "./client.js";
|
|
3
3
|
import { handleXYMessage } from "./bot.js";
|
|
4
|
+
import { parseA2AMessage } from "./parser.js";
|
|
5
|
+
import { hasActiveTask } from "./task-manager.js";
|
|
4
6
|
/**
|
|
5
7
|
* Per-session serial queue that ensures messages from the same session are processed
|
|
6
8
|
* in arrival order while allowing different sessions to run concurrently.
|
|
@@ -94,11 +96,39 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
94
96
|
log(`[MONITOR-HANDLER] 🧹 Cleaned up messageKey=${messageKey}, remaining active: ${activeMessages.size}`);
|
|
95
97
|
}
|
|
96
98
|
};
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
// 🔑 核心改造:检测steer模式
|
|
100
|
+
// 需要提前解析消息以获取sessionId
|
|
101
|
+
try {
|
|
102
|
+
const parsed = parseA2AMessage(message);
|
|
103
|
+
const steerMode = cfg.messages?.queue?.mode === "steer";
|
|
104
|
+
const hasActiveRun = hasActiveTask(parsed.sessionId);
|
|
105
|
+
if (steerMode && hasActiveRun) {
|
|
106
|
+
// Steer模式且有活跃任务:不入队列,直接并发执行
|
|
107
|
+
log(`[MONITOR-HANDLER] 🔄 STEER MODE: Executing concurrently for messageKey=${messageKey}`);
|
|
108
|
+
log(`[MONITOR-HANDLER] - sessionId: ${parsed.sessionId}`);
|
|
109
|
+
log(`[MONITOR-HANDLER] - Bypassing queue to allow message insertion`);
|
|
110
|
+
void task().catch((err) => {
|
|
111
|
+
error(`XY gateway: concurrent steer task failed for ${messageKey}: ${String(err)}`);
|
|
112
|
+
activeMessages.delete(messageKey);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// 正常模式:入队列串行执行
|
|
117
|
+
log(`[MONITOR-HANDLER] 📋 NORMAL MODE: Enqueuing for messageKey=${messageKey}`);
|
|
118
|
+
void enqueue(sessionId, task).catch((err) => {
|
|
119
|
+
error(`XY gateway: queue processing failed for session ${sessionId}: ${String(err)}`);
|
|
120
|
+
activeMessages.delete(messageKey);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (parseErr) {
|
|
125
|
+
// 解析失败,回退到正常队列模式
|
|
126
|
+
error(`[MONITOR-HANDLER] Failed to parse message for steer detection: ${String(parseErr)}`);
|
|
127
|
+
void enqueue(sessionId, task).catch((err) => {
|
|
128
|
+
error(`XY gateway: queue processing failed for session ${sessionId}: ${String(err)}`);
|
|
129
|
+
activeMessages.delete(messageKey);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
102
132
|
};
|
|
103
133
|
const connectedHandler = (serverId) => {
|
|
104
134
|
if (!loggedServers.has(serverId)) {
|
package/dist/src/outbound.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { resolveXYConfig } from "./config.js";
|
|
2
|
-
import { XYFileUploadService } from "./file-upload.js";
|
|
3
2
|
import { XYPushService } from "./push.js";
|
|
4
|
-
import {
|
|
3
|
+
import { getCurrentSessionContext } from "./tools/session-manager.js";
|
|
5
4
|
// Special marker for default push delivery when no target is specified
|
|
6
5
|
const DEFAULT_PUSH_MARKER = "default";
|
|
7
6
|
// File extension to MIME type mapping
|
|
@@ -65,8 +64,8 @@ export const xyOutbound = {
|
|
|
65
64
|
// If the target doesn't contain "::", try to enhance it with taskId from session context
|
|
66
65
|
if (!trimmedTo.includes("::")) {
|
|
67
66
|
console.log(`[xyOutbound.resolveTarget] Target "${trimmedTo}" missing taskId, looking up session context`);
|
|
68
|
-
// Try to get the
|
|
69
|
-
const sessionContext =
|
|
67
|
+
// Try to get the current session context
|
|
68
|
+
const sessionContext = getCurrentSessionContext();
|
|
70
69
|
if (sessionContext && sessionContext.sessionId === trimmedTo) {
|
|
71
70
|
const enhancedTarget = `${trimmedTo}::${sessionContext.taskId}`;
|
|
72
71
|
console.log(`[xyOutbound.resolveTarget] Enhanced target: ${enhancedTarget}`);
|
|
@@ -130,81 +129,94 @@ export const xyOutbound = {
|
|
|
130
129
|
mediaUrl,
|
|
131
130
|
mediaLocalRoots,
|
|
132
131
|
});
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
const [sessionId, taskId] = parts;
|
|
139
|
-
// Resolve configuration
|
|
140
|
-
const config = resolveXYConfig(cfg);
|
|
141
|
-
// Create upload service
|
|
142
|
-
const uploadService = new XYFileUploadService(config.fileUploadUrl, config.apiKey, config.uid);
|
|
143
|
-
// Validate mediaUrl
|
|
144
|
-
if (!mediaUrl) {
|
|
145
|
-
throw new Error("mediaUrl is required for sendMedia");
|
|
146
|
-
}
|
|
147
|
-
// Upload file
|
|
148
|
-
const fileId = await uploadService.uploadFile(mediaUrl);
|
|
149
|
-
// Check if fileId is empty
|
|
150
|
-
if (!fileId) {
|
|
151
|
-
console.log(`[xyOutbound.sendMedia] ⚠️ File upload failed: fileId is empty, aborting sendMedia`);
|
|
152
|
-
return {
|
|
153
|
-
channel: "xiaoyi-channel",
|
|
154
|
-
messageId: "",
|
|
155
|
-
chatId: to,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
console.log(`[xyOutbound.sendMedia] File uploaded:`, {
|
|
159
|
-
fileId,
|
|
160
|
-
sessionId,
|
|
161
|
-
taskId,
|
|
162
|
-
});
|
|
163
|
-
// Get filename and mime type from mediaUrl
|
|
164
|
-
// mediaUrl may be a local file path or URL
|
|
165
|
-
const fileName = mediaUrl.split("/").pop() || "unknown";
|
|
166
|
-
const mimeType = getMimeTypeFromFilename(fileName);
|
|
167
|
-
// Build agent_response message
|
|
168
|
-
const agentResponse = {
|
|
169
|
-
msgType: "agent_response",
|
|
170
|
-
agentId: config.agentId,
|
|
171
|
-
sessionId: sessionId,
|
|
172
|
-
taskId: taskId,
|
|
173
|
-
msgDetail: JSON.stringify({
|
|
174
|
-
jsonrpc: "2.0",
|
|
175
|
-
id: taskId,
|
|
176
|
-
result: {
|
|
177
|
-
kind: "artifact-update",
|
|
178
|
-
append: true,
|
|
179
|
-
lastChunk: false,
|
|
180
|
-
final: false,
|
|
181
|
-
artifact: {
|
|
182
|
-
artifactId: taskId,
|
|
183
|
-
parts: [
|
|
184
|
-
{
|
|
185
|
-
kind: "file",
|
|
186
|
-
file: {
|
|
187
|
-
name: fileName,
|
|
188
|
-
mimeType: mimeType,
|
|
189
|
-
fileId: fileId,
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
error: { code: 0 },
|
|
196
|
-
}),
|
|
197
|
-
};
|
|
198
|
-
// Get WebSocket manager and send message
|
|
199
|
-
const { getXYWebSocketManager } = await import("./client.js");
|
|
200
|
-
const wsManager = getXYWebSocketManager(config);
|
|
201
|
-
await wsManager.sendMessage(sessionId, agentResponse);
|
|
202
|
-
console.log(`[xyOutbound.sendMedia] WebSocket message sent successfully`);
|
|
203
|
-
// Return message info
|
|
132
|
+
// All sendMedia processing logic has been disabled
|
|
133
|
+
// Use send_file_to_user tool instead for file transfers to user device
|
|
134
|
+
console.log(`[xyOutbound.sendMedia] Processing disabled, use send_file_to_user tool`);
|
|
135
|
+
// Return empty message info
|
|
204
136
|
return {
|
|
205
137
|
channel: "xiaoyi-channel",
|
|
206
|
-
messageId:
|
|
138
|
+
messageId: "把文件/图片发送给用户请使用send_file_to_user工具",
|
|
207
139
|
chatId: to,
|
|
208
140
|
};
|
|
141
|
+
// // Parse to: "sessionId::taskId"
|
|
142
|
+
// const parts = to.split("::");
|
|
143
|
+
// if (parts.length !== 2) {
|
|
144
|
+
// throw new Error(`Invalid to format: "${to}". Expected "sessionId::taskId"`);
|
|
145
|
+
// }
|
|
146
|
+
// const [sessionId, taskId] = parts;
|
|
147
|
+
// // Resolve configuration
|
|
148
|
+
// const config = resolveXYConfig(cfg);
|
|
149
|
+
// // Create upload service
|
|
150
|
+
// const uploadService = new XYFileUploadService(
|
|
151
|
+
// config.fileUploadUrl,
|
|
152
|
+
// config.apiKey,
|
|
153
|
+
// config.uid
|
|
154
|
+
// );
|
|
155
|
+
// // Validate mediaUrl
|
|
156
|
+
// if (!mediaUrl) {
|
|
157
|
+
// throw new Error("mediaUrl is required for sendMedia");
|
|
158
|
+
// }
|
|
159
|
+
// // Upload file
|
|
160
|
+
// const fileId = await uploadService.uploadFile(mediaUrl);
|
|
161
|
+
// // Check if fileId is empty
|
|
162
|
+
// if (!fileId) {
|
|
163
|
+
// console.log(`[xyOutbound.sendMedia] ⚠️ File upload failed: fileId is empty, aborting sendMedia`);
|
|
164
|
+
// return {
|
|
165
|
+
// channel: "xiaoyi-channel",
|
|
166
|
+
// messageId: "",
|
|
167
|
+
// chatId: to,
|
|
168
|
+
// };
|
|
169
|
+
// }
|
|
170
|
+
// console.log(`[xyOutbound.sendMedia] File uploaded:`, {
|
|
171
|
+
// fileId,
|
|
172
|
+
// sessionId,
|
|
173
|
+
// taskId,
|
|
174
|
+
// });
|
|
175
|
+
// // Get filename and mime type from mediaUrl
|
|
176
|
+
// // mediaUrl may be a local file path or URL
|
|
177
|
+
// const fileName = mediaUrl.split("/").pop() || "unknown";
|
|
178
|
+
// const mimeType = getMimeTypeFromFilename(fileName);
|
|
179
|
+
// // Build agent_response message
|
|
180
|
+
// const agentResponse: OutboundWebSocketMessage = {
|
|
181
|
+
// msgType: "agent_response",
|
|
182
|
+
// agentId: config.agentId,
|
|
183
|
+
// sessionId: sessionId,
|
|
184
|
+
// taskId: taskId,
|
|
185
|
+
// msgDetail: JSON.stringify({
|
|
186
|
+
// jsonrpc: "2.0",
|
|
187
|
+
// id: taskId,
|
|
188
|
+
// result: {
|
|
189
|
+
// kind: "artifact-update",
|
|
190
|
+
// append: true,
|
|
191
|
+
// lastChunk: false,
|
|
192
|
+
// final: false,
|
|
193
|
+
// artifact: {
|
|
194
|
+
// artifactId: taskId,
|
|
195
|
+
// parts: [
|
|
196
|
+
// {
|
|
197
|
+
// kind: "file",
|
|
198
|
+
// file: {
|
|
199
|
+
// name: fileName,
|
|
200
|
+
// mimeType: mimeType,
|
|
201
|
+
// fileId: fileId,
|
|
202
|
+
// },
|
|
203
|
+
// },
|
|
204
|
+
// ],
|
|
205
|
+
// },
|
|
206
|
+
// },
|
|
207
|
+
// error: { code: 0 },
|
|
208
|
+
// }),
|
|
209
|
+
// };
|
|
210
|
+
// // Get WebSocket manager and send message
|
|
211
|
+
// const { getXYWebSocketManager } = await import("./client.js");
|
|
212
|
+
// const wsManager = getXYWebSocketManager(config);
|
|
213
|
+
// await wsManager.sendMessage(sessionId, agentResponse);
|
|
214
|
+
// console.log(`[xyOutbound.sendMedia] WebSocket message sent successfully`);
|
|
215
|
+
// // Return message info
|
|
216
|
+
// return {
|
|
217
|
+
// channel: "xiaoyi-channel",
|
|
218
|
+
// messageId: fileId,
|
|
219
|
+
// chatId: to,
|
|
220
|
+
// };
|
|
209
221
|
},
|
|
210
222
|
};
|