clawmate 1.3.0 → 1.4.1

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.
Files changed (43) hide show
  1. package/electron-builder.yml +1 -1
  2. package/index.js +589 -406
  3. package/main/ai-bridge.js +64 -58
  4. package/main/ai-connector.js +67 -62
  5. package/main/autostart.js +7 -7
  6. package/main/desktop-path.js +4 -4
  7. package/main/file-command-parser.js +77 -41
  8. package/main/file-ops.js +27 -27
  9. package/main/index.js +18 -16
  10. package/main/ipc-handlers.js +27 -24
  11. package/main/manifest.js +2 -2
  12. package/main/platform.js +16 -16
  13. package/main/smart-file-ops.js +64 -64
  14. package/main/store.js +1 -1
  15. package/main/telegram.js +154 -121
  16. package/main/tray.js +226 -71
  17. package/main/updater.js +13 -13
  18. package/openclaw.plugin.json +1 -1
  19. package/package.json +3 -4
  20. package/preload/preload.js +18 -18
  21. package/renderer/css/effects.css +6 -6
  22. package/renderer/css/pet.css +8 -8
  23. package/renderer/css/speech.css +5 -5
  24. package/renderer/first-run.html +15 -15
  25. package/renderer/index.html +4 -4
  26. package/renderer/js/ai-controller.js +99 -88
  27. package/renderer/js/app.js +26 -23
  28. package/renderer/js/browser-watcher.js +32 -32
  29. package/renderer/js/character.js +33 -33
  30. package/renderer/js/interactions.js +57 -14
  31. package/renderer/js/memory.js +144 -37
  32. package/renderer/js/metrics.js +141 -141
  33. package/renderer/js/mode-manager.js +59 -15
  34. package/renderer/js/pet-engine.js +236 -236
  35. package/renderer/js/speech.js +19 -19
  36. package/renderer/js/state-machine.js +23 -23
  37. package/renderer/js/time-aware.js +15 -15
  38. package/renderer/launcher.html +9 -9
  39. package/shared/constants.js +11 -11
  40. package/shared/messages.js +130 -130
  41. package/shared/personalities.js +72 -37
  42. package/skills/launch-pet/index.js +13 -13
  43. package/skills/launch-pet/skill.json +12 -23
@@ -1,9 +1,9 @@
1
1
  /**
2
- * 사용자 상호작용 기억 + 진화 시스템
2
+ * User interaction memory + evolution system
3
3
  *
4
- * - 클릭 횟수, 일수, 마일스톤 추적
5
- * - 진화 단계 관리: 항상 긍정적/귀여운 방향으로만 진화
6
- * - 무서운/끔찍한 모습으로는 절대 변하지 않음
4
+ * - Tracks click count, days, milestones
5
+ * - Evolution stage management: always evolves in positive/cute directions only
6
+ * - Never transforms into scary/creepy appearances
7
7
  */
8
8
  const Memory = (() => {
9
9
  let data = {
@@ -13,9 +13,23 @@ const Memory = (() => {
13
13
  lastVisitDate: null,
14
14
  milestones: [],
15
15
  evolutionStage: 0,
16
- interactionStreak: 0, // 연속 방문 일수
16
+ interactionStreak: 0, // Consecutive visit days
17
+
18
+ // --- Motion history ---
19
+ motionHistory: [], // Recent 100 state transition records [{state, timestamp, duration}]
20
+ motionStats: {}, // Accumulated time per state {idle: 12345, walking: 6789, ...}
21
+
22
+ // --- User reaction storage ---
23
+ reactionLog: [], // Recent 50 user reactions [{action, reaction, timestamp}]
24
+ favoriteActions: {}, // Positive reaction count per action {excited: 5, walking: 2, ...}
25
+ dislikedActions: {}, // Negative reaction count per action (ignored/abandoned)
17
26
  };
18
27
 
28
+ let lastMotionState = null;
29
+ let lastMotionTime = 0;
30
+ const MAX_MOTION_HISTORY = 100;
31
+ const MAX_REACTION_LOG = 50;
32
+
19
33
  let evolutionStages = null;
20
34
 
21
35
  async function init() {
@@ -26,22 +40,22 @@ const Memory = (() => {
26
40
 
27
41
  evolutionStages = window._evolutionStages;
28
42
 
29
- // 실행
43
+ // First run
30
44
  if (!data.firstRunDate) {
31
45
  data.firstRunDate = new Date().toISOString();
32
46
  await save();
33
47
  }
34
48
 
35
- // 일수 계산
49
+ // Calculate days
36
50
  updateDayCount();
37
51
 
38
- // 마일스톤 체크
52
+ // Check milestones
39
53
  checkMilestones();
40
54
 
41
- // 진화 체크
55
+ // Check evolution
42
56
  checkEvolution();
43
57
 
44
- // 진화 시각 효과 적용
58
+ // Apply evolution visual effects
45
59
  applyEvolutionVisuals();
46
60
  }
47
61
 
@@ -50,7 +64,7 @@ const Memory = (() => {
50
64
  const now = new Date();
51
65
  data.totalDays = Math.floor((now - firstRun) / (1000 * 60 * 60 * 24));
52
66
 
53
- // 연속 방문 체크
67
+ // Check consecutive visits
54
68
  const lastVisit = data.lastVisitDate ? new Date(data.lastVisitDate) : null;
55
69
  const today = now.toDateString();
56
70
  if (lastVisit && lastVisit.toDateString() !== today) {
@@ -89,7 +103,7 @@ const Memory = (() => {
89
103
  data.milestones.push(check.key);
90
104
  const msg = Speech.getMilestoneMessage(check.key);
91
105
  if (msg) {
92
- // 약간의 딜레이 마일스톤 메시지 표시
106
+ // Display milestone message after a slight delay
93
107
  setTimeout(() => {
94
108
  Speech.show(msg);
95
109
  Interactions.spawnStarEffect();
@@ -100,9 +114,9 @@ const Memory = (() => {
100
114
  }
101
115
 
102
116
  /**
103
- * 진화 단계 체크
104
- * 항상 올라가기만 (퇴화 없음)
105
- * 조건: 클릭 횟수 + 함께한 일수 모두 충족
117
+ * Check evolution stage
118
+ * Only goes up (no devolution)
119
+ * Condition: both click count and days together must be met
106
120
  */
107
121
  function checkEvolution() {
108
122
  if (!evolutionStages) return;
@@ -127,22 +141,22 @@ const Memory = (() => {
127
141
  }
128
142
 
129
143
  /**
130
- * 진화 발생 시 이벤트
131
- * - 밝은 플래시 효과 (부드러운 )
132
- * - 반짝이 파티클
133
- * - 축하 메시지
144
+ * Evolution event handler
145
+ * - Bright flash effect (soft light)
146
+ * - Sparkle particles
147
+ * - Congratulatory message
134
148
  */
135
149
  function onEvolution(prevStage, newStage) {
136
150
  const msgs = window._messages;
137
151
  const stageInfo = evolutionStages[newStage];
138
152
 
139
- // 밝은 플래시 (무섭지 않은 부드러운 효과)
153
+ // Bright flash (soft, non-scary effect)
140
154
  const flash = document.createElement('div');
141
155
  flash.className = 'evolve-flash';
142
156
  document.body.appendChild(flash);
143
157
  setTimeout(() => flash.remove(), 600);
144
158
 
145
- // 진화 반짝임 파티클 (밝은 색상만)
159
+ // Evolution sparkle particles (bright colors only)
146
160
  const pos = PetEngine.getPosition();
147
161
  const sparkleColors = ['#FFD700', '#FF69B4', '#87CEEB', '#98FB98', '#DDA0DD'];
148
162
  for (let i = 0; i < 16; i++) {
@@ -155,7 +169,7 @@ const Memory = (() => {
155
169
  setTimeout(() => sparkle.remove(), 800);
156
170
  }
157
171
 
158
- // 진화 이펙트 (따뜻한 )
172
+ // Evolution ring effect (warm colors)
159
173
  const ring = document.createElement('div');
160
174
  ring.className = 'evolve-ring';
161
175
  ring.style.width = '64px';
@@ -166,7 +180,7 @@ const Memory = (() => {
166
180
  document.getElementById('world').appendChild(ring);
167
181
  setTimeout(() => ring.remove(), 1000);
168
182
 
169
- // 축하 메시지
183
+ // Congratulatory message
170
184
  if (msgs && msgs.evolution) {
171
185
  const evolveMsg = msgs.evolution[`stage_${newStage}`];
172
186
  if (evolveMsg) {
@@ -174,13 +188,13 @@ const Memory = (() => {
174
188
  }
175
189
  }
176
190
 
177
- // 시각 효과 업데이트
191
+ // Update visual effects
178
192
  applyEvolutionVisuals();
179
193
  }
180
194
 
181
195
  /**
182
- * 진화 단계에 따른 시각적 변화 적용
183
- * 항상 긍정적: 밝아지고, 반짝이고, 귀여운 악세사리 추가
196
+ * Apply visual changes based on evolution stage
197
+ * Always positive: gets brighter, sparkly, and cute accessories added
184
198
  */
185
199
  function applyEvolutionVisuals() {
186
200
  if (!evolutionStages) return;
@@ -190,17 +204,17 @@ const Memory = (() => {
190
204
  const pet = document.getElementById('pet-container');
191
205
  if (!pet) return;
192
206
 
193
- // 크기 스케일
207
+ // Size scale
194
208
  pet.style.transform = pet.style.transform || '';
195
209
 
196
- // 밝기/채도 진화할수록 밝고 화사해짐
210
+ // Brightness/saturation -- gets brighter and more vibrant with evolution
197
211
  const { brightness, saturation } = stage.colorMod;
198
212
  const canvas = pet.querySelector('canvas');
199
213
  if (canvas) {
200
214
  canvas.style.filter = `brightness(${brightness}) saturate(${saturation})`;
201
215
  }
202
216
 
203
- // 악세사리 제거 재적용
217
+ // Remove accessories then reapply
204
218
  pet.querySelectorAll('.accessory').forEach(a => a.remove());
205
219
 
206
220
  for (const acc of stage.accessories) {
@@ -209,8 +223,8 @@ const Memory = (() => {
209
223
  }
210
224
 
211
225
  /**
212
- * 귀여운 악세사리 추가
213
- * 모든 악세사리는 밝고 귀여운 요소만
226
+ * Add cute accessories
227
+ * All accessories are bright and cute elements only
214
228
  */
215
229
  function addAccessory(container, type) {
216
230
  const acc = document.createElement('div');
@@ -221,7 +235,7 @@ const Memory = (() => {
221
235
 
222
236
  switch (type) {
223
237
  case 'blush':
224
- // 볼에 핑크 동그라미
238
+ // Pink circles on both cheeks
225
239
  acc.style.width = '8px';
226
240
  acc.style.height = '6px';
227
241
  acc.style.borderRadius = '50%';
@@ -229,14 +243,14 @@ const Memory = (() => {
229
243
  acc.style.left = '12px';
230
244
  acc.style.top = '38px';
231
245
  container.appendChild(acc);
232
- // 오른쪽
246
+ // Right cheek
233
247
  const blush2 = acc.cloneNode();
234
248
  blush2.style.left = '44px';
235
249
  container.appendChild(blush2);
236
250
  return;
237
251
 
238
252
  case 'sparkle_eyes':
239
- // 눈에 반짝임 (흰색 작은 )
253
+ // Eye sparkles (small white dots)
240
254
  acc.style.width = '3px';
241
255
  acc.style.height = '3px';
242
256
  acc.style.borderRadius = '50%';
@@ -286,14 +300,14 @@ const Memory = (() => {
286
300
  break;
287
301
 
288
302
  case 'wings':
289
- // 작은 천사 날개 (왼쪽)
303
+ // Small angel wings (left)
290
304
  acc.textContent = '\u{1FABD}';
291
305
  acc.style.fontSize = '10px';
292
306
  acc.style.left = '-6px';
293
307
  acc.style.top = '20px';
294
308
  acc.style.opacity = '0.7';
295
309
  container.appendChild(acc);
296
- // 오른쪽 날개
310
+ // Right wing
297
311
  const wing2 = acc.cloneNode(true);
298
312
  wing2.style.left = '58px';
299
313
  wing2.style.transform = 'scaleX(-1)';
@@ -304,6 +318,95 @@ const Memory = (() => {
304
318
  container.appendChild(acc);
305
319
  }
306
320
 
321
+ // --- Motion history recording ---
322
+
323
+ /**
324
+ * Record state transition
325
+ * Called when state changes in StateMachine
326
+ */
327
+ function recordMotion(newState) {
328
+ const now = Date.now();
329
+
330
+ // Calculate previous state duration -> accumulate statistics
331
+ if (lastMotionState && lastMotionTime > 0) {
332
+ const duration = now - lastMotionTime;
333
+ if (!data.motionStats[lastMotionState]) data.motionStats[lastMotionState] = 0;
334
+ data.motionStats[lastMotionState] += duration;
335
+ }
336
+
337
+ // Add to history
338
+ data.motionHistory.push({
339
+ state: newState,
340
+ timestamp: now,
341
+ from: lastMotionState || 'init',
342
+ });
343
+
344
+ // Remove old entries when exceeding max size
345
+ if (data.motionHistory.length > MAX_MOTION_HISTORY) {
346
+ data.motionHistory = data.motionHistory.slice(-MAX_MOTION_HISTORY);
347
+ }
348
+
349
+ lastMotionState = newState;
350
+ lastMotionTime = now;
351
+
352
+ // Auto-save every 10 transitions
353
+ if (data.motionHistory.length % 10 === 0) save();
354
+ }
355
+
356
+ /**
357
+ * Record user reaction
358
+ * When user shows reactions like click/drag during a specific action
359
+ *
360
+ * @param {string} action - Action the pet was performing
361
+ * @param {string} reaction - 'click' | 'drag' | 'cursor_near' | 'triple_click' | 'double_click'
362
+ */
363
+ function recordReaction(action, reaction) {
364
+ const now = Date.now();
365
+
366
+ // Add to reaction log
367
+ data.reactionLog.push({ action, reaction, timestamp: now });
368
+ if (data.reactionLog.length > MAX_REACTION_LOG) {
369
+ data.reactionLog = data.reactionLog.slice(-MAX_REACTION_LOG);
370
+ }
371
+
372
+ // Click/double-click classified as positive reactions
373
+ if (reaction === 'click' || reaction === 'double_click' || reaction === 'cursor_near') {
374
+ if (!data.favoriteActions[action]) data.favoriteActions[action] = 0;
375
+ data.favoriteActions[action]++;
376
+ }
377
+ // Drag (grab and move) is a slightly negative reaction
378
+ if (reaction === 'drag') {
379
+ if (!data.dislikedActions[action]) data.dislikedActions[action] = 0;
380
+ data.dislikedActions[action]++;
381
+ }
382
+
383
+ save();
384
+ }
385
+
386
+ /**
387
+ * Return top N user-preferred actions
388
+ * Referenced by AI when deciding actions
389
+ */
390
+ function getFavoriteActions(topN = 5) {
391
+ const entries = Object.entries(data.favoriteActions || {});
392
+ entries.sort((a, b) => b[1] - a[1]);
393
+ return entries.slice(0, topN).map(([action, count]) => ({ action, count }));
394
+ }
395
+
396
+ /**
397
+ * Return recent motion history
398
+ */
399
+ function getMotionHistory(limit = 20) {
400
+ return (data.motionHistory || []).slice(-limit);
401
+ }
402
+
403
+ /**
404
+ * Return accumulated time per state
405
+ */
406
+ function getMotionStats() {
407
+ return { ...(data.motionStats || {}) };
408
+ }
409
+
307
410
  async function save() {
308
411
  try {
309
412
  await window.clawmate.saveMemory(data);
@@ -318,5 +421,9 @@ const Memory = (() => {
318
421
  return data.evolutionStage;
319
422
  }
320
423
 
321
- return { init, recordClick, getData, getEvolutionStage, save };
424
+ return {
425
+ init, recordClick, getData, getEvolutionStage, save,
426
+ recordMotion, recordReaction, getFavoriteActions,
427
+ getMotionHistory, getMotionStats,
428
+ };
322
429
  })();