spora 0.2.12 → 0.2.14

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 (34) hide show
  1. package/dist/{chunk-TSKHCOCJ.js → chunk-DHT5ORFX.js} +1 -1
  2. package/dist/{chunk-IN2KCKTO.js → chunk-GBOY5OQ6.js} +2 -2
  3. package/dist/{chunk-VSVZU476.js → chunk-TIY2L4F5.js} +3 -3
  4. package/dist/{chunk-Q57HNZDI.js → chunk-VTWZZNME.js} +43 -1
  5. package/dist/chunk-VTWZZNME.js.map +1 -0
  6. package/dist/cli.js +25 -25
  7. package/dist/cli.js.map +1 -1
  8. package/dist/{colony-JHR2B5OC.js → colony-4G3JMXXW.js} +2 -2
  9. package/dist/{decision-engine-JDAIVQH3.js → decision-engine-5FC6NCT4.js} +4 -4
  10. package/dist/{heartbeat-4CHHSMFQ.js → heartbeat-WFEX774V.js} +6 -6
  11. package/dist/{init-SOIKE75S.js → init-7TST23CG.js} +3 -3
  12. package/dist/{mcp-server-4YBCA4QH.js → mcp-server.js} +20 -20
  13. package/dist/{prompt-builder-7NIQIHTB.js → prompt-builder-VG7CUPU2.js} +4 -2
  14. package/dist/{queue-WMUESED4.js → queue-YEVE53NQ.js} +2 -2
  15. package/dist/web-chat/chat.html +143 -21
  16. package/dist/{web-chat-DUY5W7S3.js → web-chat-LNNJUCFA.js} +134 -6
  17. package/dist/web-chat-LNNJUCFA.js.map +1 -0
  18. package/dist/{x-client-7LK3F56M.js → x-client-YE6QFHEN.js} +2 -2
  19. package/package.json +1 -1
  20. package/dist/chat.html +0 -414
  21. package/dist/chunk-Q57HNZDI.js.map +0 -1
  22. package/dist/logo.png +0 -0
  23. package/dist/web-chat-DUY5W7S3.js.map +0 -1
  24. /package/dist/{chunk-TSKHCOCJ.js.map → chunk-DHT5ORFX.js.map} +0 -0
  25. /package/dist/{chunk-IN2KCKTO.js.map → chunk-GBOY5OQ6.js.map} +0 -0
  26. /package/dist/{chunk-VSVZU476.js.map → chunk-TIY2L4F5.js.map} +0 -0
  27. /package/dist/{colony-JHR2B5OC.js.map → colony-4G3JMXXW.js.map} +0 -0
  28. /package/dist/{decision-engine-JDAIVQH3.js.map → decision-engine-5FC6NCT4.js.map} +0 -0
  29. /package/dist/{heartbeat-4CHHSMFQ.js.map → heartbeat-WFEX774V.js.map} +0 -0
  30. /package/dist/{init-SOIKE75S.js.map → init-7TST23CG.js.map} +0 -0
  31. /package/dist/{mcp-server-4YBCA4QH.js.map → mcp-server.js.map} +0 -0
  32. /package/dist/{prompt-builder-7NIQIHTB.js.map → prompt-builder-VG7CUPU2.js.map} +0 -0
  33. /package/dist/{queue-WMUESED4.js.map → queue-YEVE53NQ.js.map} +0 -0
  34. /package/dist/{x-client-7LK3F56M.js.map → x-client-YE6QFHEN.js.map} +0 -0
@@ -20,38 +20,84 @@
20
20
  flex-direction: column;
21
21
  }
22
22
 
23
- .header {
24
- padding: 0.875rem 1.25rem;
23
+ /* Twitter DM-style profile header */
24
+ .profile-header {
25
+ padding: 1.25rem 1rem 1rem;
25
26
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
26
27
  display: flex;
28
+ flex-direction: column;
27
29
  align-items: center;
28
- gap: 0.75rem;
29
30
  background: #0a0a0a;
31
+ flex-shrink: 0;
32
+ }
33
+
34
+ .profile-avatar {
35
+ width: 56px;
36
+ height: 56px;
37
+ border-radius: 50%;
38
+ background: #1a1a1a;
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ overflow: hidden;
43
+ border: 2px solid rgba(255, 255, 255, 0.1);
44
+ margin-bottom: 0.5rem;
45
+ }
46
+
47
+ .profile-avatar img {
48
+ width: 100%;
49
+ height: 100%;
50
+ object-fit: cover;
51
+ }
52
+
53
+ .profile-avatar-letter {
54
+ font-size: 1.25rem;
55
+ font-weight: 700;
56
+ color: #fff;
30
57
  }
31
58
 
32
- .logo-img {
33
- height: 24px;
34
- width: auto;
59
+ .profile-name {
60
+ font-size: 0.9375rem;
61
+ font-weight: 700;
62
+ color: #fff;
63
+ line-height: 1.2;
64
+ }
65
+
66
+ .profile-handle {
67
+ font-size: 0.8125rem;
68
+ color: rgba(255, 255, 255, 0.45);
69
+ margin-top: 0.125rem;
35
70
  }
36
71
 
37
- .header-right {
38
- margin-left: auto;
72
+ .profile-meta {
39
73
  display: flex;
40
74
  align-items: center;
41
- gap: 0.5rem;
75
+ gap: 0.625rem;
76
+ margin-top: 0.375rem;
77
+ }
78
+
79
+ .profile-joined {
80
+ font-size: 0.75rem;
81
+ color: rgba(255, 255, 255, 0.35);
82
+ }
83
+
84
+ .profile-status {
85
+ display: flex;
86
+ align-items: center;
87
+ gap: 0.3rem;
42
88
  }
43
89
 
44
90
  .status-dot {
45
- width: 7px;
46
- height: 7px;
91
+ width: 6px;
92
+ height: 6px;
47
93
  border-radius: 50%;
48
94
  background: #389e77;
49
95
  animation: pulse 2s infinite;
50
96
  }
51
97
 
52
- .status-text {
98
+ .status-label {
53
99
  font-size: 0.75rem;
54
- color: rgba(255, 255, 255, 0.4);
100
+ color: rgba(255, 255, 255, 0.35);
55
101
  }
56
102
 
57
103
  @keyframes pulse {
@@ -101,8 +147,8 @@
101
147
  }
102
148
 
103
149
  .message-avatar {
104
- width: 40px;
105
- height: 40px;
150
+ width: 32px;
151
+ height: 32px;
106
152
  border-radius: 50%;
107
153
  background: #1a1a1a;
108
154
  display: flex;
@@ -220,11 +266,18 @@
220
266
  </style>
221
267
  </head>
222
268
  <body>
223
- <div class="header">
224
- <img class="logo-img" src="/logo.png" alt="Spora" />
225
- <div class="header-right">
226
- <span class="status-dot"></span>
227
- <span class="status-text">Online</span>
269
+ <div class="profile-header">
270
+ <div class="profile-avatar" id="profileAvatar">
271
+ <span class="profile-avatar-letter" id="profileAvatarLetter"></span>
272
+ </div>
273
+ <div class="profile-name" id="profileName">Your Spore</div>
274
+ <div class="profile-handle" id="profileHandle"></div>
275
+ <div class="profile-meta">
276
+ <span class="profile-joined" id="profileJoined"></span>
277
+ <div class="profile-status">
278
+ <span class="status-dot"></span>
279
+ <span class="status-label">Online</span>
280
+ </div>
228
281
  </div>
229
282
  </div>
230
283
 
@@ -251,7 +304,18 @@
251
304
  let agentName = 'Your Spore';
252
305
  let agentPfp = null;
253
306
 
254
- // Fetch agent identity and show welcome message
307
+ function formatJoinedDate(isoString) {
308
+ try {
309
+ const date = new Date(isoString);
310
+ const month = date.toLocaleString('en-US', { month: 'long' });
311
+ const year = date.getFullYear();
312
+ return `Joined ${month} ${year}`;
313
+ } catch {
314
+ return '';
315
+ }
316
+ }
317
+
318
+ // Fetch agent identity and populate profile header
255
319
  async function init() {
256
320
  try {
257
321
  const response = await fetch('/api/identity');
@@ -262,6 +326,36 @@
262
326
 
263
327
  // Update page title
264
328
  document.title = `${agentName} - Spora`;
329
+
330
+ // Populate profile header
331
+ document.getElementById('profileName').textContent = agentName;
332
+
333
+ if (data.identity.handle) {
334
+ document.getElementById('profileHandle').textContent = `@${data.identity.handle}`;
335
+ }
336
+
337
+ if (data.identity.createdAt) {
338
+ document.getElementById('profileJoined').textContent = formatJoinedDate(data.identity.createdAt);
339
+ }
340
+
341
+ // Profile avatar
342
+ const avatarEl = document.getElementById('profileAvatar');
343
+ const letterEl = document.getElementById('profileAvatarLetter');
344
+ if (agentPfp) {
345
+ const img = document.createElement('img');
346
+ img.src = agentPfp;
347
+ img.alt = agentName;
348
+ img.onerror = function() {
349
+ this.remove();
350
+ letterEl.textContent = agentName.charAt(0).toUpperCase();
351
+ letterEl.style.display = '';
352
+ };
353
+ letterEl.style.display = 'none';
354
+ avatarEl.appendChild(img);
355
+ } else {
356
+ letterEl.textContent = agentName.charAt(0).toUpperCase();
357
+ avatarEl.style.background = '#389e77';
358
+ }
265
359
  }
266
360
  } catch (error) {
267
361
  console.error('Failed to load identity:', error);
@@ -406,6 +500,34 @@
406
500
  }
407
501
  });
408
502
 
503
+ // Poll for new messages (heartbeat narration)
504
+ let lastPollTimestamp = Date.now();
505
+ let polling = false;
506
+
507
+ async function pollNewMessages() {
508
+ if (polling || isLoading) return;
509
+ polling = true;
510
+ try {
511
+ const response = await fetch(`/api/messages?since=${lastPollTimestamp}`);
512
+ const data = await response.json();
513
+ if (data.messages && data.messages.length > 0) {
514
+ for (const msg of data.messages) {
515
+ // Only show messages we didn't create locally
516
+ addMessage(msg.role, msg.content);
517
+ if (msg.timestamp > lastPollTimestamp) {
518
+ lastPollTimestamp = msg.timestamp;
519
+ }
520
+ }
521
+ }
522
+ } catch (error) {
523
+ // Ignore poll errors
524
+ }
525
+ polling = false;
526
+ }
527
+
528
+ // Poll every 3 seconds for heartbeat updates
529
+ setInterval(pollNewMessages, 3000);
530
+
409
531
  // Initialize
410
532
  init();
411
533
  messageInput.focus();
@@ -105,8 +105,16 @@ var WebChatServer = class {
105
105
  return;
106
106
  }
107
107
  if (url.pathname === "/api/messages" && req.method === "GET") {
108
- res.writeHead(200, { "Content-Type": "application/json" });
109
- res.end(JSON.stringify({ messages: this.messages }));
108
+ const since = url.searchParams.get("since");
109
+ if (since) {
110
+ const sinceTs = parseInt(since, 10);
111
+ const newMessages = this.messages.filter((m) => m.timestamp > sinceTs);
112
+ res.writeHead(200, { "Content-Type": "application/json" });
113
+ res.end(JSON.stringify({ messages: newMessages }));
114
+ } else {
115
+ res.writeHead(200, { "Content-Type": "application/json" });
116
+ res.end(JSON.stringify({ messages: this.messages }));
117
+ }
110
118
  return;
111
119
  }
112
120
  if (url.pathname === "/api/message" && req.method === "POST") {
@@ -160,6 +168,15 @@ var WebChatServer = class {
160
168
  });
161
169
  });
162
170
  }
171
+ /**
172
+ * Push a message into the chat from the server side (used by heartbeat narration)
173
+ */
174
+ pushMessage(role, content) {
175
+ this.messages.push({ role, content, timestamp: Date.now() });
176
+ }
177
+ getMessageCount() {
178
+ return this.messages.length;
179
+ }
163
180
  stop() {
164
181
  if (this.server) {
165
182
  this.server.close();
@@ -185,7 +202,7 @@ async function extractAndSaveLearnings(responseText) {
185
202
  return responseText.replace(/<<LEARN:\s*.+?>>/g, "").trim();
186
203
  }
187
204
  async function extractAndExecuteActions(responseText) {
188
- const { parseActions, executeActions } = await import("./decision-engine-JDAIVQH3.js");
205
+ const { parseActions, executeActions } = await import("./decision-engine-5FC6NCT4.js");
189
206
  const jsonBlockPattern = /```json\s*([\s\S]*?)```/g;
190
207
  const blocks = [...responseText.matchAll(jsonBlockPattern)];
191
208
  const results = [];
@@ -250,7 +267,8 @@ async function startWebChat() {
250
267
  name: identity.name,
251
268
  handle: identity.handle,
252
269
  bio: identity.bio,
253
- profileImage: identity.profileImage
270
+ profileImage: identity.profileImage,
271
+ createdAt: identity.createdAt
254
272
  });
255
273
  const chatHistory = [];
256
274
  let systemPrompt = null;
@@ -258,7 +276,7 @@ async function startWebChat() {
258
276
  server.setMessageHandler(async (message) => {
259
277
  try {
260
278
  if (!systemPrompt || messageCount % 10 === 0) {
261
- const { buildChatPrompt } = await import("./prompt-builder-7NIQIHTB.js");
279
+ const { buildChatPrompt } = await import("./prompt-builder-VG7CUPU2.js");
262
280
  systemPrompt = buildChatPrompt();
263
281
  }
264
282
  messageCount++;
@@ -287,13 +305,123 @@ async function startWebChat() {
287
305
  console.log(chalk.dim(`Press Ctrl+C to stop the server
288
306
  `));
289
307
  openBrowser(url);
308
+ startNarratedHeartbeat(server).catch(
309
+ (err) => console.error(chalk.red("Heartbeat failed to start:"), err)
310
+ );
290
311
  process.on("SIGINT", () => {
291
312
  console.log(chalk.yellow("\n\nStopping chat server..."));
313
+ heartbeatRunning = false;
292
314
  server.stop();
293
315
  process.exit(0);
294
316
  });
295
317
  return server;
296
318
  }
319
+ var heartbeatRunning = false;
320
+ async function startNarratedHeartbeat(server) {
321
+ const { loadConfig } = await import("./config-HTKAPY7S.js");
322
+ const { hasLLMKey, generateResponse } = await import("./llm-WLEJLNEA.js");
323
+ const { hasXCredentials } = await import("./paths-5GFUUHCZ.js");
324
+ if (!hasLLMKey()) {
325
+ console.log(chalk.dim(" [Heartbeat] No LLM key \u2014 heartbeat disabled."));
326
+ return;
327
+ }
328
+ if (!hasXCredentials()) {
329
+ console.log(chalk.dim(" [Heartbeat] No X credentials \u2014 heartbeat disabled."));
330
+ return;
331
+ }
332
+ const config = loadConfig();
333
+ const intervalMs = config.runtime?.heartbeatIntervalMs ?? 6e4;
334
+ const maxActions = config.runtime?.actionsPerHeartbeat ?? 5;
335
+ heartbeatRunning = true;
336
+ let heartbeatCount = 0;
337
+ console.log(chalk.cyan(` [Heartbeat] Running every ${Math.round(intervalMs / 1e3)}s
338
+ `));
339
+ while (heartbeatRunning) {
340
+ heartbeatCount++;
341
+ console.log(chalk.cyan(` [Heartbeat] #${heartbeatCount} starting...`));
342
+ try {
343
+ await runNarratedHeartbeat(server, maxActions);
344
+ } catch (error) {
345
+ console.error(chalk.red(` [Heartbeat] #${heartbeatCount} failed:`), error.message);
346
+ server.pushMessage("assistant", `Heartbeat error: ${error.message}`);
347
+ }
348
+ const jitter = Math.floor(Math.random() * intervalMs * 0.2);
349
+ const sleepMs = intervalMs + jitter;
350
+ console.log(chalk.dim(` [Heartbeat] Sleeping ${Math.round(sleepMs / 1e3)}s...`));
351
+ let slept = 0;
352
+ while (slept < sleepMs && heartbeatRunning) {
353
+ await new Promise((r) => setTimeout(r, Math.min(5e3, sleepMs - slept)));
354
+ slept += 5e3;
355
+ }
356
+ }
357
+ }
358
+ async function runNarratedHeartbeat(server, maxActions) {
359
+ const { getXClient } = await import("./x-client-YE6QFHEN.js");
360
+ const { buildSystemPrompt, buildNarratedHeartbeatMessage } = await import("./prompt-builder-VG7CUPU2.js");
361
+ const { generateResponse } = await import("./llm-WLEJLNEA.js");
362
+ const { parseActions, executeActions } = await import("./decision-engine-5FC6NCT4.js");
363
+ const { flushQueue } = await import("./queue-YEVE53NQ.js");
364
+ try {
365
+ const flushed = await flushQueue();
366
+ if (flushed.posted > 0) {
367
+ server.pushMessage("assistant", `Posted ${flushed.posted} scheduled tweet(s) from my queue.`);
368
+ }
369
+ } catch {
370
+ }
371
+ server.pushMessage("assistant", "Waking up... checking my timeline and mentions.");
372
+ const client = await getXClient();
373
+ let timeline = [];
374
+ let mentions = [];
375
+ try {
376
+ timeline = await client.getTimeline({ count: 20 });
377
+ } catch (error) {
378
+ console.log(chalk.dim(` [Heartbeat] Timeline read failed: ${error.message}`));
379
+ }
380
+ try {
381
+ mentions = await client.getMentions({ count: 10 });
382
+ } catch (error) {
383
+ console.log(chalk.dim(` [Heartbeat] Mentions read failed: ${error.message}`));
384
+ }
385
+ const systemPrompt = buildSystemPrompt();
386
+ const userMessage = buildNarratedHeartbeatMessage(timeline, mentions);
387
+ const response = await generateResponse(systemPrompt, userMessage);
388
+ let narration = response.content.replace(/```json\s*[\s\S]*?```/g, "").replace(/\n{3,}/g, "\n\n").trim();
389
+ const actions = parseActions(response.content);
390
+ const limitedActions = actions.slice(0, maxActions);
391
+ if (narration) {
392
+ let fullMessage = narration;
393
+ if (limitedActions.length > 0) {
394
+ const results = await executeActions(limitedActions);
395
+ const resultLines = [];
396
+ for (const r of results) {
397
+ if (r.success) {
398
+ const detail = r.detail ? ` (${r.detail})` : "";
399
+ resultLines.push(`[${r.action}] Done${detail}`);
400
+ console.log(chalk.green(` [Heartbeat] ${r.action}: success${detail}`));
401
+ } else {
402
+ resultLines.push(`[${r.action}] Failed: ${r.error}`);
403
+ console.log(chalk.red(` [Heartbeat] ${r.action}: ${r.error}`));
404
+ }
405
+ }
406
+ fullMessage += "\n\n" + resultLines.join("\n");
407
+ }
408
+ server.pushMessage("assistant", fullMessage);
409
+ } else if (limitedActions.length > 0) {
410
+ const results = await executeActions(limitedActions);
411
+ const resultLines = [];
412
+ for (const r of results) {
413
+ if (r.success) {
414
+ resultLines.push(`[${r.action}] Done`);
415
+ } else {
416
+ resultLines.push(`[${r.action}] Failed: ${r.error}`);
417
+ }
418
+ }
419
+ server.pushMessage("assistant", "Executed actions:\n" + resultLines.join("\n"));
420
+ } else {
421
+ server.pushMessage("assistant", "Checked in \u2014 nothing to do right now. Going back to sleep.");
422
+ }
423
+ console.log(chalk.cyan(` [Heartbeat] Complete.`));
424
+ }
297
425
  function openBrowser(url) {
298
426
  const platform = process.platform;
299
427
  try {
@@ -328,4 +456,4 @@ export {
328
456
  openBrowser,
329
457
  startWebChat
330
458
  };
331
- //# sourceMappingURL=web-chat-DUY5W7S3.js.map
459
+ //# sourceMappingURL=web-chat-LNNJUCFA.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 } 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 AgentIdentity {\n name: string;\n handle: string;\n bio?: string;\n profileImage?: string;\n createdAt?: string;\n}\n\nexport class WebChatServer {\n private server: http.Server | null = null;\n private port: number;\n private messages: ChatMessage[] = [];\n private onUserMessage?: (message: string) => Promise<string>;\n private identity?: AgentIdentity;\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 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 // Try multiple paths since __dirname changes based on build output\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 // Serve logo\n if (url.pathname === \"/logo.png\") {\n try {\n const logoPaths = [\n join(__dirname, \"web-chat\", \"logo.png\"),\n join(__dirname, \"logo.png\"),\n join(__dirname, \"..\", \"web-chat\", \"logo.png\"),\n join(__dirname, \"..\", \"src\", \"web-chat\", \"logo.png\"),\n ];\n let logoData: Buffer | null = null;\n for (const p of logoPaths) {\n try {\n logoData = readFileSync(p) as unknown as Buffer;\n break;\n } catch {\n continue;\n }\n }\n if (logoData) {\n res.writeHead(200, { \"Content-Type\": \"image/png\", \"Cache-Control\": \"public, max-age=86400\" });\n res.end(logoData);\n } else {\n res.writeHead(404);\n res.end(\"Logo not found\");\n }\n } catch {\n res.writeHead(404);\n res.end(\"Logo not found\");\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 // API: Get messages (optionally since a timestamp)\n if (url.pathname === \"/api/messages\" && req.method === \"GET\") {\n const since = url.searchParams.get(\"since\");\n if (since) {\n const sinceTs = parseInt(since, 10);\n const newMessages = this.messages.filter((m) => m.timestamp > sinceTs);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ messages: newMessages }));\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ messages: this.messages }));\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 // Port in use, try next port\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 /**\n * Push a message into the chat from the server side (used by heartbeat narration)\n */\n pushMessage(role: \"user\" | \"assistant\", content: string) {\n this.messages.push({ role, content, timestamp: Date.now() });\n }\n\n getMessageCount(): number {\n return this.messages.length;\n }\n\n stop() {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n }\n}\n","/**\n * Web chat integration\n */\n\nimport { WebChatServer } from \"./server.js\";\nimport { loadIdentity } from \"../identity/index.js\";\nimport { execSync } from \"node:child_process\";\nimport chalk from \"chalk\";\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 JSON action blocks from the LLM response, execute them, and return cleaned text + results\n */\nasync function extractAndExecuteActions(responseText: string): Promise<{ cleanText: string; results: string[] }> {\n const { parseActions, executeActions } = await import(\"../runtime/decision-engine.js\");\n\n // Try to find JSON code blocks in the response\n const jsonBlockPattern = /```json\\s*([\\s\\S]*?)```/g;\n const blocks = [...responseText.matchAll(jsonBlockPattern)];\n const results: string[] = [];\n\n if (blocks.length > 0) {\n for (const block of blocks) {\n const actions = parseActions(block[1]);\n if (actions.length > 0) {\n console.log(chalk.cyan(` [Actions] Executing ${actions.length} action(s)...`));\n const actionResults = await executeActions(actions);\n for (const r of actionResults) {\n if (r.success) {\n const detail = r.detail ? ` (${r.detail})` : \"\";\n results.push(`[${r.action}] Done${detail}`);\n console.log(chalk.green(` [Actions] ${r.action}: success${detail}`));\n } else {\n results.push(`[${r.action}] Failed: ${r.error}`);\n console.log(chalk.red(` [Actions] ${r.action}: ${r.error}`));\n }\n }\n }\n }\n } else {\n // Also try raw JSON (no code block wrapper)\n const actions = parseActions(responseText);\n if (actions.length > 0 && actions[0].action) {\n console.log(chalk.cyan(` [Actions] Executing ${actions.length} action(s)...`));\n const actionResults = await executeActions(actions);\n for (const r of actionResults) {\n if (r.success) {\n const detail = r.detail ? ` (${r.detail})` : \"\";\n results.push(`[${r.action}] Done${detail}`);\n console.log(chalk.green(` [Actions] ${r.action}: success${detail}`));\n } else {\n results.push(`[${r.action}] Failed: ${r.error}`);\n console.log(chalk.red(` [Actions] ${r.action}: ${r.error}`));\n }\n }\n }\n }\n\n // Strip JSON code blocks from the user-visible text\n let cleanText = responseText.replace(/```json\\s*[\\s\\S]*?```/g, \"\").trim();\n // Clean up extra whitespace from removal\n cleanText = cleanText.replace(/\\n{3,}/g, \"\\n\\n\").trim();\n\n // Append action results if any\n if (results.length > 0) {\n cleanText += \"\\n\\n\" + results.join(\"\\n\");\n }\n\n return { cleanText, results };\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\nexport async function startWebChat() {\n const identity = loadIdentity();\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 createdAt: identity.createdAt,\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 system prompt on first message, rebuild every 10 messages to refresh memory\n if (!systemPrompt || messageCount % 10 === 0) {\n const { buildChatPrompt } = await import(\"../runtime/prompt-builder.js\");\n systemPrompt = buildChatPrompt();\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 set-llm-key` 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 and execute any actions (posts, likes, etc.) from the response\n const { cleanText: actionCleanedText } = await extractAndExecuteActions(response.content);\n\n // Extract learnings from response and clean the text\n const cleanResponse = await extractAndSaveLearnings(actionCleanedText);\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 background heartbeat (narrates into chat)\n startNarratedHeartbeat(server).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 heartbeatRunning = false;\n server.stop();\n process.exit(0);\n });\n\n // Return the server instance for external control\n return server;\n}\n\n/**\n * Background heartbeat that narrates into the chat UI\n */\nlet heartbeatRunning = false;\n\nasync function startNarratedHeartbeat(server: WebChatServer) {\n const { loadConfig } = await import(\"../utils/config.js\");\n const { hasLLMKey, generateResponse } = await import(\"../runtime/llm.js\");\n const { hasXCredentials } = await import(\"../utils/paths.js\");\n\n if (!hasLLMKey()) {\n console.log(chalk.dim(\" [Heartbeat] No LLM key — heartbeat disabled.\"));\n return;\n }\n\n if (!hasXCredentials()) {\n console.log(chalk.dim(\" [Heartbeat] No X credentials — heartbeat disabled.\"));\n return;\n }\n\n const config = loadConfig();\n const intervalMs = config.runtime?.heartbeatIntervalMs ?? 60_000;\n const maxActions = config.runtime?.actionsPerHeartbeat ?? 5;\n\n heartbeatRunning = true;\n let heartbeatCount = 0;\n\n console.log(chalk.cyan(` [Heartbeat] Running every ${Math.round(intervalMs / 1000)}s\\n`));\n\n while (heartbeatRunning) {\n heartbeatCount++;\n console.log(chalk.cyan(` [Heartbeat] #${heartbeatCount} starting...`));\n\n try {\n await runNarratedHeartbeat(server, maxActions);\n } catch (error) {\n console.error(chalk.red(` [Heartbeat] #${heartbeatCount} failed:`), (error as Error).message);\n server.pushMessage(\"assistant\", `Heartbeat error: ${(error as Error).message}`);\n }\n\n // Sleep with jitter\n const jitter = Math.floor(Math.random() * intervalMs * 0.2);\n const sleepMs = intervalMs + jitter;\n console.log(chalk.dim(` [Heartbeat] Sleeping ${Math.round(sleepMs / 1000)}s...`));\n\n let slept = 0;\n while (slept < sleepMs && heartbeatRunning) {\n await new Promise((r) => setTimeout(r, Math.min(5000, sleepMs - slept)));\n slept += 5000;\n }\n }\n}\n\nasync function runNarratedHeartbeat(server: WebChatServer, maxActions: number) {\n const { getXClient } = await import(\"../x-client/index.js\");\n const { buildSystemPrompt, buildNarratedHeartbeatMessage } = await import(\"../runtime/prompt-builder.js\");\n const { generateResponse } = await import(\"../runtime/llm.js\");\n const { parseActions, executeActions } = await import(\"../runtime/decision-engine.js\");\n const { flushQueue } = await import(\"../scheduler/queue.js\");\n\n // 1. Flush queued posts\n try {\n const flushed = await flushQueue();\n if (flushed.posted > 0) {\n server.pushMessage(\"assistant\", `Posted ${flushed.posted} scheduled tweet(s) from my queue.`);\n }\n } catch {\n // Queue flush failed, not critical\n }\n\n // 2. Read timeline and mentions\n server.pushMessage(\"assistant\", \"Waking up... checking my timeline and mentions.\");\n\n const client = await getXClient();\n let timeline: Awaited<ReturnType<typeof client.getTimeline>> = [];\n let mentions: Awaited<ReturnType<typeof client.getMentions>> = [];\n\n try {\n timeline = await client.getTimeline({ count: 20 });\n } catch (error) {\n console.log(chalk.dim(` [Heartbeat] Timeline read failed: ${(error as Error).message}`));\n }\n\n try {\n mentions = await client.getMentions({ count: 10 });\n } catch (error) {\n console.log(chalk.dim(` [Heartbeat] Mentions read failed: ${(error as Error).message}`));\n }\n\n // 3. Build prompts and ask LLM\n const systemPrompt = buildSystemPrompt();\n const userMessage = buildNarratedHeartbeatMessage(timeline, mentions);\n\n const response = await generateResponse(systemPrompt, userMessage);\n\n // 4. Extract narration text (everything outside JSON blocks)\n let narration = response.content.replace(/```json\\s*[\\s\\S]*?```/g, \"\").replace(/\\n{3,}/g, \"\\n\\n\").trim();\n\n // 5. Parse and execute actions\n const actions = parseActions(response.content);\n const limitedActions = actions.slice(0, maxActions);\n\n if (narration) {\n // Push the agent's narration into the chat\n let fullMessage = narration;\n\n if (limitedActions.length > 0) {\n // Execute actions and build results\n const results = await executeActions(limitedActions);\n const resultLines: string[] = [];\n for (const r of results) {\n if (r.success) {\n const detail = r.detail ? ` (${r.detail})` : \"\";\n resultLines.push(`[${r.action}] Done${detail}`);\n console.log(chalk.green(` [Heartbeat] ${r.action}: success${detail}`));\n } else {\n resultLines.push(`[${r.action}] Failed: ${r.error}`);\n console.log(chalk.red(` [Heartbeat] ${r.action}: ${r.error}`));\n }\n }\n fullMessage += \"\\n\\n\" + resultLines.join(\"\\n\");\n }\n\n server.pushMessage(\"assistant\", fullMessage);\n } else if (limitedActions.length > 0) {\n // No narration text, but actions exist\n const results = await executeActions(limitedActions);\n const resultLines: string[] = [];\n for (const r of results) {\n if (r.success) {\n resultLines.push(`[${r.action}] Done`);\n } else {\n resultLines.push(`[${r.action}] Failed: ${r.error}`);\n }\n }\n server.pushMessage(\"assistant\", \"Executed actions:\\n\" + resultLines.join(\"\\n\"));\n } else {\n server.pushMessage(\"assistant\", \"Checked in — nothing to do right now. Going back to sleep.\");\n }\n\n console.log(chalk.cyan(` [Heartbeat] Complete.`));\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=420,620`, { 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=420,620`, { 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=420,620`, { 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=420,620`, { 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":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getXClient,
3
3
  resetXClient
4
- } from "./chunk-TSKHCOCJ.js";
4
+ } from "./chunk-DHT5ORFX.js";
5
5
  import "./chunk-RCJQI7FR.js";
6
6
  import "./chunk-KELPENM3.js";
7
7
  import "./chunk-53YLFYJF.js";
@@ -9,4 +9,4 @@ export {
9
9
  getXClient,
10
10
  resetXClient
11
11
  };
12
- //# sourceMappingURL=x-client-7LK3F56M.js.map
12
+ //# sourceMappingURL=x-client-YE6QFHEN.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spora",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "AI agents (Spores) that autonomously manage X/Twitter accounts",
5
5
  "type": "module",
6
6
  "author": "Spora",