@yahaha-studio/kichi-forwarder 0.1.2-beta.16 → 0.1.2-beta.18
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/README.md +2 -2
- package/dist/index.js +85 -15
- package/index.ts +102 -17
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/kichi-forwarder/SKILL.md +9 -12
- package/skills/kichi-forwarder/references/install.md +4 -19
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ Use the bare package name for installation. OpenClaw tries ClawHub first and fal
|
|
|
31
31
|
|
|
32
32
|
Kichi provides the install command and the connection details you need to connect a companion.
|
|
33
33
|
|
|
34
|
-
Get the `
|
|
34
|
+
Get the environment, `avatarId`, and test `host` when using test, then use them with `kichi_join`.
|
|
35
35
|
|
|
36
36
|
## What Your Companion Can Do
|
|
37
37
|
|
|
@@ -48,7 +48,7 @@ Get the `host` and `avatarId` from Kichi, then use them with `kichi_switch_host`
|
|
|
48
48
|
|
|
49
49
|
1. Install the plugin.
|
|
50
50
|
2. Start OpenClaw with the plugin enabled.
|
|
51
|
-
3. Use `
|
|
51
|
+
3. Use `kichi_join` to connect your companion to Kichi.
|
|
52
52
|
4. Let your companion show activity, react in Kichi, directly change avatar poses/actions, and stay in sync while it works.
|
|
53
53
|
5. Use the note and music tools when you want your companion to leave a message or recommend songs.
|
|
54
54
|
|
package/dist/index.js
CHANGED
|
@@ -192,6 +192,23 @@ function resolveEnvironmentHost(environment) {
|
|
|
192
192
|
}
|
|
193
193
|
return { error: `environment "${environment}" has no configured host — update config/environments.json first` };
|
|
194
194
|
}
|
|
195
|
+
function resolveJoinEnvironmentHost(params) {
|
|
196
|
+
if (!isKichiEnvironment(params.environment)) {
|
|
197
|
+
return { error: `environment must be one of: ${VALID_ENVIRONMENTS.join(", ")}` };
|
|
198
|
+
}
|
|
199
|
+
if (params.environment === "test") {
|
|
200
|
+
const testHost = typeof params.host === "string" ? params.host.trim() : "";
|
|
201
|
+
if (!testHost) {
|
|
202
|
+
return { error: "host is required for the test environment" };
|
|
203
|
+
}
|
|
204
|
+
return { environment: params.environment, host: testHost };
|
|
205
|
+
}
|
|
206
|
+
const resolved = resolveEnvironmentHost(params.environment);
|
|
207
|
+
if (resolved.error) {
|
|
208
|
+
return { environment: params.environment, error: resolved.error };
|
|
209
|
+
}
|
|
210
|
+
return { environment: params.environment, host: resolved.host };
|
|
211
|
+
}
|
|
195
212
|
function sendStatusUpdate(service, status) {
|
|
196
213
|
const actionDefinition = getActionDefinition(status.poseType, status.action);
|
|
197
214
|
service.sendStatus(status.poseType, actionDefinition.name, status.bubble || status.action, typeof status.log === "string" ? status.log.trim() : "", getActionPlayback(actionDefinition), status.propId);
|
|
@@ -843,9 +860,12 @@ function buildKichiIdlePlanDescription() {
|
|
|
843
860
|
function buildKichiPrompt() {
|
|
844
861
|
return [
|
|
845
862
|
"Kichi avatar control and status sync are available via `kichi_action` and `kichi_clock`.",
|
|
863
|
+
"",
|
|
864
|
+
"IMAGE GENERATION RULES FOR SELFIES AND AVATAR APPEARANCE:",
|
|
865
|
+
"- If the user asks for a selfie, portrait, photo, avatar image, or any generated image of your appearance, you MUST read the workspace `IDENTITY.md` first and use it as the source of truth for your actual avatar description. If it references an avatar image URL, analyze that image with the available image analysis capability before calling image generation. Never guess or invent your appearance from personality, SOUL.md traits, or conversation tone alone. If the identity source is missing or cannot be analyzed, say so instead of fabricating your appearance.",
|
|
866
|
+
"",
|
|
846
867
|
"If the user gives a direct Kichi pose or action request, fulfill it with `kichi_action` and set `verify: true` so you can confirm the avatar actually applied the pose. If the result contains a warning about a fallback, tell the user what actually happened instead of assuming success.",
|
|
847
868
|
"Write the visible reply as a natural user-facing response. Keep `kichi_action`, `kichi_clock`, and sync steps internal and absent from the visible reply.",
|
|
848
|
-
"If the user asks OpenClaw to send a selfie, base the visual appearance on the avatar details in IDENTITY.",
|
|
849
869
|
"",
|
|
850
870
|
"kichi_action timing (all required when sync is active):",
|
|
851
871
|
"1. Task start: call BEFORE your first tool call OR before composing a multi-paragraph reply. For most work, start from a sit pose unless the user asked for a different pose or the task clearly fits another pose better.",
|
|
@@ -945,18 +965,27 @@ const plugin = {
|
|
|
945
965
|
api.registerTool((ctx) => ({
|
|
946
966
|
name: "kichi_join",
|
|
947
967
|
label: "kichi_join",
|
|
948
|
-
description: "Join Kichi world with avatarId, the current bot name, a short bio, and personality tags",
|
|
968
|
+
description: "Join Kichi world in the target environment with avatarId, the current bot name, a short bio, and personality tags. For test, pass host.",
|
|
949
969
|
parameters: {
|
|
950
970
|
type: "object",
|
|
951
971
|
properties: {
|
|
952
972
|
avatarId: { type: "string", description: "Avatar ID to join Kichi world" },
|
|
973
|
+
environment: {
|
|
974
|
+
type: "string",
|
|
975
|
+
enum: VALID_ENVIRONMENTS,
|
|
976
|
+
description: "Target environment. kichi_join switches to this environment before joining.",
|
|
977
|
+
},
|
|
978
|
+
host: {
|
|
979
|
+
type: "string",
|
|
980
|
+
description: "Test host, required when environment is test and ignored otherwise",
|
|
981
|
+
},
|
|
953
982
|
botName: {
|
|
954
983
|
type: "string",
|
|
955
984
|
description: "Current bot name to include in the join message",
|
|
956
985
|
},
|
|
957
986
|
bio: {
|
|
958
987
|
type: "string",
|
|
959
|
-
description: "Short bio covering
|
|
988
|
+
description: "Short bio extracted from SOUL.md, covering persona and idle plan goals if present",
|
|
960
989
|
},
|
|
961
990
|
tags: {
|
|
962
991
|
type: "array",
|
|
@@ -968,7 +997,7 @@ const plugin = {
|
|
|
968
997
|
description: "Optional join source identifier. Defaults to Kichi World join-source.json, then openclaw.",
|
|
969
998
|
},
|
|
970
999
|
},
|
|
971
|
-
required: ["botName", "bio"],
|
|
1000
|
+
required: ["environment", "avatarId", "botName", "bio"],
|
|
972
1001
|
},
|
|
973
1002
|
execute: async (_toolCallId, params) => {
|
|
974
1003
|
const locator = resolveToolLocator(ctx);
|
|
@@ -977,17 +1006,23 @@ const plugin = {
|
|
|
977
1006
|
return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
|
|
978
1007
|
}
|
|
979
1008
|
const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
|
|
980
|
-
|
|
981
|
-
const
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
if (
|
|
986
|
-
|
|
1009
|
+
const p = params;
|
|
1010
|
+
const target = resolveJoinEnvironmentHost({
|
|
1011
|
+
environment: p?.environment,
|
|
1012
|
+
host: p?.host,
|
|
1013
|
+
});
|
|
1014
|
+
if (target.error) {
|
|
1015
|
+
return jsonResult({ success: false, error: target.error });
|
|
987
1016
|
}
|
|
988
|
-
|
|
989
|
-
|
|
1017
|
+
const currentStatus = service.getConnectionStatus();
|
|
1018
|
+
let avatarId = p?.avatarId;
|
|
1019
|
+
if (!avatarId && currentStatus.host === target.host) {
|
|
1020
|
+
avatarId = service.readSavedAvatarId() ?? undefined;
|
|
990
1021
|
}
|
|
1022
|
+
const botName = p?.botName?.trim();
|
|
1023
|
+
const bio = p?.bio?.trim();
|
|
1024
|
+
const rawSource = p?.source;
|
|
1025
|
+
const { tags, error: tagsError } = normalizeJoinTags(p?.tags);
|
|
991
1026
|
if (!botName) {
|
|
992
1027
|
return jsonResult({ success: false, error: "No botName" });
|
|
993
1028
|
}
|
|
@@ -1009,14 +1044,49 @@ const plugin = {
|
|
|
1009
1044
|
if (tagsError) {
|
|
1010
1045
|
return jsonResult({ success: false, error: tagsError });
|
|
1011
1046
|
}
|
|
1047
|
+
let leaveStatus;
|
|
1048
|
+
const shouldLeaveCurrentConnection = currentStatus.connected && currentStatus.hasAuthKey && ((!!currentStatus.host && currentStatus.host !== target.host) ||
|
|
1049
|
+
(currentStatus.host === target.host && !!currentStatus.avatarId && !!avatarId && currentStatus.avatarId !== avatarId));
|
|
1050
|
+
if (shouldLeaveCurrentConnection) {
|
|
1051
|
+
try {
|
|
1052
|
+
leaveStatus = await service.leave();
|
|
1053
|
+
}
|
|
1054
|
+
catch (err) {
|
|
1055
|
+
leaveStatus = {
|
|
1056
|
+
success: false,
|
|
1057
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
let switchStatus;
|
|
1062
|
+
if (target.environment && target.host && service.getCurrentHost() !== target.host) {
|
|
1063
|
+
switchStatus = await service.switchHost(target.host, target.environment);
|
|
1064
|
+
}
|
|
1065
|
+
if (!avatarId) {
|
|
1066
|
+
avatarId = service.readSavedAvatarId() ?? undefined;
|
|
1067
|
+
}
|
|
1068
|
+
if (!avatarId) {
|
|
1069
|
+
return jsonResult({ success: false, error: "No avatarId" });
|
|
1070
|
+
}
|
|
1012
1071
|
const result = await service.join(avatarId, botName, bio, tags ?? [], source);
|
|
1013
1072
|
if (result.success) {
|
|
1014
|
-
return jsonResult({
|
|
1073
|
+
return jsonResult({
|
|
1074
|
+
success: true,
|
|
1075
|
+
authKey: result.authKey,
|
|
1076
|
+
...(target.environment ? { environment: target.environment } : {}),
|
|
1077
|
+
...(target.host ? { host: target.host } : {}),
|
|
1078
|
+
...(switchStatus ? { switchStatus } : {}),
|
|
1079
|
+
...(leaveStatus ? { leaveStatus } : {}),
|
|
1080
|
+
});
|
|
1015
1081
|
}
|
|
1016
1082
|
const failure = result;
|
|
1017
1083
|
return jsonResult({
|
|
1018
1084
|
success: false,
|
|
1019
1085
|
error: failure.error,
|
|
1086
|
+
...(target.environment ? { environment: target.environment } : {}),
|
|
1087
|
+
...(target.host ? { host: target.host } : {}),
|
|
1088
|
+
...(switchStatus ? { switchStatus } : {}),
|
|
1089
|
+
...(leaveStatus ? { leaveStatus } : {}),
|
|
1020
1090
|
...(failure.errorCode ? { errorCode: failure.errorCode } : {}),
|
|
1021
1091
|
...(failure.errorMessage ? { errorMessage: failure.errorMessage } : {}),
|
|
1022
1092
|
});
|
|
@@ -1036,7 +1106,7 @@ const plugin = {
|
|
|
1036
1106
|
},
|
|
1037
1107
|
host: {
|
|
1038
1108
|
type: "string",
|
|
1039
|
-
description: "Test
|
|
1109
|
+
description: "Test host (required for test environment, ignored otherwise)",
|
|
1040
1110
|
},
|
|
1041
1111
|
},
|
|
1042
1112
|
required: ["environment"],
|
package/index.ts
CHANGED
|
@@ -256,6 +256,27 @@ function resolveEnvironmentHost(environment: KichiEnvironment): { host?: string;
|
|
|
256
256
|
return { error: `environment "${environment}" has no configured host — update config/environments.json first` };
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
function resolveJoinEnvironmentHost(params: {
|
|
260
|
+
environment?: unknown;
|
|
261
|
+
host?: unknown;
|
|
262
|
+
}): { environment?: KichiEnvironment; host?: string; error?: string } {
|
|
263
|
+
if (!isKichiEnvironment(params.environment)) {
|
|
264
|
+
return { error: `environment must be one of: ${VALID_ENVIRONMENTS.join(", ")}` };
|
|
265
|
+
}
|
|
266
|
+
if (params.environment === "test") {
|
|
267
|
+
const testHost = typeof params.host === "string" ? params.host.trim() : "";
|
|
268
|
+
if (!testHost) {
|
|
269
|
+
return { error: "host is required for the test environment" };
|
|
270
|
+
}
|
|
271
|
+
return { environment: params.environment, host: testHost };
|
|
272
|
+
}
|
|
273
|
+
const resolved = resolveEnvironmentHost(params.environment);
|
|
274
|
+
if (resolved.error) {
|
|
275
|
+
return { environment: params.environment, error: resolved.error };
|
|
276
|
+
}
|
|
277
|
+
return { environment: params.environment, host: resolved.host };
|
|
278
|
+
}
|
|
279
|
+
|
|
259
280
|
function sendStatusUpdate(service: KichiForwarderService, status: ActionResult): void {
|
|
260
281
|
const actionDefinition = getActionDefinition(status.poseType, status.action);
|
|
261
282
|
service.sendStatus(
|
|
@@ -1047,9 +1068,12 @@ function buildKichiIdlePlanDescription(): string {
|
|
|
1047
1068
|
function buildKichiPrompt(): string {
|
|
1048
1069
|
return [
|
|
1049
1070
|
"Kichi avatar control and status sync are available via `kichi_action` and `kichi_clock`.",
|
|
1071
|
+
"",
|
|
1072
|
+
"IMAGE GENERATION RULES FOR SELFIES AND AVATAR APPEARANCE:",
|
|
1073
|
+
"- If the user asks for a selfie, portrait, photo, avatar image, or any generated image of your appearance, you MUST read the workspace `IDENTITY.md` first and use it as the source of truth for your actual avatar description. If it references an avatar image URL, analyze that image with the available image analysis capability before calling image generation. Never guess or invent your appearance from personality, SOUL.md traits, or conversation tone alone. If the identity source is missing or cannot be analyzed, say so instead of fabricating your appearance.",
|
|
1074
|
+
"",
|
|
1050
1075
|
"If the user gives a direct Kichi pose or action request, fulfill it with `kichi_action` and set `verify: true` so you can confirm the avatar actually applied the pose. If the result contains a warning about a fallback, tell the user what actually happened instead of assuming success.",
|
|
1051
1076
|
"Write the visible reply as a natural user-facing response. Keep `kichi_action`, `kichi_clock`, and sync steps internal and absent from the visible reply.",
|
|
1052
|
-
"If the user asks OpenClaw to send a selfie, base the visual appearance on the avatar details in IDENTITY.",
|
|
1053
1077
|
"",
|
|
1054
1078
|
"kichi_action timing (all required when sync is active):",
|
|
1055
1079
|
"1. Task start: call BEFORE your first tool call OR before composing a multi-paragraph reply. For most work, start from a sit pose unless the user asked for a different pose or the task clearly fits another pose better.",
|
|
@@ -1163,18 +1187,29 @@ const plugin = {
|
|
|
1163
1187
|
api.registerTool((ctx) => ({
|
|
1164
1188
|
name: "kichi_join",
|
|
1165
1189
|
label: "kichi_join",
|
|
1166
|
-
description:
|
|
1190
|
+
description:
|
|
1191
|
+
"Join Kichi world in the target environment with avatarId, the current bot name, a short bio, and personality tags. For test, pass host.",
|
|
1167
1192
|
parameters: {
|
|
1168
1193
|
type: "object",
|
|
1169
1194
|
properties: {
|
|
1170
1195
|
avatarId: { type: "string", description: "Avatar ID to join Kichi world" },
|
|
1196
|
+
environment: {
|
|
1197
|
+
type: "string",
|
|
1198
|
+
enum: VALID_ENVIRONMENTS,
|
|
1199
|
+
description:
|
|
1200
|
+
"Target environment. kichi_join switches to this environment before joining.",
|
|
1201
|
+
},
|
|
1202
|
+
host: {
|
|
1203
|
+
type: "string",
|
|
1204
|
+
description: "Test host, required when environment is test and ignored otherwise",
|
|
1205
|
+
},
|
|
1171
1206
|
botName: {
|
|
1172
1207
|
type: "string",
|
|
1173
1208
|
description: "Current bot name to include in the join message",
|
|
1174
1209
|
},
|
|
1175
1210
|
bio: {
|
|
1176
1211
|
type: "string",
|
|
1177
|
-
description: "Short bio covering
|
|
1212
|
+
description: "Short bio extracted from SOUL.md, covering persona and idle plan goals if present",
|
|
1178
1213
|
},
|
|
1179
1214
|
tags: {
|
|
1180
1215
|
type: "array",
|
|
@@ -1186,7 +1221,7 @@ const plugin = {
|
|
|
1186
1221
|
description: "Optional join source identifier. Defaults to Kichi World join-source.json, then openclaw.",
|
|
1187
1222
|
},
|
|
1188
1223
|
},
|
|
1189
|
-
required: ["botName", "bio"],
|
|
1224
|
+
required: ["environment", "avatarId", "botName", "bio"],
|
|
1190
1225
|
},
|
|
1191
1226
|
execute: async (_toolCallId, params) => {
|
|
1192
1227
|
const locator = resolveToolLocator(ctx);
|
|
@@ -1195,19 +1230,33 @@ const plugin = {
|
|
|
1195
1230
|
return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
|
|
1196
1231
|
}
|
|
1197
1232
|
const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1233
|
+
const p = params as {
|
|
1234
|
+
avatarId?: string;
|
|
1235
|
+
environment?: unknown;
|
|
1236
|
+
host?: unknown;
|
|
1237
|
+
botName?: string;
|
|
1238
|
+
bio?: string;
|
|
1239
|
+
source?: unknown;
|
|
1240
|
+
tags?: unknown;
|
|
1241
|
+
} | null;
|
|
1242
|
+
const target = resolveJoinEnvironmentHost({
|
|
1243
|
+
environment: p?.environment,
|
|
1244
|
+
host: p?.host,
|
|
1245
|
+
});
|
|
1246
|
+
if (target.error) {
|
|
1247
|
+
return jsonResult({ success: false, error: target.error });
|
|
1207
1248
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1249
|
+
const currentStatus = service.getConnectionStatus();
|
|
1250
|
+
let avatarId = p?.avatarId;
|
|
1251
|
+
if (!avatarId && currentStatus.host === target.host) {
|
|
1252
|
+
avatarId = service.readSavedAvatarId() ?? undefined;
|
|
1210
1253
|
}
|
|
1254
|
+
const botName = p?.botName?.trim();
|
|
1255
|
+
const bio = p?.bio?.trim();
|
|
1256
|
+
const rawSource = p?.source;
|
|
1257
|
+
const { tags, error: tagsError } = normalizeJoinTags(
|
|
1258
|
+
p?.tags,
|
|
1259
|
+
);
|
|
1211
1260
|
if (!botName) {
|
|
1212
1261
|
return jsonResult({ success: false, error: "No botName" });
|
|
1213
1262
|
}
|
|
@@ -1228,14 +1277,50 @@ const plugin = {
|
|
|
1228
1277
|
if (tagsError) {
|
|
1229
1278
|
return jsonResult({ success: false, error: tagsError });
|
|
1230
1279
|
}
|
|
1280
|
+
let leaveStatus;
|
|
1281
|
+
const shouldLeaveCurrentConnection = currentStatus.connected && currentStatus.hasAuthKey && (
|
|
1282
|
+
(!!currentStatus.host && currentStatus.host !== target.host) ||
|
|
1283
|
+
(currentStatus.host === target.host && !!currentStatus.avatarId && !!avatarId && currentStatus.avatarId !== avatarId)
|
|
1284
|
+
);
|
|
1285
|
+
if (shouldLeaveCurrentConnection) {
|
|
1286
|
+
try {
|
|
1287
|
+
leaveStatus = await service.leave();
|
|
1288
|
+
} catch (err) {
|
|
1289
|
+
leaveStatus = {
|
|
1290
|
+
success: false,
|
|
1291
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
let switchStatus;
|
|
1296
|
+
if (target.environment && target.host && service.getCurrentHost() !== target.host) {
|
|
1297
|
+
switchStatus = await service.switchHost(target.host, target.environment);
|
|
1298
|
+
}
|
|
1299
|
+
if (!avatarId) {
|
|
1300
|
+
avatarId = service.readSavedAvatarId() ?? undefined;
|
|
1301
|
+
}
|
|
1302
|
+
if (!avatarId) {
|
|
1303
|
+
return jsonResult({ success: false, error: "No avatarId" });
|
|
1304
|
+
}
|
|
1231
1305
|
const result = await service.join(avatarId, botName, bio, tags ?? [], source);
|
|
1232
1306
|
if (result.success) {
|
|
1233
|
-
return jsonResult({
|
|
1307
|
+
return jsonResult({
|
|
1308
|
+
success: true,
|
|
1309
|
+
authKey: result.authKey,
|
|
1310
|
+
...(target.environment ? { environment: target.environment } : {}),
|
|
1311
|
+
...(target.host ? { host: target.host } : {}),
|
|
1312
|
+
...(switchStatus ? { switchStatus } : {}),
|
|
1313
|
+
...(leaveStatus ? { leaveStatus } : {}),
|
|
1314
|
+
});
|
|
1234
1315
|
}
|
|
1235
1316
|
const failure = result as { success: false; error: string; errorCode?: string; errorMessage?: string };
|
|
1236
1317
|
return jsonResult({
|
|
1237
1318
|
success: false,
|
|
1238
1319
|
error: failure.error,
|
|
1320
|
+
...(target.environment ? { environment: target.environment } : {}),
|
|
1321
|
+
...(target.host ? { host: target.host } : {}),
|
|
1322
|
+
...(switchStatus ? { switchStatus } : {}),
|
|
1323
|
+
...(leaveStatus ? { leaveStatus } : {}),
|
|
1239
1324
|
...(failure.errorCode ? { errorCode: failure.errorCode } : {}),
|
|
1240
1325
|
...(failure.errorMessage ? { errorMessage: failure.errorMessage } : {}),
|
|
1241
1326
|
});
|
|
@@ -1257,7 +1342,7 @@ const plugin = {
|
|
|
1257
1342
|
},
|
|
1258
1343
|
host: {
|
|
1259
1344
|
type: "string",
|
|
1260
|
-
description: "Test
|
|
1345
|
+
description: "Test host (required for test environment, ignored otherwise)",
|
|
1261
1346
|
},
|
|
1262
1347
|
},
|
|
1263
1348
|
required: ["environment"],
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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.
|
|
5
|
+
"version": "0.1.2-beta.18",
|
|
6
6
|
"author": "OpenClaw",
|
|
7
7
|
"skills": ["./skills/kichi-forwarder"],
|
|
8
8
|
"contracts": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yahaha-studio/kichi-forwarder",
|
|
3
|
-
"version": "0.1.2-beta.
|
|
3
|
+
"version": "0.1.2-beta.18",
|
|
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",
|
|
@@ -23,7 +23,7 @@ Install and connect requests use `on <environment>` syntax. Supported environmen
|
|
|
23
23
|
|
|
24
24
|
- `steam`: connects to `focus-wss.yahaha.com`
|
|
25
25
|
- `steam-playtest`: connects to `focus-steam-playtest-wss-int.yahaha.com`
|
|
26
|
-
- `test`: no fixed host —
|
|
26
|
+
- `test`: no fixed host — use the test host from the user request when provided; otherwise ask the user for the test host before calling `kichi_join`
|
|
27
27
|
|
|
28
28
|
## Runtime State
|
|
29
29
|
|
|
@@ -69,26 +69,23 @@ For install/onboarding/connect requests:
|
|
|
69
69
|
|
|
70
70
|
Use this order unless the user asks for a different explicit action. For install/onboarding requests, follow `install.md` first.
|
|
71
71
|
|
|
72
|
-
1.
|
|
73
|
-
2. If the requested environment differs from the current environment, call `kichi_switch_host` with the target environment.
|
|
74
|
-
3. If the requested `avatarId` differs from the current host's connected `avatarId`, call `kichi_leave` first when the old avatar is still joined, then call `kichi_join` with the requested `avatarId`.
|
|
75
|
-
4. If no `authKey` is available, call `kichi_join`.
|
|
76
|
-
5. If `authKey` exists but websocket is not open, call `kichi_rejoin` or wait for automatic reconnect and rejoin.
|
|
77
|
-
6. Use `kichi_action`, `kichi_glance`, `kichi_clock`, note board tools, and music album tools after status is ready.
|
|
72
|
+
1. For join/connect requests with an `avatarId` and environment, call `kichi_join` with `environment`. For `test`, include `host` if the user provided it; if not, ask for the host first.
|
|
78
73
|
|
|
79
74
|
## Tools
|
|
80
75
|
|
|
81
76
|
### kichi_join
|
|
82
77
|
|
|
83
78
|
```text
|
|
84
|
-
kichi_join(avatarId: "your-avatar-id", botName: "<from IDENTITY.md>", bio: "<from SOUL.md>", tags: ["calm", "focused", "curious"])
|
|
79
|
+
kichi_join(environment: "steam-playtest", avatarId: "your-avatar-id", botName: "<from IDENTITY.md>", bio: "<from SOUL.md>", tags: ["calm", "focused", "curious"])
|
|
80
|
+
kichi_join(environment: "test", host: "192.168.1.100", avatarId: "your-avatar-id", botName: "<from IDENTITY.md>", bio: "<from SOUL.md>", tags: ["calm", "focused", "curious"])
|
|
85
81
|
```
|
|
86
82
|
|
|
83
|
+
- `environment`: required. One of `steam`, `steam-playtest`, `test`. `kichi_join` switches to the target environment before joining.
|
|
84
|
+
- `host`: required for `test` environment, ignored otherwise. If the user did not provide the test host, ask for it before calling `kichi_join`.
|
|
85
|
+
- `avatarId`: required
|
|
87
86
|
- `botName`: required
|
|
88
|
-
- `bio`: required
|
|
89
|
-
- `avatarId`: optional. If omitted, the tool reads `avatarId` from the current host's `identity.json`. If missing, the call fails.
|
|
87
|
+
- `bio`: required. Extract from `SOUL.md`, covering persona and idle plan goals if present.
|
|
90
88
|
- `tags`: optional string list. Empty strings are ignored and duplicates are removed. If omitted, the join payload sends `[]`.
|
|
91
|
-
- If the current host is still joined with a different `avatarId`, call `kichi_leave` first, then call `kichi_join` with the new `avatarId`.
|
|
92
89
|
|
|
93
90
|
### kichi_switch_host
|
|
94
91
|
|
|
@@ -98,7 +95,7 @@ kichi_switch_host(environment: "test", host: "192.168.1.100")
|
|
|
98
95
|
```
|
|
99
96
|
|
|
100
97
|
- `environment`: required. One of `steam`, `steam-playtest`, `test`.
|
|
101
|
-
- `host`: required for `test` environment, ignored otherwise.
|
|
98
|
+
- `host`: required for `test` environment, ignored otherwise.
|
|
102
99
|
- For `steam` and `steam-playtest`, the host is resolved automatically from the bundled config.
|
|
103
100
|
- This reloads the host-specific `identity.json` and reconnects the websocket immediately.
|
|
104
101
|
|
|
@@ -6,7 +6,7 @@ Install and connect requests use `on <environment>` syntax. Supported environmen
|
|
|
6
6
|
|
|
7
7
|
- `steam`: connects to `focus-wss.yahaha.com`
|
|
8
8
|
- `steam-playtest`: connects to `focus-steam-playtest-wss-int.yahaha.com`
|
|
9
|
-
- `test`: no fixed host —
|
|
9
|
+
- `test`: no fixed host — use the test host from the user request when provided; otherwise ask the user for the test host before calling `kichi_join`
|
|
10
10
|
|
|
11
11
|
## Runtime Files
|
|
12
12
|
|
|
@@ -23,17 +23,6 @@ Persist runtime state to the current agent's `state.json`:
|
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
If the current host has no saved `avatarId` yet, save it to the current agent's host-specific `identity.json` (this helps `kichi_join` resolve the avatar automatically when `avatarId` is omitted):
|
|
27
|
-
|
|
28
|
-
- Linux/macOS: `~/.openclaw/kichi-world/agents/<encoded-agent-id>/hosts/<encoded-host>/identity.json`
|
|
29
|
-
- Windows: `%USERPROFILE%\.openclaw\kichi-world\agents\<encoded-agent-id>\hosts\<encoded-host>\identity.json`
|
|
30
|
-
|
|
31
|
-
```json
|
|
32
|
-
{
|
|
33
|
-
"avatarId": "your-avatar-id"
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
26
|
## Install
|
|
38
27
|
|
|
39
28
|
1. Download the npm package archive:
|
|
@@ -64,7 +53,7 @@ When the user asks with one of the commands above, execute in this fixed order:
|
|
|
64
53
|
|
|
65
54
|
1. If loaded from a remote URL, read `install.md` and `heartbeat.md` from the published skill URLs first. If installed locally, use the local files.
|
|
66
55
|
2. Parse `avatarId` from user text (`AvatarId`/`avatarId`, case-insensitive).
|
|
67
|
-
3. Parse environment from the `on <environment>` part of the command (e.g. `on steam-playtest`).
|
|
56
|
+
3. Parse environment from the `on <environment>` part of the command (e.g. `on steam-playtest`). For `test`, parse the host from the request or ask the user for it before continuing.
|
|
68
57
|
4. Run `npm pack @yahaha-studio/kichi-forwarder`, then install the generated `.tgz` with `openclaw plugins install <tgz-path>`.
|
|
69
58
|
5. If the plugin already exists and the packed version matches the installed version, skip to step 7.
|
|
70
59
|
6. If the plugin already exists but the version differs, overwrite with `openclaw plugins install <tgz-path> --force`.
|
|
@@ -72,11 +61,7 @@ When the user asks with one of the commands above, execute in this fixed order:
|
|
|
72
61
|
8. Run `openclaw --version`. If the version is **5.7 or later**, ensure `openclaw.json` has `plugins.entries.kichi-forwarder.hooks.allowConversationAccess` set to `true`. If missing, add it. On older versions, skip this step.
|
|
73
62
|
9. If the plugin was newly installed or upgraded in this flow, check workspace `HEARTBEAT.md` against the latest Kichi heartbeat requirements before continuing. An empty or blank `HEARTBEAT.md` means the snippet is missing — treat it the same as "snippet not found", not as a read failure.
|
|
74
63
|
10. Update workspace `HEARTBEAT.md` by following `Session Startup Rule` and `First Join Setup` from [heartbeat.md](heartbeat.md). If the update fails, warn the user and continue.
|
|
75
|
-
11. Call `
|
|
76
|
-
12. If the current agent runtime environment does not match the requested one, call `kichi_switch_host` with the target environment (and host for test).
|
|
77
|
-
13. If the current host is still connected with a different `avatarId`, call `kichi_leave` first, then call `kichi_join` with parsed `avatarId`, `botName`, `bio`, and `tags`.
|
|
78
|
-
14. Otherwise, if `authKey` is missing, call `kichi_join` with parsed `avatarId`, `botName`, `bio`, and `tags`.
|
|
79
|
-
15. Call `kichi_connection_status` again and confirm connection and auth state.
|
|
64
|
+
11. Call `kichi_join` with parsed `environment`, `host` for test, `avatarId`, `botName`, `bio`, and `tags`.
|
|
80
65
|
|
|
81
66
|
## Required Post-install Integration
|
|
82
67
|
|
|
@@ -85,7 +70,7 @@ Use this completion checklist:
|
|
|
85
70
|
- [ ] plugin installed, enabled, and at latest version
|
|
86
71
|
- [ ] `openclaw.json` has `plugins.entries.kichi-forwarder.hooks.allowConversationAccess: true` (OpenClaw >= 5.7 only)
|
|
87
72
|
- [ ] `HEARTBEAT.md` updated with the Kichi heartbeat workflow snippet from [heartbeat.md](heartbeat.md)
|
|
88
|
-
- [ ] `
|
|
73
|
+
- [ ] `kichi_join` completed successfully
|
|
89
74
|
|
|
90
75
|
If any box is unchecked, the onboarding remains incomplete.
|
|
91
76
|
|