spora 0.2.13 → 0.2.15

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 (36) 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-PQAAEOB7.js} +7 -6
  4. package/dist/chunk-PQAAEOB7.js.map +1 -0
  5. package/dist/{chunk-VTWZZNME.js → chunk-SJMQDGRN.js} +27 -14
  6. package/dist/chunk-SJMQDGRN.js.map +1 -0
  7. package/dist/cli.js +25 -25
  8. package/dist/cli.js.map +1 -1
  9. package/dist/{colony-JHR2B5OC.js → colony-4G3JMXXW.js} +2 -2
  10. package/dist/{decision-engine-JDAIVQH3.js → decision-engine-TEYVBE7F.js} +4 -4
  11. package/dist/{heartbeat-BU74IJO4.js → heartbeat-SQRFUVIJ.js} +9 -9
  12. package/dist/heartbeat-SQRFUVIJ.js.map +1 -0
  13. package/dist/{init-RY7VY2H2.js → init-FBDXTEZJ.js} +3 -3
  14. package/dist/{mcp-server-4YBCA4QH.js → mcp-server.js} +20 -20
  15. package/dist/{prompt-builder-VG7CUPU2.js → prompt-builder-D5PFEN4S.js} +2 -2
  16. package/dist/{queue-WMUESED4.js → queue-YEVE53NQ.js} +2 -2
  17. package/dist/web-chat/chat.html +115 -21
  18. package/dist/{web-chat-F5K6GQ67.js → web-chat-Z42YFXCH.js} +18 -14
  19. package/dist/web-chat-Z42YFXCH.js.map +1 -0
  20. package/dist/{x-client-7LK3F56M.js → x-client-YE6QFHEN.js} +2 -2
  21. package/package.json +1 -1
  22. package/dist/chat.html +0 -442
  23. package/dist/chunk-VSVZU476.js.map +0 -1
  24. package/dist/chunk-VTWZZNME.js.map +0 -1
  25. package/dist/heartbeat-BU74IJO4.js.map +0 -1
  26. package/dist/logo.png +0 -0
  27. package/dist/web-chat-F5K6GQ67.js.map +0 -1
  28. /package/dist/{chunk-TSKHCOCJ.js.map → chunk-DHT5ORFX.js.map} +0 -0
  29. /package/dist/{chunk-IN2KCKTO.js.map → chunk-GBOY5OQ6.js.map} +0 -0
  30. /package/dist/{colony-JHR2B5OC.js.map → colony-4G3JMXXW.js.map} +0 -0
  31. /package/dist/{decision-engine-JDAIVQH3.js.map → decision-engine-TEYVBE7F.js.map} +0 -0
  32. /package/dist/{init-RY7VY2H2.js.map → init-FBDXTEZJ.js.map} +0 -0
  33. /package/dist/{mcp-server-4YBCA4QH.js.map → mcp-server.js.map} +0 -0
  34. /package/dist/{prompt-builder-VG7CUPU2.js.map → prompt-builder-D5PFEN4S.js.map} +0 -0
  35. /package/dist/{queue-WMUESED4.js.map → queue-YEVE53NQ.js.map} +0 -0
  36. /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;
30
51
  }
31
52
 
32
- .logo-img {
33
- height: 24px;
34
- width: auto;
53
+ .profile-avatar-letter {
54
+ font-size: 1.25rem;
55
+ font-weight: 700;
56
+ color: #fff;
57
+ }
58
+
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);
@@ -202,7 +202,7 @@ async function extractAndSaveLearnings(responseText) {
202
202
  return responseText.replace(/<<LEARN:\s*.+?>>/g, "").trim();
203
203
  }
204
204
  async function extractAndExecuteActions(responseText) {
205
- const { parseActions, executeActions } = await import("./decision-engine-JDAIVQH3.js");
205
+ const { parseActions, executeActions } = await import("./decision-engine-TEYVBE7F.js");
206
206
  const jsonBlockPattern = /```json\s*([\s\S]*?)```/g;
207
207
  const blocks = [...responseText.matchAll(jsonBlockPattern)];
208
208
  const results = [];
@@ -267,7 +267,8 @@ async function startWebChat() {
267
267
  name: identity.name,
268
268
  handle: identity.handle,
269
269
  bio: identity.bio,
270
- profileImage: identity.profileImage
270
+ profileImage: identity.profileImage,
271
+ createdAt: identity.createdAt
271
272
  });
272
273
  const chatHistory = [];
273
274
  let systemPrompt = null;
@@ -275,7 +276,7 @@ async function startWebChat() {
275
276
  server.setMessageHandler(async (message) => {
276
277
  try {
277
278
  if (!systemPrompt || messageCount % 10 === 0) {
278
- const { buildChatPrompt } = await import("./prompt-builder-VG7CUPU2.js");
279
+ const { buildChatPrompt } = await import("./prompt-builder-D5PFEN4S.js");
279
280
  systemPrompt = buildChatPrompt();
280
281
  }
281
282
  messageCount++;
@@ -330,7 +331,7 @@ async function startNarratedHeartbeat(server) {
330
331
  }
331
332
  const config = loadConfig();
332
333
  const intervalMs = config.runtime?.heartbeatIntervalMs ?? 6e4;
333
- const maxActions = config.runtime?.actionsPerHeartbeat ?? 5;
334
+ const maxActions = config.runtime?.actionsPerHeartbeat ?? 10;
334
335
  heartbeatRunning = true;
335
336
  let heartbeatCount = 0;
336
337
  console.log(chalk.cyan(` [Heartbeat] Running every ${Math.round(intervalMs / 1e3)}s
@@ -339,7 +340,7 @@ async function startNarratedHeartbeat(server) {
339
340
  heartbeatCount++;
340
341
  console.log(chalk.cyan(` [Heartbeat] #${heartbeatCount} starting...`));
341
342
  try {
342
- await runNarratedHeartbeat(server, maxActions);
343
+ await runNarratedHeartbeat(server, maxActions, intervalMs);
343
344
  } catch (error) {
344
345
  console.error(chalk.red(` [Heartbeat] #${heartbeatCount} failed:`), error.message);
345
346
  server.pushMessage("assistant", `Heartbeat error: ${error.message}`);
@@ -354,16 +355,19 @@ async function startNarratedHeartbeat(server) {
354
355
  }
355
356
  }
356
357
  }
357
- async function runNarratedHeartbeat(server, maxActions) {
358
- const { getXClient } = await import("./x-client-7LK3F56M.js");
359
- const { buildSystemPrompt, buildNarratedHeartbeatMessage } = await import("./prompt-builder-VG7CUPU2.js");
358
+ async function runNarratedHeartbeat(server, maxActions, intervalMs) {
359
+ const { getXClient } = await import("./x-client-YE6QFHEN.js");
360
+ const { buildSystemPrompt, buildNarratedHeartbeatMessage } = await import("./prompt-builder-D5PFEN4S.js");
360
361
  const { generateResponse } = await import("./llm-WLEJLNEA.js");
361
- const { parseActions, executeActions } = await import("./decision-engine-JDAIVQH3.js");
362
- const { flushQueue } = await import("./queue-WMUESED4.js");
362
+ const { parseActions, executeActions } = await import("./decision-engine-TEYVBE7F.js");
363
+ const { flushQueue } = await import("./queue-YEVE53NQ.js");
363
364
  try {
364
365
  const flushed = await flushQueue();
365
- if (flushed.posted > 0) {
366
- server.pushMessage("assistant", `Posted ${flushed.posted} scheduled tweet(s) from my queue.`);
366
+ if (flushed.posted > 0 || flushed.remaining > 0) {
367
+ const parts = [];
368
+ if (flushed.posted > 0) parts.push(`posted ${flushed.posted} scheduled tweet(s)`);
369
+ if (flushed.remaining > 0) parts.push(`${flushed.remaining} still queued`);
370
+ server.pushMessage("assistant", `Queue check: ${parts.join(", ")}.`);
367
371
  }
368
372
  } catch {
369
373
  }
@@ -382,7 +386,7 @@ async function runNarratedHeartbeat(server, maxActions) {
382
386
  console.log(chalk.dim(` [Heartbeat] Mentions read failed: ${error.message}`));
383
387
  }
384
388
  const systemPrompt = buildSystemPrompt();
385
- const userMessage = buildNarratedHeartbeatMessage(timeline, mentions);
389
+ const userMessage = buildNarratedHeartbeatMessage(timeline, mentions, intervalMs);
386
390
  const response = await generateResponse(systemPrompt, userMessage);
387
391
  let narration = response.content.replace(/```json\s*[\s\S]*?```/g, "").replace(/\n{3,}/g, "\n\n").trim();
388
392
  const actions = parseActions(response.content);
@@ -455,4 +459,4 @@ export {
455
459
  openBrowser,
456
460
  startWebChat
457
461
  };
458
- //# sourceMappingURL=web-chat-F5K6GQ67.js.map
462
+ //# sourceMappingURL=web-chat-Z42YFXCH.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 ?? 10;\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, intervalMs);\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, intervalMs: 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 || flushed.remaining > 0) {\n const parts = [];\n if (flushed.posted > 0) parts.push(`posted ${flushed.posted} scheduled tweet(s)`);\n if (flushed.remaining > 0) parts.push(`${flushed.remaining} still queued`);\n server.pushMessage(\"assistant\", `Queue check: ${parts.join(\", \")}.`);\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, intervalMs);\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.13",
3
+ "version": "0.2.15",
4
4
  "description": "AI agents (Spores) that autonomously manage X/Twitter accounts",
5
5
  "type": "module",
6
6
  "author": "Spora",