@yahaha-studio/kichi-forwarder 0.0.1-alpha.33 → 0.0.1-alpha.35

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;
244
256
  }
245
- return;
257
+ service.sendHookNotify("message_received", pickRandomAction(MESSAGE_RECEIVED_BUBBLES));
258
+ }
259
+
260
+ function handleMessageSentHook(): void {
261
+ if (!service?.hasValidIdentity() || !service?.isConnected()) {
262
+ return;
263
+ }
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;
@@ -501,16 +524,13 @@ function buildKichiPrompt(): string {
501
524
  "Kichi App status sync is available via `kichi_action` and `kichi_clock`.",
502
525
  "",
503
526
  "kichi_action timing (all REQUIRED unless skipping):",
504
- "1. Task start: call BEFORE your first tool call. Pick action matching upcoming work.",
527
+ "1. Task start: call BEFORE your first tool call OR before composing a multi-paragraph reply. Pick action matching upcoming work.",
505
528
  "2. Step switch: call when work nature changes (search→analyze→write). Especially before web_search/web_fetch/read.",
506
529
  "3. Task end: call BEFORE final reply. Never skip. Order: kichi_action → reply.",
507
- "Skip start/switch only if entire turn is a short reply with zero tool calls.",
508
- "bubble: 2-5 word companion speech. log: first-person reaction, max 20 words.",
530
+ "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.",
509
531
  "",
510
532
  "kichi_clock: set countDown for tasks with 2+ steps or >10s work. Skip for quick one-shots.",
511
533
  "",
512
- "kichi_music_album_create: call kichi_query_status first. Tracks from ~/kichi-world/album-config.json.",
513
- "",
514
534
  "Skip all sync if: user opts out, task is kichi config/test, or user requests specific pose.",
515
535
  ].join("\n");
516
536
  }
@@ -653,7 +673,7 @@ const plugin = {
653
673
  log: {
654
674
  type: "string",
655
675
  description:
656
- "Optional first-person log that blends the chosen action feeling with personality-forward feedback, not a dry work summary (max 20 words)",
676
+ "Vivid first-person status under 15 words, no questions. Blend current action with inner thoughts or sensory details as a real companion.",
657
677
  },
658
678
  },
659
679
  required: ["poseType", "action"],
@@ -1,10 +1,10 @@
1
- {
2
- "id": "kichi-forwarder",
3
- "name": "Kichi Forwarder",
1
+ {
2
+ "id": "kichi-forwarder",
3
+ "name": "Kichi Forwarder",
4
4
  "description": "Sync agent lifecycle/status to Kichi world and provide Kichi timer/noteboard tools",
5
- "version": "0.2.1",
6
- "author": "OpenClaw",
7
- "skills": ["./skills/kichi-forwarder"],
5
+ "version": "0.0.1",
6
+ "author": "OpenClaw",
7
+ "skills": ["./skills/kichi-forwarder"],
8
8
  "configSchema": {
9
9
  "type": "object",
10
10
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yahaha-studio/kichi-forwarder",
3
- "version": "0.0.1-alpha.33",
3
+ "version": "0.0.1-alpha.35",
4
4
  "description": "Forward OpenClaw agent events to external WebSocket server for visualization",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -162,9 +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`: 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`
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.`
168
167
 
169
168
  ### kichi_clock
170
169
 
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";