@ynhcj/xiaoyi-channel 0.0.156-beta → 0.0.158-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/cron-query-handler.d.ts +17 -0
- package/dist/src/cron-query-handler.js +101 -0
- package/dist/src/formatter.d.ts +2 -0
- package/dist/src/formatter.js +1 -1
- package/dist/src/monitor.js +9 -0
- package/dist/src/self-evolution-handler.js +1 -1
- package/dist/src/tools/agent-as-skill-tool.js +55 -4
- package/dist/src/websocket.js +19 -1
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type CronQueryAction = "list" | "status" | "runs" | "add" | "update" | "remove" | "run";
|
|
2
|
+
export interface CronQueryEventContext {
|
|
3
|
+
action: CronQueryAction;
|
|
4
|
+
jobId?: string;
|
|
5
|
+
params?: Record<string, unknown>;
|
|
6
|
+
/** Original A2A message fields for routing the response. */
|
|
7
|
+
sessionId?: string;
|
|
8
|
+
taskId?: string;
|
|
9
|
+
messageId?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Handle a cron-query-event.
|
|
13
|
+
*
|
|
14
|
+
* Calls the Gateway cron RPC and sends the result back through sendCommand
|
|
15
|
+
* as a System.CronQuery command with the full result object in payload.ans.
|
|
16
|
+
*/
|
|
17
|
+
export declare function handleCronQueryEvent(context: CronQueryEventContext, cfg?: unknown): Promise<void>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Cron query event handler.
|
|
2
|
+
// Listens for cron-query-event from the WebSocket manager,
|
|
3
|
+
// calls Gateway cron RPC via callGatewayTool, and sends the
|
|
4
|
+
// result back to the client via sendCommand as a System.CronQuery
|
|
5
|
+
// command with the result in payload.ans.
|
|
6
|
+
import { callGatewayTool } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
7
|
+
import { sendCommand } from "./formatter.js";
|
|
8
|
+
import { resolveXYConfig } from "./config.js";
|
|
9
|
+
import { logger } from "./utils/logger.js";
|
|
10
|
+
const GATEWAY_TIMEOUT_MS = 60_000;
|
|
11
|
+
/**
|
|
12
|
+
* Handle a cron-query-event.
|
|
13
|
+
*
|
|
14
|
+
* Calls the Gateway cron RPC and sends the result back through sendCommand
|
|
15
|
+
* as a System.CronQuery command with the full result object in payload.ans.
|
|
16
|
+
*/
|
|
17
|
+
export async function handleCronQueryEvent(context, cfg) {
|
|
18
|
+
const { action, jobId, params, sessionId, taskId, messageId } = context;
|
|
19
|
+
logger.log(`[CRON-QUERY] Received event: action=${action}, jobId=${jobId ?? "(none)"}`);
|
|
20
|
+
let result;
|
|
21
|
+
let error;
|
|
22
|
+
try {
|
|
23
|
+
switch (action) {
|
|
24
|
+
case "list":
|
|
25
|
+
result = await callGatewayTool("cron.list", { timeoutMs: GATEWAY_TIMEOUT_MS }, params ?? {});
|
|
26
|
+
break;
|
|
27
|
+
case "status":
|
|
28
|
+
result = await callGatewayTool("cron.status", { timeoutMs: GATEWAY_TIMEOUT_MS }, {});
|
|
29
|
+
break;
|
|
30
|
+
case "runs":
|
|
31
|
+
result = await callGatewayTool("cron.runs", { timeoutMs: GATEWAY_TIMEOUT_MS }, {
|
|
32
|
+
jobId,
|
|
33
|
+
...params,
|
|
34
|
+
});
|
|
35
|
+
break;
|
|
36
|
+
case "add":
|
|
37
|
+
result = await callGatewayTool("cron.add", { timeoutMs: GATEWAY_TIMEOUT_MS }, params ?? {});
|
|
38
|
+
break;
|
|
39
|
+
case "update":
|
|
40
|
+
result = await callGatewayTool("cron.update", { timeoutMs: GATEWAY_TIMEOUT_MS }, {
|
|
41
|
+
jobId,
|
|
42
|
+
...params,
|
|
43
|
+
});
|
|
44
|
+
break;
|
|
45
|
+
case "remove":
|
|
46
|
+
result = await callGatewayTool("cron.remove", { timeoutMs: GATEWAY_TIMEOUT_MS }, {
|
|
47
|
+
jobId,
|
|
48
|
+
});
|
|
49
|
+
break;
|
|
50
|
+
case "run":
|
|
51
|
+
result = await callGatewayTool("cron.run", { timeoutMs: GATEWAY_TIMEOUT_MS }, {
|
|
52
|
+
jobId,
|
|
53
|
+
mode: "force",
|
|
54
|
+
...params,
|
|
55
|
+
});
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
error = `Unknown action: ${context.action}`;
|
|
59
|
+
logger.error(`[CRON-QUERY] ${error}`);
|
|
60
|
+
result = { error };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
error = err instanceof Error ? err.message : String(err);
|
|
65
|
+
logger.error(`[CRON-QUERY] RPC call failed for action=${action}:`, err);
|
|
66
|
+
result = { error };
|
|
67
|
+
}
|
|
68
|
+
// Log the result
|
|
69
|
+
logger.log(`[CRON-QUERY] RPC result for action=${action}: ${JSON.stringify(result, null, 2)}`);
|
|
70
|
+
// Send result back via sendCommand as System.CronQuery with payload.ans
|
|
71
|
+
if (cfg && sessionId && taskId && messageId) {
|
|
72
|
+
try {
|
|
73
|
+
const config = resolveXYConfig(cfg);
|
|
74
|
+
const command = {
|
|
75
|
+
header: {
|
|
76
|
+
namespace: "System",
|
|
77
|
+
name: "CronQuery",
|
|
78
|
+
},
|
|
79
|
+
payload: {
|
|
80
|
+
action,
|
|
81
|
+
ans: result,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
await sendCommand({
|
|
85
|
+
config,
|
|
86
|
+
sessionId,
|
|
87
|
+
taskId,
|
|
88
|
+
messageId,
|
|
89
|
+
command,
|
|
90
|
+
final: true,
|
|
91
|
+
});
|
|
92
|
+
logger.log(`[CRON-QUERY] Sent response via sendCommand, action=${action}`);
|
|
93
|
+
}
|
|
94
|
+
catch (sendErr) {
|
|
95
|
+
logger.error(`[CRON-QUERY] Failed to send response via sendCommand:`, sendErr);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
logger.warn(`[CRON-QUERY] Missing cfg/sessionId/taskId/messageId, skipping sendCommand`);
|
|
100
|
+
}
|
|
101
|
+
}
|
package/dist/src/formatter.d.ts
CHANGED
|
@@ -67,6 +67,8 @@ export interface SendCommandParams {
|
|
|
67
67
|
commands?: A2ACommand[];
|
|
68
68
|
/** toolCallId from the tool's execute() — used for cron detection via hook-set Map. */
|
|
69
69
|
toolCallId?: string;
|
|
70
|
+
/** When true, the artifact-update is sent with final=true. Default: false. */
|
|
71
|
+
final?: boolean;
|
|
70
72
|
}
|
|
71
73
|
/**
|
|
72
74
|
* Send a command as an artifact update (final=false).
|
package/dist/src/formatter.js
CHANGED
package/dist/src/monitor.js
CHANGED
|
@@ -7,6 +7,7 @@ import { sendA2AResponse } from "./formatter.js";
|
|
|
7
7
|
import { handleTriggerEvent } from "./trigger-handler.js";
|
|
8
8
|
import { handleSelfEvolutionEvent, handleSelfEvolutionStateGetEvent } from "./self-evolution-handler.js";
|
|
9
9
|
import { handleLoginTokenEvent } from "./login-token-handler.js";
|
|
10
|
+
import { handleCronQueryEvent } from "./cron-query-handler.js";
|
|
10
11
|
import { cleanupStaleTempFiles } from "./reply-dispatcher.js";
|
|
11
12
|
import { cleanupStaleSessions, getActiveSessionCount, cleanupAllSessions } from "./tools/session-manager.js";
|
|
12
13
|
import { logger } from "./utils/logger.js";
|
|
@@ -186,6 +187,12 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
186
187
|
logger.log(`[MONITOR] Received login-token-event, dispatching to handler...`);
|
|
187
188
|
handleLoginTokenEvent(context, runtime);
|
|
188
189
|
};
|
|
190
|
+
const cronQueryEventHandler = (context) => {
|
|
191
|
+
logger.log(`[MONITOR] Received cron-query-event, dispatching to handler...`);
|
|
192
|
+
handleCronQueryEvent(context, cfg).catch((err) => {
|
|
193
|
+
logger.error(`[MONITOR] Failed to handle cron-query-event:`, err);
|
|
194
|
+
});
|
|
195
|
+
};
|
|
189
196
|
const cleanup = () => {
|
|
190
197
|
logger.log("XY gateway: cleaning up...");
|
|
191
198
|
// 🔍 Diagnose before cleanup
|
|
@@ -206,6 +213,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
206
213
|
wsManager.off("self-evolution-event", selfEvolutionHandler);
|
|
207
214
|
wsManager.off("self-evolution-state-get-event", selfEvolutionStateGetHandler);
|
|
208
215
|
wsManager.off("login-token-event", loginTokenEventHandler);
|
|
216
|
+
wsManager.off("cron-query-event", cronQueryEventHandler);
|
|
209
217
|
// ✅ Disconnect the wsManager to prevent connection leaks
|
|
210
218
|
// This is safe because each gateway lifecycle should have clean connections
|
|
211
219
|
wsManager.disconnect();
|
|
@@ -269,6 +277,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
269
277
|
wsManager.on("self-evolution-event", selfEvolutionHandler);
|
|
270
278
|
wsManager.on("self-evolution-state-get-event", selfEvolutionStateGetHandler);
|
|
271
279
|
wsManager.on("login-token-event", loginTokenEventHandler);
|
|
280
|
+
wsManager.on("cron-query-event", cronQueryEventHandler);
|
|
272
281
|
// Start periodic health check (every 6 hours)
|
|
273
282
|
logger.log("Starting periodic health check (every 6 hours)...");
|
|
274
283
|
healthCheckInterval = setInterval(() => {
|
|
@@ -3,6 +3,7 @@ import { getXYWebSocketManager } from "../client.js";
|
|
|
3
3
|
import { sendCommand } from "../formatter.js";
|
|
4
4
|
import { getCurrentTaskId } from "../task-manager.js";
|
|
5
5
|
import { logger } from "../utils/logger.js";
|
|
6
|
+
import { XYFileUploadService } from "../file-upload.js";
|
|
6
7
|
/**
|
|
7
8
|
* Agent-as-skill tool - invokes a registered agent by agentId as a skill.
|
|
8
9
|
* The tool receives the agentId, query, and optional file attachments,
|
|
@@ -11,7 +12,7 @@ import { logger } from "../utils/logger.js";
|
|
|
11
12
|
export function createAgentAsSkillTool(ctx) {
|
|
12
13
|
const { config, sessionId, taskId, messageId } = ctx;
|
|
13
14
|
return {
|
|
14
|
-
name: "
|
|
15
|
+
name: "agent_as_a_tool",
|
|
15
16
|
label: "Agent as Skill Tool",
|
|
16
17
|
description: `智能体作为skill的执行元工具。当需要调用其他已注册的Agent来执行特定任务时使用此工具。
|
|
17
18
|
该工具会将用户请求和可选的附件文件转发给目标Agent执行,并返回执行结果。
|
|
@@ -37,8 +38,7 @@ export function createAgentAsSkillTool(ctx) {
|
|
|
37
38
|
description: "用户原始请求文本,原样转发给目标Agent执行",
|
|
38
39
|
},
|
|
39
40
|
filesInfo: {
|
|
40
|
-
|
|
41
|
-
description: "附件文件/图片信息列表,无文件时可传null或空数组",
|
|
41
|
+
description: "附件文件/图片信息列表,无文件时可传null或空数组,支持传入数组或JSON字符串",
|
|
42
42
|
items: {
|
|
43
43
|
type: "object",
|
|
44
44
|
properties: {
|
|
@@ -55,6 +55,10 @@ export function createAgentAsSkillTool(ctx) {
|
|
|
55
55
|
type: "string",
|
|
56
56
|
description: "文件可访问下载链接(完整HTTP/HTTPS地址)",
|
|
57
57
|
},
|
|
58
|
+
fileUrlLocal: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: "文件本地路径,如果提供此字段,工具会自动上传文件并将公网URL填入fileUrl",
|
|
61
|
+
},
|
|
58
62
|
},
|
|
59
63
|
},
|
|
60
64
|
},
|
|
@@ -71,6 +75,53 @@ export function createAgentAsSkillTool(ctx) {
|
|
|
71
75
|
if (!params.query || typeof params.query !== "string") {
|
|
72
76
|
throw new Error("Missing or invalid required parameter: query must be a non-empty string");
|
|
73
77
|
}
|
|
78
|
+
// Robust parsing: normalize filesInfo from array or JSON string
|
|
79
|
+
let filesInfo = null;
|
|
80
|
+
if (params.filesInfo) {
|
|
81
|
+
if (Array.isArray(params.filesInfo)) {
|
|
82
|
+
filesInfo = params.filesInfo;
|
|
83
|
+
}
|
|
84
|
+
else if (typeof params.filesInfo === 'string') {
|
|
85
|
+
try {
|
|
86
|
+
const parsed = JSON.parse(params.filesInfo);
|
|
87
|
+
if (Array.isArray(parsed)) {
|
|
88
|
+
filesInfo = parsed;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
throw new Error("filesInfo must be an array or a JSON string representing an array");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (parseError) {
|
|
95
|
+
throw new Error(`filesInfo JSON解析失败: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
filesInfo = null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Upload local files and fill fileUrl
|
|
103
|
+
if (filesInfo && filesInfo.length > 0) {
|
|
104
|
+
const uploadService = new XYFileUploadService(config.fileUploadUrl, config.apiKey, config.uid);
|
|
105
|
+
for (const fileInfo of filesInfo) {
|
|
106
|
+
if (fileInfo.fileUrlLocal && !fileInfo.fileUrl) {
|
|
107
|
+
try {
|
|
108
|
+
const publicUrl = await uploadService.uploadFileAndGetUrl(fileInfo.fileUrlLocal, "TEMPORARY_MATERIAL_DOC");
|
|
109
|
+
if (publicUrl) {
|
|
110
|
+
fileInfo.fileUrl = publicUrl;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
logger.warn("[AGENT-AS-SKILL] 上传文件未返回公网URL", { fileUrlLocal: fileInfo.fileUrlLocal });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (uploadError) {
|
|
117
|
+
logger.error("[AGENT-AS-SKILL] 上传本地文件失败", { fileUrlLocal: fileInfo.fileUrlLocal, error: uploadError });
|
|
118
|
+
throw new Error(`上传本地文件失败 (${fileInfo.fileUrlLocal}): ${uploadError instanceof Error ? uploadError.message : String(uploadError)}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Remove fileUrlLocal from the final payload
|
|
122
|
+
delete fileInfo.fileUrlLocal;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
74
125
|
// Get WebSocket manager
|
|
75
126
|
const wsManager = getXYWebSocketManager(config);
|
|
76
127
|
// Build ExecuteAgentAsSkill command
|
|
@@ -82,7 +133,7 @@ export function createAgentAsSkillTool(ctx) {
|
|
|
82
133
|
payload: {
|
|
83
134
|
agentId: params.agentId,
|
|
84
135
|
query: params.query,
|
|
85
|
-
filesInfo:
|
|
136
|
+
filesInfo: filesInfo || null,
|
|
86
137
|
},
|
|
87
138
|
};
|
|
88
139
|
// Send command and wait for response (5 minute timeout)
|
package/dist/src/websocket.js
CHANGED
|
@@ -367,7 +367,7 @@ export class XYWebSocketManager extends EventEmitter {
|
|
|
367
367
|
const payloadIntentName = typeof item?.payload?.intentName === "string" ? item.payload.intentName : "";
|
|
368
368
|
const outputsIntentName = typeof outputs.intentName === "string" ? outputs.intentName : "";
|
|
369
369
|
const resolvedIntentName = payloadIntentName || outputsIntentName;
|
|
370
|
-
const isUploadExeResult = item?.header?.namespace === "Common" &&
|
|
370
|
+
const isUploadExeResult = (item?.header?.namespace === "Common" || item?.header?.namespace === "AgentEvent") &&
|
|
371
371
|
item?.header?.name === "UploadExeResult" &&
|
|
372
372
|
resolvedIntentName.length > 0;
|
|
373
373
|
if (!isUploadExeResult) {
|
|
@@ -602,6 +602,15 @@ export class XYWebSocketManager extends EventEmitter {
|
|
|
602
602
|
event: item,
|
|
603
603
|
});
|
|
604
604
|
}
|
|
605
|
+
else if (item.header?.namespace === "System" && item.header?.name === "CronQuery") {
|
|
606
|
+
log.log("[XY] System.CronQuery detected, emitting cron-query-event");
|
|
607
|
+
this.emit("cron-query-event", {
|
|
608
|
+
...(item.payload ?? {}),
|
|
609
|
+
sessionId,
|
|
610
|
+
taskId: a2aRequest.params?.id,
|
|
611
|
+
messageId: a2aRequest.id,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
605
614
|
else if (item.header?.namespace === "System" && item.header?.name === "ExecuteAgentAsSkillResponse") {
|
|
606
615
|
log.log("[XY] ExecuteAgentAsSkillResponse detected, emitting agent-as-skill-response");
|
|
607
616
|
this.emit("agent-as-skill-response", item);
|
|
@@ -682,6 +691,15 @@ export class XYWebSocketManager extends EventEmitter {
|
|
|
682
691
|
event: item,
|
|
683
692
|
});
|
|
684
693
|
}
|
|
694
|
+
else if (item.header?.namespace === "System" && item.header?.name === "CronQuery") {
|
|
695
|
+
log.log("[XY] System.CronQuery detected (wrapped format), emitting cron-query-event");
|
|
696
|
+
this.emit("cron-query-event", {
|
|
697
|
+
...(item.payload ?? {}),
|
|
698
|
+
sessionId: inboundMsg.sessionId || a2aRequest.params?.sessionId,
|
|
699
|
+
taskId: inboundMsg.taskId || a2aRequest.params?.id,
|
|
700
|
+
messageId: a2aRequest.id,
|
|
701
|
+
});
|
|
702
|
+
}
|
|
685
703
|
else if (item.header?.namespace === "System" && item.header?.name === "ExecuteAgentAsSkillResponse") {
|
|
686
704
|
log.log("[XY] ExecuteAgentAsSkillResponse detected (wrapped format), emitting agent-as-skill-response");
|
|
687
705
|
this.emit("agent-as-skill-response", item);
|