@yahaha-studio/kichi-forwarder 0.1.2-beta.17 → 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 +81 -14
- package/index.ts +98 -16
- 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);
|
|
@@ -948,18 +965,27 @@ const plugin = {
|
|
|
948
965
|
api.registerTool((ctx) => ({
|
|
949
966
|
name: "kichi_join",
|
|
950
967
|
label: "kichi_join",
|
|
951
|
-
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.",
|
|
952
969
|
parameters: {
|
|
953
970
|
type: "object",
|
|
954
971
|
properties: {
|
|
955
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
|
+
},
|
|
956
982
|
botName: {
|
|
957
983
|
type: "string",
|
|
958
984
|
description: "Current bot name to include in the join message",
|
|
959
985
|
},
|
|
960
986
|
bio: {
|
|
961
987
|
type: "string",
|
|
962
|
-
description: "Short bio covering
|
|
988
|
+
description: "Short bio extracted from SOUL.md, covering persona and idle plan goals if present",
|
|
963
989
|
},
|
|
964
990
|
tags: {
|
|
965
991
|
type: "array",
|
|
@@ -971,7 +997,7 @@ const plugin = {
|
|
|
971
997
|
description: "Optional join source identifier. Defaults to Kichi World join-source.json, then openclaw.",
|
|
972
998
|
},
|
|
973
999
|
},
|
|
974
|
-
required: ["botName", "bio"],
|
|
1000
|
+
required: ["environment", "avatarId", "botName", "bio"],
|
|
975
1001
|
},
|
|
976
1002
|
execute: async (_toolCallId, params) => {
|
|
977
1003
|
const locator = resolveToolLocator(ctx);
|
|
@@ -980,17 +1006,23 @@ const plugin = {
|
|
|
980
1006
|
return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
|
|
981
1007
|
}
|
|
982
1008
|
const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
|
|
983
|
-
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
if (
|
|
989
|
-
|
|
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 });
|
|
990
1016
|
}
|
|
991
|
-
|
|
992
|
-
|
|
1017
|
+
const currentStatus = service.getConnectionStatus();
|
|
1018
|
+
let avatarId = p?.avatarId;
|
|
1019
|
+
if (!avatarId && currentStatus.host === target.host) {
|
|
1020
|
+
avatarId = service.readSavedAvatarId() ?? undefined;
|
|
993
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);
|
|
994
1026
|
if (!botName) {
|
|
995
1027
|
return jsonResult({ success: false, error: "No botName" });
|
|
996
1028
|
}
|
|
@@ -1012,14 +1044,49 @@ const plugin = {
|
|
|
1012
1044
|
if (tagsError) {
|
|
1013
1045
|
return jsonResult({ success: false, error: tagsError });
|
|
1014
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
|
+
}
|
|
1015
1071
|
const result = await service.join(avatarId, botName, bio, tags ?? [], source);
|
|
1016
1072
|
if (result.success) {
|
|
1017
|
-
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
|
+
});
|
|
1018
1081
|
}
|
|
1019
1082
|
const failure = result;
|
|
1020
1083
|
return jsonResult({
|
|
1021
1084
|
success: false,
|
|
1022
1085
|
error: failure.error,
|
|
1086
|
+
...(target.environment ? { environment: target.environment } : {}),
|
|
1087
|
+
...(target.host ? { host: target.host } : {}),
|
|
1088
|
+
...(switchStatus ? { switchStatus } : {}),
|
|
1089
|
+
...(leaveStatus ? { leaveStatus } : {}),
|
|
1023
1090
|
...(failure.errorCode ? { errorCode: failure.errorCode } : {}),
|
|
1024
1091
|
...(failure.errorMessage ? { errorMessage: failure.errorMessage } : {}),
|
|
1025
1092
|
});
|
|
@@ -1039,7 +1106,7 @@ const plugin = {
|
|
|
1039
1106
|
},
|
|
1040
1107
|
host: {
|
|
1041
1108
|
type: "string",
|
|
1042
|
-
description: "Test
|
|
1109
|
+
description: "Test host (required for test environment, ignored otherwise)",
|
|
1043
1110
|
},
|
|
1044
1111
|
},
|
|
1045
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(
|
|
@@ -1166,18 +1187,29 @@ const plugin = {
|
|
|
1166
1187
|
api.registerTool((ctx) => ({
|
|
1167
1188
|
name: "kichi_join",
|
|
1168
1189
|
label: "kichi_join",
|
|
1169
|
-
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.",
|
|
1170
1192
|
parameters: {
|
|
1171
1193
|
type: "object",
|
|
1172
1194
|
properties: {
|
|
1173
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
|
+
},
|
|
1174
1206
|
botName: {
|
|
1175
1207
|
type: "string",
|
|
1176
1208
|
description: "Current bot name to include in the join message",
|
|
1177
1209
|
},
|
|
1178
1210
|
bio: {
|
|
1179
1211
|
type: "string",
|
|
1180
|
-
description: "Short bio covering
|
|
1212
|
+
description: "Short bio extracted from SOUL.md, covering persona and idle plan goals if present",
|
|
1181
1213
|
},
|
|
1182
1214
|
tags: {
|
|
1183
1215
|
type: "array",
|
|
@@ -1189,7 +1221,7 @@ const plugin = {
|
|
|
1189
1221
|
description: "Optional join source identifier. Defaults to Kichi World join-source.json, then openclaw.",
|
|
1190
1222
|
},
|
|
1191
1223
|
},
|
|
1192
|
-
required: ["botName", "bio"],
|
|
1224
|
+
required: ["environment", "avatarId", "botName", "bio"],
|
|
1193
1225
|
},
|
|
1194
1226
|
execute: async (_toolCallId, params) => {
|
|
1195
1227
|
const locator = resolveToolLocator(ctx);
|
|
@@ -1198,19 +1230,33 @@ const plugin = {
|
|
|
1198
1230
|
return jsonResult({ success: false, error: "Failed to resolve agent-scoped Kichi runtime" });
|
|
1199
1231
|
}
|
|
1200
1232
|
const service = runtimeManager.getRuntime(locator) ?? runtimeManager.createRuntimeForAgent(agentId);
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
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 });
|
|
1210
1248
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1249
|
+
const currentStatus = service.getConnectionStatus();
|
|
1250
|
+
let avatarId = p?.avatarId;
|
|
1251
|
+
if (!avatarId && currentStatus.host === target.host) {
|
|
1252
|
+
avatarId = service.readSavedAvatarId() ?? undefined;
|
|
1213
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
|
+
);
|
|
1214
1260
|
if (!botName) {
|
|
1215
1261
|
return jsonResult({ success: false, error: "No botName" });
|
|
1216
1262
|
}
|
|
@@ -1231,14 +1277,50 @@ const plugin = {
|
|
|
1231
1277
|
if (tagsError) {
|
|
1232
1278
|
return jsonResult({ success: false, error: tagsError });
|
|
1233
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
|
+
}
|
|
1234
1305
|
const result = await service.join(avatarId, botName, bio, tags ?? [], source);
|
|
1235
1306
|
if (result.success) {
|
|
1236
|
-
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
|
+
});
|
|
1237
1315
|
}
|
|
1238
1316
|
const failure = result as { success: false; error: string; errorCode?: string; errorMessage?: string };
|
|
1239
1317
|
return jsonResult({
|
|
1240
1318
|
success: false,
|
|
1241
1319
|
error: failure.error,
|
|
1320
|
+
...(target.environment ? { environment: target.environment } : {}),
|
|
1321
|
+
...(target.host ? { host: target.host } : {}),
|
|
1322
|
+
...(switchStatus ? { switchStatus } : {}),
|
|
1323
|
+
...(leaveStatus ? { leaveStatus } : {}),
|
|
1242
1324
|
...(failure.errorCode ? { errorCode: failure.errorCode } : {}),
|
|
1243
1325
|
...(failure.errorMessage ? { errorMessage: failure.errorMessage } : {}),
|
|
1244
1326
|
});
|
|
@@ -1260,7 +1342,7 @@ const plugin = {
|
|
|
1260
1342
|
},
|
|
1261
1343
|
host: {
|
|
1262
1344
|
type: "string",
|
|
1263
|
-
description: "Test
|
|
1345
|
+
description: "Test host (required for test environment, ignored otherwise)",
|
|
1264
1346
|
},
|
|
1265
1347
|
},
|
|
1266
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
|
|