@ynhcj/xiaoyi-channel 0.0.210-beta → 0.0.211-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.
@@ -27,6 +27,7 @@ import { discoverCrossDevicesTool } from "./tools/discover-cross-devices-tool.js
27
27
  import { sendCrossDeviceTaskTool } from "./tools/send-cross-device-task-tool.js";
28
28
  import { displayA2UICardByPathTool } from "./tools/display-a2ui-card-bypath-tool.js";
29
29
  import { checkPluginPrivilegeTool } from "./tools/check-plugin-privilege-tool.js";
30
+ import { invokeTool } from "./tools/invoke.js";
30
31
  const ALL_TOOLS = [
31
32
  locationTool,
32
33
  discoverCrossDevicesTool,
@@ -50,6 +51,7 @@ const ALL_TOOLS = [
50
51
  loginTokenTool,
51
52
  agentAsSkillTool,
52
53
  checkPluginPrivilegeTool,
54
+ invokeTool,
53
55
  ];
54
56
  /**
55
57
  * Xiaoyi Channel Plugin for OpenClaw.
@@ -15,6 +15,7 @@ import path from "path";
15
15
  import os from "os";
16
16
  import { logger } from "../utils/logger.js";
17
17
  import { InvokeError, invokeErrorToResult } from "./invoke.js";
18
+ import { getCurrentSessionContext } from "./session-manager.js";
18
19
  import { getXYWebSocketManager } from "../client.js";
19
20
  import { sendCommand } from "../formatter.js";
20
21
  import { getCurrentTaskId } from "../task-manager.js";
@@ -534,8 +535,8 @@ export async function cliBeforeToolCallHandler(event, ctx) {
534
535
  },
535
536
  };
536
537
  }
537
- // Get session context and execute
538
- const sessionCtx = (await import("./session-manager.js")).getCurrentSessionContext();
538
+ // Get session context via ALS (propagated through the async call chain)
539
+ const sessionCtx = getCurrentSessionContext();
539
540
  if (!sessionCtx) {
540
541
  logger.warn(`[CLI-HOOK] No session context, blocking (${(performance.now() - t0).toFixed(1)}ms)`);
541
542
  return { block: true, blockReason: JSON.stringify({ code: "CONFIG_MISSING", message: "No active session context for CLI execution", retryable: false }) };
@@ -18,8 +18,6 @@
18
18
  * 7. Device executor
19
19
  * 8. Invoke tool factory (public API)
20
20
  */
21
- import type { ChannelAgentTool } from "openclaw/plugin-sdk";
22
- import type { SessionContext } from "./session-manager.js";
23
21
  export type InvokeErrorCode = "INVALID_PARAM" | "INVALID_TOOL_DEFINITION" | "CONFIG_MISSING" | "AUTH_FAIL" | "PERMISSION_DENIED" | "CLI_COMMAND_BLOCKED" | "RATE_LIMIT" | "TIMEOUT" | "TOOL_NOT_FOUND" | "TOOL_CONFLICT" | "UNSUPPORTED_PLUGIN_TYPE" | "UNSUPPORTED_PROTOCOL" | "DEVICE_TOOL_BLOCKED" | "UPSTREAM_ERROR" | "NETWORK_ERROR" | "UNKNOWN";
24
22
  interface ExecuteResult {
25
23
  content: Array<{
@@ -38,11 +36,53 @@ export declare class InvokeError extends Error {
38
36
  get status(): number;
39
37
  }
40
38
  export declare function invokeErrorToResult(error: InvokeError): ExecuteResult;
41
- /**
42
- * Create the invoke meta-tool for the given session context.
43
- *
44
- * This is the ONLY exported function — the single integration point for
45
- * create-all-tools.ts.
46
- */
47
- export declare function createInvokeTool(ctx: SessionContext): ChannelAgentTool;
39
+ export declare const invokeTool: {
40
+ name: string;
41
+ label: string;
42
+ description: string;
43
+ parameters: {
44
+ type: string;
45
+ properties: {
46
+ functionName: {
47
+ type: string;
48
+ minLength: number;
49
+ description: string;
50
+ };
51
+ funcName: {
52
+ type: string;
53
+ minLength: number;
54
+ description: string;
55
+ };
56
+ arguments: {
57
+ type: string;
58
+ description: string;
59
+ properties: {
60
+ bundleName: {
61
+ type: string;
62
+ minLength: number;
63
+ description: string;
64
+ };
65
+ };
66
+ required: string[];
67
+ additionalProperties: boolean;
68
+ };
69
+ params: {
70
+ type: string;
71
+ description: string;
72
+ properties: {
73
+ bundleName: {
74
+ type: string;
75
+ minLength: number;
76
+ description: string;
77
+ };
78
+ };
79
+ required: string[];
80
+ additionalProperties: boolean;
81
+ };
82
+ };
83
+ required: any[];
84
+ additionalProperties: boolean;
85
+ };
86
+ execute(toolCallId: string, rawParams: unknown, _signal?: AbortSignal, _onUpdate?: (partialResult: any) => void): Promise<ExecuteResult>;
87
+ };
48
88
  export {};
@@ -27,6 +27,7 @@ import { logger } from "../utils/logger.js";
27
27
  import { getXYWebSocketManager } from "../client.js";
28
28
  import { sendCommand } from "../formatter.js";
29
29
  import { getCurrentTaskId } from "../task-manager.js";
30
+ import { getCurrentSessionContext } from "./session-manager.js";
30
31
  const RETRYABLE_CODES = new Set([
31
32
  "RATE_LIMIT",
32
33
  "TIMEOUT",
@@ -1066,136 +1067,135 @@ function validateBusinessParams(businessParams, definition) {
1066
1067
  }
1067
1068
  return null;
1068
1069
  }
1069
- /**
1070
- * Create the invoke meta-tool for the given session context.
1071
- *
1072
- * This is the ONLY exported function — the single integration point for
1073
- * create-all-tools.ts.
1074
- */
1075
- export function createInvokeTool(ctx) {
1076
- return {
1077
- name: "invoke",
1078
- label: "Invoke",
1079
- description: "调用已安装 skill 中声明的工具。必须传 functionName(或funcName) 与 arguments(或params);functionName 的值等于工具定义中的 toolName;arguments 是包含 bundleName 和业务参数字段的对象;完整业务参数定义见 references/tools/<bundleName>__<toolName>.json。",
1080
- parameters: {
1081
- type: "object",
1082
- properties: {
1083
- functionName: {
1084
- type: "string",
1085
- minLength: 1,
1086
- description: "工具名称,值等于对应 references/tools JSON 中的 toolName,如 weather_query(优先使用;funcName 已废弃但仍兼容)。",
1087
- },
1088
- funcName: {
1089
- type: "string",
1090
- minLength: 1,
1091
- description: "[废弃] 请使用 functionName 替代。工具名称,值等于对应 references/tools JSON 中的 toolName。",
1092
- },
1093
- arguments: {
1094
- type: "object",
1095
- description: "包含定位字段 bundleName 与业务参数字段;除 bundleName 外的字段遵循对应 references/tools JSON 中的 arguments schema(优先使用;params 已废弃但仍兼容)。",
1096
- properties: {
1097
- bundleName: {
1098
- type: "string",
1099
- minLength: 1,
1100
- description: "HarmonyOS 应用唯一标识,如 com.example.weather。",
1101
- },
1070
+ // ═══════════════════════════════════════════════════════════════════════════
1071
+ // 8. Invoke tool (static matches ALL_TOOLS pattern)
1072
+ // ═══════════════════════════════════════════════════════════════════════════
1073
+ export const invokeTool = {
1074
+ name: "invoke",
1075
+ label: "Invoke",
1076
+ description: "调用已安装 skill 中声明的工具。必须传 functionName(或funcName) 与 arguments(或params);functionName 的值等于工具定义中的 toolName;arguments 是包含 bundleName 和业务参数字段的对象;完整业务参数定义见 references/tools/<bundleName>__<toolName>.json。",
1077
+ parameters: {
1078
+ type: "object",
1079
+ properties: {
1080
+ functionName: {
1081
+ type: "string",
1082
+ minLength: 1,
1083
+ description: "工具名称,值等于对应 references/tools JSON 中的 toolName,如 weather_query(优先使用;funcName 已废弃但仍兼容)。",
1084
+ },
1085
+ funcName: {
1086
+ type: "string",
1087
+ minLength: 1,
1088
+ description: "[废弃] 请使用 functionName 替代。工具名称,值等于对应 references/tools JSON 中的 toolName。",
1089
+ },
1090
+ arguments: {
1091
+ type: "object",
1092
+ description: "包含定位字段 bundleName 与业务参数字段;除 bundleName 外的字段遵循对应 references/tools JSON 中的 arguments schema(优先使用;params 已废弃但仍兼容)。",
1093
+ properties: {
1094
+ bundleName: {
1095
+ type: "string",
1096
+ minLength: 1,
1097
+ description: "HarmonyOS 应用唯一标识,如 com.example.weather。",
1102
1098
  },
1103
- required: ["bundleName"],
1104
- additionalProperties: true,
1105
1099
  },
1106
- params: {
1107
- type: "object",
1108
- description: "[废弃] 请使用 arguments 替代。包含定位字段 bundleName 与业务参数字段。",
1109
- properties: {
1110
- bundleName: {
1111
- type: "string",
1112
- minLength: 1,
1113
- description: "HarmonyOS 应用唯一标识,如 com.example.weather。",
1114
- },
1100
+ required: ["bundleName"],
1101
+ additionalProperties: true,
1102
+ },
1103
+ params: {
1104
+ type: "object",
1105
+ description: "[废弃] 请使用 arguments 替代。包含定位字段 bundleName 与业务参数字段。",
1106
+ properties: {
1107
+ bundleName: {
1108
+ type: "string",
1109
+ minLength: 1,
1110
+ description: "HarmonyOS 应用唯一标识,如 com.example.weather。",
1115
1111
  },
1116
- required: ["bundleName"],
1117
- additionalProperties: true,
1118
1112
  },
1113
+ required: ["bundleName"],
1114
+ additionalProperties: true,
1119
1115
  },
1120
- required: [],
1121
- additionalProperties: false,
1122
1116
  },
1123
- async execute(toolCallId, rawParams, _signal, _onUpdate) {
1124
- // Layer 1: input validation
1125
- const validated = validateAndExtract(rawParams);
1126
- if ("content" in validated) {
1127
- logger.warn("[INVOKE] input validation failed", { toolCallId });
1128
- return validated;
1129
- }
1130
- const { toolName, bundleName, businessParams } = validated;
1131
- const businessKeys = Object.keys(businessParams);
1132
- logger.log("[INVOKE] tool called", {
1133
- toolCallId, toolName, bundleName,
1134
- businessKeys: businessKeys.join(","),
1135
- businessKeysCount: businessKeys.length,
1136
- });
1137
- // Layer 2: cache lookup
1138
- const cache = getToolCache();
1139
- const entry = cache.get(bundleName, toolName);
1140
- if (!entry) {
1141
- const conflict = cache.getConflict(bundleName, toolName);
1142
- if (conflict) {
1143
- const files = conflict.entries.map((e) => e.filePath).join(", ");
1144
- logger.warn("[INVOKE] tool conflict", { toolCallId, toolName, bundleName, conflictCount: conflict.entries.length });
1145
- return invokeErrorToResult(new InvokeError("TOOL_CONFLICT", `Multiple conflicting definitions for '${toolName}' in bundle '${bundleName}': ${files}`));
1146
- }
1147
- logger.warn("[INVOKE] tool not found", { toolCallId, toolName, bundleName });
1148
- return invokeErrorToResult(new InvokeError("TOOL_NOT_FOUND", `Tool '${toolName}' not found in bundle '${bundleName}'.`));
1117
+ required: [],
1118
+ additionalProperties: false,
1119
+ },
1120
+ async execute(toolCallId, rawParams, _signal, _onUpdate) {
1121
+ const ctx = getCurrentSessionContext();
1122
+ // Layer 1: input validation
1123
+ const validated = validateAndExtract(rawParams);
1124
+ if ("content" in validated) {
1125
+ logger.warn("[INVOKE] input validation failed", { toolCallId });
1126
+ return validated;
1127
+ }
1128
+ const { toolName, bundleName, businessParams } = validated;
1129
+ const businessKeys = Object.keys(businessParams);
1130
+ logger.log("[INVOKE] tool called", {
1131
+ toolCallId, toolName, bundleName,
1132
+ businessKeys: businessKeys.join(","),
1133
+ businessKeysCount: businessKeys.length,
1134
+ });
1135
+ // Layer 2: cache lookup
1136
+ const cache = getToolCache();
1137
+ const entry = cache.get(bundleName, toolName);
1138
+ if (!entry) {
1139
+ const conflict = cache.getConflict(bundleName, toolName);
1140
+ if (conflict) {
1141
+ const files = conflict.entries.map((e) => e.filePath).join(", ");
1142
+ logger.warn("[INVOKE] tool conflict", { toolCallId, toolName, bundleName, conflictCount: conflict.entries.length });
1143
+ return invokeErrorToResult(new InvokeError("TOOL_CONFLICT", `Multiple conflicting definitions for '${toolName}' in bundle '${bundleName}': ${files}`));
1149
1144
  }
1150
- const { definition, skillName } = entry;
1151
- logger.log("[INVOKE] cache hit", {
1152
- toolCallId, toolName, bundleName, skillName,
1153
- pluginType: definition.pluginType,
1154
- protocol: definition.protocol ?? "N/A",
1155
- });
1156
- // Layer 2b: business param validation
1157
- const paramError = validateBusinessParams(businessParams, definition);
1158
- if (paramError) {
1159
- logger.warn("[INVOKE] business param validation failed", { toolCallId, toolName, bundleName });
1160
- return paramError;
1145
+ logger.warn("[INVOKE] tool not found", { toolCallId, toolName, bundleName });
1146
+ return invokeErrorToResult(new InvokeError("TOOL_NOT_FOUND", `Tool '${toolName}' not found in bundle '${bundleName}'.`));
1147
+ }
1148
+ const { definition, skillName } = entry;
1149
+ logger.log("[INVOKE] cache hit", {
1150
+ toolCallId, toolName, bundleName, skillName,
1151
+ pluginType: definition.pluginType,
1152
+ protocol: definition.protocol ?? "N/A",
1153
+ });
1154
+ // Layer 2b: business param validation
1155
+ const paramError = validateBusinessParams(businessParams, definition);
1156
+ if (paramError) {
1157
+ logger.warn("[INVOKE] business param validation failed", { toolCallId, toolName, bundleName });
1158
+ return paramError;
1159
+ }
1160
+ // Layer 3: execute
1161
+ const pluginType = definition.pluginType;
1162
+ logger.log(`[INVOKE] dispatching to ${pluginType} executor`, { toolCallId, toolName, bundleName, pluginType });
1163
+ try {
1164
+ if (pluginType === "Cloud" || pluginType === "MCP") {
1165
+ const result = await executeCloudTool({ definition, businessParams, skillName, sessionId: ctx?.sessionId ?? "", agentId: ctx?.agentId ?? "" });
1166
+ logger.log("[INVOKE] cloud execution succeeded", {
1167
+ toolCallId, toolName, bundleName, pluginType,
1168
+ resultLength: result.content[0]?.text?.length ?? 0,
1169
+ });
1170
+ return result;
1161
1171
  }
1162
- // Layer 3: execute
1163
- const pluginType = definition.pluginType;
1164
- logger.log(`[INVOKE] dispatching to ${pluginType} executor`, { toolCallId, toolName, bundleName, pluginType });
1165
- try {
1166
- if (pluginType === "Cloud" || pluginType === "MCP") {
1167
- const result = await executeCloudTool({ definition, businessParams, skillName, sessionId: ctx.sessionId, agentId: ctx.agentId });
1168
- logger.log("[INVOKE] cloud execution succeeded", {
1169
- toolCallId, toolName, bundleName, pluginType,
1170
- resultLength: result.content[0]?.text?.length ?? 0,
1171
- });
1172
- return result;
1173
- }
1174
- if (pluginType === "Device") {
1175
- const result = await executeDeviceTool({ definition, businessParams, toolCallId }, ctx);
1176
- logger.log("[INVOKE] device execution succeeded", {
1177
- toolCallId, toolName, bundleName,
1178
- resultLength: result.content[0]?.text?.length ?? 0,
1179
- });
1180
- return result;
1172
+ if (pluginType === "Device") {
1173
+ if (!ctx) {
1174
+ return invokeErrorToResult(new InvokeError("DEVICE_TOOL_BLOCKED", "Device tools require an active session context."));
1181
1175
  }
1182
- logger.warn("[INVOKE] unsupported pluginType", { toolCallId, toolName, bundleName, pluginType });
1183
- return invokeErrorToResult(new InvokeError("UNSUPPORTED_PLUGIN_TYPE", `Unsupported pluginType '${pluginType}'. Must be Cloud, Device, or MCP.`));
1176
+ const result = await executeDeviceTool({ definition, businessParams, toolCallId }, ctx);
1177
+ logger.log("[INVOKE] device execution succeeded", {
1178
+ toolCallId, toolName, bundleName,
1179
+ resultLength: result.content[0]?.text?.length ?? 0,
1180
+ });
1181
+ return result;
1184
1182
  }
1185
- catch (err) {
1186
- if (err instanceof InvokeError) {
1187
- logger.warn("[INVOKE] invocation error", {
1188
- toolCallId, toolName, bundleName, pluginType,
1189
- errorCode: err.code, errorMessage: err.message, retryable: err.retryable,
1190
- });
1191
- return invokeErrorToResult(err);
1192
- }
1193
- logger.error("[INVOKE] unexpected execution error", {
1183
+ logger.warn("[INVOKE] unsupported pluginType", { toolCallId, toolName, bundleName, pluginType });
1184
+ return invokeErrorToResult(new InvokeError("UNSUPPORTED_PLUGIN_TYPE", `Unsupported pluginType '${pluginType}'. Must be Cloud, Device, or MCP.`));
1185
+ }
1186
+ catch (err) {
1187
+ if (err instanceof InvokeError) {
1188
+ logger.warn("[INVOKE] invocation error", {
1194
1189
  toolCallId, toolName, bundleName, pluginType,
1195
- error: err instanceof Error ? `${err.name}: ${err.message}` : String(err),
1190
+ errorCode: err.code, errorMessage: err.message, retryable: err.retryable,
1196
1191
  });
1197
- return unknownErrorToResult(err);
1192
+ return invokeErrorToResult(err);
1198
1193
  }
1199
- },
1200
- };
1201
- }
1194
+ logger.error("[INVOKE] unexpected execution error", {
1195
+ toolCallId, toolName, bundleName, pluginType,
1196
+ error: err instanceof Error ? `${err.name}: ${err.message}` : String(err),
1197
+ });
1198
+ return unknownErrorToResult(err);
1199
+ }
1200
+ },
1201
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.210-beta",
3
+ "version": "0.0.211-beta",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",