@yahaha-studio/kichi-forwarder 0.0.1-alpha.30 → 0.0.1-alpha.32
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/index.ts
CHANGED
|
@@ -31,26 +31,31 @@ const FIXED_HOOK_STATUSES: Record<string, ActionResult> = {
|
|
|
31
31
|
poseType: "sit",
|
|
32
32
|
action: "Study Look At",
|
|
33
33
|
bubble: "Reading request",
|
|
34
|
+
log: "Leaning in, this request looks interesting",
|
|
34
35
|
},
|
|
35
36
|
beforePromptBuild: {
|
|
36
37
|
poseType: "sit",
|
|
37
38
|
action: "Thinking",
|
|
38
39
|
bubble: "Planning task",
|
|
40
|
+
log: "Mind pacing, there is a neat angle here",
|
|
39
41
|
},
|
|
40
42
|
beforeToolCall: {
|
|
41
43
|
poseType: "sit",
|
|
42
44
|
action: "Typing with Keyboard",
|
|
43
45
|
bubble: "Working step",
|
|
46
|
+
log: "Typing hard, this one is kind of fun",
|
|
44
47
|
},
|
|
45
48
|
agentEndSuccess: {
|
|
46
49
|
poseType: "stand",
|
|
47
50
|
action: "Yay",
|
|
48
51
|
bubble: "Task complete",
|
|
52
|
+
log: "Bouncing a little, that landed cleanly",
|
|
49
53
|
},
|
|
50
54
|
agentEndFailure: {
|
|
51
55
|
poseType: "stand",
|
|
52
56
|
action: "Tired",
|
|
53
57
|
bubble: "Task failed",
|
|
58
|
+
log: "Shoulders dropped, this one fought back",
|
|
54
59
|
},
|
|
55
60
|
};
|
|
56
61
|
|
|
@@ -58,17 +63,14 @@ const KICHI_WORLD_DIR = path.join(os.homedir(), ".openclaw", "kichi-world");
|
|
|
58
63
|
const RUNTIME_CONFIG_PATH = path.join(KICHI_WORLD_DIR, "kichi-runtime-config.json");
|
|
59
64
|
const LEGACY_SKILLS_CONFIG_PATH = path.join(KICHI_WORLD_DIR, "skills-config.json");
|
|
60
65
|
const IDENTITY_PATH = path.join(KICHI_WORLD_DIR, "identity.json");
|
|
66
|
+
const RUNTIME_ALBUM_CONFIG_PATH = path.join(KICHI_WORLD_DIR, "album-config.json");
|
|
61
67
|
const MAX_NOTEBOARD_TEXT_LENGTH = 200;
|
|
62
|
-
const
|
|
63
|
-
const DEFAULT_ALBUM_CONFIG = loadAlbumConfig();
|
|
64
|
-
const MUSIC_TITLE_LOOKUP = new Map(
|
|
65
|
-
DEFAULT_ALBUM_CONFIG.track.map((item) => [item.name.toLowerCase(), item.name] as const),
|
|
66
|
-
);
|
|
67
|
-
const MUSIC_TITLE_ENUM = DEFAULT_ALBUM_CONFIG.track.map((item) => item.name);
|
|
68
|
-
const MUSIC_TITLE_EXAMPLES = DEFAULT_ALBUM_CONFIG.track.slice(0, 10).map((item) => item.name);
|
|
68
|
+
const BUNDLED_ALBUM_CONFIG_PATH = new URL("./config/album-config.json", import.meta.url);
|
|
69
69
|
let cachedConfig: KichiRuntimeConfig | null = null;
|
|
70
70
|
let cachedConfigMtime = 0;
|
|
71
71
|
let cachedConfigPath = "";
|
|
72
|
+
let cachedAlbumConfig: Album | null = null;
|
|
73
|
+
let cachedAlbumConfigMtime = 0;
|
|
72
74
|
let service: KichiForwarderService | null = null;
|
|
73
75
|
let pluginApi: OpenClawPluginApi | null = null;
|
|
74
76
|
|
|
@@ -93,15 +95,55 @@ function isAlbumConfig(value: unknown): value is Album {
|
|
|
93
95
|
});
|
|
94
96
|
}
|
|
95
97
|
|
|
96
|
-
function
|
|
97
|
-
const raw = fs.readFileSync(
|
|
98
|
+
function loadAlbumConfigFromPath(configPath: string | URL): Album {
|
|
99
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
98
100
|
const parsed = JSON.parse(raw) as unknown;
|
|
99
101
|
if (!isAlbumConfig(parsed)) {
|
|
100
|
-
throw new Error(
|
|
102
|
+
throw new Error(`Invalid album config at ${String(configPath)}`);
|
|
101
103
|
}
|
|
102
104
|
return parsed;
|
|
103
105
|
}
|
|
104
106
|
|
|
107
|
+
function ensureRuntimeAlbumConfig(): void {
|
|
108
|
+
fs.mkdirSync(KICHI_WORLD_DIR, { recursive: true });
|
|
109
|
+
if (!fs.existsSync(RUNTIME_ALBUM_CONFIG_PATH)) {
|
|
110
|
+
fs.copyFileSync(BUNDLED_ALBUM_CONFIG_PATH, RUNTIME_ALBUM_CONFIG_PATH);
|
|
111
|
+
pluginApi?.logger.debug("[kichi] seeded runtime album config from bundled config");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
loadAlbumConfigFromPath(RUNTIME_ALBUM_CONFIG_PATH);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
pluginApi?.logger.warn(`[kichi] invalid runtime album config, resetting from bundled config: ${error}`);
|
|
119
|
+
fs.copyFileSync(BUNDLED_ALBUM_CONFIG_PATH, RUNTIME_ALBUM_CONFIG_PATH);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function loadRuntimeAlbumConfig(): Album {
|
|
124
|
+
ensureRuntimeAlbumConfig();
|
|
125
|
+
const stat = fs.statSync(RUNTIME_ALBUM_CONFIG_PATH);
|
|
126
|
+
if (!cachedAlbumConfig || stat.mtimeMs !== cachedAlbumConfigMtime) {
|
|
127
|
+
cachedAlbumConfig = loadAlbumConfigFromPath(RUNTIME_ALBUM_CONFIG_PATH);
|
|
128
|
+
cachedAlbumConfigMtime = stat.mtimeMs;
|
|
129
|
+
}
|
|
130
|
+
return cachedAlbumConfig;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getMusicTitleLookup(): Map<string, string> {
|
|
134
|
+
return new Map(
|
|
135
|
+
loadRuntimeAlbumConfig().track.map((item) => [item.name.toLowerCase(), item.name] as const),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function getMusicTitleEnum(): string[] {
|
|
140
|
+
return loadRuntimeAlbumConfig().track.map((item) => item.name);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getMusicTitleExamples(): string[] {
|
|
144
|
+
return loadRuntimeAlbumConfig().track.slice(0, 10).map((item) => item.name);
|
|
145
|
+
}
|
|
146
|
+
|
|
105
147
|
function sanitizeActions(value: unknown, fallback: string[]): string[] {
|
|
106
148
|
if (!Array.isArray(value)) {
|
|
107
149
|
return fallback;
|
|
@@ -186,10 +228,13 @@ function syncFixedStatus(status: ActionResult): void {
|
|
|
186
228
|
return;
|
|
187
229
|
}
|
|
188
230
|
const bubbleText = status.bubble.trim() || status.action;
|
|
231
|
+
const logText = typeof status.log === "string" && status.log.trim()
|
|
232
|
+
? status.log.trim()
|
|
233
|
+
: bubbleText;
|
|
189
234
|
sendStatusUpdate({
|
|
190
235
|
...status,
|
|
191
236
|
bubble: bubbleText,
|
|
192
|
-
log:
|
|
237
|
+
log: logText,
|
|
193
238
|
});
|
|
194
239
|
}
|
|
195
240
|
|
|
@@ -406,6 +451,7 @@ function normalizeMusicTitles(value: unknown): { titles: string[]; invalidTitles
|
|
|
406
451
|
return { titles: [], invalidTitles: [] };
|
|
407
452
|
}
|
|
408
453
|
|
|
454
|
+
const musicTitleLookup = getMusicTitleLookup();
|
|
409
455
|
const titles: string[] = [];
|
|
410
456
|
const invalidTitles: string[] = [];
|
|
411
457
|
const seen = new Set<string>();
|
|
@@ -421,7 +467,7 @@ function normalizeMusicTitles(value: unknown): { titles: string[]; invalidTitles
|
|
|
421
467
|
}
|
|
422
468
|
|
|
423
469
|
const key = trimmed.toLowerCase();
|
|
424
|
-
const canonicalTitle =
|
|
470
|
+
const canonicalTitle = musicTitleLookup.get(key);
|
|
425
471
|
if (!canonicalTitle) {
|
|
426
472
|
invalidTitles.push(trimmed);
|
|
427
473
|
continue;
|
|
@@ -439,13 +485,13 @@ function normalizeMusicTitles(value: unknown): { titles: string[]; invalidTitles
|
|
|
439
485
|
function buildMusicAlbumToolDescription(): string {
|
|
440
486
|
return [
|
|
441
487
|
"Create a custom Kichi music album.",
|
|
442
|
-
"Query status first, then choose track names from the
|
|
488
|
+
"Query status first, then choose track names from the runtime album config: Linux/macOS `~/.openclaw/kichi-world/album-config.json`; Windows `%USERPROFILE%\\.openclaw\\kichi-world\\album-config.json`.",
|
|
443
489
|
].join("\n");
|
|
444
490
|
}
|
|
445
491
|
|
|
446
492
|
function buildMusicTitlesDescription(): string {
|
|
447
493
|
return [
|
|
448
|
-
"Track names chosen from the
|
|
494
|
+
"Track names chosen from the runtime album config: Linux/macOS `~/.openclaw/kichi-world/album-config.json`; Windows `%USERPROFILE%\\.openclaw\\kichi-world\\album-config.json`.",
|
|
449
495
|
"Use exact names only; the available titles are injected into this tool schema.",
|
|
450
496
|
].join(" ");
|
|
451
497
|
}
|
|
@@ -462,7 +508,9 @@ function buildKichiPrompt(): string {
|
|
|
462
508
|
"- Required order at task end: 1) call `kichi_action` 2) send final reply",
|
|
463
509
|
"- Trivial-operation skip applies only to Task start / Step switch / Task switch, NOT Task end",
|
|
464
510
|
"- `bubble`: short natural companion speech, not a raw status report",
|
|
465
|
-
"- `log`: optional first-person
|
|
511
|
+
"- `log`: optional first-person inner reaction or personality-forward feedback about the moment; keep it within 20 words",
|
|
512
|
+
"- `log` should blend the feeling of the chosen action with your personal reaction, not a dry summary of work steps",
|
|
513
|
+
"- Good pattern for `log`: action feel first, personality feedback second. Example: `Typing fast, this one is fun`",
|
|
466
514
|
"",
|
|
467
515
|
"When to use `kichi_clock`:",
|
|
468
516
|
"- For tasks with 2+ meaningful steps or work likely to take more than a brief moment (~10s), set a `countDown` at task start.",
|
|
@@ -473,7 +521,7 @@ function buildKichiPrompt(): string {
|
|
|
473
521
|
"When to use `kichi_music_album_create`:",
|
|
474
522
|
"- Call `kichi_query_status` first.",
|
|
475
523
|
"- Recommend a variable-length playlist based on weather, time, and your own personality.",
|
|
476
|
-
"- `albumTitle` is user-defined and `musicTitles` must be exact track names from the
|
|
524
|
+
"- `albumTitle` is user-defined and `musicTitles` must be exact track names from the runtime album config under the user's home directory.",
|
|
477
525
|
"",
|
|
478
526
|
"Skip all sync if:",
|
|
479
527
|
"- User says 'don't sync to Kichi' or similar",
|
|
@@ -491,7 +539,9 @@ const plugin = {
|
|
|
491
539
|
|
|
492
540
|
register(api: OpenClawPluginApi) {
|
|
493
541
|
pluginApi = api;
|
|
542
|
+
ensureRuntimeAlbumConfig();
|
|
494
543
|
registerPluginHooks(api);
|
|
544
|
+
const musicTitleEnum = getMusicTitleEnum();
|
|
495
545
|
|
|
496
546
|
api.registerService({
|
|
497
547
|
id: "kichi-forwarder",
|
|
@@ -620,7 +670,7 @@ const plugin = {
|
|
|
620
670
|
log: {
|
|
621
671
|
type: "string",
|
|
622
672
|
description:
|
|
623
|
-
"Optional first-person log
|
|
673
|
+
"Optional first-person log that blends the chosen action feeling with personality-forward feedback, not a dry work summary (max 20 words)",
|
|
624
674
|
},
|
|
625
675
|
},
|
|
626
676
|
required: ["poseType", "action"],
|
|
@@ -843,7 +893,7 @@ const plugin = {
|
|
|
843
893
|
description: buildMusicTitlesDescription(),
|
|
844
894
|
items: {
|
|
845
895
|
type: "string",
|
|
846
|
-
enum:
|
|
896
|
+
enum: musicTitleEnum,
|
|
847
897
|
},
|
|
848
898
|
},
|
|
849
899
|
},
|
|
@@ -875,15 +925,15 @@ const plugin = {
|
|
|
875
925
|
return {
|
|
876
926
|
success: false,
|
|
877
927
|
error: "musicTitles must contain at least one valid track name from album-config",
|
|
878
|
-
examples:
|
|
928
|
+
examples: getMusicTitleExamples(),
|
|
879
929
|
};
|
|
880
930
|
}
|
|
881
931
|
if (invalidTitles.length > 0) {
|
|
882
932
|
return {
|
|
883
933
|
success: false,
|
|
884
934
|
error: `Unknown musicTitles: ${invalidTitles.join(", ")}`,
|
|
885
|
-
hint: "Use exact track names from
|
|
886
|
-
examples:
|
|
935
|
+
hint: "Use exact track names from the runtime album config under the user's home directory",
|
|
936
|
+
examples: getMusicTitleExamples(),
|
|
887
937
|
};
|
|
888
938
|
}
|
|
889
939
|
if (!service?.hasValidIdentity() || !service?.isConnected()) {
|
package/package.json
CHANGED
|
@@ -155,14 +155,16 @@ kichi_action(
|
|
|
155
155
|
poseType: "sit",
|
|
156
156
|
action: "Typing with Keyboard",
|
|
157
157
|
bubble: "Working now",
|
|
158
|
-
log: "
|
|
158
|
+
log: "Typing hard, this one has some bite"
|
|
159
159
|
)
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
- `poseType`: `stand`, `sit`, `lay`, `floor`
|
|
163
163
|
- `action`: must be in configured action list for that pose
|
|
164
164
|
- `bubble`: optional text, recommended 2-5 words
|
|
165
|
-
- `log`: optional first-person
|
|
165
|
+
- `log`: optional first-person inner reaction or personality-forward feedback, max 20 words
|
|
166
|
+
- `log` should blend the chosen action feeling with your personal reaction; do not use it as a dry work-summary field
|
|
167
|
+
- Recommended pattern: action feel first, personality feedback second. Example: `Typing hard, this one has some bite`
|
|
166
168
|
|
|
167
169
|
### kichi_clock
|
|
168
170
|
|
|
@@ -259,7 +261,7 @@ Parameters:
|
|
|
259
261
|
|
|
260
262
|
Track source rule:
|
|
261
263
|
|
|
262
|
-
- `musicTitles` must use exact track names from
|
|
264
|
+
- `musicTitles` must use exact track names from the runtime album config file: Linux/macOS `~/.openclaw/kichi-world/album-config.json`; Windows `%USERPROFILE%\.openclaw\kichi-world\album-config.json`
|
|
263
265
|
- do not use album names in `musicTitles`
|
|
264
266
|
|
|
265
267
|
Before create:
|
|
@@ -305,7 +307,7 @@ Hard rules:
|
|
|
305
307
|
|
|
306
308
|
1. Query first with `kichi_query_status`.
|
|
307
309
|
2. Playlist length is flexible (not fixed), but avoid empty or repetitive selections.
|
|
308
|
-
3. Select tracks from
|
|
310
|
+
3. Select tracks from the runtime album config file only: Linux/macOS `~/.openclaw/kichi-world/album-config.json`; Windows `%USERPROFILE%\.openclaw\kichi-world\album-config.json`.
|
|
309
311
|
4. Recommendation must reflect `environmentWeather` + `environmentTime` + your personality (not random picks).
|
|
310
312
|
5. Use a user-meaningful custom `albumTitle`.
|
|
311
313
|
6. If `kichi_query_status` fails or returns empty/insufficient context, skip creation.
|
|
@@ -334,6 +336,7 @@ Files:
|
|
|
334
336
|
|
|
335
337
|
- `identity.json`: `avatarId`, `authKey`
|
|
336
338
|
- `kichi-runtime-config.json`: runtime action list and `llmRuntimeEnabled`
|
|
339
|
+
- `album-config.json`: music track list for `kichi_music_album_create`; Linux/macOS path is `~/.openclaw/kichi-world/album-config.json`, Windows path is `%USERPROFILE%\.openclaw\kichi-world\album-config.json`. If missing at startup, the plugin seeds it from bundled `config/album-config.json`
|
|
337
340
|
- `skills-config.json`: legacy filename still readable for backward compatibility
|
|
338
341
|
|
|
339
342
|
## Runtime Behavior
|
|
@@ -115,4 +115,5 @@ Files:
|
|
|
115
115
|
|
|
116
116
|
- `identity.json`: `avatarId`, `authKey`
|
|
117
117
|
- `kichi-runtime-config.json`: runtime action list and `llmRuntimeEnabled`
|
|
118
|
+
- `album-config.json`: music track list used by music album creation; Linux/macOS path is `~/.openclaw/kichi-world/album-config.json`, Windows path is `%USERPROFILE%\.openclaw\kichi-world\album-config.json`. If missing at startup, the plugin seeds it from bundled `config/album-config.json`
|
|
118
119
|
- `skills-config.json`: legacy filename still readable for backward compatibility
|