@yahaha-studio/kichi-forwarder 0.1.2-beta.8 → 0.1.2-beta.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/index.js CHANGED
@@ -811,7 +811,7 @@ function buildKichiActionDescription(service) {
811
811
  `lay actions: ${actions.lay.map((entry) => entry.name).join(", ")}`,
812
812
  `floor actions: ${actions.floor.map((entry) => entry.name).join(", ")}`,
813
813
  ];
814
- const roomContext = service.getCachedRoomContext();
814
+ const roomContext = service?.getCachedRoomContext();
815
815
  const poseableProps = roomContext?.PoseableProps;
816
816
  if (Array.isArray(poseableProps) && poseableProps.length > 0) {
817
817
  lines.push("", "Cached RoomContext.PoseableProps (from last kichi_query_status):", JSON.stringify(poseableProps), "When using a sit or lay pose, pick the propId whose DisplayName best matches the current task context and whose OccupancyState is not fully_occupied. If no prop fits, omit propId.");
@@ -858,17 +858,6 @@ function buildKichiPrompt() {
858
858
  "User opt-out, Kichi config/test work, and explicit pose requests take priority over sync.",
859
859
  ].join("\n");
860
860
  }
861
- function createAgentScopedTool(runtimeManager, factory) {
862
- return (ctx) => {
863
- const locator = resolveToolLocator(ctx);
864
- const agentId = runtimeManager.resolveRuntimeAgentId(locator);
865
- if (!agentId) {
866
- throw new Error("Failed to resolve agent-scoped Kichi runtime");
867
- }
868
- const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
869
- return factory(service, ctx);
870
- };
871
- }
872
861
  const GLOBAL_RUNTIME_MANAGER_KEY = "__kichi_forwarder_runtime_manager__";
873
862
  function getRuntimeManager(logger) {
874
863
  const globalState = globalThis;
@@ -889,6 +878,11 @@ const plugin = {
889
878
  configSchema: { parse },
890
879
  register(api) {
891
880
  const runtimeManager = getRuntimeManager(api.logger);
881
+ runtimeManager.setEnvironmentHostResolver((environment) => {
882
+ const config = loadEnvironmentsConfig();
883
+ const host = config[environment];
884
+ return typeof host === "string" && host.trim() ? host : null;
885
+ });
892
886
  registerPluginHooks(api, runtimeManager);
893
887
  const musicTitleEnum = getMusicTitleEnum();
894
888
  runtimeManager.setBotMessageHandler((service, msg) => {
@@ -936,11 +930,6 @@ const plugin = {
936
930
  id: "kichi-forwarder",
937
931
  start: (ctx) => {
938
932
  parse(ctx.config.plugins?.entries?.["kichi-forwarder"]?.config);
939
- runtimeManager.setEnvironmentHostResolver((environment) => {
940
- const config = loadEnvironmentsConfig();
941
- const host = config[environment];
942
- return typeof host === "string" && host.trim() ? host : null;
943
- });
944
933
  runtimeManager.initializeStartupRuntimes();
945
934
  },
946
935
  stop: () => {
@@ -951,7 +940,7 @@ const plugin = {
951
940
  }
952
941
  },
953
942
  });
954
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
943
+ api.registerTool((ctx) => ({
955
944
  name: "kichi_join",
956
945
  label: "kichi_join",
957
946
  description: "Join Kichi world with avatarId, the current bot name, a short bio, and personality tags",
@@ -976,6 +965,12 @@ const plugin = {
976
965
  required: ["botName", "bio"],
977
966
  },
978
967
  execute: async (_toolCallId, params) => {
968
+ const locator = resolveToolLocator(ctx);
969
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
970
+ if (!agentId) {
971
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
972
+ }
973
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
979
974
  let avatarId = params?.avatarId;
980
975
  const botName = params?.botName?.trim();
981
976
  const bio = params?.bio?.trim();
@@ -1007,54 +1002,58 @@ const plugin = {
1007
1002
  ...(failure.errorMessage ? { errorMessage: failure.errorMessage } : {}),
1008
1003
  });
1009
1004
  },
1010
- })));
1011
- api.registerTool((ctx) => {
1012
- const locator = resolveToolLocator(ctx);
1013
- const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1014
- if (!agentId) {
1015
- throw new Error("Failed to resolve agent-scoped Kichi runtime");
1016
- }
1017
- const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1018
- return ({
1019
- name: "kichi_switch_host",
1020
- label: "kichi_switch_host",
1021
- description: "Switch Kichi runtime environment and reconnect immediately without restarting the gateway. Host is resolved from config/environments.json.",
1022
- parameters: {
1023
- type: "object",
1024
- properties: {
1025
- environment: {
1026
- type: "string",
1027
- enum: VALID_ENVIRONMENTS,
1028
- description: "Target environment: steam, steam-playtest, or test",
1029
- },
1005
+ }));
1006
+ api.registerTool((ctx) => ({
1007
+ name: "kichi_switch_host",
1008
+ label: "kichi_switch_host",
1009
+ description: "Switch Kichi runtime environment and reconnect immediately without restarting the gateway. Host is resolved from config/environments.json.",
1010
+ parameters: {
1011
+ type: "object",
1012
+ properties: {
1013
+ environment: {
1014
+ type: "string",
1015
+ enum: VALID_ENVIRONMENTS,
1016
+ description: "Target environment: steam, steam-playtest, or test",
1030
1017
  },
1031
- required: ["environment"],
1032
- },
1033
- execute: async (_toolCallId, params) => {
1034
- const environment = params?.environment;
1035
- if (!isKichiEnvironment(environment)) {
1036
- return jsonResult({ success: false, error: `environment must be one of: ${VALID_ENVIRONMENTS.join(", ")}` });
1037
- }
1038
- const resolved = resolveEnvironmentHost(environment);
1039
- if (resolved.error) {
1040
- return jsonResult({ success: false, error: resolved.error });
1041
- }
1042
- const status = await service.switchHost(resolved.host, environment);
1043
- return jsonResult({
1044
- success: true,
1045
- environment,
1046
- host: resolved.host,
1047
- status,
1048
- });
1049
1018
  },
1050
- });
1051
- });
1052
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1019
+ required: ["environment"],
1020
+ },
1021
+ execute: async (_toolCallId, params) => {
1022
+ const locator = resolveToolLocator(ctx);
1023
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1024
+ if (!agentId) {
1025
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1026
+ }
1027
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1028
+ const environment = params?.environment;
1029
+ if (!isKichiEnvironment(environment)) {
1030
+ return jsonResult({ success: false, error: `environment must be one of: ${VALID_ENVIRONMENTS.join(", ")}` });
1031
+ }
1032
+ const resolved = resolveEnvironmentHost(environment);
1033
+ if (resolved.error) {
1034
+ return jsonResult({ success: false, error: resolved.error });
1035
+ }
1036
+ const status = await service.switchHost(resolved.host, environment);
1037
+ return jsonResult({
1038
+ success: true,
1039
+ environment,
1040
+ host: resolved.host,
1041
+ status,
1042
+ });
1043
+ },
1044
+ }));
1045
+ api.registerTool((ctx) => ({
1053
1046
  name: "kichi_rejoin",
1054
1047
  label: "kichi_rejoin",
1055
1048
  description: "Request an immediate rejoin attempt with saved avatarId/authKey. Rejoin is also sent automatically after reconnect.",
1056
1049
  parameters: { type: "object", properties: {} },
1057
- execute: async () => {
1050
+ execute: async (_toolCallId, _params) => {
1051
+ const locator = resolveToolLocator(ctx);
1052
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1053
+ if (!agentId) {
1054
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1055
+ }
1056
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1058
1057
  const result = service.requestRejoin();
1059
1058
  return jsonResult({
1060
1059
  success: result.accepted,
@@ -1062,13 +1061,19 @@ const plugin = {
1062
1061
  status: service.getConnectionStatus(),
1063
1062
  });
1064
1063
  },
1065
- })));
1066
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1064
+ }));
1065
+ api.registerTool((ctx) => ({
1067
1066
  name: "kichi_leave",
1068
1067
  label: "kichi_leave",
1069
1068
  description: "Leave Kichi world",
1070
1069
  parameters: { type: "object", properties: {} },
1071
- execute: async () => {
1070
+ execute: async (_toolCallId, _params) => {
1071
+ const locator = resolveToolLocator(ctx);
1072
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1073
+ if (!agentId) {
1074
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1075
+ }
1076
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1072
1077
  const result = await service.leave();
1073
1078
  if (result.success) {
1074
1079
  return jsonResult({ success: true });
@@ -1081,110 +1086,126 @@ const plugin = {
1081
1086
  ...(failure.errorMessage ? { errorMessage: failure.errorMessage } : {}),
1082
1087
  });
1083
1088
  },
1084
- })));
1085
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1089
+ }));
1090
+ api.registerTool((ctx) => ({
1086
1091
  name: "kichi_connection_status",
1087
1092
  label: "kichi_connection_status",
1088
1093
  description: "Check WebSocket connection status and identity readiness only. Does NOT return room info, avatar state, or personnel — use kichi_query_status for that.",
1089
1094
  parameters: { type: "object", properties: {} },
1090
- execute: async () => {
1095
+ execute: async (_toolCallId, _params) => {
1096
+ const locator = resolveToolLocator(ctx);
1097
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1098
+ if (!agentId) {
1099
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1100
+ }
1101
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1091
1102
  return jsonResult({
1092
1103
  success: true,
1093
1104
  status: service.getConnectionStatus(),
1094
1105
  });
1095
1106
  },
1096
- })));
1097
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1098
- name: "kichi_action",
1099
- label: "kichi_action",
1100
- description: buildKichiActionDescription(service),
1101
- parameters: {
1102
- type: "object",
1103
- properties: {
1104
- poseType: { type: "string", description: "Pose type: stand, sit, lay, or floor" },
1105
- action: {
1106
- type: "string",
1107
- description: "Action name for the selected pose (for example Sit Nicely, Typing with Keyboard, Reading, High Five, or Meditate)",
1108
- },
1109
- bubble: { type: "string", description: "Optional bubble text to display (max 5 words)" },
1110
- log: {
1111
- type: "string",
1112
- description: "Short natural first-person sentence under 15 words. Match the language of the bubble and mention the current action and immediate focus.",
1113
- },
1114
- verify: {
1115
- type: "boolean",
1116
- description: "Set true ONLY when the user explicitly requests a pose or action. Omit during routine sync steps.",
1117
- },
1118
- propId: {
1119
- type: "string",
1120
- description: "Optional poseable prop ID from RoomContext.PoseableProps (obtained via kichi_query_status or cached). When specified, the avatar is seated at this prop; when omitted, the server picks the nearest available prop.",
1107
+ }));
1108
+ api.registerTool((ctx) => {
1109
+ const locator = resolveToolLocator(ctx);
1110
+ const existingService = runtimeManager.getRuntime(locator);
1111
+ return ({
1112
+ name: "kichi_action",
1113
+ label: "kichi_action",
1114
+ description: buildKichiActionDescription(existingService ?? undefined),
1115
+ parameters: {
1116
+ type: "object",
1117
+ properties: {
1118
+ poseType: { type: "string", description: "Pose type: stand, sit, lay, or floor" },
1119
+ action: {
1120
+ type: "string",
1121
+ description: "Action name for the selected pose (for example Sit Nicely, Typing with Keyboard, Reading, High Five, or Meditate)",
1122
+ },
1123
+ bubble: { type: "string", description: "Optional bubble text to display (max 5 words)" },
1124
+ log: {
1125
+ type: "string",
1126
+ description: "Short natural first-person sentence under 15 words. Match the language of the bubble and mention the current action and immediate focus.",
1127
+ },
1128
+ verify: {
1129
+ type: "boolean",
1130
+ description: "Set true ONLY when the user explicitly requests a pose or action. Omit during routine sync steps.",
1131
+ },
1132
+ propId: {
1133
+ type: "string",
1134
+ description: "Optional poseable prop ID from RoomContext.PoseableProps (obtained via kichi_query_status or cached). When specified, the avatar is seated at this prop; when omitted, the server picks the nearest available prop.",
1135
+ },
1121
1136
  },
1137
+ required: ["poseType", "action"],
1122
1138
  },
1123
- required: ["poseType", "action"],
1124
- },
1125
- execute: async (_toolCallId, params) => {
1126
- const { poseType, action, bubble, log, verify, propId } = (params || {});
1127
- if (!poseType || !action) {
1128
- return jsonResult({ success: false, error: "poseType and action parameters are required" });
1129
- }
1130
- if (!["stand", "sit", "lay", "floor"].includes(poseType)) {
1131
- return jsonResult({
1132
- success: false,
1133
- error: `Invalid poseType: ${poseType}. Must be stand, sit, lay, or floor`,
1134
- });
1135
- }
1136
- if (!service.hasValidIdentity() || !service.isConnected()) {
1137
- return jsonResult({ success: false, error: "Not connected to Kichi world" });
1138
- }
1139
- const normalizedPoseType = poseType;
1140
- const poseActions = loadStaticConfig().actions[normalizedPoseType];
1141
- const matched = poseActions.find((entry) => entry.name.toLowerCase() === action.toLowerCase());
1142
- if (!matched) {
1143
- return jsonResult({
1144
- success: false,
1145
- error: `Unknown action "${action}" for poseType "${poseType}"`,
1146
- available: poseActions.map((entry) => entry.name),
1147
- });
1148
- }
1149
- const bubbleText = typeof bubble === "string" && bubble.trim() ? bubble.trim() : matched.name;
1150
- const logText = typeof log === "string" ? log.trim() : "";
1151
- const playback = getActionPlayback(matched);
1152
- if (verify) {
1153
- try {
1154
- const ack = await service.sendStatusVerified(normalizedPoseType, matched.name, bubbleText, logText, playback, propId);
1155
- if (ack.warning) {
1156
- return jsonResult({
1157
- success: true,
1158
- requested: { poseType: normalizedPoseType, action: matched.name },
1159
- actual: { poseType: ack.poseType, action: ack.action },
1160
- warning: ack.warning,
1161
- });
1139
+ execute: async (_toolCallId, params) => {
1140
+ const locator = resolveToolLocator(ctx);
1141
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1142
+ if (!agentId) {
1143
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1144
+ }
1145
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1146
+ const { poseType, action, bubble, log, verify, propId } = (params || {});
1147
+ if (!poseType || !action) {
1148
+ return jsonResult({ success: false, error: "poseType and action parameters are required" });
1149
+ }
1150
+ if (!["stand", "sit", "lay", "floor"].includes(poseType)) {
1151
+ return jsonResult({
1152
+ success: false,
1153
+ error: `Invalid poseType: ${poseType}. Must be stand, sit, lay, or floor`,
1154
+ });
1155
+ }
1156
+ if (!service.hasValidIdentity() || !service.isConnected()) {
1157
+ return jsonResult({ success: false, error: "Not connected to Kichi world" });
1158
+ }
1159
+ const normalizedPoseType = poseType;
1160
+ const poseActions = loadStaticConfig().actions[normalizedPoseType];
1161
+ const matched = poseActions.find((entry) => entry.name.toLowerCase() === action.toLowerCase());
1162
+ if (!matched) {
1163
+ return jsonResult({
1164
+ success: false,
1165
+ error: `Unknown action "${action}" for poseType "${poseType}"`,
1166
+ available: poseActions.map((entry) => entry.name),
1167
+ });
1168
+ }
1169
+ const bubbleText = typeof bubble === "string" && bubble.trim() ? bubble.trim() : matched.name;
1170
+ const logText = typeof log === "string" ? log.trim() : "";
1171
+ const playback = getActionPlayback(matched);
1172
+ if (verify) {
1173
+ try {
1174
+ const ack = await service.sendStatusVerified(normalizedPoseType, matched.name, bubbleText, logText, playback, propId);
1175
+ if (ack.warning) {
1176
+ return jsonResult({
1177
+ success: true,
1178
+ requested: { poseType: normalizedPoseType, action: matched.name },
1179
+ actual: { poseType: ack.poseType, action: ack.action },
1180
+ warning: ack.warning,
1181
+ });
1182
+ }
1183
+ }
1184
+ catch {
1185
+ // Server not updated or timeout — fall through to normal success
1162
1186
  }
1163
1187
  }
1164
- catch {
1165
- // Server not updated or timeout — fall through to normal success
1188
+ else {
1189
+ sendStatusUpdate(service, {
1190
+ poseType: normalizedPoseType,
1191
+ action: matched.name,
1192
+ bubble: bubbleText,
1193
+ log: logText,
1194
+ propId,
1195
+ });
1166
1196
  }
1167
- }
1168
- else {
1169
- sendStatusUpdate(service, {
1197
+ return jsonResult({
1198
+ success: true,
1170
1199
  poseType: normalizedPoseType,
1171
1200
  action: matched.name,
1172
1201
  bubble: bubbleText,
1173
1202
  log: logText,
1174
- propId,
1203
+ playback,
1175
1204
  });
1176
- }
1177
- return jsonResult({
1178
- success: true,
1179
- poseType: normalizedPoseType,
1180
- action: matched.name,
1181
- bubble: bubbleText,
1182
- log: logText,
1183
- playback,
1184
- });
1185
- },
1186
- })));
1187
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1205
+ },
1206
+ });
1207
+ });
1208
+ api.registerTool((ctx) => ({
1188
1209
  name: "kichi_idle_plan",
1189
1210
  label: "kichi_idle_plan",
1190
1211
  description: buildKichiIdlePlanDescription(),
@@ -1268,6 +1289,12 @@ const plugin = {
1268
1289
  required: ["heartbeatIntervalSeconds", "goal", "stages"],
1269
1290
  },
1270
1291
  execute: async (_toolCallId, params) => {
1292
+ const locator = resolveToolLocator(ctx);
1293
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1294
+ if (!agentId) {
1295
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1296
+ }
1297
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1271
1298
  const { idlePlan, error } = normalizeIdlePlan(params);
1272
1299
  if (!idlePlan) {
1273
1300
  return jsonResult({ success: false, error: error ?? "Invalid idle plan payload" });
@@ -1293,8 +1320,8 @@ const plugin = {
1293
1320
  stages: idlePlan.stages,
1294
1321
  });
1295
1322
  },
1296
- })));
1297
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1323
+ }));
1324
+ api.registerTool((ctx) => ({
1298
1325
  name: "kichi_clock",
1299
1326
  label: "kichi_clock",
1300
1327
  description: "Send clock commands to Kichi world. Supported actions are set and stop.",
@@ -1363,6 +1390,12 @@ const plugin = {
1363
1390
  required: ["action"],
1364
1391
  },
1365
1392
  execute: async (_toolCallId, params) => {
1393
+ const locator = resolveToolLocator(ctx);
1394
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1395
+ if (!agentId) {
1396
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1397
+ }
1398
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1366
1399
  const { action, requestId, clock } = (params || {});
1367
1400
  if (!isClockAction(action)) {
1368
1401
  return jsonResult({
@@ -1396,8 +1429,8 @@ const plugin = {
1396
1429
  ...(normalizedClock ? { clock: normalizedClock } : {}),
1397
1430
  });
1398
1431
  },
1399
- })));
1400
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1432
+ }));
1433
+ api.registerTool((ctx) => ({
1401
1434
  name: "kichi_query_status",
1402
1435
  label: "kichi_query_status",
1403
1436
  description: "Query Kichi room and avatar status — includes room personnel, notes, ownerState, idlePlan, weather/time, timer snapshot, daily note quota, `hasCreatedMusicAlbumToday`, and RoomContext.PoseableProps (poseable props with PropId, DisplayName, SupportedPoseTypes, OccupancyState). The PoseableProps list is cached internally so that kichi_action can reference a propId during regular work sync without re-querying. Use this when the user asks to check kichi status, room status, or who is in the room. Also use this before creating a new note or daily recommended music album. For heartbeat planning, use the returned idlePlan as reference when shaping the next idle plan.",
@@ -1411,6 +1444,12 @@ const plugin = {
1411
1444
  },
1412
1445
  },
1413
1446
  execute: async (_toolCallId, params) => {
1447
+ const locator = resolveToolLocator(ctx);
1448
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1449
+ if (!agentId) {
1450
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1451
+ }
1452
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1414
1453
  const requestId = params?.requestId;
1415
1454
  if (requestId !== undefined && typeof requestId !== "string") {
1416
1455
  return jsonResult({ success: false, error: "requestId must be a string when provided" });
@@ -1429,8 +1468,8 @@ const plugin = {
1429
1468
  });
1430
1469
  }
1431
1470
  },
1432
- })));
1433
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1471
+ }));
1472
+ api.registerTool((ctx) => ({
1434
1473
  name: "kichi_music_album_create",
1435
1474
  label: "kichi_music_album_create",
1436
1475
  description: buildMusicAlbumToolDescription(),
@@ -1457,6 +1496,12 @@ const plugin = {
1457
1496
  required: ["albumTitle", "musicTitles"],
1458
1497
  },
1459
1498
  execute: async (_toolCallId, params) => {
1499
+ const locator = resolveToolLocator(ctx);
1500
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1501
+ if (!agentId) {
1502
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1503
+ }
1504
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1460
1505
  const { requestId, albumTitle, musicTitles, } = (params || {});
1461
1506
  if (requestId !== undefined && typeof requestId !== "string") {
1462
1507
  return jsonResult({ success: false, error: "requestId must be a string when provided" });
@@ -1503,8 +1548,8 @@ const plugin = {
1503
1548
  });
1504
1549
  }
1505
1550
  },
1506
- })));
1507
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1551
+ }));
1552
+ api.registerTool((ctx) => ({
1508
1553
  name: "kichi_noteboard_create",
1509
1554
  label: "kichi_noteboard_create",
1510
1555
  description: "Create a new note on a specific Kichi note board. Prefer querying first so you can avoid duplicate posts and respect rate limits.",
@@ -1523,6 +1568,12 @@ const plugin = {
1523
1568
  required: ["propId", "data"],
1524
1569
  },
1525
1570
  execute: async (_toolCallId, params) => {
1571
+ const locator = resolveToolLocator(ctx);
1572
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1573
+ if (!agentId) {
1574
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1575
+ }
1576
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1526
1577
  const { propId, data } = (params || {});
1527
1578
  if (typeof propId !== "string" || !propId.trim()) {
1528
1579
  return jsonResult({ success: false, error: "propId is required" });
@@ -1550,8 +1601,8 @@ const plugin = {
1550
1601
  });
1551
1602
  }
1552
1603
  },
1553
- })));
1554
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1604
+ }));
1605
+ api.registerTool((ctx) => ({
1555
1606
  name: "kichi_bot_message",
1556
1607
  label: "kichi_bot_message",
1557
1608
  description: "Send a message to another bot in the same Kichi world. The bubble is the visible message content. Do not repeat what has already been said in the conversation history. When targeting a specific bot by name, call kichi_query_status first to resolve their avatarId. Only use \"*\" when broadcasting to all bots without a specific target.",
@@ -1587,6 +1638,12 @@ const plugin = {
1587
1638
  required: ["toAvatarId", "depth", "bubble"],
1588
1639
  },
1589
1640
  execute: async (_toolCallId, params) => {
1641
+ const locator = resolveToolLocator(ctx);
1642
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1643
+ if (!agentId) {
1644
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1645
+ }
1646
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1590
1647
  const { toAvatarId, depth, bubble, poseType, action, log } = (params || {});
1591
1648
  if (typeof toAvatarId !== "string" || !toAvatarId.trim()) {
1592
1649
  return jsonResult({ success: false, error: "toAvatarId is required" });
@@ -1618,7 +1675,7 @@ const plugin = {
1618
1675
  return jsonResult({ success: false, error: `Failed to send bot message: ${error}` });
1619
1676
  }
1620
1677
  },
1621
- })));
1678
+ }));
1622
1679
  },
1623
1680
  };
1624
1681
  export default plugin;
package/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import type {
4
- AnyAgentTool,
5
4
  OpenClawPluginApi,
6
5
  } from "openclaw/plugin-sdk";
7
6
  import type { OpenClawPluginToolContext } from "openclaw/plugin-sdk/core";
@@ -995,7 +994,7 @@ function formatActionList(actions: ActionDefinition[], playback: ActionPlayback[
995
994
  .join(", ");
996
995
  }
997
996
 
998
- function buildKichiActionDescription(service: KichiForwarderService): string {
997
+ function buildKichiActionDescription(service?: KichiForwarderService): string {
999
998
  const actions = loadStaticConfig().actions;
1000
999
  const lines = [
1001
1000
  "Directly control the avatar inside Kichi World.",
@@ -1008,7 +1007,7 @@ function buildKichiActionDescription(service: KichiForwarderService): string {
1008
1007
  `floor actions: ${actions.floor.map((entry) => entry.name).join(", ")}`,
1009
1008
  ];
1010
1009
 
1011
- const roomContext = service.getCachedRoomContext();
1010
+ const roomContext = service?.getCachedRoomContext();
1012
1011
  const poseableProps = roomContext?.PoseableProps;
1013
1012
  if (Array.isArray(poseableProps) && poseableProps.length > 0) {
1014
1013
  lines.push(
@@ -1064,20 +1063,6 @@ function buildKichiPrompt(): string {
1064
1063
  ].join("\n");
1065
1064
  }
1066
1065
 
1067
- function createAgentScopedTool(
1068
- runtimeManager: KichiRuntimeManager,
1069
- factory: (service: KichiForwarderService, ctx: OpenClawPluginToolContext) => AnyAgentTool,
1070
- ) {
1071
- return (ctx: OpenClawPluginToolContext) => {
1072
- const locator = resolveToolLocator(ctx);
1073
- const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1074
- if (!agentId) {
1075
- throw new Error("Failed to resolve agent-scoped Kichi runtime");
1076
- }
1077
- const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1078
- return factory(service, ctx);
1079
- };
1080
- }
1081
1066
 
1082
1067
  const GLOBAL_RUNTIME_MANAGER_KEY = "__kichi_forwarder_runtime_manager__";
1083
1068
 
@@ -1107,6 +1092,13 @@ const plugin = {
1107
1092
 
1108
1093
  register(api: OpenClawPluginApi) {
1109
1094
  const runtimeManager = getRuntimeManager(api.logger);
1095
+
1096
+ runtimeManager.setEnvironmentHostResolver((environment) => {
1097
+ const config = loadEnvironmentsConfig();
1098
+ const host = config[environment];
1099
+ return typeof host === "string" && host.trim() ? host : null;
1100
+ });
1101
+
1110
1102
  registerPluginHooks(api, runtimeManager);
1111
1103
  const musicTitleEnum = getMusicTitleEnum();
1112
1104
 
@@ -1155,11 +1147,6 @@ const plugin = {
1155
1147
  id: "kichi-forwarder",
1156
1148
  start: (ctx) => {
1157
1149
  parse(ctx.config.plugins?.entries?.["kichi-forwarder"]?.config);
1158
- runtimeManager.setEnvironmentHostResolver((environment) => {
1159
- const config = loadEnvironmentsConfig();
1160
- const host = config[environment];
1161
- return typeof host === "string" && host.trim() ? host : null;
1162
- });
1163
1150
  runtimeManager.initializeStartupRuntimes();
1164
1151
  },
1165
1152
  stop: () => {
@@ -1171,7 +1158,7 @@ const plugin = {
1171
1158
  },
1172
1159
  });
1173
1160
 
1174
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1161
+ api.registerTool((ctx) => ({
1175
1162
  name: "kichi_join",
1176
1163
  label: "kichi_join",
1177
1164
  description: "Join Kichi world with avatarId, the current bot name, a short bio, and personality tags",
@@ -1196,6 +1183,12 @@ const plugin = {
1196
1183
  required: ["botName", "bio"],
1197
1184
  },
1198
1185
  execute: async (_toolCallId, params) => {
1186
+ const locator = resolveToolLocator(ctx);
1187
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1188
+ if (!agentId) {
1189
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1190
+ }
1191
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1199
1192
  let avatarId = (params as { avatarId?: string } | null)?.avatarId;
1200
1193
  const botName = (params as { botName?: string } | null)?.botName?.trim();
1201
1194
  const bio = (params as { bio?: string } | null)?.bio?.trim();
@@ -1229,16 +1222,9 @@ const plugin = {
1229
1222
  ...(failure.errorMessage ? { errorMessage: failure.errorMessage } : {}),
1230
1223
  });
1231
1224
  },
1232
- })));
1225
+ }));
1233
1226
 
1234
- api.registerTool((ctx: OpenClawPluginToolContext) => {
1235
- const locator = resolveToolLocator(ctx);
1236
- const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1237
- if (!agentId) {
1238
- throw new Error("Failed to resolve agent-scoped Kichi runtime");
1239
- }
1240
- const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1241
- return ({
1227
+ api.registerTool((ctx) => ({
1242
1228
  name: "kichi_switch_host",
1243
1229
  label: "kichi_switch_host",
1244
1230
  description:
@@ -1255,6 +1241,12 @@ const plugin = {
1255
1241
  required: ["environment"],
1256
1242
  },
1257
1243
  execute: async (_toolCallId, params) => {
1244
+ const locator = resolveToolLocator(ctx);
1245
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1246
+ if (!agentId) {
1247
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1248
+ }
1249
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1258
1250
  const environment = (params as { environment?: unknown } | null)?.environment;
1259
1251
  if (!isKichiEnvironment(environment)) {
1260
1252
  return jsonResult({ success: false, error: `environment must be one of: ${VALID_ENVIRONMENTS.join(", ")}` });
@@ -1273,16 +1265,21 @@ const plugin = {
1273
1265
  status,
1274
1266
  });
1275
1267
  },
1276
- });
1277
- });
1268
+ }));
1278
1269
 
1279
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1270
+ api.registerTool((ctx) => ({
1280
1271
  name: "kichi_rejoin",
1281
1272
  label: "kichi_rejoin",
1282
1273
  description:
1283
1274
  "Request an immediate rejoin attempt with saved avatarId/authKey. Rejoin is also sent automatically after reconnect.",
1284
1275
  parameters: { type: "object", properties: {} },
1285
- execute: async () => {
1276
+ execute: async (_toolCallId, _params) => {
1277
+ const locator = resolveToolLocator(ctx);
1278
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1279
+ if (!agentId) {
1280
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1281
+ }
1282
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1286
1283
  const result = service.requestRejoin();
1287
1284
  return jsonResult({
1288
1285
  success: result.accepted,
@@ -1290,14 +1287,20 @@ const plugin = {
1290
1287
  status: service.getConnectionStatus(),
1291
1288
  });
1292
1289
  },
1293
- })));
1290
+ }));
1294
1291
 
1295
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1292
+ api.registerTool((ctx) => ({
1296
1293
  name: "kichi_leave",
1297
1294
  label: "kichi_leave",
1298
1295
  description: "Leave Kichi world",
1299
1296
  parameters: { type: "object", properties: {} },
1300
- execute: async () => {
1297
+ execute: async (_toolCallId, _params) => {
1298
+ const locator = resolveToolLocator(ctx);
1299
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1300
+ if (!agentId) {
1301
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1302
+ }
1303
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1301
1304
  const result = await service.leave();
1302
1305
  if (result.success) {
1303
1306
  return jsonResult({ success: true });
@@ -1310,25 +1313,34 @@ const plugin = {
1310
1313
  ...(failure.errorMessage ? { errorMessage: failure.errorMessage } : {}),
1311
1314
  });
1312
1315
  },
1313
- })));
1316
+ }));
1314
1317
 
1315
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1318
+ api.registerTool((ctx) => ({
1316
1319
  name: "kichi_connection_status",
1317
1320
  label: "kichi_connection_status",
1318
1321
  description: "Check WebSocket connection status and identity readiness only. Does NOT return room info, avatar state, or personnel — use kichi_query_status for that.",
1319
1322
  parameters: { type: "object", properties: {} },
1320
- execute: async () => {
1323
+ execute: async (_toolCallId, _params) => {
1324
+ const locator = resolveToolLocator(ctx);
1325
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1326
+ if (!agentId) {
1327
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1328
+ }
1329
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1321
1330
  return jsonResult({
1322
1331
  success: true,
1323
1332
  status: service.getConnectionStatus(),
1324
1333
  });
1325
1334
  },
1326
- })));
1335
+ }));
1327
1336
 
1328
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1337
+ api.registerTool((ctx) => {
1338
+ const locator = resolveToolLocator(ctx);
1339
+ const existingService = runtimeManager.getRuntime(locator);
1340
+ return ({
1329
1341
  name: "kichi_action",
1330
1342
  label: "kichi_action",
1331
- description: buildKichiActionDescription(service),
1343
+ description: buildKichiActionDescription(existingService ?? undefined),
1332
1344
  parameters: {
1333
1345
  type: "object",
1334
1346
  properties: {
@@ -1357,6 +1369,12 @@ const plugin = {
1357
1369
  required: ["poseType", "action"],
1358
1370
  },
1359
1371
  execute: async (_toolCallId, params) => {
1372
+ const locator = resolveToolLocator(ctx);
1373
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1374
+ if (!agentId) {
1375
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1376
+ }
1377
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1360
1378
  const { poseType, action, bubble, log, verify, propId } = (params || {}) as {
1361
1379
  poseType?: string;
1362
1380
  action?: string;
@@ -1428,8 +1446,8 @@ const plugin = {
1428
1446
  playback,
1429
1447
  });
1430
1448
  },
1431
- })));
1432
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1449
+ })});
1450
+ api.registerTool((ctx) => ({
1433
1451
  name: "kichi_idle_plan",
1434
1452
  label: "kichi_idle_plan",
1435
1453
  description: buildKichiIdlePlanDescription(),
@@ -1513,6 +1531,12 @@ const plugin = {
1513
1531
  required: ["heartbeatIntervalSeconds", "goal", "stages"],
1514
1532
  },
1515
1533
  execute: async (_toolCallId, params) => {
1534
+ const locator = resolveToolLocator(ctx);
1535
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1536
+ if (!agentId) {
1537
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1538
+ }
1539
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1516
1540
  const { idlePlan, error } = normalizeIdlePlan(params);
1517
1541
  if (!idlePlan) {
1518
1542
  return jsonResult({ success: false, error: error ?? "Invalid idle plan payload" });
@@ -1538,8 +1562,8 @@ const plugin = {
1538
1562
  stages: idlePlan.stages,
1539
1563
  });
1540
1564
  },
1541
- })));
1542
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1565
+ }));
1566
+ api.registerTool((ctx) => ({
1543
1567
  name: "kichi_clock",
1544
1568
  label: "kichi_clock",
1545
1569
  description:
@@ -1609,6 +1633,12 @@ const plugin = {
1609
1633
  required: ["action"],
1610
1634
  },
1611
1635
  execute: async (_toolCallId, params) => {
1636
+ const locator = resolveToolLocator(ctx);
1637
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1638
+ if (!agentId) {
1639
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1640
+ }
1641
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1612
1642
  const { action, requestId, clock } = (params || {}) as {
1613
1643
  action?: unknown;
1614
1644
  requestId?: unknown;
@@ -1650,9 +1680,9 @@ const plugin = {
1650
1680
  ...(normalizedClock ? { clock: normalizedClock } : {}),
1651
1681
  });
1652
1682
  },
1653
- })));
1683
+ }));
1654
1684
 
1655
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1685
+ api.registerTool((ctx) => ({
1656
1686
  name: "kichi_query_status",
1657
1687
  label: "kichi_query_status",
1658
1688
  description:
@@ -1667,6 +1697,12 @@ const plugin = {
1667
1697
  },
1668
1698
  },
1669
1699
  execute: async (_toolCallId, params) => {
1700
+ const locator = resolveToolLocator(ctx);
1701
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1702
+ if (!agentId) {
1703
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1704
+ }
1705
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1670
1706
  const requestId = (params as { requestId?: unknown } | null)?.requestId;
1671
1707
  if (requestId !== undefined && typeof requestId !== "string") {
1672
1708
  return jsonResult({ success: false, error: "requestId must be a string when provided" });
@@ -1687,9 +1723,9 @@ const plugin = {
1687
1723
  });
1688
1724
  }
1689
1725
  },
1690
- })));
1726
+ }));
1691
1727
 
1692
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1728
+ api.registerTool((ctx) => ({
1693
1729
  name: "kichi_music_album_create",
1694
1730
  label: "kichi_music_album_create",
1695
1731
  description: buildMusicAlbumToolDescription(),
@@ -1716,6 +1752,12 @@ const plugin = {
1716
1752
  required: ["albumTitle", "musicTitles"],
1717
1753
  },
1718
1754
  execute: async (_toolCallId, params) => {
1755
+ const locator = resolveToolLocator(ctx);
1756
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1757
+ if (!agentId) {
1758
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1759
+ }
1760
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1719
1761
  const {
1720
1762
  requestId,
1721
1763
  albumTitle,
@@ -1776,9 +1818,9 @@ const plugin = {
1776
1818
  });
1777
1819
  }
1778
1820
  },
1779
- })));
1821
+ }));
1780
1822
 
1781
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1823
+ api.registerTool((ctx) => ({
1782
1824
  name: "kichi_noteboard_create",
1783
1825
  label: "kichi_noteboard_create",
1784
1826
  description:
@@ -1798,6 +1840,12 @@ const plugin = {
1798
1840
  required: ["propId", "data"],
1799
1841
  },
1800
1842
  execute: async (_toolCallId, params) => {
1843
+ const locator = resolveToolLocator(ctx);
1844
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1845
+ if (!agentId) {
1846
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1847
+ }
1848
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1801
1849
  const { propId, data } = (params || {}) as {
1802
1850
  propId?: unknown;
1803
1851
  data?: unknown;
@@ -1828,9 +1876,9 @@ const plugin = {
1828
1876
  });
1829
1877
  }
1830
1878
  },
1831
- })));
1879
+ }));
1832
1880
 
1833
- api.registerTool(createAgentScopedTool(runtimeManager, (service) => ({
1881
+ api.registerTool((ctx) => ({
1834
1882
  name: "kichi_bot_message",
1835
1883
  label: "kichi_bot_message",
1836
1884
  description:
@@ -1867,6 +1915,12 @@ const plugin = {
1867
1915
  required: ["toAvatarId", "depth", "bubble"],
1868
1916
  },
1869
1917
  execute: async (_toolCallId, params) => {
1918
+ const locator = resolveToolLocator(ctx);
1919
+ const agentId = runtimeManager.resolveRuntimeAgentId(locator);
1920
+ if (!agentId) {
1921
+ return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
1922
+ }
1923
+ const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
1870
1924
  const { toAvatarId, depth, bubble, poseType, action, log } = (params || {}) as {
1871
1925
  toAvatarId?: string;
1872
1926
  depth?: number;
@@ -1904,7 +1958,7 @@ const plugin = {
1904
1958
  return jsonResult({ success: false, error: `Failed to send bot message: ${error}` });
1905
1959
  }
1906
1960
  },
1907
- })));
1961
+ }));
1908
1962
 
1909
1963
  },
1910
1964
  };
@@ -2,9 +2,25 @@
2
2
  "id": "kichi-forwarder",
3
3
  "name": "Kichi Forwarder",
4
4
  "description": "Native OpenClaw plugin for Kichi World with direct avatar control, status sync, timers, notes, and music tools",
5
- "version": "0.1.2-beta.8",
5
+ "version": "0.1.2-beta.9",
6
6
  "author": "OpenClaw",
7
7
  "skills": ["./skills/kichi-forwarder"],
8
+ "contracts": {
9
+ "tools": [
10
+ "kichi_join",
11
+ "kichi_switch_host",
12
+ "kichi_rejoin",
13
+ "kichi_leave",
14
+ "kichi_connection_status",
15
+ "kichi_action",
16
+ "kichi_idle_plan",
17
+ "kichi_clock",
18
+ "kichi_query_status",
19
+ "kichi_music_album_create",
20
+ "kichi_noteboard_create",
21
+ "kichi_bot_message"
22
+ ]
23
+ },
8
24
  "configSchema": {
9
25
  "type": "object",
10
26
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yahaha-studio/kichi-forwarder",
3
- "version": "0.1.2-beta.8",
3
+ "version": "0.1.2-beta.9",
4
4
  "description": "Native OpenClaw plugin for Kichi World with direct avatar control, status sync, timers, notes, and music tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,12 +22,12 @@
22
22
  "./dist/index.js"
23
23
  ],
24
24
  "compat": {
25
- "pluginApi": ">=2026.3.24",
26
- "minGatewayVersion": "2026.3.24"
25
+ "pluginApi": ">=2026.4.25",
26
+ "minGatewayVersion": "2026.4.25"
27
27
  },
28
28
  "build": {
29
- "openclawVersion": "2026.3.24",
30
- "pluginSdkVersion": "2026.3.24"
29
+ "openclawVersion": "2026.5.7",
30
+ "pluginSdkVersion": "2026.5.7"
31
31
  }
32
32
  },
33
33
  "scripts": {
@@ -43,7 +43,7 @@
43
43
  "devDependencies": {
44
44
  "@types/node": "^24.3.0",
45
45
  "@types/ws": "^8.18.1",
46
- "openclaw": "2026.3.24",
46
+ "openclaw": "2026.5.7",
47
47
  "typescript": "^6.0.3"
48
48
  }
49
49
  }