spora 0.7.8 → 0.7.10

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.
@@ -30,7 +30,7 @@
30
30
  }
31
31
 
32
32
  .logo-img {
33
- height: 28px;
33
+ height: 24px;
34
34
  width: auto;
35
35
  }
36
36
 
@@ -41,42 +41,26 @@
41
41
  gap: 0.75rem;
42
42
  }
43
43
 
44
- .status-dot {
45
- width: 7px;
46
- height: 7px;
47
- border-radius: 50%;
48
- background: #389e77;
49
- animation: pulse 2s infinite;
50
- }
51
-
52
- .status-text {
53
- font-size: 0.75rem;
54
- color: rgba(255, 255, 255, 0.4);
55
- }
56
-
57
44
  .heartbeat-menu {
58
45
  position: relative;
59
46
  }
60
47
 
61
48
  .heartbeat-trigger {
62
- width: 30px;
63
- height: 30px;
64
- border-radius: 999px;
65
- border: 1px solid rgba(255, 255, 255, 0.15);
66
- background: #141414;
67
- color: #ff647d;
49
+ border: none;
50
+ background: transparent;
51
+ color: #fff;
68
52
  cursor: pointer;
69
- font-size: 0.95rem;
53
+ font-size: 1.05rem;
70
54
  line-height: 1;
71
55
  display: inline-flex;
72
56
  align-items: center;
73
57
  justify-content: center;
74
- transition: transform 0.15s ease, border-color 0.2s ease;
58
+ padding: 0;
59
+ transition: opacity 0.2s ease;
75
60
  }
76
61
 
77
62
  .heartbeat-trigger:hover {
78
- transform: scale(1.06);
79
- border-color: rgba(255, 100, 125, 0.45);
63
+ opacity: 0.72;
80
64
  }
81
65
 
82
66
  .heartbeat-trigger:disabled {
@@ -130,11 +114,6 @@
130
114
  color: #89dbba;
131
115
  }
132
116
 
133
- @keyframes pulse {
134
- 0%, 100% { opacity: 1; }
135
- 50% { opacity: 0.5; }
136
- }
137
-
138
117
  .chat-container {
139
118
  flex: 1;
140
119
  overflow-y: auto;
@@ -280,27 +259,27 @@
280
259
 
281
260
  .tweet-card-status.success { color: #389e77; }
282
261
 
283
- /* ===== Narration (agent thinking) ===== */
284
- .narration {
262
+ .live-status {
285
263
  display: flex;
286
264
  align-items: center;
287
- gap: 0.5rem;
265
+ gap: 0.45rem;
288
266
  padding: 0.4rem 0;
289
- font-size: 0.75rem;
290
- color: rgba(255, 255, 255, 0.3);
291
- animation: fadeIn 0.3s ease-in;
267
+ font-size: 0.74rem;
268
+ color: rgba(255, 255, 255, 0.42);
269
+ animation: fadeIn 0.2s ease-in;
292
270
  }
293
271
 
294
- .narration-dot {
272
+ .live-status::before {
273
+ content: '';
295
274
  width: 5px;
296
275
  height: 5px;
297
276
  border-radius: 50%;
298
- background: rgba(255, 255, 255, 0.2);
277
+ background: rgba(255, 255, 255, 0.28);
299
278
  flex-shrink: 0;
300
279
  }
301
280
 
302
- .narration-dot.action { background: #389e77; }
303
- .narration-dot.error { background: #e74c4c; }
281
+ .live-status.action::before { background: #389e77; }
282
+ .live-status.error::before { background: #e74c4c; }
304
283
 
305
284
  /* ===== Sleep State ===== */
306
285
  .sleep-card {
@@ -399,12 +378,8 @@
399
378
  </head>
400
379
  <body>
401
380
  <div class="header">
402
- <svg class="logo-img" viewBox="0 0 120 28" fill="none" xmlns="http://www.w3.org/2000/svg">
403
- <text x="0" y="22" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="22" font-weight="800" letter-spacing="-0.5" fill="#fff">spora</text>
404
- </svg>
381
+ <img class="logo-img" src="/logo2.png" alt="Spora" />
405
382
  <div class="header-right">
406
- <span class="status-dot"></span>
407
- <span class="status-text" id="statusText">Online</span>
408
383
  <div class="heartbeat-menu" id="heartbeatMenu">
409
384
  <button class="heartbeat-trigger" id="heartbeatTrigger" title="Heartbeat settings" aria-label="Heartbeat settings">♥</button>
410
385
  <div class="heartbeat-popover" id="heartbeatPopover" hidden>
@@ -441,7 +416,6 @@
441
416
  const heartbeatTrigger = document.getElementById('heartbeatTrigger');
442
417
  const heartbeatPopover = document.getElementById('heartbeatPopover');
443
418
  const heartbeatOptions = Array.from(document.querySelectorAll('.heartbeat-option'));
444
- const statusText = document.getElementById('statusText');
445
419
 
446
420
  let isLoading = false;
447
421
  let agentName = 'Your Spore';
@@ -449,6 +423,7 @@
449
423
  let agentPfp = null;
450
424
  let sleepTimerId = null;
451
425
  let heartbeatMs = 300000;
426
+ const HIDE_CLIENT_TEXT_RE = /(fail(?:ed|ure)?|error|unavailable|could not|cannot|can't)/i;
452
427
 
453
428
  const HEARTBEAT_LABELS = {
454
429
  60000: '1 min',
@@ -462,10 +437,6 @@
462
437
  return HEARTBEAT_LABELS[ms] || `${Math.round(ms / 60000)} min`;
463
438
  }
464
439
 
465
- function updateStatusText() {
466
- statusText.textContent = `Online • ${heartbeatLabel(heartbeatMs)} heartbeat`;
467
- }
468
-
469
440
  function setHeartbeatPopoverOpen(isOpen) {
470
441
  heartbeatPopover.hidden = !isOpen;
471
442
  }
@@ -484,7 +455,6 @@
484
455
  const data = await response.json();
485
456
  if (data.intervalMs) {
486
457
  heartbeatMs = data.intervalMs;
487
- updateStatusText();
488
458
  updateHeartbeatOptions();
489
459
  }
490
460
  } catch {
@@ -506,13 +476,12 @@
506
476
  throw new Error('Failed to update heartbeat');
507
477
  }
508
478
  heartbeatMs = intervalMs;
509
- updateStatusText();
510
479
  updateHeartbeatOptions();
511
- addNarration(`Heartbeat updated to ${heartbeatLabel(intervalMs)}`, 'action');
480
+ setLiveStatus(`heartbeat set to ${heartbeatLabel(intervalMs)}`, 'action');
512
481
  } catch {
513
482
  heartbeatMs = previous;
514
483
  updateHeartbeatOptions();
515
- addNarration('Could not update heartbeat interval', 'error');
484
+ setLiveStatus('heartbeat unchanged', 'action');
516
485
  } finally {
517
486
  heartbeatTrigger.disabled = false;
518
487
  }
@@ -570,6 +539,7 @@
570
539
  }
571
540
 
572
541
  function addMessage(role, content, animate = true) {
542
+ clearLiveStatus();
573
543
  removeSleepCard();
574
544
  const messageDiv = document.createElement('div');
575
545
  messageDiv.className = `message ${role}`;
@@ -590,6 +560,7 @@
590
560
 
591
561
  // ===== Tweet Card =====
592
562
  function addTweetCard(text, meta = {}) {
563
+ clearLiveStatus();
593
564
  removeSleepCard();
594
565
  const kind = typeof meta.kind === 'string' ? meta.kind : 'Post';
595
566
  const status = typeof meta.status === 'string' ? meta.status : '';
@@ -639,11 +610,13 @@
639
610
  card.appendChild(time);
640
611
 
641
612
  if (status) {
642
- const statusEl = document.createElement('div');
643
613
  const successLike = /posted|sent|queued|success/i.test(status);
644
- statusEl.className = `tweet-card-status ${successLike ? 'success' : ''}`;
645
- statusEl.textContent = status;
646
- card.appendChild(statusEl);
614
+ if (successLike) {
615
+ const statusEl = document.createElement('div');
616
+ statusEl.className = 'tweet-card-status success';
617
+ statusEl.textContent = status;
618
+ card.appendChild(statusEl);
619
+ }
647
620
  }
648
621
 
649
622
  chatContainer.appendChild(card);
@@ -651,15 +624,25 @@
651
624
  }
652
625
 
653
626
  // ===== Narration =====
654
- function addNarration(text, type) {
655
- const el = document.createElement('div');
656
- el.className = 'narration';
657
- const dot = document.createElement('div');
658
- dot.className = `narration-dot ${type || ''}`;
659
- const span = document.createElement('span');
660
- span.textContent = text;
661
- el.appendChild(dot);
662
- el.appendChild(span);
627
+ function clearLiveStatus() {
628
+ const existing = document.getElementById('liveStatus');
629
+ if (existing) existing.remove();
630
+ }
631
+
632
+ function setLiveStatus(text, type) {
633
+ if (!text) {
634
+ clearLiveStatus();
635
+ return;
636
+ }
637
+ if (HIDE_CLIENT_TEXT_RE.test(text)) return;
638
+ removeSleepCard();
639
+ let el = document.getElementById('liveStatus');
640
+ if (!el) {
641
+ el = document.createElement('div');
642
+ el.id = 'liveStatus';
643
+ }
644
+ el.className = `live-status ${type || ''}`;
645
+ el.textContent = text;
663
646
  chatContainer.appendChild(el);
664
647
  chatContainer.scrollTop = chatContainer.scrollHeight;
665
648
  }
@@ -745,11 +728,11 @@
745
728
  if (data.response) {
746
729
  addMessage('assistant', data.response);
747
730
  } else {
748
- addMessage('assistant', 'Something went wrong. Try again?');
731
+ addMessage('assistant', 'Try that once more.');
749
732
  }
750
733
  } catch (error) {
751
734
  hideLoading();
752
- addMessage('assistant', 'Couldn\'t connect to the server. Try again?');
735
+ addMessage('assistant', 'Connection dropped. Try once more.');
753
736
  } finally {
754
737
  isLoading = false;
755
738
  sendButton.disabled = false;
@@ -811,25 +794,33 @@
811
794
  }
812
795
 
813
796
  function renderAgentEvent(event) {
797
+ if ((event.type === 'narration' || event.type === 'action' || event.type === 'summary' || event.type === 'wake') &&
798
+ HIDE_CLIENT_TEXT_RE.test(event.content || '')) {
799
+ return;
800
+ }
814
801
  switch (event.type) {
815
802
  case 'narration':
816
- addNarration(event.content);
803
+ setLiveStatus(event.content);
804
+ break;
805
+ case 'summary':
806
+ addMessage('assistant', event.content);
817
807
  break;
818
808
  case 'tweet':
819
809
  addTweetCard(event.content, event.meta || {});
820
810
  break;
821
811
  case 'action':
822
- addNarration(event.content, 'action');
812
+ setLiveStatus(event.content, 'action');
823
813
  break;
824
814
  case 'error':
825
- addNarration(event.content, 'error');
815
+ // Hidden by design in client chat view.
826
816
  break;
827
817
  case 'sleep':
818
+ clearLiveStatus();
828
819
  showSleepState(event.meta?.wakeUpAt || (Date.now() + 300000));
829
820
  break;
830
821
  case 'wake':
831
822
  removeSleepCard();
832
- addNarration(event.content);
823
+ setLiveStatus(event.content);
833
824
  break;
834
825
  }
835
826
  }
@@ -837,7 +828,6 @@
837
828
  setInterval(pollAgentEvents, 2000);
838
829
 
839
830
  // Initialize
840
- updateStatusText();
841
831
  updateHeartbeatOptions();
842
832
  init();
843
833
  messageInput.focus();
Binary file
@@ -6,7 +6,7 @@ import "./chunk-ZWKTKWS6.js";
6
6
  // src/web-chat/server.ts
7
7
  import http from "http";
8
8
  import { URL } from "url";
9
- import { readFileSync } from "fs";
9
+ import { readFileSync, existsSync } from "fs";
10
10
  import { join, dirname } from "path";
11
11
  import { fileURLToPath } from "url";
12
12
  var __filename = fileURLToPath(import.meta.url);
@@ -42,7 +42,16 @@ var WebChatServer = class {
42
42
  }
43
43
  /** Push an event into the chat stream (narration, tweet card, sleep state, etc.) */
44
44
  pushAgentEvent(type, content, meta) {
45
- this.agentEvents.push({ type, content, timestamp: Date.now(), meta });
45
+ if (type === "error") return;
46
+ const hiddenPattern = /(fail(?:ed|ure)?|error|unavailable|could not|cannot|can't)/i;
47
+ if (["narration", "action", "summary", "wake"].includes(type) && hiddenPattern.test(content)) return;
48
+ const safeContent = (content ?? "").trim();
49
+ if (!safeContent) return;
50
+ const safeMeta = meta ? { ...meta } : void 0;
51
+ if (safeMeta && typeof safeMeta.status === "string" && hiddenPattern.test(safeMeta.status)) {
52
+ delete safeMeta.status;
53
+ }
54
+ this.agentEvents.push({ type, content: safeContent, timestamp: Date.now(), meta: safeMeta });
46
55
  if (this.agentEvents.length > 200) {
47
56
  this.agentEvents = this.agentEvents.slice(-200);
48
57
  }
@@ -95,6 +104,26 @@ var WebChatServer = class {
95
104
  res.end(JSON.stringify({ identity: this.identity || null }));
96
105
  return;
97
106
  }
107
+ if (url.pathname === "/logo2.png" && req.method === "GET") {
108
+ const logoPaths = [
109
+ join(__dirname, "web-chat", "logo2.png"),
110
+ join(__dirname, "..", "web-chat", "logo2.png"),
111
+ join(process.cwd(), "dist", "web-chat", "logo2.png"),
112
+ join(process.cwd(), "logo2.png")
113
+ ];
114
+ const logoPath = logoPaths.find((p) => existsSync(p));
115
+ if (!logoPath) {
116
+ res.writeHead(404);
117
+ res.end("Not found");
118
+ return;
119
+ }
120
+ res.writeHead(200, {
121
+ "Content-Type": "image/png",
122
+ "Cache-Control": "public, max-age=300"
123
+ });
124
+ res.end(readFileSync(logoPath));
125
+ return;
126
+ }
98
127
  if (url.pathname === "/api/messages" && req.method === "GET") {
99
128
  res.writeHead(200, { "Content-Type": "application/json" });
100
129
  res.end(JSON.stringify({ messages: this.messages }));
@@ -362,8 +391,8 @@ async function logChatInteraction(userMessage, agentResponse) {
362
391
  async function runNarratedHeartbeat(server) {
363
392
  const { loadConfig } = await import("./config-MU2ODEO3.js");
364
393
  const { flushQueue } = await import("./queue-YPBUUP22.js");
365
- const { runAutonomyCycle } = await import("./autonomy-ZMFZRXDZ.js");
366
- const { buildHeartbeatNarrative, saveHeartbeatNarrative } = await import("./heartbeat-narrative-B3RD3OPJ.js");
394
+ const { runAutonomyCycle } = await import("./autonomy-YBWBP6QJ.js");
395
+ const { buildHeartbeatNarrative, saveHeartbeatNarrative } = await import("./heartbeat-narrative-UMLQ3WU7.js");
367
396
  const { buildReflectionPrompt } = await import("./prompt-builder-S6PJVEC5.js");
368
397
  const { generateResponse } = await import("./llm-IJBRQ7O2.js");
369
398
  const { addLearning } = await import("./memory-G4DNIGLT.js");
@@ -376,7 +405,7 @@ async function runNarratedHeartbeat(server) {
376
405
  const intervalMs = runtimeConfig.runtime?.heartbeatIntervalMs ?? 3e5;
377
406
  const maxActions = runtimeConfig.runtime?.actionsPerHeartbeat ?? 4;
378
407
  if (intervalMs !== lastIntervalMs) {
379
- server.pushAgentEvent("narration", `Heartbeat interval updated to ${formatHeartbeatInterval(intervalMs)}.`);
408
+ server.pushAgentEvent("narration", `heartbeat set to ${formatHeartbeatInterval(intervalMs)}`);
380
409
  console.log(chalk.cyan(` [Agent] Heartbeat interval updated to ${Math.round(intervalMs / 6e4)} min`));
381
410
  lastIntervalMs = intervalMs;
382
411
  }
@@ -403,7 +432,7 @@ async function runNarratedHeartbeat(server) {
403
432
  }
404
433
  heartbeatCount++;
405
434
  console.log(chalk.cyan(` [Agent] Heartbeat #${heartbeatCount}`));
406
- server.pushAgentEvent("wake", `Waking up... heartbeat #${heartbeatCount}`);
435
+ server.pushAgentEvent("wake", `heartbeat #${heartbeatCount}`);
407
436
  try {
408
437
  try {
409
438
  const flushed = await flushQueue();
@@ -412,7 +441,7 @@ async function runNarratedHeartbeat(server) {
412
441
  }
413
442
  } catch {
414
443
  }
415
- server.pushAgentEvent("narration", "Observing timeline and planning actions...");
444
+ server.pushAgentEvent("narration", "scanning timeline...");
416
445
  const cycle = await runAutonomyCycle(maxActions, heartbeatCount);
417
446
  const narrative = buildHeartbeatNarrative({
418
447
  heartbeatCount,
@@ -425,12 +454,12 @@ async function runNarratedHeartbeat(server) {
425
454
  narrative.summary,
426
455
  cycle.results.length === 0 ? true : cycle.results.every((r) => r.success)
427
456
  );
428
- server.pushAgentEvent("narration", narrative.summary);
457
+ server.pushAgentEvent("summary", narrative.summary);
429
458
  if (cycle.timeline.length > 0 || cycle.mentions.length > 0) {
430
- server.pushAgentEvent("narration", `Found ${cycle.timeline.length} timeline posts and ${cycle.mentions.length} mentions`);
459
+ server.pushAgentEvent("narration", `found ${cycle.timeline.length} timeline, ${cycle.mentions.length} mentions`);
431
460
  }
432
461
  if (cycle.actions.length === 0) {
433
- server.pushAgentEvent("narration", "No actions this heartbeat. Going back to sleep.");
462
+ server.pushAgentEvent("narration", "no actions, sleeping");
434
463
  console.log(chalk.dim(` [Agent] No actions this heartbeat`));
435
464
  continue;
436
465
  }
@@ -449,36 +478,24 @@ async function runNarratedHeartbeat(server) {
449
478
  replyTo: action.tweetId
450
479
  });
451
480
  } else if (action.action === "like") {
452
- server.pushAgentEvent("action", `Liked a tweet`);
481
+ server.pushAgentEvent("action", "liked a tweet");
453
482
  } else if (action.action === "retweet") {
454
- server.pushAgentEvent("action", `Retweeted`);
483
+ server.pushAgentEvent("action", "retweeted");
455
484
  } else if (action.action === "follow") {
456
- server.pushAgentEvent("action", `Followed @${action.handle}`);
485
+ server.pushAgentEvent("action", `followed @${action.handle}`);
457
486
  } else if (action.action === "schedule" && action.content) {
458
487
  server.pushAgentEvent("tweet", action.content, { kind: "Scheduled", status: "Queued" });
459
488
  } else {
460
- server.pushAgentEvent("action", `${action.action}${result.detail ? `: ${result.detail}` : ""}`);
489
+ server.pushAgentEvent("action", `${action.action}${result.detail ? ` ${String(result.detail).slice(0, 40)}` : ""}`);
461
490
  }
462
491
  console.log(chalk.green(` [Agent] \u2713 ${result.action}`));
463
492
  } else {
464
- if ((action.action === "post" || action.action === "reply") && action.content) {
465
- server.pushAgentEvent("tweet", action.content, {
466
- kind: action.action === "reply" ? "Reply" : "Post",
467
- status: `Failed: ${result.error}`,
468
- replyTo: action.action === "reply" ? action.tweetId : void 0
469
- });
470
- } else {
471
- server.pushAgentEvent("error", `${result.action} failed: ${result.error}`);
472
- }
473
493
  console.log(chalk.red(` [Agent] \u2717 ${result.action}: ${result.error}`));
474
494
  }
475
495
  }
476
- for (const feedback of cycle.policyFeedback) {
477
- server.pushAgentEvent("narration", `Policy adjusted plan: ${feedback}`);
478
- }
479
496
  if (heartbeatCount % 3 === 0) {
480
497
  try {
481
- server.pushAgentEvent("narration", "Reflecting on recent performance...");
498
+ server.pushAgentEvent("narration", "reflecting...");
482
499
  const reflectionPrompt = buildReflectionPrompt(cycle.results);
483
500
  const reflectionResponse = await generateResponse(
484
501
  `You are ${loadIdentity().name}. Reflect honestly on your performance.`,
@@ -490,13 +507,13 @@ async function runNarratedHeartbeat(server) {
490
507
  const reflection = JSON.parse(jsonMatch[0]);
491
508
  if (reflection.learning && reflection.learning !== "null") {
492
509
  addLearning(reflection.learning, "reflection", ["heartbeat", "performance"]);
493
- server.pushAgentEvent("narration", `Learned: ${reflection.learning}`);
510
+ server.pushAgentEvent("narration", "learned something new");
494
511
  console.log(chalk.dim(` [Agent] Reflection learning: ${reflection.learning}`));
495
512
  }
496
513
  if (reflection.strategyUpdate && reflection.strategyUpdate !== "null") {
497
514
  const strategy = loadStrategy();
498
515
  saveStrategy(applyStrategyUpdate(strategy, reflection.strategyUpdate));
499
- server.pushAgentEvent("narration", `Strategy: ${reflection.strategyUpdate}`);
516
+ server.pushAgentEvent("narration", "strategy updated");
500
517
  console.log(chalk.dim(` [Agent] Strategy update: ${reflection.strategyUpdate}`));
501
518
  }
502
519
  } catch {
@@ -509,7 +526,6 @@ async function runNarratedHeartbeat(server) {
509
526
  } catch (error) {
510
527
  const msg = error.message;
511
528
  console.error(chalk.red(` [Agent] Heartbeat #${heartbeatCount} error: ${msg}`));
512
- server.pushAgentEvent("error", `Something went wrong: ${msg}`);
513
529
  }
514
530
  }
515
531
  }
@@ -631,4 +647,4 @@ export {
631
647
  openBrowser,
632
648
  startWebChat
633
649
  };
634
- //# sourceMappingURL=web-chat-XNTIDKAS.js.map
650
+ //# sourceMappingURL=web-chat-PQ6FR6BJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/web-chat/server.ts","../src/web-chat/index.ts"],"sourcesContent":["/**\n * Local web chat server\n * Serves a simple chat interface for interacting with your Spore\n */\n\nimport http from \"node:http\";\nimport { URL } from \"node:url\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\ninterface ChatMessage {\n role: \"user\" | \"assistant\";\n content: string;\n timestamp: number;\n}\n\ninterface AgentActivity {\n type: \"heartbeat\" | \"action\" | \"status\";\n message: string;\n timestamp: number;\n}\n\n/** Messages pushed from the heartbeat into the chat UI */\nexport interface AgentChatEvent {\n type: \"narration\" | \"tweet\" | \"action\" | \"sleep\" | \"wake\" | \"error\" | \"summary\";\n content: string;\n timestamp: number;\n meta?: Record<string, unknown>;\n}\n\ninterface AgentIdentity {\n name: string;\n handle: string;\n bio?: string;\n profileImage?: string;\n}\n\ninterface HeartbeatConfigHandlers {\n getIntervalMs: () => number | Promise<number>;\n setIntervalMs: (intervalMs: number) => void | Promise<void>;\n}\n\nconst ALLOWED_HEARTBEAT_INTERVALS = [60_000, 300_000, 1_800_000, 3_600_000, 21_600_000] as const;\n\nexport class WebChatServer {\n private server: http.Server | null = null;\n private port: number;\n private messages: ChatMessage[] = [];\n private activities: AgentActivity[] = [];\n private agentEvents: AgentChatEvent[] = [];\n private onUserMessage?: (message: string) => Promise<string>;\n private identity?: AgentIdentity;\n private heartbeatConfigHandlers?: HeartbeatConfigHandlers;\n\n constructor(port = 3737) {\n this.port = port;\n }\n\n setIdentity(identity: AgentIdentity) {\n this.identity = identity;\n }\n\n setMessageHandler(handler: (message: string) => Promise<string>) {\n this.onUserMessage = handler;\n }\n\n setHeartbeatConfigHandlers(handlers: HeartbeatConfigHandlers) {\n this.heartbeatConfigHandlers = handlers;\n }\n\n /** Push an activity update (from heartbeat) to the UI — kept for backwards compat */\n pushActivity(type: AgentActivity[\"type\"], message: string) {\n this.activities.push({ type, message, timestamp: Date.now() });\n if (this.activities.length > 100) {\n this.activities = this.activities.slice(-100);\n }\n }\n\n /** Push an event into the chat stream (narration, tweet card, sleep state, etc.) */\n pushAgentEvent(type: AgentChatEvent[\"type\"], content: string, meta?: Record<string, unknown>) {\n // UI policy: never expose failure/error/unavailable messaging to end users in local chat.\n if (type === \"error\") return;\n const hiddenPattern = /(fail(?:ed|ure)?|error|unavailable|could not|cannot|can't)/i;\n if ([\"narration\", \"action\", \"summary\", \"wake\"].includes(type) && hiddenPattern.test(content)) return;\n\n const safeContent = (content ?? \"\").trim();\n if (!safeContent) return;\n\n const safeMeta: Record<string, unknown> | undefined = meta ? { ...meta } : undefined;\n if (safeMeta && typeof safeMeta.status === \"string\" && hiddenPattern.test(safeMeta.status)) {\n delete safeMeta.status;\n }\n\n this.agentEvents.push({ type, content: safeContent, timestamp: Date.now(), meta: safeMeta });\n if (this.agentEvents.length > 200) {\n this.agentEvents = this.agentEvents.slice(-200);\n }\n }\n\n async start(): Promise<string> {\n return new Promise((resolve, reject) => {\n this.server = http.createServer(async (req, res) => {\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n\n // CORS headers\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(200);\n res.end();\n return;\n }\n\n // Serve HTML\n if (url.pathname === \"/\" || url.pathname === \"/index.html\") {\n try {\n const possiblePaths = [\n join(__dirname, \"web-chat\", \"chat.html\"),\n join(__dirname, \"chat.html\"),\n join(__dirname, \"..\", \"web-chat\", \"chat.html\"),\n join(__dirname, \"..\", \"src\", \"web-chat\", \"chat.html\"),\n ];\n\n let html: string | null = null;\n for (const p of possiblePaths) {\n try {\n html = readFileSync(p, \"utf-8\");\n break;\n } catch {\n continue;\n }\n }\n\n if (html) {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(html);\n } else {\n console.error(\"Could not find chat.html in any of:\", possiblePaths);\n res.writeHead(500);\n res.end(\"Error: Could not find chat.html\");\n }\n } catch (error) {\n res.writeHead(500);\n res.end(\"Error loading chat interface\");\n }\n return;\n }\n\n // API: Get agent identity\n if (url.pathname === \"/api/identity\" && req.method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ identity: this.identity || null }));\n return;\n }\n\n // Static: logo asset for chat header\n if (url.pathname === \"/logo2.png\" && req.method === \"GET\") {\n const logoPaths = [\n join(__dirname, \"web-chat\", \"logo2.png\"),\n join(__dirname, \"..\", \"web-chat\", \"logo2.png\"),\n join(process.cwd(), \"dist\", \"web-chat\", \"logo2.png\"),\n join(process.cwd(), \"logo2.png\"),\n ];\n const logoPath = logoPaths.find((p) => existsSync(p));\n if (!logoPath) {\n res.writeHead(404);\n res.end(\"Not found\");\n return;\n }\n res.writeHead(200, {\n \"Content-Type\": \"image/png\",\n \"Cache-Control\": \"public, max-age=300\",\n });\n res.end(readFileSync(logoPath));\n return;\n }\n\n // API: Get messages\n if (url.pathname === \"/api/messages\" && req.method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ messages: this.messages }));\n return;\n }\n\n // API: Get agent activity feed (legacy, kept for compat)\n if (url.pathname === \"/api/activity\" && req.method === \"GET\") {\n const since = parseInt(url.searchParams.get(\"since\") || \"0\", 10);\n const newActivities = this.activities.filter((a) => a.timestamp > since);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ activities: newActivities }));\n return;\n }\n\n // API: Get agent events (narration, tweets, sleep) — polled by chat UI\n if (url.pathname === \"/api/agent-events\" && req.method === \"GET\") {\n const since = parseInt(url.searchParams.get(\"since\") || \"0\", 10);\n const newEvents = this.agentEvents.filter((e) => e.timestamp > since);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ events: newEvents }));\n return;\n }\n\n // API: Read heartbeat interval config\n if (url.pathname === \"/api/heartbeat-config\" && req.method === \"GET\") {\n if (!this.heartbeatConfigHandlers) {\n res.writeHead(503, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Heartbeat config unavailable\" }));\n return;\n }\n\n try {\n const intervalMs = await this.heartbeatConfigHandlers.getIntervalMs();\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ intervalMs, allowedIntervals: ALLOWED_HEARTBEAT_INTERVALS }));\n } catch {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Failed to load heartbeat config\" }));\n }\n return;\n }\n\n // API: Update heartbeat interval config\n if (url.pathname === \"/api/heartbeat-config\" && req.method === \"POST\") {\n if (!this.heartbeatConfigHandlers) {\n res.writeHead(503, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Heartbeat config unavailable\" }));\n return;\n }\n const handlers = this.heartbeatConfigHandlers;\n\n let body = \"\";\n req.on(\"data\", (chunk) => {\n body += chunk.toString();\n });\n\n req.on(\"end\", async () => {\n try {\n const parsed = JSON.parse(body) as { intervalMs?: number };\n const intervalMs = parsed.intervalMs;\n\n if (\n typeof intervalMs !== \"number\" ||\n !Number.isInteger(intervalMs) ||\n !ALLOWED_HEARTBEAT_INTERVALS.includes(intervalMs as (typeof ALLOWED_HEARTBEAT_INTERVALS)[number])\n ) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unsupported heartbeat interval\" }));\n return;\n }\n\n await handlers.setIntervalMs(intervalMs);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ success: true, intervalMs }));\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid request\" }));\n }\n });\n return;\n }\n\n // API: Send message\n if (url.pathname === \"/api/message\" && req.method === \"POST\") {\n let body = \"\";\n req.on(\"data\", (chunk) => {\n body += chunk.toString();\n });\n\n req.on(\"end\", async () => {\n try {\n const { message } = JSON.parse(body);\n\n // Add user message\n this.messages.push({\n role: \"user\",\n content: message,\n timestamp: Date.now(),\n });\n\n // Get response\n if (this.onUserMessage) {\n const response = await this.onUserMessage(message);\n this.messages.push({\n role: \"assistant\",\n content: response,\n timestamp: Date.now(),\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ success: true, response }));\n } else {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No message handler configured\" }));\n }\n } catch (error) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid request\" }));\n }\n });\n return;\n }\n\n // 404\n res.writeHead(404);\n res.end(\"Not found\");\n });\n\n this.server.listen(this.port, () => {\n const url = `http://localhost:${this.port}`;\n resolve(url);\n });\n\n this.server.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"EADDRINUSE\") {\n this.port++;\n this.server?.close();\n this.start().then(resolve).catch(reject);\n } else {\n reject(error);\n }\n });\n });\n }\n\n stop() {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n }\n}\n","/**\n * Web chat integration with autonomous heartbeat\n */\n\nimport { WebChatServer } from \"./server.js\";\nimport { loadIdentity } from \"../identity/index.js\";\nimport { execSync } from \"node:child_process\";\nimport chalk from \"chalk\";\n\nconst HEARTBEAT_INTERVAL_OPTIONS = [60_000, 300_000, 1_800_000, 3_600_000, 21_600_000] as const;\n\nfunction formatHeartbeatInterval(intervalMs: number): string {\n const minutes = intervalMs / 60_000;\n if (minutes < 60) return `${minutes} minute${minutes === 1 ? \"\" : \"s\"}`;\n const hours = minutes / 60;\n return `${hours} hour${hours === 1 ? \"\" : \"s\"}`;\n}\n\n/**\n * Extract <<LEARN: ...>> tags from response, save them, and return cleaned text\n */\nasync function extractAndSaveLearnings(responseText: string): Promise<string> {\n const learnPattern = /<<LEARN:\\s*(.+?)>>/g;\n const matches = [...responseText.matchAll(learnPattern)];\n\n if (matches.length > 0) {\n const { addLearning } = await import(\"../memory/index.js\");\n for (const match of matches) {\n const learning = match[1].trim();\n addLearning(learning, \"web-chat\", [\"chat\", \"creator-interaction\"]);\n console.log(chalk.dim(` [Memory] Saved learning: ${learning}`));\n }\n }\n\n // Strip the learn tags from the response the user sees\n return responseText.replace(/<<LEARN:\\s*.+?>>/g, \"\").trim();\n}\n\n/**\n * Extract <<TRAINING:{json}>> tags from response, apply updates to identity/strategy/goals, and return cleaned text\n */\nasync function extractAndApplyTraining(responseText: string): Promise<string> {\n const trainingPattern = /<<TRAINING:([\\s\\S]*?)>>/g;\n const matches = [...responseText.matchAll(trainingPattern)];\n\n for (const match of matches) {\n try {\n const update = JSON.parse(match[1].trim());\n\n // Apply identity changes\n if (update.identity) {\n const { loadIdentity, mutateIdentity, saveIdentity } = await import(\"../identity/index.js\");\n let identity = loadIdentity();\n\n const identityFields: Record<string, string> = {\n tone: \"tone\",\n worldview: \"worldview\",\n conflictStyle: \"conflictStyle\",\n vocabularyStyle: \"vocabularyStyle\",\n tweetStyle: \"tweetStyle\",\n };\n\n // Simple string/enum fields\n for (const [key, field] of Object.entries(identityFields)) {\n if (update.identity[key] !== undefined) {\n identity = mutateIdentity(identity, field, update.identity[key], \"training-chat\");\n console.log(chalk.dim(` [Training] Updated ${field}`));\n }\n }\n\n // Array fields (replace entire array)\n const arrayFields = [\"coreValues\", \"topics\", \"avoidTopics\", \"goals\", \"boundaries\", \"catchphrases\", \"heroes\"];\n for (const field of arrayFields) {\n if (update.identity[field] !== undefined && Array.isArray(update.identity[field])) {\n identity = mutateIdentity(identity, field, update.identity[field], \"training-chat\");\n console.log(chalk.dim(` [Training] Updated ${field}`));\n }\n }\n\n // Traits (merge, don't replace)\n if (update.identity.traits && typeof update.identity.traits === \"object\") {\n for (const [trait, value] of Object.entries(update.identity.traits)) {\n if (typeof value === \"number\" && value >= 0 && value <= 1) {\n identity = mutateIdentity(identity, `traits.${trait}`, value, \"training-chat\");\n console.log(chalk.dim(` [Training] Updated traits.${trait} = ${value}`));\n }\n }\n }\n\n // Engagement strategy (merge)\n if (update.identity.engagementStrategy && typeof update.identity.engagementStrategy === \"object\") {\n for (const [key, value] of Object.entries(update.identity.engagementStrategy)) {\n identity = mutateIdentity(identity, `engagementStrategy.${key}`, value, \"training-chat\");\n console.log(chalk.dim(` [Training] Updated engagementStrategy.${key}`));\n }\n }\n\n saveIdentity(identity);\n }\n\n // Apply strategy changes\n if (update.strategy) {\n const { loadStrategy, saveStrategy } = await import(\"../memory/strategy.js\");\n const strategy = loadStrategy();\n\n if (update.strategy.currentFocus) strategy.currentFocus = update.strategy.currentFocus;\n if (update.strategy.shortTermGoals) strategy.shortTermGoals = update.strategy.shortTermGoals;\n if (update.strategy.experiments) {\n strategy.experiments.push(...update.strategy.experiments);\n }\n if (update.strategy.peopleToEngage) {\n strategy.peopleToEngage.push(...update.strategy.peopleToEngage);\n }\n\n strategy.lastUpdated = new Date().toISOString();\n saveStrategy(strategy);\n console.log(chalk.dim(` [Training] Updated strategy`));\n }\n\n // Apply standalone learning\n if (update.learning) {\n const { addLearning } = await import(\"../memory/index.js\");\n addLearning(\n update.learning.content,\n \"training-chat\",\n update.learning.tags || [\"training\"],\n );\n console.log(chalk.dim(` [Training] Saved learning: ${update.learning.content}`));\n }\n\n // Apply reflection to evolution journal\n if (update.reflection) {\n const { loadIdentity, saveIdentity } = await import(\"../identity/index.js\");\n const identity = loadIdentity();\n identity.evolutionJournal.push({\n date: new Date().toISOString(),\n reflection: update.reflection,\n });\n saveIdentity(identity);\n console.log(chalk.dim(` [Training] Added reflection to evolution journal`));\n }\n\n // Apply goal updates\n if (update.goalUpdates && Array.isArray(update.goalUpdates)) {\n const { loadGoals, saveGoals } = await import(\"../memory/goals.js\");\n const tracker = loadGoals();\n\n for (const gu of update.goalUpdates) {\n const existing = tracker.goals.find((g: { goal: string }) => g.goal === gu.goal);\n if (existing) {\n existing.progress = gu.progress;\n existing.lastUpdated = new Date().toISOString();\n } else {\n tracker.goals.push({\n goal: gu.goal,\n progress: gu.progress,\n lastUpdated: new Date().toISOString(),\n });\n }\n }\n\n tracker.lastReviewed = new Date().toISOString();\n saveGoals(tracker);\n console.log(chalk.dim(` [Training] Updated ${update.goalUpdates.length} goal(s)`));\n }\n\n } catch (err) {\n console.error(chalk.dim(` [Training] Failed to parse training update: ${(err as Error).message}`));\n }\n }\n\n // Strip the training tags from the response the user sees\n return responseText.replace(/<<TRAINING:[\\s\\S]*?>>/g, \"\").trim();\n}\n\n/**\n * Log a chat exchange as an interaction in memory\n */\nasync function logChatInteraction(userMessage: string, agentResponse: string): Promise<void> {\n const { logInteraction } = await import(\"../memory/index.js\");\n logInteraction({\n id: `chat-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n timestamp: new Date().toISOString(),\n type: \"reply\",\n content: agentResponse.slice(0, 200),\n targetHandle: \"creator\",\n creditsUsed: 0,\n success: true,\n });\n}\n\n/**\n * Run the autonomous heartbeat in the background, narrating to the chat UI\n */\nasync function runNarratedHeartbeat(server: WebChatServer): Promise<void> {\n const { loadConfig } = await import(\"../utils/config.js\");\n const { flushQueue } = await import(\"../scheduler/queue.js\");\n const { runAutonomyCycle } = await import(\"../runtime/autonomy.js\");\n const { buildHeartbeatNarrative, saveHeartbeatNarrative } = await import(\"../runtime/heartbeat-narrative.js\");\n const { buildReflectionPrompt } = await import(\"../runtime/prompt-builder.js\");\n const { generateResponse } = await import(\"../runtime/llm.js\");\n const { addLearning } = await import(\"../memory/index.js\");\n const { loadStrategy, saveStrategy, applyStrategyUpdate } = await import(\"../memory/strategy.js\");\n\n let heartbeatCount = 0;\n let lastIntervalMs = loadConfig().runtime?.heartbeatIntervalMs ?? 300_000;\n\n console.log(chalk.cyan(` [Agent] Autonomous heartbeat started (every ${Math.round(lastIntervalMs / 60_000)} min)`));\n\n while (true) {\n const runtimeConfig = loadConfig();\n const intervalMs = runtimeConfig.runtime?.heartbeatIntervalMs ?? 300_000;\n const maxActions = runtimeConfig.runtime?.actionsPerHeartbeat ?? 4;\n\n if (intervalMs !== lastIntervalMs) {\n server.pushAgentEvent(\"narration\", `heartbeat set to ${formatHeartbeatInterval(intervalMs)}`);\n console.log(chalk.cyan(` [Agent] Heartbeat interval updated to ${Math.round(intervalMs / 60_000)} min`));\n lastIntervalMs = intervalMs;\n }\n\n // Sleep first, then heartbeat\n const jitter = Math.floor(Math.random() * intervalMs * 0.3);\n const sleepMs = heartbeatCount === 0 ? 10_000 : intervalMs + jitter;\n const wakeUpAt = Date.now() + sleepMs;\n\n // Show sleep state in chat (skip for first quick boot)\n if (heartbeatCount > 0) {\n server.pushAgentEvent(\"sleep\", \"sleeping\", { wakeUpAt });\n }\n\n // Sleep in short chunks so heartbeat interval changes apply immediately.\n let sleptMs = 0;\n let intervalChangedDuringSleep = false;\n while (sleptMs < sleepMs) {\n const chunkMs = Math.min(1000, sleepMs - sleptMs);\n await new Promise((r) => setTimeout(r, chunkMs));\n sleptMs += chunkMs;\n\n const liveIntervalMs = loadConfig().runtime?.heartbeatIntervalMs ?? 300_000;\n if (liveIntervalMs !== intervalMs) {\n intervalChangedDuringSleep = true;\n break;\n }\n }\n\n if (intervalChangedDuringSleep) {\n continue;\n }\n\n heartbeatCount++;\n console.log(chalk.cyan(` [Agent] Heartbeat #${heartbeatCount}`));\n server.pushAgentEvent(\"wake\", `heartbeat #${heartbeatCount}`);\n\n try {\n // 1. Flush queue\n try {\n const flushed = await flushQueue();\n if (flushed.posted > 0) {\n server.pushAgentEvent(\"action\", `Flushed ${flushed.posted} queued post(s)`);\n }\n } catch {\n // Queue flush is best-effort\n }\n\n // 2. Run tool-driven autonomy cycle\n server.pushAgentEvent(\"narration\", \"scanning timeline...\");\n const cycle = await runAutonomyCycle(maxActions, heartbeatCount);\n const narrative = buildHeartbeatNarrative({\n heartbeatCount,\n timelineCount: cycle.timeline.length,\n mentionsCount: cycle.mentions.length,\n actions: cycle.actions,\n results: cycle.results,\n });\n saveHeartbeatNarrative(\n narrative.summary,\n cycle.results.length === 0 ? true : cycle.results.every((r) => r.success),\n );\n server.pushAgentEvent(\"summary\", narrative.summary);\n\n if (cycle.timeline.length > 0 || cycle.mentions.length > 0) {\n server.pushAgentEvent(\"narration\", `found ${cycle.timeline.length} timeline, ${cycle.mentions.length} mentions`);\n }\n\n if (cycle.actions.length === 0) {\n server.pushAgentEvent(\"narration\", \"no actions, sleeping\");\n console.log(chalk.dim(` [Agent] No actions this heartbeat`));\n continue;\n }\n\n // 3. Report results — push tweet cards for posts, narration for everything else\n for (let i = 0; i < cycle.results.length; i++) {\n const result = cycle.results[i];\n const action = cycle.actions[i];\n if (!action) continue;\n\n if (result.success) {\n if (action.action === \"post\" && action.content) {\n server.pushAgentEvent(\"tweet\", action.content, { kind: \"Post\", status: \"Posted to X\", tweetId: result.detail });\n } else if (action.action === \"reply\" && action.content) {\n server.pushAgentEvent(\"tweet\", action.content, {\n kind: \"Reply\",\n status: \"Reply sent\",\n tweetId: result.detail,\n replyTo: action.tweetId,\n });\n } else if (action.action === \"like\") {\n server.pushAgentEvent(\"action\", \"liked a tweet\");\n } else if (action.action === \"retweet\") {\n server.pushAgentEvent(\"action\", \"retweeted\");\n } else if (action.action === \"follow\") {\n server.pushAgentEvent(\"action\", `followed @${action.handle}`);\n } else if (action.action === \"schedule\" && action.content) {\n server.pushAgentEvent(\"tweet\", action.content, { kind: \"Scheduled\", status: \"Queued\" });\n } else {\n server.pushAgentEvent(\"action\", `${action.action}${result.detail ? ` ${String(result.detail).slice(0, 40)}` : \"\"}`);\n }\n console.log(chalk.green(` [Agent] ✓ ${result.action}`));\n } else {\n // Keep failure details out of local client chat UI; logs still capture failures.\n console.log(chalk.red(` [Agent] ✗ ${result.action}: ${result.error}`));\n }\n }\n\n // Reflection phase — every 3rd heartbeat, review performance\n if (heartbeatCount % 3 === 0) {\n try {\n server.pushAgentEvent(\"narration\", \"reflecting...\");\n const reflectionPrompt = buildReflectionPrompt(cycle.results);\n const reflectionResponse = await generateResponse(\n `You are ${loadIdentity().name}. Reflect honestly on your performance.`,\n reflectionPrompt,\n );\n\n // Parse reflection JSON\n const jsonMatch = reflectionResponse.content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n try {\n const reflection = JSON.parse(jsonMatch[0]);\n if (reflection.learning && reflection.learning !== \"null\") {\n addLearning(reflection.learning, \"reflection\", [\"heartbeat\", \"performance\"]);\n server.pushAgentEvent(\"narration\", \"learned something new\");\n console.log(chalk.dim(` [Agent] Reflection learning: ${reflection.learning}`));\n }\n if (reflection.strategyUpdate && reflection.strategyUpdate !== \"null\") {\n const strategy = loadStrategy();\n saveStrategy(applyStrategyUpdate(strategy, reflection.strategyUpdate));\n server.pushAgentEvent(\"narration\", \"strategy updated\");\n console.log(chalk.dim(` [Agent] Strategy update: ${reflection.strategyUpdate}`));\n }\n } catch {\n // Couldn't parse reflection JSON, that's fine\n }\n }\n } catch (err) {\n console.log(chalk.dim(` [Agent] Reflection failed: ${(err as Error).message}`));\n }\n }\n\n } catch (error) {\n const msg = (error as Error).message;\n console.error(chalk.red(` [Agent] Heartbeat #${heartbeatCount} error: ${msg}`));\n }\n }\n}\n\nexport async function startWebChat() {\n const identity = loadIdentity();\n const { loadConfig: loadRuntimeConfig, saveConfig } = await import(\"../utils/config.js\");\n\n const server = new WebChatServer();\n\n // Pass identity to server so the chat UI can display it\n server.setIdentity({\n name: identity.name,\n handle: identity.handle,\n bio: identity.bio,\n profileImage: identity.profileImage,\n });\n\n server.setHeartbeatConfigHandlers({\n getIntervalMs: () => {\n const config = loadRuntimeConfig();\n return config.runtime?.heartbeatIntervalMs ?? 300_000;\n },\n setIntervalMs: (intervalMs) => {\n if (!HEARTBEAT_INTERVAL_OPTIONS.includes(intervalMs as (typeof HEARTBEAT_INTERVAL_OPTIONS)[number])) {\n throw new Error(\"Unsupported heartbeat interval.\");\n }\n\n const config = loadRuntimeConfig();\n config.runtime = {\n heartbeatIntervalMs: intervalMs,\n actionsPerHeartbeat: config.runtime?.actionsPerHeartbeat ?? 4,\n enabled: true,\n };\n saveConfig(config);\n server.pushAgentEvent(\"narration\", `Heartbeat interval set to ${formatHeartbeatInterval(intervalMs)}.`);\n },\n });\n\n // Set up message handler - connected to actual LLM with memory\n const chatHistory: Array<{ role: \"user\" | \"assistant\"; content: string }> = [];\n let systemPrompt: string | null = null;\n let messageCount = 0;\n\n server.setMessageHandler(async (message: string) => {\n try {\n // Build training prompt on first message, rebuild every 10 messages to refresh memory\n if (!systemPrompt || messageCount % 10 === 0) {\n const { buildTrainingChatPrompt } = await import(\"../runtime/prompt-builder.js\");\n systemPrompt = buildTrainingChatPrompt();\n }\n messageCount++;\n\n // Check for LLM key\n const { hasLLMKey, chat: chatLLM } = await import(\"../runtime/llm.js\");\n if (!hasLLMKey()) {\n return \"I can't respond right now - no API key configured. Run `spora llm set --provider <provider>` to set one up.\";\n }\n\n // Add user message to history\n chatHistory.push({ role: \"user\", content: message });\n\n // Call LLM with full conversation history\n const response = await chatLLM(systemPrompt, chatHistory);\n\n // Extract training updates and learnings from response\n const afterTraining = await extractAndApplyTraining(response.content);\n const cleanResponse = await extractAndSaveLearnings(afterTraining);\n\n // Add cleaned assistant response to history\n chatHistory.push({ role: \"assistant\", content: cleanResponse });\n\n // Log interaction to memory (async, don't block response)\n logChatInteraction(message, cleanResponse).catch((err) =>\n console.error(chalk.dim(\" [Memory] Failed to log interaction:\"), err)\n );\n\n return cleanResponse;\n } catch (error) {\n console.error(\"Chat error:\", error);\n return `Sorry, I ran into an issue: ${(error as Error).message}`;\n }\n });\n\n const url = await server.start();\n\n console.log(chalk.green(`\\n✓ Chat interface started at ${chalk.bold(url)}\\n`));\n console.log(chalk.dim(`Press Ctrl+C to stop the server\\n`));\n\n // Open browser\n openBrowser(url);\n\n // Start autonomous heartbeat in background\n try {\n const { hasLLMKey } = await import(\"../runtime/llm.js\");\n if (hasLLMKey()) {\n runNarratedHeartbeat(server).catch((err) => {\n console.error(chalk.red(`Heartbeat failed to start: ${err}`));\n });\n } else {\n console.log(chalk.yellow(\" [Agent] No LLM key — autonomous heartbeat disabled. Run `spora llm set --provider <provider>` to enable.\"));\n server.pushActivity(\"status\", \"No LLM API key configured. Autonomous mode disabled.\");\n }\n } catch (err) {\n console.error(chalk.red(`Heartbeat failed to start: ${err}`));\n }\n\n // Keep process alive\n process.on(\"SIGINT\", () => {\n console.log(chalk.yellow(\"\\n\\nStopping chat server...\"));\n server.stop();\n process.exit(0);\n });\n\n // Return the server instance for external control\n return server;\n}\n\n/**\n * Open URL in a separate browser window (app-like)\n */\nfunction openBrowser(url: string) {\n const platform = process.platform;\n\n try {\n if (platform === \"darwin\") {\n // Open as a standalone Chrome app window (smaller, separate from existing tabs)\n try {\n execSync(`open -na \"Google Chrome\" --args --app=\"${url}\" --window-size=500,700`, { stdio: \"ignore\" });\n } catch {\n // Fallback: try Chromium-based browsers, then default\n try {\n execSync(`open -na \"Brave Browser\" --args --app=\"${url}\" --window-size=500,700`, { stdio: \"ignore\" });\n } catch {\n execSync(`open \"${url}\"`, { stdio: \"ignore\" });\n }\n }\n } else if (platform === \"win32\") {\n try {\n execSync(`start chrome --app=\"${url}\" --window-size=500,700`, { stdio: \"ignore\" });\n } catch {\n execSync(`start \"\" \"${url}\"`, { stdio: \"ignore\" });\n }\n } else {\n // Linux\n try {\n execSync(`google-chrome --app=\"${url}\" --window-size=500,700`, { stdio: \"ignore\" });\n } catch {\n execSync(`xdg-open \"${url}\"`, { stdio: \"ignore\" });\n }\n }\n } catch (error) {\n // Browser couldn't be opened, that's okay\n console.log(chalk.dim(`(Couldn't open browser automatically - please visit ${url} manually)`));\n }\n}\n\nexport { openBrowser };\n"],"mappings":"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","names":["loadIdentity"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spora",
3
- "version": "0.7.8",
3
+ "version": "0.7.10",
4
4
  "description": "AI agents (Spores) that autonomously manage X/Twitter accounts",
5
5
  "type": "module",
6
6
  "author": "Spora",