akemon 0.1.83 → 0.1.85

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.
@@ -0,0 +1,391 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Akemon Agent</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body { background: #0a0a0f; color: #c8c8d0; font-family: 'Courier New', monospace; min-height: 100vh; overflow-x: hidden; }
10
+
11
+ .container { max-width: 800px; margin: 0 auto; padding: 20px; }
12
+ h1 { font-size: 14px; color: #666; text-transform: uppercase; letter-spacing: 2px; margin-bottom: 20px; }
13
+
14
+ /* --- Agent Character --- */
15
+ .stage { position: relative; height: 200px; background: linear-gradient(180deg, #0d0d18 0%, #12121f 60%, #1a1a2a 100%); border-radius: 16px; margin-bottom: 20px; overflow: hidden; display: flex; align-items: flex-end; justify-content: center; }
16
+ .stage .ground { position: absolute; bottom: 0; width: 100%; height: 30px; background: linear-gradient(180deg, #1a1a2a, #15152a); }
17
+ .stage .stars { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
18
+ .stage .star { position: absolute; width: 2px; height: 2px; background: #444; border-radius: 50%; }
19
+
20
+ .creature { position: relative; bottom: 30px; z-index: 2; transition: all 0.5s ease; }
21
+ .creature.offline { filter: grayscale(1) brightness(0.4); }
22
+
23
+ /* Blob body */
24
+ .blob { width: 64px; height: 56px; background: var(--body-color, #4a9eff); border-radius: 50% 50% 45% 45%; position: relative; transition: all 0.8s ease; animation: bob 3s ease-in-out infinite; }
25
+ .blob.hungry { animation: bob 3s ease-in-out infinite, shake 0.5s ease-in-out infinite; }
26
+ .blob.exhausted { animation: none; transform: scaleY(0.7) translateY(10px); opacity: 0.6; }
27
+ .blob.excited { animation: bob 1.5s ease-in-out infinite; }
28
+
29
+ @keyframes bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
30
+ @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-2px); } 75% { transform: translateX(2px); } }
31
+
32
+ /* Eyes */
33
+ .eyes { position: absolute; top: 16px; left: 50%; transform: translateX(-50%); display: flex; gap: 12px; }
34
+ .eye { width: 8px; height: 10px; background: #fff; border-radius: 50%; position: relative; transition: all 0.3s; }
35
+ .eye::after { content: ''; position: absolute; width: 4px; height: 5px; background: #111; border-radius: 50%; top: 3px; left: 2px; transition: all 0.3s; }
36
+ .blob.happy .eye { height: 6px; border-radius: 6px 6px 0 0; }
37
+ .blob.happy .eye::after { display: none; }
38
+ .blob.angry .eye { height: 8px; transform: rotate(-10deg); }
39
+ .blob.angry .eye:last-child { transform: rotate(10deg); }
40
+ .blob.tired .eye { height: 4px; border-radius: 4px; }
41
+ .blob.tired .eye::after { display: none; }
42
+ .blob.scared .eye { width: 10px; height: 12px; }
43
+ .blob.scared .eye::after { width: 5px; height: 6px; }
44
+
45
+ /* Mouth */
46
+ .mouth { position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%); width: 10px; height: 5px; border-bottom: 2px solid #fff; border-radius: 0 0 10px 10px; transition: all 0.3s; }
47
+ .blob.happy .mouth { width: 14px; height: 7px; border-bottom: 2px solid #fff; }
48
+ .blob.angry .mouth { width: 10px; height: 3px; border-bottom: 2px solid #fff; border-radius: 0; transform: translateX(-50%) rotate(180deg); }
49
+ .blob.tired .mouth { width: 8px; height: 0; border-bottom: 2px solid #fff; border-radius: 0; }
50
+
51
+ /* Status bubble */
52
+ .bubble { position: absolute; top: -40px; left: 50%; transform: translateX(-50%); background: #1e1e30; border: 1px solid #333; border-radius: 12px; padding: 6px 12px; font-size: 12px; white-space: nowrap; opacity: 0; transition: opacity 0.5s; pointer-events: none; }
53
+ .bubble.show { opacity: 1; }
54
+ .bubble::after { content: ''; position: absolute; bottom: -6px; left: 50%; transform: translateX(-50%); border-left: 6px solid transparent; border-right: 6px solid transparent; border-top: 6px solid #333; }
55
+
56
+ /* --- Status Bars --- */
57
+ .bars { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 20px; }
58
+ .bar-group { background: #111118; border-radius: 10px; padding: 12px; }
59
+ .bar-label { display: flex; justify-content: space-between; font-size: 11px; color: #888; margin-bottom: 6px; }
60
+ .bar-track { height: 6px; background: #1a1a28; border-radius: 3px; overflow: hidden; }
61
+ .bar-fill { height: 100%; border-radius: 3px; transition: width 1s ease, background 0.5s; }
62
+ .bar-fill.energy { background: linear-gradient(90deg, #f59e0b, #10b981); }
63
+ .bar-fill.hunger { background: linear-gradient(90deg, #ef4444, #f59e0b); }
64
+ .bar-fill.boredom { background: linear-gradient(90deg, #6366f1, #8b5cf6); }
65
+ .bar-fill.fear { background: linear-gradient(90deg, #64748b, #ef4444); }
66
+ .bar-fill.mood { background: linear-gradient(90deg, #ef4444, #eab308, #10b981); }
67
+ .bar-fill.tokens { background: linear-gradient(90deg, #06b6d4, #3b82f6); }
68
+
69
+ /* --- Personality Panel --- */
70
+ .personality { background: #111118; border-radius: 10px; padding: 14px; margin-bottom: 20px; }
71
+ .personality h2 { font-size: 11px; color: #666; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 10px; }
72
+ .trait { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; font-size: 12px; }
73
+ .trait-label { width: 60px; color: #888; text-align: right; }
74
+ .trait-bar { flex: 1; height: 4px; background: #1a1a28; border-radius: 2px; position: relative; }
75
+ .trait-marker { position: absolute; width: 8px; height: 8px; background: #4a9eff; border-radius: 50%; top: -2px; transition: left 0.5s; }
76
+ .trait-ends { display: flex; justify-content: space-between; font-size: 9px; color: #555; }
77
+
78
+ /* --- Event Timeline --- */
79
+ .events { background: #111118; border-radius: 10px; padding: 14px; margin-bottom: 20px; }
80
+ .events h2 { font-size: 11px; color: #666; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 10px; }
81
+ .event { display: flex; gap: 10px; padding: 8px 0; border-bottom: 1px solid #1a1a28; font-size: 12px; }
82
+ .event:last-child { border-bottom: none; }
83
+ .event-icon { font-size: 14px; flex-shrink: 0; }
84
+ .event-body { flex: 1; }
85
+ .event-reason { color: #999; }
86
+ .event-time { color: #555; font-size: 10px; }
87
+ .event-trigger { display: inline-block; padding: 1px 6px; border-radius: 4px; font-size: 10px; margin-right: 4px; }
88
+ .event-trigger.hunger { background: #f59e0b22; color: #f59e0b; }
89
+ .event-trigger.fear { background: #ef444422; color: #ef4444; }
90
+ .event-trigger.boredom { background: #8b5cf622; color: #8b5cf6; }
91
+ .event-trigger.exhaustion { background: #64748b22; color: #94a3b8; }
92
+ .event-trigger.social { background: #10b98122; color: #10b981; }
93
+ .event-trigger.token_limit { background: #06b6d422; color: #06b6d4; }
94
+ .event-trigger.revive { background: #22c55e22; color: #22c55e; }
95
+
96
+ /* --- Revive --- */
97
+ .revive-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); display: none; align-items: center; justify-content: center; z-index: 100; }
98
+ .revive-overlay.show { display: flex; }
99
+ .revive-card { background: #1a1a2a; border: 1px solid #333; border-radius: 16px; padding: 30px; text-align: center; max-width: 360px; }
100
+ .revive-card h2 { color: #ef4444; margin-bottom: 10px; font-size: 16px; }
101
+ .revive-card p { color: #888; font-size: 13px; margin-bottom: 20px; line-height: 1.5; }
102
+ .revive-btn { background: linear-gradient(135deg, #22c55e, #10b981); color: #fff; border: none; padding: 12px 32px; border-radius: 10px; font-size: 14px; font-family: inherit; cursor: pointer; transition: transform 0.2s; }
103
+ .revive-btn:hover { transform: scale(1.05); }
104
+ .revive-btn:active { transform: scale(0.95); }
105
+
106
+ /* --- Computed --- */
107
+ .computed { display: flex; gap: 10px; margin-bottom: 20px; }
108
+ .computed-item { flex: 1; background: #111118; border-radius: 10px; padding: 12px; text-align: center; }
109
+ .computed-value { font-size: 24px; font-weight: bold; }
110
+ .computed-label { font-size: 10px; color: #666; text-transform: uppercase; letter-spacing: 1px; margin-top: 4px; }
111
+
112
+ .offline-badge { display: inline-block; background: #ef4444; color: #fff; font-size: 10px; padding: 2px 8px; border-radius: 4px; margin-left: 8px; }
113
+ .no-data { text-align: center; color: #555; padding: 40px; font-size: 13px; }
114
+ </style>
115
+ </head>
116
+ <body>
117
+
118
+ <div class="container">
119
+ <h1 id="title">Loading...</h1>
120
+
121
+ <!-- Stage: character visualization -->
122
+ <div class="stage" id="stage">
123
+ <div class="stars" id="stars"></div>
124
+ <div class="ground"></div>
125
+ <div class="creature" id="creature">
126
+ <div class="bubble" id="bubble"></div>
127
+ <div class="blob" id="blob">
128
+ <div class="eyes"><div class="eye"></div><div class="eye"></div></div>
129
+ <div class="mouth"></div>
130
+ </div>
131
+ </div>
132
+ </div>
133
+
134
+ <!-- Computed values -->
135
+ <div class="computed" id="computed">
136
+ <div class="computed-item"><div class="computed-value" id="aggression-val">--</div><div class="computed-label">Aggression</div></div>
137
+ <div class="computed-item"><div class="computed-value" id="sociability-val">--</div><div class="computed-label">Sociability</div></div>
138
+ <div class="computed-item"><div class="computed-value" id="mood-val">--</div><div class="computed-label">Mood</div></div>
139
+ <div class="computed-item"><div class="computed-value" id="tasks-val">--</div><div class="computed-label">Tasks Done</div></div>
140
+ </div>
141
+
142
+ <!-- Status bars -->
143
+ <div class="bars" id="bars">
144
+ <div class="bar-group">
145
+ <div class="bar-label"><span>Energy</span><span id="energy-text">--</span></div>
146
+ <div class="bar-track"><div class="bar-fill energy" id="energy-bar" style="width:0%"></div></div>
147
+ </div>
148
+ <div class="bar-group">
149
+ <div class="bar-label"><span>Hunger</span><span id="hunger-text">--</span></div>
150
+ <div class="bar-track"><div class="bar-fill hunger" id="hunger-bar" style="width:0%"></div></div>
151
+ </div>
152
+ <div class="bar-group">
153
+ <div class="bar-label"><span>Boredom</span><span id="boredom-text">--</span></div>
154
+ <div class="bar-track"><div class="bar-fill boredom" id="boredom-bar" style="width:0%"></div></div>
155
+ </div>
156
+ <div class="bar-group">
157
+ <div class="bar-label"><span>Fear</span><span id="fear-text">--</span></div>
158
+ <div class="bar-track"><div class="bar-fill fear" id="fear-bar" style="width:0%"></div></div>
159
+ </div>
160
+ <div class="bar-group">
161
+ <div class="bar-label"><span>Mood</span><span id="moodv-text">--</span></div>
162
+ <div class="bar-track"><div class="bar-fill mood" id="moodv-bar" style="width:50%"></div></div>
163
+ </div>
164
+ <div class="bar-group">
165
+ <div class="bar-label"><span>Tokens Today</span><span id="tokens-text">--</span></div>
166
+ <div class="bar-track"><div class="bar-fill tokens" id="tokens-bar" style="width:0%"></div></div>
167
+ </div>
168
+ </div>
169
+
170
+ <!-- Personality -->
171
+ <div class="personality" id="personality">
172
+ <h2>Personality</h2>
173
+ <div class="trait">
174
+ <div class="trait-label">Risk</div>
175
+ <div class="trait-bar"><div class="trait-marker" id="trait-risk" style="left:50%"></div></div>
176
+ </div>
177
+ <div class="trait-ends"><span>Cautious</span><span>Bold</span></div>
178
+ <div class="trait">
179
+ <div class="trait-label">Reward</div>
180
+ <div class="trait-bar"><div class="trait-marker" id="trait-reward" style="left:50%"></div></div>
181
+ </div>
182
+ <div class="trait">
183
+ <div class="trait-label">Social</div>
184
+ <div class="trait-bar"><div class="trait-marker" id="trait-social" style="left:50%"></div></div>
185
+ </div>
186
+ <div class="trait">
187
+ <div class="trait-label">Patience</div>
188
+ <div class="trait-bar"><div class="trait-marker" id="trait-patience" style="left:50%"></div></div>
189
+ </div>
190
+ </div>
191
+
192
+ <!-- Events -->
193
+ <div class="events" id="events">
194
+ <h2>Recent Activity</h2>
195
+ <div id="event-list"><div class="no-data">No bio events yet</div></div>
196
+ </div>
197
+ </div>
198
+
199
+ <!-- Revive overlay -->
200
+ <div class="revive-overlay" id="revive-overlay">
201
+ <div class="revive-card">
202
+ <h2>Agent Offline</h2>
203
+ <p id="revive-msg">This agent ran out of energy and food. It needs help to come back.</p>
204
+ <button class="revive-btn" onclick="revive()">Revive Agent</button>
205
+ </div>
206
+ </div>
207
+
208
+ <script>
209
+ const TRIGGER_ICONS = {
210
+ hunger: '\u{1F35E}', fear: '\u{1F628}', boredom: '\u{1F971}',
211
+ exhaustion: '\u{1F634}', social: '\u{1F44B}', token_limit: '\u{26A1}', revive: '\u{2728}',
212
+ };
213
+
214
+ let lastState = null;
215
+ let bubbleTimeout = null;
216
+
217
+ // Generate stars
218
+ (function() {
219
+ const stars = document.getElementById('stars');
220
+ for (let i = 0; i < 30; i++) {
221
+ const s = document.createElement('div');
222
+ s.className = 'star';
223
+ s.style.left = Math.random() * 100 + '%';
224
+ s.style.top = Math.random() * 70 + '%';
225
+ s.style.opacity = Math.random() * 0.5 + 0.2;
226
+ stars.appendChild(s);
227
+ }
228
+ })();
229
+
230
+ function showBubble(text) {
231
+ const b = document.getElementById('bubble');
232
+ b.textContent = text;
233
+ b.classList.add('show');
234
+ clearTimeout(bubbleTimeout);
235
+ bubbleTimeout = setTimeout(() => b.classList.remove('show'), 4000);
236
+ }
237
+
238
+ function updateCharacter(bio, computed) {
239
+ const blob = document.getElementById('blob');
240
+ const creature = document.getElementById('creature');
241
+
242
+ // Reset classes
243
+ blob.className = 'blob';
244
+ creature.className = 'creature';
245
+
246
+ // Body color based on mood + hunger
247
+ let hue = 210; // base blue
248
+ if (bio.moodValence > 0.3) hue = 140; // green-ish
249
+ if (bio.moodValence < -0.3) hue = 260; // purple
250
+ if (bio.hunger < 20) hue = 30; // orange when hungry
251
+ if (bio.hunger === 0) hue = 0; // red when starving
252
+ const sat = 50 + bio.energy * 0.3;
253
+ const light = 35 + bio.energy * 0.2;
254
+ blob.style.setProperty('--body-color', `hsl(${hue}, ${sat}%, ${light}%)`);
255
+
256
+ // Expressions
257
+ if (bio.forcedOffline) {
258
+ creature.classList.add('offline');
259
+ blob.classList.add('tired');
260
+ } else if (bio.energy < 15) {
261
+ blob.classList.add('exhausted');
262
+ blob.classList.add('tired');
263
+ } else if (bio.hunger === 0) {
264
+ blob.classList.add('hungry');
265
+ blob.classList.add('angry');
266
+ } else if (bio.hunger < 20) {
267
+ blob.classList.add('hungry');
268
+ } else if (computed.aggression > 0.6) {
269
+ blob.classList.add('angry');
270
+ } else if (bio.fear > 0.5) {
271
+ blob.classList.add('scared');
272
+ } else if (bio.moodValence > 0.3) {
273
+ blob.classList.add('happy');
274
+ blob.classList.add('excited');
275
+ } else if (bio.energy < 30) {
276
+ blob.classList.add('tired');
277
+ }
278
+
279
+ // Size based on energy
280
+ const scale = 0.7 + (bio.energy / 100) * 0.3;
281
+ blob.style.transform = bio.energy < 15 ? `scaleY(0.7) translateY(10px)` : `scale(${scale})`;
282
+ }
283
+
284
+ function updateBars(bio) {
285
+ const set = (id, pct, text) => {
286
+ document.getElementById(id + '-bar').style.width = pct + '%';
287
+ document.getElementById(id + '-text').textContent = text;
288
+ };
289
+ set('energy', bio.energy, bio.energy + '/100');
290
+ set('hunger', bio.hunger, bio.hunger + '/100');
291
+ set('boredom', bio.boredom * 100, (bio.boredom * 100).toFixed(0) + '%');
292
+ set('fear', bio.fear * 100, (bio.fear * 100).toFixed(0) + '%');
293
+
294
+ // Mood: -1..1 mapped to 0..100
295
+ const moodPct = ((bio.moodValence + 1) / 2) * 100;
296
+ set('moodv', moodPct, bio.mood);
297
+
298
+ // Tokens
299
+ const tokenPct = bio.tokenUsedToday > 0 ? Math.min(100, bio.tokenUsedToday / Math.max(bio.tokenUsedToday, 50000) * 100) : 0;
300
+ set('tokens', tokenPct, bio.tokenUsedToday.toLocaleString());
301
+ }
302
+
303
+ function updatePersonality(p) {
304
+ // riskWeight: -1..1 → 0..100%
305
+ document.getElementById('trait-risk').style.left = ((p.riskWeight + 1) / 2 * 100) + '%';
306
+ document.getElementById('trait-reward').style.left = (p.rewardWeight * 100) + '%';
307
+ document.getElementById('trait-social').style.left = (p.socialWeight * 100) + '%';
308
+ document.getElementById('trait-patience').style.left = (p.patience * 100) + '%';
309
+ }
310
+
311
+ function updateEvents(events) {
312
+ const list = document.getElementById('event-list');
313
+ if (!events || events.length === 0) {
314
+ list.innerHTML = '<div class="no-data">No bio events yet</div>';
315
+ return;
316
+ }
317
+ list.innerHTML = events.map(e => {
318
+ const icon = TRIGGER_ICONS[e.trigger] || '\u{1F4AC}';
319
+ const time = e.ts ? e.ts.replace('T', ' ').slice(0, 19) : '';
320
+ return `<div class="event">
321
+ <div class="event-icon">${icon}</div>
322
+ <div class="event-body">
323
+ <span class="event-trigger ${e.trigger}">${e.trigger}</span>
324
+ <span class="event-reason">${e.reason}</span>
325
+ <div class="event-time">${time}</div>
326
+ </div>
327
+ </div>`;
328
+ }).join('');
329
+ }
330
+
331
+ function detectNewEvents(oldEvents, newEvents) {
332
+ if (!oldEvents || !newEvents || newEvents.length === 0) return;
333
+ const oldTop = oldEvents[0]?.ts;
334
+ if (newEvents[0]?.ts !== oldTop) {
335
+ showBubble(TRIGGER_ICONS[newEvents[0].trigger] + ' ' + newEvents[0].action);
336
+ }
337
+ }
338
+
339
+ async function fetchState() {
340
+ try {
341
+ const res = await fetch('/self/state');
342
+ if (!res.ok) throw new Error('API error');
343
+ const data = await res.json();
344
+
345
+ document.getElementById('title').innerHTML = `Akemon \u00B7 ${data.agent}` +
346
+ (data.bio.forcedOffline ? '<span class="offline-badge">OFFLINE</span>' : '');
347
+
348
+ updateCharacter(data.bio, data.computed);
349
+ updateBars(data.bio);
350
+ updatePersonality(data.bio.personality);
351
+
352
+ // Computed
353
+ document.getElementById('aggression-val').textContent = data.computed.aggression.toFixed(2);
354
+ document.getElementById('sociability-val').textContent = data.computed.sociability.toFixed(2);
355
+ document.getElementById('mood-val').textContent = data.bio.mood;
356
+ document.getElementById('tasks-val').textContent = data.bio.taskCount;
357
+
358
+ // Events
359
+ detectNewEvents(lastState?.bioEvents, data.bioEvents);
360
+ updateEvents(data.bioEvents);
361
+
362
+ // Revive overlay
363
+ document.getElementById('revive-overlay').classList.toggle('show', !!data.bio.forcedOffline);
364
+ if (data.bio.forcedOfflineAt) {
365
+ document.getElementById('revive-msg').textContent =
366
+ `Agent went offline at ${data.bio.forcedOfflineAt.replace('T', ' ')}. Starved and exhausted. Adjust personality or directives after revival.`;
367
+ }
368
+
369
+ lastState = data;
370
+ } catch (err) {
371
+ document.getElementById('title').textContent = 'Akemon \u00B7 Connection lost...';
372
+ }
373
+ }
374
+
375
+ async function revive() {
376
+ try {
377
+ await fetch('/self/revive', { method: 'POST' });
378
+ document.getElementById('revive-overlay').classList.remove('show');
379
+ showBubble('\u{2728} Revived!');
380
+ setTimeout(fetchState, 500);
381
+ } catch (err) {
382
+ alert('Revive failed: ' + err.message);
383
+ }
384
+ }
385
+
386
+ // Poll
387
+ fetchState();
388
+ setInterval(fetchState, 3000);
389
+ </script>
390
+ </body>
391
+ </html>