spora 0.2.25 → 0.2.26
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.
- package/dist/{chunk-KH6VMWWA.js → chunk-ZNLJN7YO.js} +4 -4
- package/dist/chunk-ZNLJN7YO.js.map +1 -0
- package/dist/cli.js +5 -5
- package/dist/{decision-engine-G46P555I.js → decision-engine-RVQRDHEI.js} +2 -2
- package/dist/{heartbeat-KOVTDKGQ.js → heartbeat-M2QHCKAD.js} +2 -2
- package/dist/{init-2JE7QZM3.js → init-NT4EJKJL.js} +3 -3
- package/dist/web-chat/chat.html +258 -1
- package/dist/{web-chat-WGGCHG3P.js → web-chat-PG75Q2VE.js} +12 -6
- package/dist/web-chat-PG75Q2VE.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-KH6VMWWA.js.map +0 -1
- package/dist/web-chat-WGGCHG3P.js.map +0 -1
- /package/dist/{decision-engine-G46P555I.js.map → decision-engine-RVQRDHEI.js.map} +0 -0
- /package/dist/{heartbeat-KOVTDKGQ.js.map → heartbeat-M2QHCKAD.js.map} +0 -0
- /package/dist/{init-2JE7QZM3.js.map → init-NT4EJKJL.js.map} +0 -0
|
@@ -59,7 +59,7 @@ async function executeAction(action) {
|
|
|
59
59
|
if (result.success) {
|
|
60
60
|
logger.info(`Posted: "${action.content.slice(0, 50)}..."`);
|
|
61
61
|
}
|
|
62
|
-
return { action: type, success: result.success, detail: result.tweetId, error: result.error };
|
|
62
|
+
return { action: type, success: result.success, detail: result.tweetId, error: result.error, content: action.content };
|
|
63
63
|
}
|
|
64
64
|
case "reply": {
|
|
65
65
|
if (!action.tweetId || !action.content) {
|
|
@@ -70,7 +70,7 @@ async function executeAction(action) {
|
|
|
70
70
|
if (result.success) {
|
|
71
71
|
logger.info(`Replied to ${action.tweetId}: "${action.content.slice(0, 50)}..."`);
|
|
72
72
|
}
|
|
73
|
-
return { action: type, success: result.success, detail: result.tweetId, error: result.error };
|
|
73
|
+
return { action: type, success: result.success, detail: result.tweetId, error: result.error, content: action.content };
|
|
74
74
|
}
|
|
75
75
|
case "like": {
|
|
76
76
|
if (!action.tweetId) return { action: type, success: false, error: "Missing tweetId" };
|
|
@@ -95,7 +95,7 @@ async function executeAction(action) {
|
|
|
95
95
|
const entry = addToQueue(action.content, action.scheduledFor);
|
|
96
96
|
const time = new Date(entry.scheduledFor).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" });
|
|
97
97
|
logger.info(`Scheduled: "${action.content.slice(0, 50)}..." for ${time}`);
|
|
98
|
-
return { action: type, success: true, detail: `Queued for ${time}
|
|
98
|
+
return { action: type, success: true, detail: `Queued for ${time}`, content: action.content };
|
|
99
99
|
}
|
|
100
100
|
case "learn": {
|
|
101
101
|
if (!action.content) return { action: type, success: false, error: "No content" };
|
|
@@ -182,4 +182,4 @@ export {
|
|
|
182
182
|
executeAction,
|
|
183
183
|
executeActions
|
|
184
184
|
};
|
|
185
|
-
//# sourceMappingURL=chunk-
|
|
185
|
+
//# sourceMappingURL=chunk-ZNLJN7YO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/decision-engine.ts"],"sourcesContent":["import { logger } from \"../utils/logger.js\";\nimport { getXClient } from \"../x-client/index.js\";\nimport { logInteraction, addLearning } from \"../memory/index.js\";\nimport { loadIdentity, saveIdentity } from \"../identity/index.js\";\nimport { addToQueue } from \"../scheduler/queue.js\";\n\nexport interface AgentAction {\n action: string;\n content?: string;\n tweetId?: string;\n handle?: string;\n query?: string;\n tags?: string[];\n reason?: string;\n reasoning?: string;\n scheduledFor?: string;\n}\n\nexport interface ActionResult {\n action: string;\n success: boolean;\n detail?: string;\n error?: string;\n content?: string; // The tweet text for post/reply/schedule actions\n}\n\nexport function parseActions(llmResponse: string): AgentAction[] {\n // Extract JSON array from the response (may be wrapped in markdown code blocks)\n const jsonMatch = llmResponse.match(/\\[[\\s\\S]*?\\]/);\n if (!jsonMatch) {\n // Try to parse as a single action object\n const objMatch = llmResponse.match(/\\{[\\s\\S]*?\\}/);\n if (objMatch) {\n try {\n return [JSON.parse(objMatch[0]) as AgentAction];\n } catch {\n logger.warn(\"Could not parse LLM response as action object\");\n return [];\n }\n }\n logger.warn(\"No JSON found in LLM response\");\n return [];\n }\n\n try {\n const actions = JSON.parse(jsonMatch[0]) as AgentAction[];\n return Array.isArray(actions) ? actions : [actions];\n } catch {\n logger.warn(\"Failed to parse actions JSON from LLM response\");\n return [];\n }\n}\n\nfunction sanitizeTweetId(id: string): string {\n // Strip \"tweet:\" prefix if LLM includes it from prompt format [tweet:123]\n return id.replace(/^tweet:/i, \"\").trim();\n}\n\nexport async function executeAction(action: AgentAction): Promise<ActionResult> {\n const { action: type } = action;\n\n // Clean up tweetId if present\n if (action.tweetId) {\n action.tweetId = sanitizeTweetId(action.tweetId);\n }\n\n try {\n switch (type) {\n case \"post\": {\n if (!action.content) return { action: type, success: false, error: \"No content provided\" };\n if (action.content.length > 280) {\n return { action: type, success: false, error: `Tweet too long: ${action.content.length} chars (max 280)` };\n }\n\n const client = await getXClient();\n const result = await client.postTweet(action.content);\n if (result.success) {\n logger.info(`Posted: \"${action.content.slice(0, 50)}...\"`);\n }\n return { action: type, success: result.success, detail: result.tweetId, error: result.error, content: action.content };\n }\n\n case \"reply\": {\n if (!action.tweetId || !action.content) {\n return { action: type, success: false, error: \"Missing tweetId or content\" };\n }\n\n const client = await getXClient();\n const result = await client.replyToTweet(action.tweetId, action.content);\n if (result.success) {\n logger.info(`Replied to ${action.tweetId}: \"${action.content.slice(0, 50)}...\"`);\n }\n return { action: type, success: result.success, detail: result.tweetId, error: result.error, content: action.content };\n }\n\n case \"like\": {\n if (!action.tweetId) return { action: type, success: false, error: \"Missing tweetId\" };\n const client = await getXClient();\n const result = await client.likeTweet(action.tweetId);\n return { action: type, success: result.success, error: result.error };\n }\n\n case \"retweet\": {\n if (!action.tweetId) return { action: type, success: false, error: \"Missing tweetId\" };\n const client = await getXClient();\n const result = await client.retweet(action.tweetId);\n return { action: type, success: result.success, error: result.error };\n }\n\n case \"follow\": {\n if (!action.handle) return { action: type, success: false, error: \"Missing handle\" };\n const client = await getXClient();\n const result = await client.followUser(action.handle);\n return { action: type, success: result.success, error: result.error };\n }\n\n case \"schedule\": {\n if (!action.content) return { action: type, success: false, error: \"No content\" };\n const entry = addToQueue(action.content, action.scheduledFor);\n const time = new Date(entry.scheduledFor).toLocaleTimeString(\"en-US\", { hour: \"numeric\", minute: \"2-digit\" });\n logger.info(`Scheduled: \"${action.content.slice(0, 50)}...\" for ${time}`);\n return { action: type, success: true, detail: `Queued for ${time}`, content: action.content };\n }\n\n case \"learn\": {\n if (!action.content) return { action: type, success: false, error: \"No content\" };\n addLearning(action.content, \"agent\", action.tags ?? [\"heartbeat\"]);\n logger.info(`Learned: \"${action.content.slice(0, 50)}...\"`);\n return { action: type, success: true };\n }\n\n case \"reflect\": {\n if (!action.content) return { action: type, success: false, error: \"No content\" };\n const identity = loadIdentity();\n identity.evolutionJournal.push({\n date: new Date().toISOString(),\n reflection: action.content,\n });\n saveIdentity(identity);\n logger.info(`Reflected: \"${action.content.slice(0, 50)}...\"`);\n return { action: type, success: true };\n }\n\n case \"search\": {\n if (!action.query) return { action: type, success: false, error: \"No query provided\" };\n const client = await getXClient();\n const tweets = await client.searchTweets(action.query, { count: 10 });\n if (tweets.length === 0) {\n return { action: type, success: true, detail: \"No results found\" };\n }\n const results = tweets.map(t => `@${t.authorHandle}: \"${t.text}\" [tweet:${t.id}]`).join(\"\\n\");\n logger.info(`Search \"${action.query}\": ${tweets.length} results`);\n return { action: type, success: true, detail: results };\n }\n\n case \"skip\": {\n logger.info(`Skipping: ${action.reason ?? action.reasoning ?? \"no reason given\"}`);\n return { action: type, success: true, detail: action.reason ?? action.reasoning };\n }\n\n default:\n logger.warn(`Unknown action: ${type}`);\n return { action: type, success: false, error: `Unknown action: ${type}` };\n }\n } catch (error) {\n const msg = (error as Error).message;\n logger.error(`Action ${type} failed: ${msg}`);\n return { action: type, success: false, error: msg };\n }\n}\n\nexport async function executeActions(actions: AgentAction[]): Promise<ActionResult[]> {\n const results: ActionResult[] = [];\n let instantPostCount = 0;\n let scheduleCount = 0;\n const MAX_INSTANT_POSTS = 1; // Hard cap: max 1 immediate post per batch\n const MAX_SCHEDULES = 3; // Allow up to 3 scheduled posts (they'll be spaced out)\n\n for (const action of actions) {\n // Enforce instant post limit\n if (action.action === \"post\") {\n if (instantPostCount >= MAX_INSTANT_POSTS) {\n results.push({\n action: action.action,\n success: false,\n error: \"Instant post limit reached (max 1 per heartbeat)\",\n });\n logger.info(`Skipped post: instant post limit reached`);\n continue;\n }\n instantPostCount++;\n }\n\n // Enforce schedule limit\n if (action.action === \"schedule\") {\n if (scheduleCount >= MAX_SCHEDULES) {\n results.push({\n action: action.action,\n success: false,\n error: \"Schedule limit reached (max 3 per heartbeat)\",\n });\n logger.info(`Skipped schedule: schedule limit reached`);\n continue;\n }\n scheduleCount++;\n }\n\n const result = await executeAction(action);\n results.push(result);\n // Small delay between actions to be human-like\n await new Promise((r) => setTimeout(r, 2000 + Math.random() * 3000));\n }\n return results;\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -123,7 +123,7 @@ program.command("init").description("Set up X account credentials for your Spore
|
|
|
123
123
|
console.log(chalk.cyan(BANNER));
|
|
124
124
|
console.log(chalk.bold("Welcome to Spora."));
|
|
125
125
|
console.log(chalk.gray("The global town square for AI agents.\n"));
|
|
126
|
-
const { runInit } = await import("./init-
|
|
126
|
+
const { runInit } = await import("./init-NT4EJKJL.js");
|
|
127
127
|
await runInit(opts.token);
|
|
128
128
|
});
|
|
129
129
|
program.command("serve").description("Start the Spora MCP server (stdio)").action(async () => {
|
|
@@ -135,7 +135,7 @@ program.command("chat").description("Open web-based chat interface with your Spo
|
|
|
135
135
|
console.log(chalk.red("\u2717 No identity found. Run `spora create` first."));
|
|
136
136
|
process.exit(1);
|
|
137
137
|
}
|
|
138
|
-
const { startWebChat } = await import("./web-chat-
|
|
138
|
+
const { startWebChat } = await import("./web-chat-PG75Q2VE.js");
|
|
139
139
|
await startWebChat();
|
|
140
140
|
});
|
|
141
141
|
program.command("tui").description("Start terminal-based chat interface (TUI)").action(async () => {
|
|
@@ -557,11 +557,11 @@ program.command("start").description("Start the autonomous Spora agent").option(
|
|
|
557
557
|
}
|
|
558
558
|
console.log(chalk.cyan(BANNER));
|
|
559
559
|
console.log(chalk.bold("Starting Spora agent...\n"));
|
|
560
|
-
const { startHeartbeatLoop } = await import("./heartbeat-
|
|
560
|
+
const { startHeartbeatLoop } = await import("./heartbeat-M2QHCKAD.js");
|
|
561
561
|
await startHeartbeatLoop();
|
|
562
562
|
});
|
|
563
563
|
program.command("stop").description("Stop the running Spora agent").action(async () => {
|
|
564
|
-
const { getRunningPid, requestStop } = await import("./heartbeat-
|
|
564
|
+
const { getRunningPid, requestStop } = await import("./heartbeat-M2QHCKAD.js");
|
|
565
565
|
const pid = getRunningPid();
|
|
566
566
|
if (!pid) {
|
|
567
567
|
console.log(JSON.stringify({ message: "Spora agent is not running." }));
|
|
@@ -599,7 +599,7 @@ program.command("set-llm-key").description("Set your Anthropic API key for the a
|
|
|
599
599
|
console.log(JSON.stringify({ success: true, message: "LLM API key saved." }));
|
|
600
600
|
});
|
|
601
601
|
program.command("agent-status").description("Check if the Spora agent is running").action(async () => {
|
|
602
|
-
const { getRunningPid } = await import("./heartbeat-
|
|
602
|
+
const { getRunningPid } = await import("./heartbeat-M2QHCKAD.js");
|
|
603
603
|
const pid = getRunningPid();
|
|
604
604
|
const { hasLLMKey } = await import("./llm-WLEJLNEA.js");
|
|
605
605
|
console.log(JSON.stringify({
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
executeAction,
|
|
3
3
|
executeActions,
|
|
4
4
|
parseActions
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZNLJN7YO.js";
|
|
6
6
|
import "./chunk-NJHDY4ON.js";
|
|
7
7
|
import "./chunk-M23MGGHE.js";
|
|
8
8
|
import "./chunk-RCJQI7FR.js";
|
|
@@ -15,4 +15,4 @@ export {
|
|
|
15
15
|
executeActions,
|
|
16
16
|
parseActions
|
|
17
17
|
};
|
|
18
|
-
//# sourceMappingURL=decision-engine-
|
|
18
|
+
//# sourceMappingURL=decision-engine-RVQRDHEI.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
executeActions,
|
|
3
3
|
parseActions
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZNLJN7YO.js";
|
|
5
5
|
import {
|
|
6
6
|
getXClient
|
|
7
7
|
} from "./chunk-NJHDY4ON.js";
|
|
@@ -200,4 +200,4 @@ export {
|
|
|
200
200
|
requestStop,
|
|
201
201
|
startHeartbeatLoop
|
|
202
202
|
};
|
|
203
|
-
//# sourceMappingURL=heartbeat-
|
|
203
|
+
//# sourceMappingURL=heartbeat-M2QHCKAD.js.map
|
|
@@ -203,7 +203,7 @@ async function loginFlow() {
|
|
|
203
203
|
console.log(chalk.green("\u2713 Logged in!\n"));
|
|
204
204
|
console.log(chalk.gray("Opening chat interface...\n"));
|
|
205
205
|
try {
|
|
206
|
-
const { startWebChat } = await import("./web-chat-
|
|
206
|
+
const { startWebChat } = await import("./web-chat-PG75Q2VE.js");
|
|
207
207
|
await startWebChat();
|
|
208
208
|
} catch (error) {
|
|
209
209
|
console.log(chalk.yellow(`Could not start chat interface: ${error.message}
|
|
@@ -275,7 +275,7 @@ async function showDoneAndOpenChat() {
|
|
|
275
275
|
console.log(chalk.bold.cyan("\u2501\u2501\u2501 Your Spore is Ready! \u2501\u2501\u2501\n"));
|
|
276
276
|
console.log(chalk.gray("Opening chat interface...\n"));
|
|
277
277
|
try {
|
|
278
|
-
const { startWebChat } = await import("./web-chat-
|
|
278
|
+
const { startWebChat } = await import("./web-chat-PG75Q2VE.js");
|
|
279
279
|
await startWebChat();
|
|
280
280
|
} catch (error) {
|
|
281
281
|
console.log(chalk.yellow(`Could not start chat interface: ${error.message}
|
|
@@ -400,4 +400,4 @@ async function runInit(token) {
|
|
|
400
400
|
export {
|
|
401
401
|
runInit
|
|
402
402
|
};
|
|
403
|
-
//# sourceMappingURL=init-
|
|
403
|
+
//# sourceMappingURL=init-NT4EJKJL.js.map
|
package/dist/web-chat/chat.html
CHANGED
|
@@ -275,6 +275,133 @@
|
|
|
275
275
|
0%, 80%, 100% { transform: scale(0); }
|
|
276
276
|
40% { transform: scale(1); }
|
|
277
277
|
}
|
|
278
|
+
|
|
279
|
+
/* X/Twitter-style tweet preview card */
|
|
280
|
+
.tweet-card {
|
|
281
|
+
background: #16181c;
|
|
282
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
283
|
+
border-radius: 12px;
|
|
284
|
+
padding: 12px;
|
|
285
|
+
margin-top: 8px;
|
|
286
|
+
max-width: 100%;
|
|
287
|
+
cursor: pointer;
|
|
288
|
+
transition: background 0.15s;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.tweet-card:hover {
|
|
292
|
+
background: #1d1f23;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.tweet-card-header {
|
|
296
|
+
display: flex;
|
|
297
|
+
align-items: center;
|
|
298
|
+
gap: 8px;
|
|
299
|
+
margin-bottom: 6px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.tweet-card-avatar {
|
|
303
|
+
width: 32px;
|
|
304
|
+
height: 32px;
|
|
305
|
+
border-radius: 50%;
|
|
306
|
+
background: #389e77;
|
|
307
|
+
display: flex;
|
|
308
|
+
align-items: center;
|
|
309
|
+
justify-content: center;
|
|
310
|
+
flex-shrink: 0;
|
|
311
|
+
overflow: hidden;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.tweet-card-avatar img {
|
|
315
|
+
width: 100%;
|
|
316
|
+
height: 100%;
|
|
317
|
+
object-fit: cover;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.tweet-card-avatar-letter {
|
|
321
|
+
font-size: 0.75rem;
|
|
322
|
+
font-weight: 700;
|
|
323
|
+
color: #fff;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.tweet-card-names {
|
|
327
|
+
display: flex;
|
|
328
|
+
flex-direction: column;
|
|
329
|
+
min-width: 0;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.tweet-card-name {
|
|
333
|
+
font-size: 0.8125rem;
|
|
334
|
+
font-weight: 700;
|
|
335
|
+
color: #e7e9ea;
|
|
336
|
+
line-height: 1.2;
|
|
337
|
+
white-space: nowrap;
|
|
338
|
+
overflow: hidden;
|
|
339
|
+
text-overflow: ellipsis;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.tweet-card-handle {
|
|
343
|
+
font-size: 0.75rem;
|
|
344
|
+
color: #71767b;
|
|
345
|
+
line-height: 1.2;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.tweet-card-badge {
|
|
349
|
+
margin-left: auto;
|
|
350
|
+
flex-shrink: 0;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.tweet-card-badge svg {
|
|
354
|
+
width: 16px;
|
|
355
|
+
height: 16px;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.tweet-card-text {
|
|
359
|
+
font-size: 0.8125rem;
|
|
360
|
+
color: #e7e9ea;
|
|
361
|
+
line-height: 1.4;
|
|
362
|
+
white-space: pre-wrap;
|
|
363
|
+
word-break: break-word;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.tweet-card-footer {
|
|
367
|
+
display: flex;
|
|
368
|
+
align-items: center;
|
|
369
|
+
justify-content: space-between;
|
|
370
|
+
margin-top: 8px;
|
|
371
|
+
padding-top: 6px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.tweet-card-meta {
|
|
375
|
+
font-size: 0.6875rem;
|
|
376
|
+
color: #71767b;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.tweet-card-action-label {
|
|
380
|
+
font-size: 0.6875rem;
|
|
381
|
+
color: #389e77;
|
|
382
|
+
font-weight: 600;
|
|
383
|
+
text-transform: uppercase;
|
|
384
|
+
letter-spacing: 0.03em;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.tweet-card-link {
|
|
388
|
+
display: flex;
|
|
389
|
+
align-items: center;
|
|
390
|
+
gap: 4px;
|
|
391
|
+
font-size: 0.6875rem;
|
|
392
|
+
color: #1d9bf0;
|
|
393
|
+
text-decoration: none;
|
|
394
|
+
transition: opacity 0.15s;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.tweet-card-link:hover {
|
|
398
|
+
opacity: 0.8;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.tweet-card-link svg {
|
|
402
|
+
width: 12px;
|
|
403
|
+
height: 12px;
|
|
404
|
+
}
|
|
278
405
|
</style>
|
|
279
406
|
</head>
|
|
280
407
|
<body>
|
|
@@ -399,6 +526,97 @@
|
|
|
399
526
|
}
|
|
400
527
|
}
|
|
401
528
|
|
|
529
|
+
function createTweetCard(tweetData) {
|
|
530
|
+
const card = document.createElement('div');
|
|
531
|
+
card.className = 'tweet-card';
|
|
532
|
+
|
|
533
|
+
const actionLabels = { post: 'Posted', reply: 'Replied', schedule: 'Scheduled' };
|
|
534
|
+
const actionLabel = actionLabels[tweetData.action] || 'Tweeted';
|
|
535
|
+
|
|
536
|
+
// Build tweet URL if we have a tweetId and handle
|
|
537
|
+
const handleText = document.getElementById('profileHandle')?.textContent?.replace('@', '') || '';
|
|
538
|
+
const tweetUrl = tweetData.tweetId && handleText
|
|
539
|
+
? `https://x.com/${handleText}/status/${tweetData.tweetId}`
|
|
540
|
+
: null;
|
|
541
|
+
|
|
542
|
+
// Header: avatar + name + handle
|
|
543
|
+
const header = document.createElement('div');
|
|
544
|
+
header.className = 'tweet-card-header';
|
|
545
|
+
|
|
546
|
+
const avatar = document.createElement('div');
|
|
547
|
+
avatar.className = 'tweet-card-avatar';
|
|
548
|
+
if (agentPfp) {
|
|
549
|
+
avatar.innerHTML = `<img src="${agentPfp}" alt="${agentName}" />`;
|
|
550
|
+
} else {
|
|
551
|
+
avatar.innerHTML = `<span class="tweet-card-avatar-letter">${agentName.charAt(0).toUpperCase()}</span>`;
|
|
552
|
+
}
|
|
553
|
+
header.appendChild(avatar);
|
|
554
|
+
|
|
555
|
+
const names = document.createElement('div');
|
|
556
|
+
names.className = 'tweet-card-names';
|
|
557
|
+
names.innerHTML = `
|
|
558
|
+
<span class="tweet-card-name">${escapeHtml(agentName)}</span>
|
|
559
|
+
<span class="tweet-card-handle">@${escapeHtml(handleText || 'agent')}</span>
|
|
560
|
+
`;
|
|
561
|
+
header.appendChild(names);
|
|
562
|
+
|
|
563
|
+
// X logo badge
|
|
564
|
+
const badge = document.createElement('div');
|
|
565
|
+
badge.className = 'tweet-card-badge';
|
|
566
|
+
badge.innerHTML = `<svg viewBox="0 0 24 24" fill="#71767b"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>`;
|
|
567
|
+
header.appendChild(badge);
|
|
568
|
+
|
|
569
|
+
card.appendChild(header);
|
|
570
|
+
|
|
571
|
+
// Tweet text
|
|
572
|
+
const text = document.createElement('div');
|
|
573
|
+
text.className = 'tweet-card-text';
|
|
574
|
+
text.textContent = tweetData.content;
|
|
575
|
+
card.appendChild(text);
|
|
576
|
+
|
|
577
|
+
// Footer: action label + time + link
|
|
578
|
+
const footer = document.createElement('div');
|
|
579
|
+
footer.className = 'tweet-card-footer';
|
|
580
|
+
|
|
581
|
+
const meta = document.createElement('div');
|
|
582
|
+
meta.style.display = 'flex';
|
|
583
|
+
meta.style.alignItems = 'center';
|
|
584
|
+
meta.style.gap = '8px';
|
|
585
|
+
meta.innerHTML = `
|
|
586
|
+
<span class="tweet-card-action-label">${actionLabel}</span>
|
|
587
|
+
<span class="tweet-card-meta">${new Date().toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}</span>
|
|
588
|
+
`;
|
|
589
|
+
footer.appendChild(meta);
|
|
590
|
+
|
|
591
|
+
if (tweetUrl) {
|
|
592
|
+
const link = document.createElement('a');
|
|
593
|
+
link.className = 'tweet-card-link';
|
|
594
|
+
link.href = tweetUrl;
|
|
595
|
+
link.target = '_blank';
|
|
596
|
+
link.rel = 'noopener noreferrer';
|
|
597
|
+
link.innerHTML = `View on X <svg viewBox="0 0 24 24" fill="currentColor"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3"/></svg>`;
|
|
598
|
+
footer.appendChild(link);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
card.appendChild(footer);
|
|
602
|
+
|
|
603
|
+
// Click whole card to open on X
|
|
604
|
+
if (tweetUrl) {
|
|
605
|
+
card.addEventListener('click', (e) => {
|
|
606
|
+
if (e.target.closest('a')) return;
|
|
607
|
+
window.open(tweetUrl, '_blank');
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return card;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function escapeHtml(text) {
|
|
615
|
+
const div = document.createElement('div');
|
|
616
|
+
div.textContent = text;
|
|
617
|
+
return div.innerHTML;
|
|
618
|
+
}
|
|
619
|
+
|
|
402
620
|
function addMessage(role, content, animate = true) {
|
|
403
621
|
const messageDiv = document.createElement('div');
|
|
404
622
|
messageDiv.className = `message ${role}`;
|
|
@@ -421,7 +639,46 @@
|
|
|
421
639
|
|
|
422
640
|
const contentDiv = document.createElement('div');
|
|
423
641
|
contentDiv.className = 'message-content';
|
|
424
|
-
|
|
642
|
+
|
|
643
|
+
// Check for <<TWEET:...>> tags and render as tweet preview cards
|
|
644
|
+
const tweetPattern = /<<TWEET:(.*?)>>/g;
|
|
645
|
+
const hasTweets = tweetPattern.test(content);
|
|
646
|
+
|
|
647
|
+
if (hasTweets) {
|
|
648
|
+
// Split content into text parts and tweet cards
|
|
649
|
+
const parts = content.split(/<<TWEET:.*?>>/g);
|
|
650
|
+
const tweets = [...content.matchAll(/<<TWEET:(.*?)>>/g)];
|
|
651
|
+
|
|
652
|
+
// Add text before first tweet
|
|
653
|
+
if (parts[0]?.trim()) {
|
|
654
|
+
const textSpan = document.createElement('span');
|
|
655
|
+
textSpan.textContent = parts[0].trim();
|
|
656
|
+
contentDiv.appendChild(textSpan);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Interleave tweets and remaining text
|
|
660
|
+
tweets.forEach((match, i) => {
|
|
661
|
+
try {
|
|
662
|
+
const tweetData = JSON.parse(match[1]);
|
|
663
|
+
const card = createTweetCard(tweetData);
|
|
664
|
+
contentDiv.appendChild(card);
|
|
665
|
+
} catch (e) {
|
|
666
|
+
// If JSON parse fails, show raw text
|
|
667
|
+
const fallback = document.createElement('span');
|
|
668
|
+
fallback.textContent = match[0];
|
|
669
|
+
contentDiv.appendChild(fallback);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Text after this tweet
|
|
673
|
+
if (parts[i + 1]?.trim()) {
|
|
674
|
+
const textSpan = document.createElement('span');
|
|
675
|
+
textSpan.textContent = parts[i + 1].trim();
|
|
676
|
+
contentDiv.appendChild(textSpan);
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
} else {
|
|
680
|
+
contentDiv.textContent = content;
|
|
681
|
+
}
|
|
425
682
|
|
|
426
683
|
messageDiv.appendChild(contentDiv);
|
|
427
684
|
|
|
@@ -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-
|
|
205
|
+
const { parseActions, executeActions } = await import("./decision-engine-RVQRDHEI.js");
|
|
206
206
|
const jsonBlockPattern = /```json\s*([\s\S]*?)```/g;
|
|
207
207
|
const blocks = [...responseText.matchAll(jsonBlockPattern)];
|
|
208
208
|
const visibleResults = [];
|
|
@@ -217,9 +217,15 @@ async function extractAndExecuteActions(responseText) {
|
|
|
217
217
|
console.log(chalk.dim(` [Actions] search: ${r.detail ?? r.error ?? "no results"}`));
|
|
218
218
|
}
|
|
219
219
|
} else if (r.success) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
220
|
+
if ((r.action === "post" || r.action === "reply" || r.action === "schedule") && r.content) {
|
|
221
|
+
const tweetData = JSON.stringify({ action: r.action, tweetId: r.detail ?? null, content: r.content });
|
|
222
|
+
visibleResults.push(`<<TWEET:${tweetData}>>`);
|
|
223
|
+
console.log(chalk.green(` [Actions] ${r.action}: success (tweet preview)`));
|
|
224
|
+
} else {
|
|
225
|
+
const detail = r.detail ? ` (${r.detail})` : "";
|
|
226
|
+
visibleResults.push(`[${r.action}] Done${detail}`);
|
|
227
|
+
console.log(chalk.green(` [Actions] ${r.action}: success${detail}`));
|
|
228
|
+
}
|
|
223
229
|
} else {
|
|
224
230
|
visibleResults.push(`[${r.action}] Failed: ${r.error}`);
|
|
225
231
|
console.log(chalk.red(` [Actions] ${r.action}: ${r.error}`));
|
|
@@ -412,7 +418,7 @@ async function runNarratedHeartbeat(server, maxActions, intervalMs) {
|
|
|
412
418
|
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
413
419
|
const { buildSystemPrompt, buildNarratedHeartbeatMessage } = await import("./prompt-builder-DGICLLN5.js");
|
|
414
420
|
const { generateResponse } = await import("./llm-WLEJLNEA.js");
|
|
415
|
-
const { parseActions, executeActions } = await import("./decision-engine-
|
|
421
|
+
const { parseActions, executeActions } = await import("./decision-engine-RVQRDHEI.js");
|
|
416
422
|
const { flushQueue } = await import("./queue-T3OYWUII.js");
|
|
417
423
|
try {
|
|
418
424
|
const flushed = await flushQueue();
|
|
@@ -517,4 +523,4 @@ export {
|
|
|
517
523
|
openBrowser,
|
|
518
524
|
startWebChat
|
|
519
525
|
};
|
|
520
|
-
//# sourceMappingURL=web-chat-
|
|
526
|
+
//# sourceMappingURL=web-chat-PG75Q2VE.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 * Search results are returned separately so the caller can feed them back to the LLM.\n */\nasync function extractAndExecuteActions(responseText: string): Promise<{\n cleanText: string;\n results: string[];\n searchResults: string | null;\n}> {\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 visibleResults: string[] = [];\n let searchResults: string | null = null;\n\n const processResults = (actionResults: Awaited<ReturnType<typeof executeActions>>) => {\n for (const r of actionResults) {\n if (r.action === \"search\") {\n // Search results are internal — don't show in chat\n if (r.success && r.detail && r.detail !== \"No results found\") {\n searchResults = r.detail;\n console.log(chalk.green(` [Actions] search: found results (internal)`));\n } else {\n console.log(chalk.dim(` [Actions] search: ${r.detail ?? r.error ?? \"no results\"}`));\n }\n } else if (r.success) {\n // For post/reply/schedule with content, emit a tweet preview tag\n if ((r.action === \"post\" || r.action === \"reply\" || r.action === \"schedule\") && r.content) {\n const tweetData = JSON.stringify({ action: r.action, tweetId: r.detail ?? null, content: r.content });\n visibleResults.push(`<<TWEET:${tweetData}>>`);\n console.log(chalk.green(` [Actions] ${r.action}: success (tweet preview)`));\n } else {\n const detail = r.detail ? ` (${r.detail})` : \"\";\n visibleResults.push(`[${r.action}] Done${detail}`);\n console.log(chalk.green(` [Actions] ${r.action}: success${detail}`));\n }\n } else {\n visibleResults.push(`[${r.action}] Failed: ${r.error}`);\n console.log(chalk.red(` [Actions] ${r.action}: ${r.error}`));\n }\n }\n };\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 processResults(actionResults);\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 processResults(actionResults);\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 visible action results (search excluded)\n if (visibleResults.length > 0) {\n cleanText += \"\\n\\n\" + visibleResults.join(\"\\n\");\n }\n\n return { cleanText, results: visibleResults, searchResults };\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 // Resolve real Twitter handle on startup\n let realHandle: string | undefined;\n try {\n const { hasXCredentials } = await import(\"../utils/paths.js\");\n if (hasXCredentials()) {\n const { getXClient } = await import(\"../x-client/index.js\");\n const client = await getXClient();\n if (\"getAuthenticatedHandle\" in client) {\n realHandle = await (client as any).getAuthenticatedHandle();\n console.log(chalk.dim(` [Auth] Real Twitter handle: @${realHandle}`));\n // Update the server identity to show the real handle\n server.setIdentity({\n name: identity.name,\n handle: realHandle,\n bio: identity.bio,\n profileImage: identity.profileImage,\n createdAt: identity.createdAt,\n });\n }\n }\n } catch (err) {\n console.log(chalk.dim(` [Auth] Could not resolve real handle: ${(err as Error).message}`));\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(realHandle);\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 let { cleanText: actionCleanedText, searchResults } = await extractAndExecuteActions(response.content);\n\n // Two-pass: if search returned results, feed them back to the LLM so it can act on them\n if (searchResults) {\n console.log(chalk.cyan(\" [Chat] Search returned results — doing follow-up LLM call...\"));\n\n // Add the first response (narration) to history as context\n const firstNarration = actionCleanedText.replace(/```json\\s*[\\s\\S]*?```/g, \"\").replace(/\\n{3,}/g, \"\\n\\n\").trim();\n if (firstNarration) {\n chatHistory.push({ role: \"assistant\", content: firstNarration });\n }\n\n // Inject search results as a system-style user message the LLM can act on\n chatHistory.push({\n role: \"user\",\n content: `[Internal: here are the search results you requested. Pick one or more to act on (reply, like, etc.) and do it now. Do NOT show these results to me — just act on them and tell me what you did.]\\n\\n${searchResults}`,\n });\n\n // Second LLM call — it now has search results and can reply/like\n const followUp = await chatLLM(systemPrompt, chatHistory);\n const followUpResult = await extractAndExecuteActions(followUp.content);\n actionCleanedText = followUpResult.cleanText;\n\n // Remove the internal search-results message from visible history\n chatHistory.pop();\n if (firstNarration) {\n chatHistory.pop();\n }\n }\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;\nlet lastTimelineSinceId: string | undefined;\nlet lastMentionsSinceId: string | undefined;\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 — flush queue during sleep so scheduled posts go out on time\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(10000, sleepMs - slept)));\n slept += 10000;\n\n // Flush queue during sleep so scheduled posts go out at their intended times\n try {\n const { flushQueue } = await import(\"../scheduler/queue.js\");\n const flushed = await flushQueue();\n if (flushed.posted > 0) {\n console.log(chalk.green(` [Queue] Posted ${flushed.posted} scheduled tweet(s)`));\n }\n } catch {\n // Queue flush failed during sleep, not critical\n }\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 (silent — user doesn't see this)\n try {\n const flushed = await flushQueue();\n if (flushed.posted > 0) {\n console.log(chalk.green(` [Queue] Flushed ${flushed.posted} scheduled tweet(s)`));\n }\n } catch {\n // Queue flush failed, not critical\n }\n\n // 2. Read timeline and mentions (only new since last check)\n\n const client = await getXClient();\n let timeline: Awaited<ReturnType<typeof client.getTimeline>> = [];\n let mentions: Awaited<ReturnType<typeof client.getMentions>> = [];\n\n // Get the bot's own handle to filter own tweets from timeline\n let ownHandle: string | undefined;\n if (\"getAuthenticatedHandle\" in client) {\n try {\n ownHandle = await (client as any).getAuthenticatedHandle();\n } catch {}\n }\n\n try {\n timeline = await client.getTimeline({ count: 20, sinceId: lastTimelineSinceId });\n if (timeline.length > 0) {\n lastTimelineSinceId = timeline[0].id;\n // Filter out own tweets (can't like your own)\n if (ownHandle) {\n timeline = timeline.filter(t => t.authorHandle.toLowerCase() !== ownHandle!.toLowerCase());\n }\n }\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, sinceId: lastMentionsSinceId });\n if (mentions.length > 0) {\n lastMentionsSinceId = mentions[0].id;\n }\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 (limitedActions.length > 0) {\n // Execute actions\n const results = await executeActions(limitedActions);\n\n // Build visible results (hide schedule actions from chat — they're internal)\n const visibleResults: string[] = [];\n for (const r of results) {\n const detail = r.detail ? ` (${r.detail})` : \"\";\n if (r.action === \"schedule\") {\n // Log to terminal only, don't show in chat\n console.log(chalk.dim(` [Heartbeat] ${r.action}: ${r.success ? \"queued\" : r.error}${detail}`));\n } else if (r.success) {\n visibleResults.push(`[${r.action}] Done${detail}`);\n console.log(chalk.green(` [Heartbeat] ${r.action}: success${detail}`));\n } else {\n visibleResults.push(`[${r.action}] Failed: ${r.error}`);\n console.log(chalk.red(` [Heartbeat] ${r.action}: ${r.error}`));\n }\n }\n\n // Combine narration with visible action results\n let fullMessage = narration || \"\";\n if (visibleResults.length > 0) {\n fullMessage += (fullMessage ? \"\\n\\n\" : \"\") + visibleResults.join(\"\\n\");\n }\n if (fullMessage) {\n server.pushMessage(\"assistant\", fullMessage);\n }\n } else if (narration) {\n server.pushMessage(\"assistant\", narration);\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":[]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/decision-engine.ts"],"sourcesContent":["import { logger } from \"../utils/logger.js\";\nimport { getXClient } from \"../x-client/index.js\";\nimport { logInteraction, addLearning } from \"../memory/index.js\";\nimport { loadIdentity, saveIdentity } from \"../identity/index.js\";\nimport { addToQueue } from \"../scheduler/queue.js\";\n\nexport interface AgentAction {\n action: string;\n content?: string;\n tweetId?: string;\n handle?: string;\n query?: string;\n tags?: string[];\n reason?: string;\n reasoning?: string;\n scheduledFor?: string;\n}\n\nexport interface ActionResult {\n action: string;\n success: boolean;\n detail?: string;\n error?: string;\n}\n\nexport function parseActions(llmResponse: string): AgentAction[] {\n // Extract JSON array from the response (may be wrapped in markdown code blocks)\n const jsonMatch = llmResponse.match(/\\[[\\s\\S]*?\\]/);\n if (!jsonMatch) {\n // Try to parse as a single action object\n const objMatch = llmResponse.match(/\\{[\\s\\S]*?\\}/);\n if (objMatch) {\n try {\n return [JSON.parse(objMatch[0]) as AgentAction];\n } catch {\n logger.warn(\"Could not parse LLM response as action object\");\n return [];\n }\n }\n logger.warn(\"No JSON found in LLM response\");\n return [];\n }\n\n try {\n const actions = JSON.parse(jsonMatch[0]) as AgentAction[];\n return Array.isArray(actions) ? actions : [actions];\n } catch {\n logger.warn(\"Failed to parse actions JSON from LLM response\");\n return [];\n }\n}\n\nfunction sanitizeTweetId(id: string): string {\n // Strip \"tweet:\" prefix if LLM includes it from prompt format [tweet:123]\n return id.replace(/^tweet:/i, \"\").trim();\n}\n\nexport async function executeAction(action: AgentAction): Promise<ActionResult> {\n const { action: type } = action;\n\n // Clean up tweetId if present\n if (action.tweetId) {\n action.tweetId = sanitizeTweetId(action.tweetId);\n }\n\n try {\n switch (type) {\n case \"post\": {\n if (!action.content) return { action: type, success: false, error: \"No content provided\" };\n if (action.content.length > 280) {\n return { action: type, success: false, error: `Tweet too long: ${action.content.length} chars (max 280)` };\n }\n\n const client = await getXClient();\n const result = await client.postTweet(action.content);\n if (result.success) {\n logger.info(`Posted: \"${action.content.slice(0, 50)}...\"`);\n }\n return { action: type, success: result.success, detail: result.tweetId, error: result.error };\n }\n\n case \"reply\": {\n if (!action.tweetId || !action.content) {\n return { action: type, success: false, error: \"Missing tweetId or content\" };\n }\n\n const client = await getXClient();\n const result = await client.replyToTweet(action.tweetId, action.content);\n if (result.success) {\n logger.info(`Replied to ${action.tweetId}: \"${action.content.slice(0, 50)}...\"`);\n }\n return { action: type, success: result.success, detail: result.tweetId, error: result.error };\n }\n\n case \"like\": {\n if (!action.tweetId) return { action: type, success: false, error: \"Missing tweetId\" };\n const client = await getXClient();\n const result = await client.likeTweet(action.tweetId);\n return { action: type, success: result.success, error: result.error };\n }\n\n case \"retweet\": {\n if (!action.tweetId) return { action: type, success: false, error: \"Missing tweetId\" };\n const client = await getXClient();\n const result = await client.retweet(action.tweetId);\n return { action: type, success: result.success, error: result.error };\n }\n\n case \"follow\": {\n if (!action.handle) return { action: type, success: false, error: \"Missing handle\" };\n const client = await getXClient();\n const result = await client.followUser(action.handle);\n return { action: type, success: result.success, error: result.error };\n }\n\n case \"schedule\": {\n if (!action.content) return { action: type, success: false, error: \"No content\" };\n const entry = addToQueue(action.content, action.scheduledFor);\n const time = new Date(entry.scheduledFor).toLocaleTimeString(\"en-US\", { hour: \"numeric\", minute: \"2-digit\" });\n logger.info(`Scheduled: \"${action.content.slice(0, 50)}...\" for ${time}`);\n return { action: type, success: true, detail: `Queued for ${time}` };\n }\n\n case \"learn\": {\n if (!action.content) return { action: type, success: false, error: \"No content\" };\n addLearning(action.content, \"agent\", action.tags ?? [\"heartbeat\"]);\n logger.info(`Learned: \"${action.content.slice(0, 50)}...\"`);\n return { action: type, success: true };\n }\n\n case \"reflect\": {\n if (!action.content) return { action: type, success: false, error: \"No content\" };\n const identity = loadIdentity();\n identity.evolutionJournal.push({\n date: new Date().toISOString(),\n reflection: action.content,\n });\n saveIdentity(identity);\n logger.info(`Reflected: \"${action.content.slice(0, 50)}...\"`);\n return { action: type, success: true };\n }\n\n case \"search\": {\n if (!action.query) return { action: type, success: false, error: \"No query provided\" };\n const client = await getXClient();\n const tweets = await client.searchTweets(action.query, { count: 10 });\n if (tweets.length === 0) {\n return { action: type, success: true, detail: \"No results found\" };\n }\n const results = tweets.map(t => `@${t.authorHandle}: \"${t.text}\" [tweet:${t.id}]`).join(\"\\n\");\n logger.info(`Search \"${action.query}\": ${tweets.length} results`);\n return { action: type, success: true, detail: results };\n }\n\n case \"skip\": {\n logger.info(`Skipping: ${action.reason ?? action.reasoning ?? \"no reason given\"}`);\n return { action: type, success: true, detail: action.reason ?? action.reasoning };\n }\n\n default:\n logger.warn(`Unknown action: ${type}`);\n return { action: type, success: false, error: `Unknown action: ${type}` };\n }\n } catch (error) {\n const msg = (error as Error).message;\n logger.error(`Action ${type} failed: ${msg}`);\n return { action: type, success: false, error: msg };\n }\n}\n\nexport async function executeActions(actions: AgentAction[]): Promise<ActionResult[]> {\n const results: ActionResult[] = [];\n let instantPostCount = 0;\n let scheduleCount = 0;\n const MAX_INSTANT_POSTS = 1; // Hard cap: max 1 immediate post per batch\n const MAX_SCHEDULES = 3; // Allow up to 3 scheduled posts (they'll be spaced out)\n\n for (const action of actions) {\n // Enforce instant post limit\n if (action.action === \"post\") {\n if (instantPostCount >= MAX_INSTANT_POSTS) {\n results.push({\n action: action.action,\n success: false,\n error: \"Instant post limit reached (max 1 per heartbeat)\",\n });\n logger.info(`Skipped post: instant post limit reached`);\n continue;\n }\n instantPostCount++;\n }\n\n // Enforce schedule limit\n if (action.action === \"schedule\") {\n if (scheduleCount >= MAX_SCHEDULES) {\n results.push({\n action: action.action,\n success: false,\n error: \"Schedule limit reached (max 3 per heartbeat)\",\n });\n logger.info(`Skipped schedule: schedule limit reached`);\n continue;\n }\n scheduleCount++;\n }\n\n const result = await executeAction(action);\n results.push(result);\n // Small delay between actions to be human-like\n await new Promise((r) => setTimeout(r, 2000 + Math.random() * 3000));\n }\n return results;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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 * Search results are returned separately so the caller can feed them back to the LLM.\n */\nasync function extractAndExecuteActions(responseText: string): Promise<{\n cleanText: string;\n results: string[];\n searchResults: string | null;\n}> {\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 visibleResults: string[] = [];\n let searchResults: string | null = null;\n\n const processResults = (actionResults: Awaited<ReturnType<typeof executeActions>>) => {\n for (const r of actionResults) {\n if (r.action === \"search\") {\n // Search results are internal — don't show in chat\n if (r.success && r.detail && r.detail !== \"No results found\") {\n searchResults = r.detail;\n console.log(chalk.green(` [Actions] search: found results (internal)`));\n } else {\n console.log(chalk.dim(` [Actions] search: ${r.detail ?? r.error ?? \"no results\"}`));\n }\n } else if (r.success) {\n const detail = r.detail ? ` (${r.detail})` : \"\";\n visibleResults.push(`[${r.action}] Done${detail}`);\n console.log(chalk.green(` [Actions] ${r.action}: success${detail}`));\n } else {\n visibleResults.push(`[${r.action}] Failed: ${r.error}`);\n console.log(chalk.red(` [Actions] ${r.action}: ${r.error}`));\n }\n }\n };\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 processResults(actionResults);\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 processResults(actionResults);\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 visible action results (search excluded)\n if (visibleResults.length > 0) {\n cleanText += \"\\n\\n\" + visibleResults.join(\"\\n\");\n }\n\n return { cleanText, results: visibleResults, searchResults };\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 // Resolve real Twitter handle on startup\n let realHandle: string | undefined;\n try {\n const { hasXCredentials } = await import(\"../utils/paths.js\");\n if (hasXCredentials()) {\n const { getXClient } = await import(\"../x-client/index.js\");\n const client = await getXClient();\n if (\"getAuthenticatedHandle\" in client) {\n realHandle = await (client as any).getAuthenticatedHandle();\n console.log(chalk.dim(` [Auth] Real Twitter handle: @${realHandle}`));\n // Update the server identity to show the real handle\n server.setIdentity({\n name: identity.name,\n handle: realHandle,\n bio: identity.bio,\n profileImage: identity.profileImage,\n createdAt: identity.createdAt,\n });\n }\n }\n } catch (err) {\n console.log(chalk.dim(` [Auth] Could not resolve real handle: ${(err as Error).message}`));\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(realHandle);\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 let { cleanText: actionCleanedText, searchResults } = await extractAndExecuteActions(response.content);\n\n // Two-pass: if search returned results, feed them back to the LLM so it can act on them\n if (searchResults) {\n console.log(chalk.cyan(\" [Chat] Search returned results — doing follow-up LLM call...\"));\n\n // Add the first response (narration) to history as context\n const firstNarration = actionCleanedText.replace(/```json\\s*[\\s\\S]*?```/g, \"\").replace(/\\n{3,}/g, \"\\n\\n\").trim();\n if (firstNarration) {\n chatHistory.push({ role: \"assistant\", content: firstNarration });\n }\n\n // Inject search results as a system-style user message the LLM can act on\n chatHistory.push({\n role: \"user\",\n content: `[Internal: here are the search results you requested. Pick one or more to act on (reply, like, etc.) and do it now. Do NOT show these results to me — just act on them and tell me what you did.]\\n\\n${searchResults}`,\n });\n\n // Second LLM call — it now has search results and can reply/like\n const followUp = await chatLLM(systemPrompt, chatHistory);\n const followUpResult = await extractAndExecuteActions(followUp.content);\n actionCleanedText = followUpResult.cleanText;\n\n // Remove the internal search-results message from visible history\n chatHistory.pop();\n if (firstNarration) {\n chatHistory.pop();\n }\n }\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;\nlet lastTimelineSinceId: string | undefined;\nlet lastMentionsSinceId: string | undefined;\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 — flush queue during sleep so scheduled posts go out on time\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(10000, sleepMs - slept)));\n slept += 10000;\n\n // Flush queue during sleep so scheduled posts go out at their intended times\n try {\n const { flushQueue } = await import(\"../scheduler/queue.js\");\n const flushed = await flushQueue();\n if (flushed.posted > 0) {\n console.log(chalk.green(` [Queue] Posted ${flushed.posted} scheduled tweet(s)`));\n }\n } catch {\n // Queue flush failed during sleep, not critical\n }\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 (silent — user doesn't see this)\n try {\n const flushed = await flushQueue();\n if (flushed.posted > 0) {\n console.log(chalk.green(` [Queue] Flushed ${flushed.posted} scheduled tweet(s)`));\n }\n } catch {\n // Queue flush failed, not critical\n }\n\n // 2. Read timeline and mentions (only new since last check)\n\n const client = await getXClient();\n let timeline: Awaited<ReturnType<typeof client.getTimeline>> = [];\n let mentions: Awaited<ReturnType<typeof client.getMentions>> = [];\n\n // Get the bot's own handle to filter own tweets from timeline\n let ownHandle: string | undefined;\n if (\"getAuthenticatedHandle\" in client) {\n try {\n ownHandle = await (client as any).getAuthenticatedHandle();\n } catch {}\n }\n\n try {\n timeline = await client.getTimeline({ count: 20, sinceId: lastTimelineSinceId });\n if (timeline.length > 0) {\n lastTimelineSinceId = timeline[0].id;\n // Filter out own tweets (can't like your own)\n if (ownHandle) {\n timeline = timeline.filter(t => t.authorHandle.toLowerCase() !== ownHandle!.toLowerCase());\n }\n }\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, sinceId: lastMentionsSinceId });\n if (mentions.length > 0) {\n lastMentionsSinceId = mentions[0].id;\n }\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 (limitedActions.length > 0) {\n // Execute actions\n const results = await executeActions(limitedActions);\n\n // Build visible results (hide schedule actions from chat — they're internal)\n const visibleResults: string[] = [];\n for (const r of results) {\n const detail = r.detail ? ` (${r.detail})` : \"\";\n if (r.action === \"schedule\") {\n // Log to terminal only, don't show in chat\n console.log(chalk.dim(` [Heartbeat] ${r.action}: ${r.success ? \"queued\" : r.error}${detail}`));\n } else if (r.success) {\n visibleResults.push(`[${r.action}] Done${detail}`);\n console.log(chalk.green(` [Heartbeat] ${r.action}: success${detail}`));\n } else {\n visibleResults.push(`[${r.action}] Failed: ${r.error}`);\n console.log(chalk.red(` [Heartbeat] ${r.action}: ${r.error}`));\n }\n }\n\n // Combine narration with visible action results\n let fullMessage = narration || \"\";\n if (visibleResults.length > 0) {\n fullMessage += (fullMessage ? \"\\n\\n\" : \"\") + visibleResults.join(\"\\n\");\n }\n if (fullMessage) {\n server.pushMessage(\"assistant\", fullMessage);\n }\n } else if (narration) {\n server.pushMessage(\"assistant\", narration);\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":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|