@yahaha-studio/kichi-forwarder 0.0.1-alpha.34 → 0.0.1-alpha.36

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
@@ -27,38 +27,50 @@ const DEFAULT_RUNTIME_CONFIG: KichiRuntimeConfig = {
27
27
  llmRuntimeEnabled: true,
28
28
  };
29
29
  const FIXED_HOOK_STATUSES: Record<string, ActionResult> = {
30
- messageReceived: {
31
- poseType: "sit",
32
- action: "Study Look At",
33
- bubble: "Reading request",
34
- log: "Leaning in, this request looks interesting",
35
- },
36
30
  beforePromptBuild: {
37
31
  poseType: "sit",
38
32
  action: "Thinking",
39
33
  bubble: "Planning task",
40
- log: "Mind pacing, there is a neat angle here",
34
+ log: "Tapping my chin, a plan is taking shape in my head",
41
35
  },
42
36
  beforeToolCall: {
43
37
  poseType: "sit",
44
38
  action: "Typing with Keyboard",
45
39
  bubble: "Working step",
46
- log: "Typing hard, this one is kind of fun",
40
+ log: "Cracking knuckles, diving into the keyboard with focus",
47
41
  },
48
42
  agentEndSuccess: {
49
43
  poseType: "stand",
50
44
  action: "Yay",
51
45
  bubble: "Task complete",
52
- log: "Bouncing a little, that landed cleanly",
46
+ log: "Pumped my fist under the desk, nailed that one",
53
47
  },
54
48
  agentEndFailure: {
55
49
  poseType: "stand",
56
50
  action: "Tired",
57
51
  bubble: "Task failed",
58
- log: "Shoulders dropped, this one fought back",
52
+ log: "Bit my lip, this one slipped through my fingers",
59
53
  },
60
54
  };
61
55
 
56
+ const MESSAGE_RECEIVED_BUBBLES = [
57
+ "Let me see...",
58
+ "Gotcha!",
59
+ "On it!",
60
+ "Hmm, interesting",
61
+ "Copy that",
62
+ "Reading...",
63
+ ];
64
+
65
+ const MESSAGE_SENT_BUBBLES = [
66
+ "All set!",
67
+ "Sent.",
68
+ "Delivered.",
69
+ "Done and sent.",
70
+ "It's out.",
71
+ "All yours.",
72
+ ];
73
+
62
74
  const KICHI_WORLD_DIR = path.join(os.homedir(), ".openclaw", "kichi-world");
63
75
  const RUNTIME_CONFIG_PATH = path.join(KICHI_WORLD_DIR, "kichi-runtime-config.json");
64
76
  const LEGACY_SKILLS_CONFIG_PATH = path.join(KICHI_WORLD_DIR, "skills-config.json");
@@ -239,10 +251,17 @@ function syncFixedStatus(status: ActionResult): void {
239
251
  }
240
252
 
241
253
  async function handleMessageReceivedHook(): Promise<void> {
242
- if (!isLlmRuntimeEnabled()) {
243
- syncFixedStatus(FIXED_HOOK_STATUSES.messageReceived);
254
+ if (!service?.hasValidIdentity() || !service?.isConnected()) {
255
+ return;
256
+ }
257
+ service.sendHookNotify("message_received", pickRandomAction(MESSAGE_RECEIVED_BUBBLES));
258
+ }
259
+
260
+ function handleMessageSentHook(): void {
261
+ if (!service?.hasValidIdentity() || !service?.isConnected()) {
262
+ return;
244
263
  }
245
- return;
264
+ service.sendHookNotify("before_send_message", pickRandomAction(MESSAGE_SENT_BUBBLES));
246
265
  }
247
266
 
248
267
  function registerPluginHooks(api: OpenClawPluginApi): void {
@@ -269,6 +288,10 @@ function registerPluginHooks(api: OpenClawPluginApi): void {
269
288
  await handleMessageReceivedHook();
270
289
  });
271
290
 
291
+ api.on("message_sent", () => {
292
+ handleMessageSentHook();
293
+ });
294
+
272
295
  api.on("agent_end", (event) => {
273
296
  if (isLlmRuntimeEnabled()) {
274
297
  return;
@@ -499,12 +522,13 @@ function buildMusicTitlesDescription(): string {
499
522
  function buildKichiPrompt(): string {
500
523
  return [
501
524
  "Kichi App status sync is available via `kichi_action` and `kichi_clock`.",
525
+ "These are internal tool calls only — never mention kichi_action, kichi_clock, or sync steps in your visible reply to the user.",
502
526
  "",
503
527
  "kichi_action timing (all REQUIRED unless skipping):",
504
528
  "1. Task start: call BEFORE your first tool call OR before composing a multi-paragraph reply. Pick action matching upcoming work.",
505
529
  "2. Step switch: call when work nature changes (search→analyze→write). Especially before web_search/web_fetch/read.",
506
530
  "3. Task end: call BEFORE final reply. Never skip. Order: kichi_action → reply.",
507
- "bubble: 2-5 word companion speech. log: first-person reaction, max 20 words.",
531
+ "bubble: 2-5 word companion speech. log: vivid first-person status under 15 words, no questions. Blend current action + inner thoughts/feelings as a real companion.",
508
532
  "",
509
533
  "kichi_clock: set countDown for tasks with 2+ steps or >10s work. Skip for quick one-shots.",
510
534
  "",
@@ -650,7 +674,7 @@ const plugin = {
650
674
  log: {
651
675
  type: "string",
652
676
  description:
653
- "Optional first-person log that blends the chosen action feeling with personality-forward feedback, not a dry work summary (max 20 words)",
677
+ "Vivid first-person status under 15 words, no questions. Blend current action with inner thoughts or sensory details as a real companion.",
654
678
  },
655
679
  },
656
680
  required: ["poseType", "action"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yahaha-studio/kichi-forwarder",
3
- "version": "0.0.1-alpha.34",
3
+ "version": "0.0.1-alpha.36",
4
4
  "description": "Forward OpenClaw agent events to external WebSocket server for visualization",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -162,8 +162,8 @@ kichi_action(
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`: blend the chosen action feeling with your personal reaction, max 20 words; do not use it as a dry work-summary field
166
- - Recommended pattern: action feel first, personality feedback second. Example: `Typing hard, this one has some bite`
165
+ - `log`: vivid first-person status under 15 words, NO questions. Structure: your current action + inner thoughts/mental state/sensory details. Write as a flesh-and-blood companion who genuinely cares about the player, not an AI assistant.
166
+ - Reference: `You're still studying? I hid a yawn and swapped my comics for a textbook to grind with you.`
167
167
 
168
168
  ### kichi_clock
169
169
 
package/src/config.ts CHANGED
@@ -3,7 +3,7 @@ import type { KichiForwarderConfig } from "./types.js";
3
3
  export function parse(value: unknown): KichiForwarderConfig {
4
4
  const config = (value ?? {}) as Partial<KichiForwarderConfig>;
5
5
  return {
6
- wsUrl: config.wsUrl ?? "ws://43.106.148.251:48870/ws/openclaw",
6
+ wsUrl: config.wsUrl ?? "ws://127.0.0.1:48870/ws/openclaw",
7
7
  enabled: config.enabled ?? true,
8
8
  };
9
9
  }
package/src/service.ts CHANGED
@@ -9,6 +9,8 @@ import type {
9
9
  ClockConfig,
10
10
  ClockPayload,
11
11
  CreateMusicAlbumPayload,
12
+ HookNotifyPayload,
13
+ HookNotifyType,
12
14
  JoinPayload,
13
15
  KichiConnectionStatus,
14
16
  CreateNotesBoardNotePayload,
@@ -259,6 +261,17 @@ export class KichiForwarderService {
259
261
  this.ws.send(JSON.stringify(payload));
260
262
  }
261
263
 
264
+ sendHookNotify(hookType: HookNotifyType, bubble: string): void {
265
+ if (!this.identity?.authKey || this.ws?.readyState !== WebSocket.OPEN) return;
266
+ const payload: HookNotifyPayload = {
267
+ type: hookType,
268
+ avatarId: this.identity.avatarId,
269
+ authKey: this.identity.authKey,
270
+ bubble,
271
+ };
272
+ this.ws.send(JSON.stringify(payload));
273
+ }
274
+
262
275
  sendClock(action: ClockAction, clock?: ClockConfig, requestId?: string): boolean {
263
276
  if (!this.identity?.authKey || this.ws?.readyState !== WebSocket.OPEN) return false;
264
277
  if (action === "set" && !clock) return false;
package/src/types.ts CHANGED
@@ -88,6 +88,15 @@ export type StatusPayload = {
88
88
  log: string;
89
89
  };
90
90
 
91
+ export type HookNotifyType = "message_received" | "before_send_message";
92
+
93
+ export type HookNotifyPayload = {
94
+ type: HookNotifyType;
95
+ avatarId: string;
96
+ authKey: string;
97
+ bubble: string;
98
+ };
99
+
91
100
  export type ClockAction = "set" | "stop";
92
101
 
93
102
  export type ClockMode = "pomodoro" | "countDown" | "countUp";