spora 0.7.3 → 0.7.5

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 (90) hide show
  1. package/dist/autonomy-DFPA3OK6.js +20 -0
  2. package/dist/{chunk-SUZUJGGW.js → chunk-342ZX72W.js} +4 -4
  3. package/dist/{chunk-HGNMHGAF.js → chunk-5R4AJZHN.js} +4 -4
  4. package/dist/{chunk-HGNMHGAF.js.map → chunk-5R4AJZHN.js.map} +1 -1
  5. package/dist/{chunk-Q3YXJ2C6.js → chunk-A2XTKC7B.js} +281 -65
  6. package/dist/chunk-A2XTKC7B.js.map +1 -0
  7. package/dist/{chunk-JBYZ7K56.js → chunk-BBXHECZ5.js} +2 -2
  8. package/dist/{chunk-WN35MRMF.js → chunk-CAWWG3MD.js} +2 -2
  9. package/dist/chunk-CP6JWCLY.js +171 -0
  10. package/dist/chunk-CP6JWCLY.js.map +1 -0
  11. package/dist/{chunk-T7L2L7ZL.js → chunk-D47OFTEK.js} +2 -2
  12. package/dist/chunk-HXI2EH5C.js +2338 -0
  13. package/dist/chunk-HXI2EH5C.js.map +1 -0
  14. package/dist/{chunk-M6YOQVSI.js → chunk-IULO3GRE.js} +4 -4
  15. package/dist/chunk-IULO3GRE.js.map +1 -0
  16. package/dist/chunk-OTZNHIXT.js +431 -0
  17. package/dist/chunk-OTZNHIXT.js.map +1 -0
  18. package/dist/{chunk-NO3NQN67.js → chunk-QYFNAGNI.js} +2 -2
  19. package/dist/{chunk-YMGJQRKG.js → chunk-RSNEVBEI.js} +2 -2
  20. package/dist/{chunk-VZBHRUZS.js → chunk-SXNZVKLJ.js} +2 -2
  21. package/dist/chunk-SXNZVKLJ.js.map +1 -0
  22. package/dist/{chunk-7OOGNZBU.js → chunk-ZLSDFYBR.js} +17 -5
  23. package/dist/chunk-ZLSDFYBR.js.map +1 -0
  24. package/dist/{chunk-3RYCUGXE.js → chunk-ZWKTKWS6.js} +4 -1
  25. package/dist/chunk-ZWKTKWS6.js.map +1 -0
  26. package/dist/cli.js +49 -49
  27. package/dist/{client-4KGOBIXE.js → client-AR5ZD6S4.js} +48 -16
  28. package/dist/client-AR5ZD6S4.js.map +1 -0
  29. package/dist/{colony-IVYR233C.js → colony-UGVYALOS.js} +7 -7
  30. package/dist/{config-FL4VJVKZ.js → config-MU2ODEO3.js} +3 -3
  31. package/dist/{crypto-NOXNL4GP.js → crypto-GDG5K3ZH.js} +3 -3
  32. package/dist/{goals-RBKLMILE.js → goals-QWX3A47Y.js} +3 -3
  33. package/dist/{heartbeat-CUL2FTFD.js → heartbeat-RAOEIKWC.js} +18 -21
  34. package/dist/heartbeat-RAOEIKWC.js.map +1 -0
  35. package/dist/{identity-VDUW4I2K.js → identity-ASHVWIN5.js} +3 -3
  36. package/dist/{init-2REECUVH.js → init-ETUTFHT6.js} +59 -13
  37. package/dist/init-ETUTFHT6.js.map +1 -0
  38. package/dist/{llm-OGOYCWBH.js → llm-IJBRQ7O2.js} +5 -5
  39. package/dist/mcp-server.js +24 -24
  40. package/dist/{memory-PNW7SX7A.js → memory-AWKIW2KW.js} +3 -3
  41. package/dist/{memory-OIAH33G2.js → memory-DTSLVSQG.js} +3 -3
  42. package/dist/{paths-BYR6MEPR.js → paths-4V5OCB5F.js} +2 -2
  43. package/dist/prompt-builder-XJHXZCSQ.js +27 -0
  44. package/dist/queue-QCGNDHH2.js +14 -0
  45. package/dist/strategy-R2BMRVJ3.js +19 -0
  46. package/dist/web-chat/chat.html +190 -3
  47. package/dist/{web-chat-O24HGJVE.js → web-chat-TB3ACPVF.js} +125 -32
  48. package/dist/web-chat-TB3ACPVF.js.map +1 -0
  49. package/dist/x-client-S2LUVEKV.js +12 -0
  50. package/package.json +1 -1
  51. package/dist/autonomy-6UWPXTPD.js +0 -19
  52. package/dist/chunk-3RYCUGXE.js.map +0 -1
  53. package/dist/chunk-4LNMA56H.js +0 -57
  54. package/dist/chunk-4LNMA56H.js.map +0 -1
  55. package/dist/chunk-7OOGNZBU.js.map +0 -1
  56. package/dist/chunk-M6YOQVSI.js.map +0 -1
  57. package/dist/chunk-P6KZIJYL.js +0 -79
  58. package/dist/chunk-P6KZIJYL.js.map +0 -1
  59. package/dist/chunk-Q3YXJ2C6.js.map +0 -1
  60. package/dist/chunk-VZBHRUZS.js.map +0 -1
  61. package/dist/chunk-ZBP2ROAZ.js +0 -377
  62. package/dist/chunk-ZBP2ROAZ.js.map +0 -1
  63. package/dist/client-4KGOBIXE.js.map +0 -1
  64. package/dist/heartbeat-CUL2FTFD.js.map +0 -1
  65. package/dist/init-2REECUVH.js.map +0 -1
  66. package/dist/prompt-builder-KJKFCGM7.js +0 -25
  67. package/dist/queue-D3MRKABU.js +0 -14
  68. package/dist/strategy-Z4JSFHSP.js +0 -12
  69. package/dist/web-chat-O24HGJVE.js.map +0 -1
  70. package/dist/x-client-ASXVQ6EV.js +0 -12
  71. /package/dist/{autonomy-6UWPXTPD.js.map → autonomy-DFPA3OK6.js.map} +0 -0
  72. /package/dist/{chunk-SUZUJGGW.js.map → chunk-342ZX72W.js.map} +0 -0
  73. /package/dist/{chunk-JBYZ7K56.js.map → chunk-BBXHECZ5.js.map} +0 -0
  74. /package/dist/{chunk-WN35MRMF.js.map → chunk-CAWWG3MD.js.map} +0 -0
  75. /package/dist/{chunk-T7L2L7ZL.js.map → chunk-D47OFTEK.js.map} +0 -0
  76. /package/dist/{chunk-NO3NQN67.js.map → chunk-QYFNAGNI.js.map} +0 -0
  77. /package/dist/{chunk-YMGJQRKG.js.map → chunk-RSNEVBEI.js.map} +0 -0
  78. /package/dist/{colony-IVYR233C.js.map → colony-UGVYALOS.js.map} +0 -0
  79. /package/dist/{config-FL4VJVKZ.js.map → config-MU2ODEO3.js.map} +0 -0
  80. /package/dist/{crypto-NOXNL4GP.js.map → crypto-GDG5K3ZH.js.map} +0 -0
  81. /package/dist/{goals-RBKLMILE.js.map → goals-QWX3A47Y.js.map} +0 -0
  82. /package/dist/{identity-VDUW4I2K.js.map → identity-ASHVWIN5.js.map} +0 -0
  83. /package/dist/{llm-OGOYCWBH.js.map → llm-IJBRQ7O2.js.map} +0 -0
  84. /package/dist/{memory-OIAH33G2.js.map → memory-AWKIW2KW.js.map} +0 -0
  85. /package/dist/{memory-PNW7SX7A.js.map → memory-DTSLVSQG.js.map} +0 -0
  86. /package/dist/{paths-BYR6MEPR.js.map → paths-4V5OCB5F.js.map} +0 -0
  87. /package/dist/{prompt-builder-KJKFCGM7.js.map → prompt-builder-XJHXZCSQ.js.map} +0 -0
  88. /package/dist/{queue-D3MRKABU.js.map → queue-QCGNDHH2.js.map} +0 -0
  89. /package/dist/{strategy-Z4JSFHSP.js.map → strategy-R2BMRVJ3.js.map} +0 -0
  90. /package/dist/{x-client-ASXVQ6EV.js.map → x-client-S2LUVEKV.js.map} +0 -0
@@ -0,0 +1,27 @@
1
+ import {
2
+ buildChatPrompt,
3
+ buildHeartbeatUserMessage,
4
+ buildOpportunityPortfolioMessage,
5
+ buildReflectionPrompt,
6
+ buildSystemPrompt,
7
+ buildToolDecisionMessage,
8
+ buildTrainingChatPrompt
9
+ } from "./chunk-A2XTKC7B.js";
10
+ import "./chunk-OTZNHIXT.js";
11
+ import "./chunk-CAWWG3MD.js";
12
+ import "./chunk-CP6JWCLY.js";
13
+ import "./chunk-IULO3GRE.js";
14
+ import "./chunk-RSNEVBEI.js";
15
+ import "./chunk-QYFNAGNI.js";
16
+ import "./chunk-BBXHECZ5.js";
17
+ import "./chunk-ZWKTKWS6.js";
18
+ export {
19
+ buildChatPrompt,
20
+ buildHeartbeatUserMessage,
21
+ buildOpportunityPortfolioMessage,
22
+ buildReflectionPrompt,
23
+ buildSystemPrompt,
24
+ buildToolDecisionMessage,
25
+ buildTrainingChatPrompt
26
+ };
27
+ //# sourceMappingURL=prompt-builder-XJHXZCSQ.js.map
@@ -0,0 +1,14 @@
1
+ import {
2
+ addToQueue,
3
+ flushQueue,
4
+ showQueue
5
+ } from "./chunk-ZLSDFYBR.js";
6
+ import "./chunk-RSNEVBEI.js";
7
+ import "./chunk-QYFNAGNI.js";
8
+ import "./chunk-ZWKTKWS6.js";
9
+ export {
10
+ addToQueue,
11
+ flushQueue,
12
+ showQueue
13
+ };
14
+ //# sourceMappingURL=queue-QCGNDHH2.js.map
@@ -0,0 +1,19 @@
1
+ import {
2
+ applyStrategyUpdate,
3
+ deriveStrategyIntent,
4
+ extractHandlesFromText,
5
+ loadStrategy,
6
+ renderStrategyForPrompt,
7
+ saveStrategy
8
+ } from "./chunk-OTZNHIXT.js";
9
+ import "./chunk-IULO3GRE.js";
10
+ import "./chunk-ZWKTKWS6.js";
11
+ export {
12
+ applyStrategyUpdate,
13
+ deriveStrategyIntent,
14
+ extractHandlesFromText,
15
+ loadStrategy,
16
+ renderStrategyForPrompt,
17
+ saveStrategy
18
+ };
19
+ //# sourceMappingURL=strategy-R2BMRVJ3.js.map
@@ -38,7 +38,7 @@
38
38
  margin-left: auto;
39
39
  display: flex;
40
40
  align-items: center;
41
- gap: 0.5rem;
41
+ gap: 0.75rem;
42
42
  }
43
43
 
44
44
  .status-dot {
@@ -54,6 +54,82 @@
54
54
  color: rgba(255, 255, 255, 0.4);
55
55
  }
56
56
 
57
+ .heartbeat-menu {
58
+ position: relative;
59
+ }
60
+
61
+ .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;
68
+ cursor: pointer;
69
+ font-size: 0.95rem;
70
+ line-height: 1;
71
+ display: inline-flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ transition: transform 0.15s ease, border-color 0.2s ease;
75
+ }
76
+
77
+ .heartbeat-trigger:hover {
78
+ transform: scale(1.06);
79
+ border-color: rgba(255, 100, 125, 0.45);
80
+ }
81
+
82
+ .heartbeat-trigger:disabled {
83
+ opacity: 0.4;
84
+ cursor: not-allowed;
85
+ }
86
+
87
+ .heartbeat-popover {
88
+ position: absolute;
89
+ right: 0;
90
+ top: calc(100% + 0.45rem);
91
+ min-width: 150px;
92
+ background: #111111;
93
+ border: 1px solid rgba(255, 255, 255, 0.14);
94
+ border-radius: 0.7rem;
95
+ padding: 0.5rem;
96
+ z-index: 10;
97
+ box-shadow: 0 8px 18px rgba(0, 0, 0, 0.45);
98
+ animation: fadeIn 0.18s ease-in;
99
+ }
100
+
101
+ .heartbeat-popover-title {
102
+ font-size: 0.67rem;
103
+ color: rgba(255, 255, 255, 0.44);
104
+ margin-bottom: 0.35rem;
105
+ padding: 0 0.2rem;
106
+ text-transform: uppercase;
107
+ letter-spacing: 0.04em;
108
+ }
109
+
110
+ .heartbeat-option {
111
+ width: 100%;
112
+ border: 1px solid transparent;
113
+ background: transparent;
114
+ color: rgba(255, 255, 255, 0.88);
115
+ padding: 0.42rem 0.5rem;
116
+ font-size: 0.75rem;
117
+ text-align: left;
118
+ border-radius: 0.5rem;
119
+ cursor: pointer;
120
+ transition: background 0.18s ease, border-color 0.18s ease;
121
+ }
122
+
123
+ .heartbeat-option:hover {
124
+ background: rgba(255, 255, 255, 0.06);
125
+ }
126
+
127
+ .heartbeat-option.active {
128
+ border-color: rgba(56, 158, 119, 0.65);
129
+ background: rgba(56, 158, 119, 0.16);
130
+ color: #89dbba;
131
+ }
132
+
57
133
  @keyframes pulse {
58
134
  0%, 100% { opacity: 1; }
59
135
  50% { opacity: 0.5; }
@@ -328,7 +404,18 @@
328
404
  </svg>
329
405
  <div class="header-right">
330
406
  <span class="status-dot"></span>
331
- <span class="status-text">Online</span>
407
+ <span class="status-text" id="statusText">Online</span>
408
+ <div class="heartbeat-menu" id="heartbeatMenu">
409
+ <button class="heartbeat-trigger" id="heartbeatTrigger" title="Heartbeat settings" aria-label="Heartbeat settings">♥</button>
410
+ <div class="heartbeat-popover" id="heartbeatPopover" hidden>
411
+ <div class="heartbeat-popover-title">Heartbeat</div>
412
+ <button class="heartbeat-option" data-interval="60000">1 min</button>
413
+ <button class="heartbeat-option" data-interval="300000">5 min</button>
414
+ <button class="heartbeat-option" data-interval="1800000">30 min</button>
415
+ <button class="heartbeat-option" data-interval="3600000">1 hour</button>
416
+ <button class="heartbeat-option" data-interval="21600000">6 hours</button>
417
+ </div>
418
+ </div>
332
419
  </div>
333
420
  </div>
334
421
 
@@ -350,12 +437,86 @@
350
437
  const chatContainer = document.getElementById('chatContainer');
351
438
  const messageInput = document.getElementById('messageInput');
352
439
  const sendButton = document.getElementById('sendButton');
440
+ const heartbeatMenu = document.getElementById('heartbeatMenu');
441
+ const heartbeatTrigger = document.getElementById('heartbeatTrigger');
442
+ const heartbeatPopover = document.getElementById('heartbeatPopover');
443
+ const heartbeatOptions = Array.from(document.querySelectorAll('.heartbeat-option'));
444
+ const statusText = document.getElementById('statusText');
353
445
 
354
446
  let isLoading = false;
355
447
  let agentName = 'Your Spore';
356
448
  let agentHandle = '';
357
449
  let agentPfp = null;
358
450
  let sleepTimerId = null;
451
+ let heartbeatMs = 300000;
452
+
453
+ const HEARTBEAT_LABELS = {
454
+ 60000: '1 min',
455
+ 300000: '5 min',
456
+ 1800000: '30 min',
457
+ 3600000: '1 hour',
458
+ 21600000: '6 hours',
459
+ };
460
+
461
+ function heartbeatLabel(ms) {
462
+ return HEARTBEAT_LABELS[ms] || `${Math.round(ms / 60000)} min`;
463
+ }
464
+
465
+ function updateStatusText() {
466
+ statusText.textContent = `Online • ${heartbeatLabel(heartbeatMs)} heartbeat`;
467
+ }
468
+
469
+ function setHeartbeatPopoverOpen(isOpen) {
470
+ heartbeatPopover.hidden = !isOpen;
471
+ }
472
+
473
+ function updateHeartbeatOptions() {
474
+ for (const option of heartbeatOptions) {
475
+ const value = parseInt(option.dataset.interval || '0', 10);
476
+ option.classList.toggle('active', value === heartbeatMs);
477
+ }
478
+ }
479
+
480
+ async function loadHeartbeatConfig() {
481
+ try {
482
+ const response = await fetch('/api/heartbeat-config');
483
+ if (!response.ok) return;
484
+ const data = await response.json();
485
+ if (data.intervalMs) {
486
+ heartbeatMs = data.intervalMs;
487
+ updateStatusText();
488
+ updateHeartbeatOptions();
489
+ }
490
+ } catch {
491
+ // best effort
492
+ }
493
+ }
494
+
495
+ async function setHeartbeatConfig(intervalMs) {
496
+ const previous = heartbeatMs;
497
+ heartbeatTrigger.disabled = true;
498
+ setHeartbeatPopoverOpen(false);
499
+ try {
500
+ const response = await fetch('/api/heartbeat-config', {
501
+ method: 'POST',
502
+ headers: { 'Content-Type': 'application/json' },
503
+ body: JSON.stringify({ intervalMs }),
504
+ });
505
+ if (!response.ok) {
506
+ throw new Error('Failed to update heartbeat');
507
+ }
508
+ heartbeatMs = intervalMs;
509
+ updateStatusText();
510
+ updateHeartbeatOptions();
511
+ addNarration(`Heartbeat updated to ${heartbeatLabel(intervalMs)}`, 'action');
512
+ } catch {
513
+ heartbeatMs = previous;
514
+ updateHeartbeatOptions();
515
+ addNarration('Could not update heartbeat interval', 'error');
516
+ } finally {
517
+ heartbeatTrigger.disabled = false;
518
+ }
519
+ }
359
520
 
360
521
  // ===== Init =====
361
522
  async function init() {
@@ -372,6 +533,7 @@
372
533
  console.error('Failed to load identity:', error);
373
534
  }
374
535
 
536
+ await loadHeartbeatConfig();
375
537
  addMessage('assistant', `Hey! I'm ${agentName}. I run on my own — posting, replying, exploring X during my heartbeat cycles. You can shape who I am by chatting with me here.`);
376
538
  await loadMessages();
377
539
  }
@@ -480,7 +642,6 @@
480
642
 
481
643
  // ===== Narration =====
482
644
  function addNarration(text, type) {
483
- removeSleepCard();
484
645
  const el = document.createElement('div');
485
646
  el.className = 'narration';
486
647
  const dot = document.createElement('div');
@@ -588,6 +749,30 @@
588
749
  }
589
750
 
590
751
  sendButton.addEventListener('click', sendMessage);
752
+ heartbeatTrigger.addEventListener('click', (e) => {
753
+ e.preventDefault();
754
+ e.stopPropagation();
755
+ setHeartbeatPopoverOpen(heartbeatPopover.hidden);
756
+ });
757
+
758
+ heartbeatPopover.addEventListener('click', (e) => {
759
+ const target = e.target.closest('.heartbeat-option');
760
+ if (!target) return;
761
+ const intervalMs = parseInt(target.dataset.interval || '0', 10);
762
+ if (!Number.isFinite(intervalMs)) return;
763
+ if (intervalMs === heartbeatMs) {
764
+ setHeartbeatPopoverOpen(false);
765
+ return;
766
+ }
767
+ setHeartbeatConfig(intervalMs);
768
+ });
769
+
770
+ document.addEventListener('click', (e) => {
771
+ if (!heartbeatMenu.contains(e.target)) {
772
+ setHeartbeatPopoverOpen(false);
773
+ }
774
+ });
775
+
591
776
  messageInput.addEventListener('keydown', (e) => {
592
777
  if (e.key === 'Enter' && !e.shiftKey) {
593
778
  e.preventDefault();
@@ -642,6 +827,8 @@
642
827
  setInterval(pollAgentEvents, 2000);
643
828
 
644
829
  // Initialize
830
+ updateStatusText();
831
+ updateHeartbeatOptions();
645
832
  init();
646
833
  messageInput.focus();
647
834
  </script>
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  loadIdentity
3
- } from "./chunk-M6YOQVSI.js";
4
- import "./chunk-3RYCUGXE.js";
3
+ } from "./chunk-IULO3GRE.js";
4
+ import "./chunk-ZWKTKWS6.js";
5
5
 
6
6
  // src/web-chat/server.ts
7
7
  import http from "http";
@@ -11,6 +11,7 @@ import { join, dirname } from "path";
11
11
  import { fileURLToPath } from "url";
12
12
  var __filename = fileURLToPath(import.meta.url);
13
13
  var __dirname = dirname(__filename);
14
+ var ALLOWED_HEARTBEAT_INTERVALS = [6e4, 3e5, 18e5, 36e5, 216e5];
14
15
  var WebChatServer = class {
15
16
  server = null;
16
17
  port;
@@ -19,6 +20,7 @@ var WebChatServer = class {
19
20
  agentEvents = [];
20
21
  onUserMessage;
21
22
  identity;
23
+ heartbeatConfigHandlers;
22
24
  constructor(port = 3737) {
23
25
  this.port = port;
24
26
  }
@@ -28,6 +30,9 @@ var WebChatServer = class {
28
30
  setMessageHandler(handler) {
29
31
  this.onUserMessage = handler;
30
32
  }
33
+ setHeartbeatConfigHandlers(handlers) {
34
+ this.heartbeatConfigHandlers = handlers;
35
+ }
31
36
  /** Push an activity update (from heartbeat) to the UI — kept for backwards compat */
32
37
  pushActivity(type, message) {
33
38
  this.activities.push({ type, message, timestamp: Date.now() });
@@ -109,6 +114,52 @@ var WebChatServer = class {
109
114
  res.end(JSON.stringify({ events: newEvents }));
110
115
  return;
111
116
  }
117
+ if (url.pathname === "/api/heartbeat-config" && req.method === "GET") {
118
+ if (!this.heartbeatConfigHandlers) {
119
+ res.writeHead(503, { "Content-Type": "application/json" });
120
+ res.end(JSON.stringify({ error: "Heartbeat config unavailable" }));
121
+ return;
122
+ }
123
+ try {
124
+ const intervalMs = await this.heartbeatConfigHandlers.getIntervalMs();
125
+ res.writeHead(200, { "Content-Type": "application/json" });
126
+ res.end(JSON.stringify({ intervalMs, allowedIntervals: ALLOWED_HEARTBEAT_INTERVALS }));
127
+ } catch {
128
+ res.writeHead(500, { "Content-Type": "application/json" });
129
+ res.end(JSON.stringify({ error: "Failed to load heartbeat config" }));
130
+ }
131
+ return;
132
+ }
133
+ if (url.pathname === "/api/heartbeat-config" && req.method === "POST") {
134
+ if (!this.heartbeatConfigHandlers) {
135
+ res.writeHead(503, { "Content-Type": "application/json" });
136
+ res.end(JSON.stringify({ error: "Heartbeat config unavailable" }));
137
+ return;
138
+ }
139
+ const handlers = this.heartbeatConfigHandlers;
140
+ let body = "";
141
+ req.on("data", (chunk) => {
142
+ body += chunk.toString();
143
+ });
144
+ req.on("end", async () => {
145
+ try {
146
+ const parsed = JSON.parse(body);
147
+ const intervalMs = parsed.intervalMs;
148
+ if (typeof intervalMs !== "number" || !Number.isInteger(intervalMs) || !ALLOWED_HEARTBEAT_INTERVALS.includes(intervalMs)) {
149
+ res.writeHead(400, { "Content-Type": "application/json" });
150
+ res.end(JSON.stringify({ error: "Unsupported heartbeat interval" }));
151
+ return;
152
+ }
153
+ await handlers.setIntervalMs(intervalMs);
154
+ res.writeHead(200, { "Content-Type": "application/json" });
155
+ res.end(JSON.stringify({ success: true, intervalMs }));
156
+ } catch {
157
+ res.writeHead(400, { "Content-Type": "application/json" });
158
+ res.end(JSON.stringify({ error: "Invalid request" }));
159
+ }
160
+ });
161
+ return;
162
+ }
112
163
  if (url.pathname === "/api/message" && req.method === "POST") {
113
164
  let body = "";
114
165
  req.on("data", (chunk) => {
@@ -171,11 +222,18 @@ var WebChatServer = class {
171
222
  // src/web-chat/index.ts
172
223
  import { execSync } from "child_process";
173
224
  import chalk from "chalk";
225
+ var HEARTBEAT_INTERVAL_OPTIONS = [6e4, 3e5, 18e5, 36e5, 216e5];
226
+ function formatHeartbeatInterval(intervalMs) {
227
+ const minutes = intervalMs / 6e4;
228
+ if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"}`;
229
+ const hours = minutes / 60;
230
+ return `${hours} hour${hours === 1 ? "" : "s"}`;
231
+ }
174
232
  async function extractAndSaveLearnings(responseText) {
175
233
  const learnPattern = /<<LEARN:\s*(.+?)>>/g;
176
234
  const matches = [...responseText.matchAll(learnPattern)];
177
235
  if (matches.length > 0) {
178
- const { addLearning } = await import("./memory-OIAH33G2.js");
236
+ const { addLearning } = await import("./memory-DTSLVSQG.js");
179
237
  for (const match of matches) {
180
238
  const learning = match[1].trim();
181
239
  addLearning(learning, "web-chat", ["chat", "creator-interaction"]);
@@ -191,7 +249,7 @@ async function extractAndApplyTraining(responseText) {
191
249
  try {
192
250
  const update = JSON.parse(match[1].trim());
193
251
  if (update.identity) {
194
- const { loadIdentity: loadIdentity2, mutateIdentity, saveIdentity } = await import("./identity-VDUW4I2K.js");
252
+ const { loadIdentity: loadIdentity2, mutateIdentity, saveIdentity } = await import("./identity-ASHVWIN5.js");
195
253
  let identity = loadIdentity2();
196
254
  const identityFields = {
197
255
  tone: "tone",
@@ -230,7 +288,7 @@ async function extractAndApplyTraining(responseText) {
230
288
  saveIdentity(identity);
231
289
  }
232
290
  if (update.strategy) {
233
- const { loadStrategy, saveStrategy } = await import("./strategy-Z4JSFHSP.js");
291
+ const { loadStrategy, saveStrategy } = await import("./strategy-R2BMRVJ3.js");
234
292
  const strategy = loadStrategy();
235
293
  if (update.strategy.currentFocus) strategy.currentFocus = update.strategy.currentFocus;
236
294
  if (update.strategy.shortTermGoals) strategy.shortTermGoals = update.strategy.shortTermGoals;
@@ -245,7 +303,7 @@ async function extractAndApplyTraining(responseText) {
245
303
  console.log(chalk.dim(` [Training] Updated strategy`));
246
304
  }
247
305
  if (update.learning) {
248
- const { addLearning } = await import("./memory-OIAH33G2.js");
306
+ const { addLearning } = await import("./memory-DTSLVSQG.js");
249
307
  addLearning(
250
308
  update.learning.content,
251
309
  "training-chat",
@@ -254,7 +312,7 @@ async function extractAndApplyTraining(responseText) {
254
312
  console.log(chalk.dim(` [Training] Saved learning: ${update.learning.content}`));
255
313
  }
256
314
  if (update.reflection) {
257
- const { loadIdentity: loadIdentity2, saveIdentity } = await import("./identity-VDUW4I2K.js");
315
+ const { loadIdentity: loadIdentity2, saveIdentity } = await import("./identity-ASHVWIN5.js");
258
316
  const identity = loadIdentity2();
259
317
  identity.evolutionJournal.push({
260
318
  date: (/* @__PURE__ */ new Date()).toISOString(),
@@ -264,7 +322,7 @@ async function extractAndApplyTraining(responseText) {
264
322
  console.log(chalk.dim(` [Training] Added reflection to evolution journal`));
265
323
  }
266
324
  if (update.goalUpdates && Array.isArray(update.goalUpdates)) {
267
- const { loadGoals, saveGoals } = await import("./goals-RBKLMILE.js");
325
+ const { loadGoals, saveGoals } = await import("./goals-QWX3A47Y.js");
268
326
  const tracker = loadGoals();
269
327
  for (const gu of update.goalUpdates) {
270
328
  const existing = tracker.goals.find((g) => g.goal === gu.goal);
@@ -290,7 +348,7 @@ async function extractAndApplyTraining(responseText) {
290
348
  return responseText.replace(/<<TRAINING:[\s\S]*?>>/g, "").trim();
291
349
  }
292
350
  async function logChatInteraction(userMessage, agentResponse) {
293
- const { logInteraction } = await import("./memory-OIAH33G2.js");
351
+ const { logInteraction } = await import("./memory-DTSLVSQG.js");
294
352
  logInteraction({
295
353
  id: `chat-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
296
354
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -302,26 +360,46 @@ async function logChatInteraction(userMessage, agentResponse) {
302
360
  });
303
361
  }
304
362
  async function runNarratedHeartbeat(server) {
305
- const { loadConfig } = await import("./config-FL4VJVKZ.js");
306
- const { flushQueue } = await import("./queue-D3MRKABU.js");
307
- const { runAutonomyCycle } = await import("./autonomy-6UWPXTPD.js");
308
- const { buildReflectionPrompt } = await import("./prompt-builder-KJKFCGM7.js");
309
- const { generateResponse } = await import("./llm-OGOYCWBH.js");
310
- const { addLearning } = await import("./memory-OIAH33G2.js");
311
- const { loadStrategy, saveStrategy } = await import("./strategy-Z4JSFHSP.js");
312
- const config = loadConfig();
313
- const intervalMs = config.runtime?.heartbeatIntervalMs ?? 3e5;
314
- const maxActions = config.runtime?.actionsPerHeartbeat ?? 4;
363
+ const { loadConfig } = await import("./config-MU2ODEO3.js");
364
+ const { flushQueue } = await import("./queue-QCGNDHH2.js");
365
+ const { runAutonomyCycle } = await import("./autonomy-DFPA3OK6.js");
366
+ const { buildReflectionPrompt } = await import("./prompt-builder-XJHXZCSQ.js");
367
+ const { generateResponse } = await import("./llm-IJBRQ7O2.js");
368
+ const { addLearning } = await import("./memory-DTSLVSQG.js");
369
+ const { loadStrategy, saveStrategy, applyStrategyUpdate } = await import("./strategy-R2BMRVJ3.js");
315
370
  let heartbeatCount = 0;
316
- console.log(chalk.cyan(` [Agent] Autonomous heartbeat started (every ${Math.round(intervalMs / 6e4)} min)`));
371
+ let lastIntervalMs = loadConfig().runtime?.heartbeatIntervalMs ?? 3e5;
372
+ console.log(chalk.cyan(` [Agent] Autonomous heartbeat started (every ${Math.round(lastIntervalMs / 6e4)} min)`));
317
373
  while (true) {
374
+ const runtimeConfig = loadConfig();
375
+ const intervalMs = runtimeConfig.runtime?.heartbeatIntervalMs ?? 3e5;
376
+ const maxActions = runtimeConfig.runtime?.actionsPerHeartbeat ?? 4;
377
+ if (intervalMs !== lastIntervalMs) {
378
+ server.pushAgentEvent("narration", `Heartbeat interval updated to ${formatHeartbeatInterval(intervalMs)}.`);
379
+ console.log(chalk.cyan(` [Agent] Heartbeat interval updated to ${Math.round(intervalMs / 6e4)} min`));
380
+ lastIntervalMs = intervalMs;
381
+ }
318
382
  const jitter = Math.floor(Math.random() * intervalMs * 0.3);
319
383
  const sleepMs = heartbeatCount === 0 ? 1e4 : intervalMs + jitter;
320
384
  const wakeUpAt = Date.now() + sleepMs;
321
385
  if (heartbeatCount > 0) {
322
386
  server.pushAgentEvent("sleep", "sleeping", { wakeUpAt });
323
387
  }
324
- await new Promise((r) => setTimeout(r, sleepMs));
388
+ let sleptMs = 0;
389
+ let intervalChangedDuringSleep = false;
390
+ while (sleptMs < sleepMs) {
391
+ const chunkMs = Math.min(1e3, sleepMs - sleptMs);
392
+ await new Promise((r) => setTimeout(r, chunkMs));
393
+ sleptMs += chunkMs;
394
+ const liveIntervalMs = loadConfig().runtime?.heartbeatIntervalMs ?? 3e5;
395
+ if (liveIntervalMs !== intervalMs) {
396
+ intervalChangedDuringSleep = true;
397
+ break;
398
+ }
399
+ }
400
+ if (intervalChangedDuringSleep) {
401
+ continue;
402
+ }
325
403
  heartbeatCount++;
326
404
  console.log(chalk.cyan(` [Agent] Heartbeat #${heartbeatCount}`));
327
405
  server.pushAgentEvent("wake", `Waking up... heartbeat #${heartbeatCount}`);
@@ -334,7 +412,7 @@ async function runNarratedHeartbeat(server) {
334
412
  } catch {
335
413
  }
336
414
  server.pushAgentEvent("narration", "Observing timeline and planning actions...");
337
- const cycle = await runAutonomyCycle(maxActions);
415
+ const cycle = await runAutonomyCycle(maxActions, heartbeatCount);
338
416
  if (cycle.timeline.length > 0 || cycle.mentions.length > 0) {
339
417
  server.pushAgentEvent("narration", `Found ${cycle.timeline.length} timeline posts and ${cycle.mentions.length} mentions`);
340
418
  }
@@ -395,12 +473,7 @@ async function runNarratedHeartbeat(server) {
395
473
  }
396
474
  if (reflection.strategyUpdate && reflection.strategyUpdate !== "null") {
397
475
  const strategy = loadStrategy();
398
- strategy.experiments.push({
399
- description: reflection.strategyUpdate,
400
- status: "pending"
401
- });
402
- strategy.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
403
- saveStrategy(strategy);
476
+ saveStrategy(applyStrategyUpdate(strategy, reflection.strategyUpdate));
404
477
  server.pushAgentEvent("narration", `Strategy: ${reflection.strategyUpdate}`);
405
478
  console.log(chalk.dim(` [Agent] Strategy update: ${reflection.strategyUpdate}`));
406
479
  }
@@ -420,6 +493,7 @@ async function runNarratedHeartbeat(server) {
420
493
  }
421
494
  async function startWebChat() {
422
495
  const identity = loadIdentity();
496
+ const { loadConfig: loadRuntimeConfig, saveConfig } = await import("./config-MU2ODEO3.js");
423
497
  const server = new WebChatServer();
424
498
  server.setIdentity({
425
499
  name: identity.name,
@@ -427,17 +501,36 @@ async function startWebChat() {
427
501
  bio: identity.bio,
428
502
  profileImage: identity.profileImage
429
503
  });
504
+ server.setHeartbeatConfigHandlers({
505
+ getIntervalMs: () => {
506
+ const config = loadRuntimeConfig();
507
+ return config.runtime?.heartbeatIntervalMs ?? 3e5;
508
+ },
509
+ setIntervalMs: (intervalMs) => {
510
+ if (!HEARTBEAT_INTERVAL_OPTIONS.includes(intervalMs)) {
511
+ throw new Error("Unsupported heartbeat interval.");
512
+ }
513
+ const config = loadRuntimeConfig();
514
+ config.runtime = {
515
+ heartbeatIntervalMs: intervalMs,
516
+ actionsPerHeartbeat: config.runtime?.actionsPerHeartbeat ?? 4,
517
+ enabled: true
518
+ };
519
+ saveConfig(config);
520
+ server.pushAgentEvent("narration", `Heartbeat interval set to ${formatHeartbeatInterval(intervalMs)}.`);
521
+ }
522
+ });
430
523
  const chatHistory = [];
431
524
  let systemPrompt = null;
432
525
  let messageCount = 0;
433
526
  server.setMessageHandler(async (message) => {
434
527
  try {
435
528
  if (!systemPrompt || messageCount % 10 === 0) {
436
- const { buildTrainingChatPrompt } = await import("./prompt-builder-KJKFCGM7.js");
529
+ const { buildTrainingChatPrompt } = await import("./prompt-builder-XJHXZCSQ.js");
437
530
  systemPrompt = buildTrainingChatPrompt();
438
531
  }
439
532
  messageCount++;
440
- const { hasLLMKey, chat: chatLLM } = await import("./llm-OGOYCWBH.js");
533
+ const { hasLLMKey, chat: chatLLM } = await import("./llm-IJBRQ7O2.js");
441
534
  if (!hasLLMKey()) {
442
535
  return "I can't respond right now - no API key configured. Run `spora llm set --provider <provider>` to set one up.";
443
536
  }
@@ -463,7 +556,7 @@ async function startWebChat() {
463
556
  `));
464
557
  openBrowser(url);
465
558
  try {
466
- const { hasLLMKey } = await import("./llm-OGOYCWBH.js");
559
+ const { hasLLMKey } = await import("./llm-IJBRQ7O2.js");
467
560
  if (hasLLMKey()) {
468
561
  runNarratedHeartbeat(server).catch((err) => {
469
562
  console.error(chalk.red(`Heartbeat failed to start: ${err}`));
@@ -516,4 +609,4 @@ export {
516
609
  openBrowser,
517
610
  startWebChat
518
611
  };
519
- //# sourceMappingURL=web-chat-O24HGJVE.js.map
612
+ //# sourceMappingURL=web-chat-TB3ACPVF.js.map