@ynhcj/xiaoyi-channel 0.0.39-next → 0.0.40-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/src/bot.d.ts CHANGED
@@ -8,6 +8,7 @@ export interface HandleXYMessageParams {
8
8
  runtime: RuntimeEnv;
9
9
  message: A2AJsonRpcRequest;
10
10
  accountId: string;
11
+ webSocketSessionId?: string;
11
12
  }
12
13
  /**
13
14
  * Handle an incoming A2A message.
package/dist/src/bot.js CHANGED
@@ -9,6 +9,7 @@ import { registerSession, unregisterSession, runWithSessionContext } from "./too
9
9
  import { configManager } from "./utils/config-manager.js";
10
10
  import { addPushId } from "./utils/pushid-manager.js";
11
11
  import { getPushDataById } from "./utils/pushdata-manager.js";
12
+ import { saveRuntimeInfo } from "./utils/runtime-manager.js";
12
13
  import { registerTaskId, decrementTaskIdRef, lockTaskId, unlockTaskId, hasActiveTask, } from "./task-manager.js";
13
14
  /**
14
15
  * Handle an incoming A2A message.
@@ -16,7 +17,7 @@ import { registerTaskId, decrementTaskIdRef, lockTaskId, unlockTaskId, hasActive
16
17
  * Runtime is expected to be validated before calling this function.
17
18
  */
18
19
  export async function handleXYMessage(params) {
19
- const { cfg, runtime, message, accountId } = params;
20
+ const { cfg, runtime, message, accountId, webSocketSessionId } = params;
20
21
  const log = runtime?.log ?? console.log;
21
22
  const error = runtime?.error ?? console.error;
22
23
  // 每次收到消息时更新缓存,供 steer 注入使用
@@ -125,6 +126,13 @@ export async function handleXYMessage(params) {
125
126
  else {
126
127
  log(`[BOT] ℹ️ No push_id found in message, will use config default`);
127
128
  }
129
+ // 保存 runtime 信息到 .xiaoyiruntime 文件(异步,不阻塞主流程)
130
+ saveRuntimeInfo(webSocketSessionId || parsed.sessionId, // SESSION_ID (WebSocket 层级,如果没有则 fallback)
131
+ parsed.sessionId, // CONVERSATION_ID (param 里的 sessionId)
132
+ parsed.taskId // TASK_ID (param.id)
133
+ ).catch((err) => {
134
+ error(`[BOT] Failed to save runtime info:`, err);
135
+ });
128
136
  // Resolve configuration (needed for status updates)
129
137
  const config = resolveXYConfig(cfg);
130
138
  // ✅ Resolve agent route (following feishu pattern)
@@ -231,6 +239,7 @@ export async function handleXYMessage(params) {
231
239
  messageId: parsed.messageId,
232
240
  agentId: route.accountId,
233
241
  };
242
+ log(`[BOT-DISPATCH] ⏳ withReplyDispatcher starting, sessionKey=${route.sessionKey}`);
234
243
  await core.channel.reply.withReplyDispatcher({
235
244
  dispatcher,
236
245
  onSettled: () => {
@@ -249,12 +258,32 @@ export async function handleXYMessage(params) {
249
258
  },
250
259
  run: () =>
251
260
  // 🔐 Use AsyncLocalStorage to provide session context to tools
252
- runWithSessionContext(sessionContext, () => core.channel.reply.dispatchReplyFromConfig({
253
- ctx: ctxPayload,
254
- cfg,
255
- dispatcher,
256
- replyOptions,
257
- })),
261
+ runWithSessionContext(sessionContext, async () => {
262
+ log(`[BOT-DISPATCH] ⏳ dispatchReplyFromConfig starting...`);
263
+ log(`[BOT-DISPATCH] - sessionKey: ${ctxPayload.SessionKey}`);
264
+ log(`[BOT-DISPATCH] - provider: ${ctxPayload.Provider}`);
265
+ log(`[BOT-DISPATCH] - surface: ${ctxPayload.Surface}`);
266
+ log(`[BOT-DISPATCH] - from: ${ctxPayload.From}`);
267
+ log(`[BOT-DISPATCH] - body length: ${ctxPayload.Body?.length ?? 0}`);
268
+ try {
269
+ const result = await core.channel.reply.dispatchReplyFromConfig({
270
+ ctx: ctxPayload,
271
+ cfg,
272
+ dispatcher,
273
+ replyOptions,
274
+ });
275
+ log(`[BOT-DISPATCH] ✅ dispatchReplyFromConfig returned`);
276
+ log(`[BOT-DISPATCH] - result: ${JSON.stringify(result)}`);
277
+ return result;
278
+ }
279
+ catch (dispatchErr) {
280
+ error(`[BOT-DISPATCH] ❌ dispatchReplyFromConfig threw`);
281
+ error(`[BOT-DISPATCH] - error name: ${dispatchErr instanceof Error ? dispatchErr.name : "unknown"}`);
282
+ error(`[BOT-DISPATCH] - error message: ${String(dispatchErr)}`);
283
+ error(`[BOT-DISPATCH] - error stack: ${dispatchErr instanceof Error ? dispatchErr.stack?.slice(0, 500) : "N/A"}`);
284
+ throw dispatchErr;
285
+ }
286
+ }),
258
287
  });
259
288
  log(`[BOT] ✅ Dispatcher completed for session: ${parsed.sessionId}`);
260
289
  log(`xy: dispatch complete (session=${parsed.sessionId})`);
@@ -15,6 +15,8 @@ export interface SendA2AResponseParams {
15
15
  fileType: string;
16
16
  fileId: string;
17
17
  }>;
18
+ errorCode?: number | string;
19
+ errorMessage?: string;
18
20
  }
19
21
  /**
20
22
  * Send an A2A artifact update response.
@@ -6,10 +6,10 @@ import { getXYRuntime } from "./runtime.js";
6
6
  * Send an A2A artifact update response.
7
7
  */
8
8
  export async function sendA2AResponse(params) {
9
- const { config, sessionId, taskId, messageId, text, append, final, files } = params;
9
+ const { config, sessionId, taskId, messageId, text, append, final, files, errorCode, errorMessage } = params;
10
10
  const runtime = getXYRuntime();
11
11
  const log = runtime?.log ?? console.log;
12
- const error = runtime?.error ?? console.error;
12
+ const errorFn = runtime?.error ?? console.error;
13
13
  // Build artifact update event
14
14
  const artifact = {
15
15
  taskId,
@@ -42,6 +42,14 @@ export async function sendA2AResponse(params) {
42
42
  id: messageId,
43
43
  result: artifact,
44
44
  };
45
+ // 🔑 添加 error 字段(仅当提供 errorCode 时)
46
+ if (errorCode !== undefined) {
47
+ jsonRpcResponse.error = {
48
+ code: errorCode,
49
+ message: errorMessage ?? "任务执行异常,请重试",
50
+ };
51
+ log(`[A2A_RESPONSE] ⚠️ Including error code: ${errorCode}`);
52
+ }
45
53
  // Send via WebSocket
46
54
  const wsManager = getXYWebSocketManager(config);
47
55
  const outboundMessage = {
@@ -83,6 +83,7 @@ export async function monitorXYProvider(opts = {}) {
83
83
  runtime,
84
84
  message,
85
85
  accountId, // ✅ Pass accountId ("default")
86
+ webSocketSessionId: sessionId, // ✅ 传递 WebSocket 层级的 sessionId
86
87
  });
87
88
  }
88
89
  catch (err) {
@@ -235,9 +235,11 @@ export function createXYReplyDispatcher(params) {
235
235
  text: "任务执行异常,请重试~",
236
236
  append: false,
237
237
  final: true,
238
+ errorCode: 99921111,
239
+ errorMessage: "任务执行异常,请重试",
238
240
  });
239
241
  finalSent = true;
240
- log(`[ON_IDLE] ✅ Sent error response`);
242
+ log(`[ON_IDLE] ✅ Sent error response with code: 99921111`);
241
243
  }
242
244
  catch (err) {
243
245
  error(`[ON_IDLE] Failed to send error response:`, err);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 保存 runtime 信息到 .xiaoyiruntime 文件
3
+ * @param webSocketSessionId - WebSocket 层级的 sessionId (SESSION_ID)
4
+ * @param conversationId - param 里的 sessionId (CONVERSATION_ID)
5
+ * @param taskId - 任务 ID (param.id)
6
+ */
7
+ export declare function saveRuntimeInfo(webSocketSessionId: string, conversationId: string, taskId: string): Promise<void>;
@@ -0,0 +1,42 @@
1
+ // xiaoyi runtime 持久化管理器
2
+ import { promises as fs } from "fs";
3
+ import * as path from "path";
4
+ import { logger } from "./logger.js";
5
+ const RUNTIME_FILE = "/home/sandbox/.openclaw/.xiaoyiruntime";
6
+ /**
7
+ * 确保目录存在
8
+ */
9
+ async function ensureDirectoryExists(filePath) {
10
+ const dir = path.dirname(filePath);
11
+ try {
12
+ await fs.mkdir(dir, { recursive: true });
13
+ }
14
+ catch (error) {
15
+ logger.error(`[RuntimeManager] Failed to create directory ${dir}:`, error);
16
+ }
17
+ }
18
+ /**
19
+ * 保存 runtime 信息到 .xiaoyiruntime 文件
20
+ * @param webSocketSessionId - WebSocket 层级的 sessionId (SESSION_ID)
21
+ * @param conversationId - param 里的 sessionId (CONVERSATION_ID)
22
+ * @param taskId - 任务 ID (param.id)
23
+ */
24
+ export async function saveRuntimeInfo(webSocketSessionId, conversationId, taskId) {
25
+ if (!webSocketSessionId || !conversationId || !taskId) {
26
+ logger.warn(`[RuntimeManager] Invalid params: SESSION_ID=${webSocketSessionId}, CONVERSATION_ID=${conversationId}, TASK_ID=${taskId}`);
27
+ return;
28
+ }
29
+ try {
30
+ await ensureDirectoryExists(RUNTIME_FILE);
31
+ const content = `SESSION_ID=${webSocketSessionId}\nCONVERSATION_ID=${conversationId}\nTASK_ID=${taskId}\n`;
32
+ await fs.writeFile(RUNTIME_FILE, content, "utf-8");
33
+ logger.log(`[RuntimeManager] ✅ Saved runtime info to .xiaoyiruntime`);
34
+ logger.log(`[RuntimeManager] - SESSION_ID: ${webSocketSessionId}`);
35
+ logger.log(`[RuntimeManager] - CONVERSATION_ID: ${conversationId}`);
36
+ logger.log(`[RuntimeManager] - TASK_ID: ${taskId}`);
37
+ }
38
+ catch (error) {
39
+ logger.error(`[RuntimeManager] Failed to save runtime info:`, error);
40
+ // 不抛出异常,避免影响主流程
41
+ }
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.39-next",
3
+ "version": "0.0.40-next",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -59,13 +59,12 @@
59
59
  "dependencies": {
60
60
  "ws": "^8.14.2",
61
61
  "uuid": "^9.0.0",
62
- "node-fetch": "^2.7.0"
62
+ "node-fetch": "^3.3.2"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@types/ws": "^8.5.8",
66
66
  "@types/uuid": "^9.0.5",
67
67
  "@types/node": "^20.8.0",
68
- "@types/node-fetch": "^2.6.2",
69
68
  "typescript": "^5.9.2"
70
69
  }
71
70
  }