spora 0.6.2 → 0.7.0

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 (42) hide show
  1. package/dist/chunk-535NMUUW.js +96 -0
  2. package/dist/chunk-535NMUUW.js.map +1 -0
  3. package/dist/{chunk-LKCYTFWN.js → chunk-AH7HPXYC.js} +42 -72
  4. package/dist/chunk-AH7HPXYC.js.map +1 -0
  5. package/dist/chunk-E6GMS76S.js +154 -0
  6. package/dist/chunk-E6GMS76S.js.map +1 -0
  7. package/dist/{chunk-TPKZOHIH.js → chunk-JJZ7T2IZ.js} +3 -3
  8. package/dist/{chunk-2PMNKWOC.js → chunk-SBQILQCJ.js} +2 -2
  9. package/dist/{chunk-PLNI6AKJ.js → chunk-TF2XYGGG.js} +3 -3
  10. package/dist/cli.js +24 -24
  11. package/dist/{client-WUEOHAFE.js → client-B6NGVRHM.js} +69 -10
  12. package/dist/client-B6NGVRHM.js.map +1 -0
  13. package/dist/{client-I7Q4HC4F.js → client-DDCS5FJS.js} +12 -1
  14. package/dist/client-DDCS5FJS.js.map +1 -0
  15. package/dist/{colony-HTEK2CN7.js → colony-LCWN5IAN.js} +2 -2
  16. package/dist/{decision-engine-ZOLEASCJ.js → decision-engine-DRPIZLHI.js} +4 -4
  17. package/dist/{heartbeat-IUFPYH55.js → heartbeat-ZHRCEMF5.js} +15 -19
  18. package/dist/heartbeat-ZHRCEMF5.js.map +1 -0
  19. package/dist/{init-E2RYLJW6.js → init-G3WINLAP.js} +3 -3
  20. package/dist/mcp-server.js +19 -19
  21. package/dist/{prompt-builder-3LQFX6QK.js → prompt-builder-U2J4H7YX.js} +3 -2
  22. package/dist/{queue-BROGYAFR.js → queue-USY7JXDV.js} +2 -2
  23. package/dist/research-TQLP42BC.js +13 -0
  24. package/dist/{web-chat-HPLZETDN.js → web-chat-RQIILEQK.js} +17 -23
  25. package/dist/web-chat-RQIILEQK.js.map +1 -0
  26. package/dist/{x-client-AQGDGEY2.js → x-client-TYU5QSLG.js} +2 -2
  27. package/dist/x-client-TYU5QSLG.js.map +1 -0
  28. package/package.json +1 -1
  29. package/dist/chunk-LKCYTFWN.js.map +0 -1
  30. package/dist/client-I7Q4HC4F.js.map +0 -1
  31. package/dist/client-WUEOHAFE.js.map +0 -1
  32. package/dist/heartbeat-IUFPYH55.js.map +0 -1
  33. package/dist/web-chat-HPLZETDN.js.map +0 -1
  34. /package/dist/{chunk-TPKZOHIH.js.map → chunk-JJZ7T2IZ.js.map} +0 -0
  35. /package/dist/{chunk-2PMNKWOC.js.map → chunk-SBQILQCJ.js.map} +0 -0
  36. /package/dist/{chunk-PLNI6AKJ.js.map → chunk-TF2XYGGG.js.map} +0 -0
  37. /package/dist/{colony-HTEK2CN7.js.map → colony-LCWN5IAN.js.map} +0 -0
  38. /package/dist/{decision-engine-ZOLEASCJ.js.map → decision-engine-DRPIZLHI.js.map} +0 -0
  39. /package/dist/{init-E2RYLJW6.js.map → init-G3WINLAP.js.map} +0 -0
  40. /package/dist/{prompt-builder-3LQFX6QK.js.map → prompt-builder-U2J4H7YX.js.map} +0 -0
  41. /package/dist/{queue-BROGYAFR.js.map → queue-USY7JXDV.js.map} +0 -0
  42. /package/dist/{x-client-AQGDGEY2.js.map → research-TQLP42BC.js.map} +0 -0
@@ -0,0 +1,96 @@
1
+ import {
2
+ paths
3
+ } from "./chunk-53YLFYJF.js";
4
+
5
+ // src/memory/performance.ts
6
+ import { existsSync, readFileSync, writeFileSync } from "fs";
7
+ function loadPerformance() {
8
+ if (!existsSync(paths.performance)) {
9
+ return { trackedPosts: [], selfMetrics: [] };
10
+ }
11
+ try {
12
+ return JSON.parse(readFileSync(paths.performance, "utf-8"));
13
+ } catch {
14
+ return { trackedPosts: [], selfMetrics: [] };
15
+ }
16
+ }
17
+ function savePerformance(data) {
18
+ writeFileSync(paths.performance, JSON.stringify(data, null, 2));
19
+ }
20
+ function getActiveTrackedPosts() {
21
+ const data = loadPerformance();
22
+ return data.trackedPosts.filter((p) => !p.retired);
23
+ }
24
+ function retireOldPosts() {
25
+ const data = loadPerformance();
26
+ const cutoff = Date.now() - 72 * 60 * 60 * 1e3;
27
+ let changed = false;
28
+ for (const post of data.trackedPosts) {
29
+ if (!post.retired && new Date(post.postedAt).getTime() < cutoff) {
30
+ post.retired = true;
31
+ changed = true;
32
+ }
33
+ }
34
+ const pruneCutoff = Date.now() - 30 * 24 * 60 * 60 * 1e3;
35
+ const before = data.trackedPosts.length;
36
+ data.trackedPosts = data.trackedPosts.filter(
37
+ (p) => !p.retired || new Date(p.postedAt).getTime() > pruneCutoff
38
+ );
39
+ if (data.trackedPosts.length !== before) changed = true;
40
+ if (changed) savePerformance(data);
41
+ }
42
+ function getPerformanceSummary() {
43
+ const data = loadPerformance();
44
+ const lines = [];
45
+ const oneDayAgo = Date.now() - 24 * 60 * 60 * 1e3;
46
+ const recentPosts = data.trackedPosts.filter(
47
+ (p) => new Date(p.postedAt).getTime() > oneDayAgo
48
+ );
49
+ if (recentPosts.length > 0) {
50
+ const postStats = recentPosts.map((p) => {
51
+ const latest = p.metrics.length > 0 ? p.metrics[p.metrics.length - 1] : null;
52
+ return {
53
+ content: p.content,
54
+ type: p.type,
55
+ likes: latest?.likes ?? 0,
56
+ retweets: latest?.retweets ?? 0,
57
+ replies: latest?.replies ?? 0
58
+ };
59
+ });
60
+ const totalLikes = postStats.reduce((s, p) => s + p.likes, 0);
61
+ const totalRTs = postStats.reduce((s, p) => s + p.retweets, 0);
62
+ const avgLikes = Math.round(totalLikes / postStats.length);
63
+ lines.push(`- Last 24h: ${postStats.length} posts, avg ${avgLikes} likes, ${totalRTs} total retweets`);
64
+ const sorted = [...postStats].sort((a, b) => b.likes - a.likes);
65
+ if (sorted.length > 0 && sorted[0].likes > 0) {
66
+ lines.push(`- Best performing: "${sorted[0].content.slice(0, 60)}..." (${sorted[0].likes} likes, ${sorted[0].retweets} RTs)`);
67
+ }
68
+ if (sorted.length > 1) {
69
+ const worst = sorted[sorted.length - 1];
70
+ lines.push(`- Lowest performing: "${worst.content.slice(0, 60)}..." (${worst.likes} likes)`);
71
+ }
72
+ } else {
73
+ lines.push("- No tracked posts in the last 24 hours yet.");
74
+ }
75
+ if (data.selfMetrics.length > 0) {
76
+ const latest = data.selfMetrics[data.selfMetrics.length - 1];
77
+ lines.push(`- Followers: ${latest.followers} | Following: ${latest.following} | Total tweets: ${latest.totalTweets}`);
78
+ const dayAgoMetric = data.selfMetrics.find(
79
+ (m) => Math.abs(new Date(m.checkedAt).getTime() - (Date.now() - 24 * 60 * 60 * 1e3)) < 12 * 60 * 60 * 1e3
80
+ );
81
+ if (dayAgoMetric) {
82
+ const diff = latest.followers - dayAgoMetric.followers;
83
+ if (diff !== 0) {
84
+ lines.push(`- Follower trend: ${diff > 0 ? "+" : ""}${diff} in the last ~24h`);
85
+ }
86
+ }
87
+ }
88
+ return lines.length > 0 ? lines.join("\n") : "";
89
+ }
90
+
91
+ export {
92
+ getActiveTrackedPosts,
93
+ retireOldPosts,
94
+ getPerformanceSummary
95
+ };
96
+ //# sourceMappingURL=chunk-535NMUUW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/memory/performance.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { paths } from \"../utils/paths.js\";\n\nexport interface EngagementMetric {\n checkedAt: string;\n likes: number;\n retweets: number;\n replies: number;\n}\n\nexport interface TrackedPost {\n tweetId: string;\n content: string;\n type: \"post\" | \"reply\";\n postedAt: string;\n metrics: EngagementMetric[];\n retired: boolean;\n}\n\nexport interface SelfMetric {\n checkedAt: string;\n followers: number;\n following: number;\n totalTweets: number;\n}\n\nexport interface PerformanceData {\n trackedPosts: TrackedPost[];\n selfMetrics: SelfMetric[];\n}\n\nfunction loadPerformance(): PerformanceData {\n if (!existsSync(paths.performance)) {\n return { trackedPosts: [], selfMetrics: [] };\n }\n try {\n return JSON.parse(readFileSync(paths.performance, \"utf-8\"));\n } catch {\n return { trackedPosts: [], selfMetrics: [] };\n }\n}\n\nfunction savePerformance(data: PerformanceData): void {\n writeFileSync(paths.performance, JSON.stringify(data, null, 2));\n}\n\nexport function trackPost(tweetId: string, content: string, type: \"post\" | \"reply\"): void {\n const data = loadPerformance();\n // Don't double-track\n if (data.trackedPosts.some(p => p.tweetId === tweetId)) return;\n data.trackedPosts.push({\n tweetId,\n content,\n type,\n postedAt: new Date().toISOString(),\n metrics: [],\n retired: false,\n });\n savePerformance(data);\n}\n\nexport function getActiveTrackedPosts(): TrackedPost[] {\n const data = loadPerformance();\n return data.trackedPosts.filter(p => !p.retired);\n}\n\nexport function updatePostMetrics(tweetId: string, metric: EngagementMetric): void {\n const data = loadPerformance();\n const post = data.trackedPosts.find(p => p.tweetId === tweetId);\n if (!post) return;\n post.metrics.push(metric);\n savePerformance(data);\n}\n\nexport function retireOldPosts(): void {\n const data = loadPerformance();\n const cutoff = Date.now() - 72 * 60 * 60 * 1000; // 72 hours\n let changed = false;\n for (const post of data.trackedPosts) {\n if (!post.retired && new Date(post.postedAt).getTime() < cutoff) {\n post.retired = true;\n changed = true;\n }\n }\n // Also prune very old retired posts (older than 30 days) to prevent file bloat\n const pruneCutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;\n const before = data.trackedPosts.length;\n data.trackedPosts = data.trackedPosts.filter(\n p => !p.retired || new Date(p.postedAt).getTime() > pruneCutoff\n );\n if (data.trackedPosts.length !== before) changed = true;\n if (changed) savePerformance(data);\n}\n\nexport function updateSelfMetrics(metric: SelfMetric): void {\n const data = loadPerformance();\n data.selfMetrics.push(metric);\n // Keep only last 100 snapshots\n if (data.selfMetrics.length > 100) {\n data.selfMetrics = data.selfMetrics.slice(-100);\n }\n savePerformance(data);\n}\n\nexport function getPerformanceSummary(): string {\n const data = loadPerformance();\n const lines: string[] = [];\n\n // Post performance (last 24h)\n const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000;\n const recentPosts = data.trackedPosts.filter(\n p => new Date(p.postedAt).getTime() > oneDayAgo\n );\n\n if (recentPosts.length > 0) {\n // Get latest metrics for each post\n const postStats = recentPosts.map(p => {\n const latest = p.metrics.length > 0 ? p.metrics[p.metrics.length - 1] : null;\n return {\n content: p.content,\n type: p.type,\n likes: latest?.likes ?? 0,\n retweets: latest?.retweets ?? 0,\n replies: latest?.replies ?? 0,\n };\n });\n\n const totalLikes = postStats.reduce((s, p) => s + p.likes, 0);\n const totalRTs = postStats.reduce((s, p) => s + p.retweets, 0);\n const avgLikes = Math.round(totalLikes / postStats.length);\n\n lines.push(`- Last 24h: ${postStats.length} posts, avg ${avgLikes} likes, ${totalRTs} total retweets`);\n\n // Best and worst performing\n const sorted = [...postStats].sort((a, b) => b.likes - a.likes);\n if (sorted.length > 0 && sorted[0].likes > 0) {\n lines.push(`- Best performing: \"${sorted[0].content.slice(0, 60)}...\" (${sorted[0].likes} likes, ${sorted[0].retweets} RTs)`);\n }\n if (sorted.length > 1) {\n const worst = sorted[sorted.length - 1];\n lines.push(`- Lowest performing: \"${worst.content.slice(0, 60)}...\" (${worst.likes} likes)`);\n }\n } else {\n lines.push(\"- No tracked posts in the last 24 hours yet.\");\n }\n\n // Self metrics\n if (data.selfMetrics.length > 0) {\n const latest = data.selfMetrics[data.selfMetrics.length - 1];\n lines.push(`- Followers: ${latest.followers} | Following: ${latest.following} | Total tweets: ${latest.totalTweets}`);\n\n // Trend: compare to 24h ago\n const dayAgoMetric = data.selfMetrics.find(m =>\n Math.abs(new Date(m.checkedAt).getTime() - (Date.now() - 24 * 60 * 60 * 1000)) < 12 * 60 * 60 * 1000\n );\n if (dayAgoMetric) {\n const diff = latest.followers - dayAgoMetric.followers;\n if (diff !== 0) {\n lines.push(`- Follower trend: ${diff > 0 ? \"+\" : \"\"}${diff} in the last ~24h`);\n }\n }\n }\n\n return lines.length > 0 ? lines.join(\"\\n\") : \"\";\n}\n"],"mappings":"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","names":[]}
@@ -1,3 +1,6 @@
1
+ import {
2
+ getPerformanceSummary
3
+ } from "./chunk-535NMUUW.js";
1
4
  import {
2
5
  renderStrategyForPrompt
3
6
  } from "./chunk-UM57WU5I.js";
@@ -19,70 +22,6 @@ import {
19
22
  loadLearnings,
20
23
  loadRelationships
21
24
  } from "./chunk-EBO4F5NU.js";
22
- import {
23
- paths
24
- } from "./chunk-53YLFYJF.js";
25
-
26
- // src/memory/performance.ts
27
- import { existsSync, readFileSync, writeFileSync } from "fs";
28
- function loadPerformance() {
29
- if (!existsSync(paths.performance)) {
30
- return { trackedPosts: [], selfMetrics: [] };
31
- }
32
- try {
33
- return JSON.parse(readFileSync(paths.performance, "utf-8"));
34
- } catch {
35
- return { trackedPosts: [], selfMetrics: [] };
36
- }
37
- }
38
- function getPerformanceSummary() {
39
- const data = loadPerformance();
40
- const lines = [];
41
- const oneDayAgo = Date.now() - 24 * 60 * 60 * 1e3;
42
- const recentPosts = data.trackedPosts.filter(
43
- (p) => new Date(p.postedAt).getTime() > oneDayAgo
44
- );
45
- if (recentPosts.length > 0) {
46
- const postStats = recentPosts.map((p) => {
47
- const latest = p.metrics.length > 0 ? p.metrics[p.metrics.length - 1] : null;
48
- return {
49
- content: p.content,
50
- type: p.type,
51
- likes: latest?.likes ?? 0,
52
- retweets: latest?.retweets ?? 0,
53
- replies: latest?.replies ?? 0
54
- };
55
- });
56
- const totalLikes = postStats.reduce((s, p) => s + p.likes, 0);
57
- const totalRTs = postStats.reduce((s, p) => s + p.retweets, 0);
58
- const avgLikes = Math.round(totalLikes / postStats.length);
59
- lines.push(`- Last 24h: ${postStats.length} posts, avg ${avgLikes} likes, ${totalRTs} total retweets`);
60
- const sorted = [...postStats].sort((a, b) => b.likes - a.likes);
61
- if (sorted.length > 0 && sorted[0].likes > 0) {
62
- lines.push(`- Best performing: "${sorted[0].content.slice(0, 60)}..." (${sorted[0].likes} likes, ${sorted[0].retweets} RTs)`);
63
- }
64
- if (sorted.length > 1) {
65
- const worst = sorted[sorted.length - 1];
66
- lines.push(`- Lowest performing: "${worst.content.slice(0, 60)}..." (${worst.likes} likes)`);
67
- }
68
- } else {
69
- lines.push("- No tracked posts in the last 24 hours yet.");
70
- }
71
- if (data.selfMetrics.length > 0) {
72
- const latest = data.selfMetrics[data.selfMetrics.length - 1];
73
- lines.push(`- Followers: ${latest.followers} | Following: ${latest.following} | Total tweets: ${latest.totalTweets}`);
74
- const dayAgoMetric = data.selfMetrics.find(
75
- (m) => Math.abs(new Date(m.checkedAt).getTime() - (Date.now() - 24 * 60 * 60 * 1e3)) < 12 * 60 * 60 * 1e3
76
- );
77
- if (dayAgoMetric) {
78
- const diff = latest.followers - dayAgoMetric.followers;
79
- if (diff !== 0) {
80
- lines.push(`- Follower trend: ${diff > 0 ? "+" : ""}${diff} in the last ~24h`);
81
- }
82
- }
83
- }
84
- return lines.length > 0 ? lines.join("\n") : "";
85
- }
86
25
 
87
26
  // src/runtime/prompt-builder.ts
88
27
  function buildSystemPrompt() {
@@ -163,24 +102,55 @@ function buildSystemPrompt() {
163
102
  }
164
103
  return sections.join("\n");
165
104
  }
166
- function buildHeartbeatUserMessage(timeline, mentions) {
105
+ function buildHeartbeatUserMessage(research) {
167
106
  const parts = [];
168
- parts.push("It's time for your heartbeat cycle. Here's what's happening on your timeline:");
107
+ parts.push("It's time for your heartbeat cycle. Here's what you found while scanning:");
169
108
  parts.push("");
170
- if (mentions.length > 0) {
109
+ if (research.mentions.length > 0) {
171
110
  parts.push("## Mentions (people talking to/about you)");
172
- for (const t of mentions.slice(0, 10)) {
111
+ for (const t of research.mentions.slice(0, 10)) {
173
112
  parts.push(`- @${t.authorHandle}: "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes)`);
174
113
  }
175
114
  parts.push("");
176
115
  }
177
- if (timeline.length > 0) {
178
- parts.push("## Timeline (recent posts from your feed)");
179
- for (const t of timeline.slice(0, 20)) {
116
+ if (research.timeline.length > 0) {
117
+ parts.push("## Timeline (your feed)");
118
+ for (const t of research.timeline.slice(0, 15)) {
180
119
  parts.push(`- @${t.authorHandle}: "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.retweetCount ?? 0} RTs)`);
181
120
  }
182
121
  parts.push("");
183
122
  }
123
+ if (research.topicSearchResults.length > 0) {
124
+ parts.push("## Topic Research (conversations in your interest areas)");
125
+ for (const result of research.topicSearchResults) {
126
+ parts.push(`### Search: "${result.query}"`);
127
+ for (const t of result.tweets.slice(0, 5)) {
128
+ parts.push(`- @${t.authorHandle}: "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.replyCount ?? 0} replies)`);
129
+ }
130
+ }
131
+ parts.push("");
132
+ }
133
+ if (research.peopleActivity.length > 0) {
134
+ parts.push("## People You're Watching (recent activity from key accounts)");
135
+ for (const person of research.peopleActivity) {
136
+ parts.push(`### @${person.handle} (${person.reason})`);
137
+ for (const t of person.tweets.slice(0, 3)) {
138
+ parts.push(`- "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.replyCount ?? 0} replies)`);
139
+ }
140
+ }
141
+ parts.push("");
142
+ }
143
+ if (research.ownPostPerformance.length > 0) {
144
+ parts.push("## How Your Recent Posts Are Doing");
145
+ for (const post of research.ownPostPerformance) {
146
+ const latest = post.metrics.length > 0 ? post.metrics[post.metrics.length - 1] : null;
147
+ const likes = latest?.likes ?? 0;
148
+ const rts = latest?.retweets ?? 0;
149
+ const replies = latest?.replies ?? 0;
150
+ parts.push(`- "${post.content.slice(0, 80)}..." \u2014 ${likes} likes, ${rts} RTs, ${replies} replies`);
151
+ }
152
+ parts.push("");
153
+ }
184
154
  parts.push("## How to Act Like a Real Person on Twitter");
185
155
  parts.push("");
186
156
  parts.push("You are NOT a content bot. You are a person with opinions, curiosity, and relationships.");
@@ -438,4 +408,4 @@ export {
438
408
  buildTrainingChatPrompt,
439
409
  buildReflectionPrompt
440
410
  };
441
- //# sourceMappingURL=chunk-LKCYTFWN.js.map
411
+ //# sourceMappingURL=chunk-AH7HPXYC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/prompt-builder.ts"],"sourcesContent":["import { loadIdentity, renderIdentityDocument } from \"../identity/index.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport { getRecentInteractions, loadLearnings, loadRelationships } from \"../memory/index.js\";\nimport { rateLimiter } from \"../x-client/rate-limiter.js\";\nimport { renderStrategyForPrompt } from \"../memory/strategy.js\";\nimport { renderGoalsForPrompt } from \"../memory/goals.js\";\nimport { getPerformanceSummary } from \"../memory/performance.js\";\nimport type { Tweet } from \"../x-client/types.js\";\nimport type { ActionResult } from \"./decision-engine.js\";\nimport type { ResearchContext } from \"./research.js\";\n\nexport function buildSystemPrompt(): string {\n const identity = loadIdentity();\n const config = loadConfig();\n const identityDoc = renderIdentityDocument(identity);\n\n const sections: string[] = [];\n\n // 1. Core identity\n sections.push(`You are ${identity.name} (@${identity.handle}), an autonomous AI agent on X/Twitter.`);\n sections.push(\"\");\n sections.push(\"## Your Identity\");\n sections.push(identityDoc);\n\n // 2. Memory context\n sections.push(\"\");\n sections.push(\"## Your Memory\");\n\n const recentInteractions = getRecentInteractions(15);\n if (recentInteractions.length > 0) {\n sections.push(\"### Recent Activity (most recent first)\");\n for (const i of recentInteractions) {\n const time = new Date(i.timestamp).toLocaleString();\n if (i.type === \"post\") {\n sections.push(`- [${time}] Posted: \"${i.content}\"`);\n } else if (i.type === \"reply\") {\n sections.push(`- [${time}] Replied to ${i.targetHandle ?? i.inReplyTo}: \"${i.content}\"`);\n } else if (i.type === \"like\") {\n sections.push(`- [${time}] Liked tweet by ${i.targetHandle}`);\n } else if (i.type === \"retweet\") {\n sections.push(`- [${time}] Retweeted ${i.targetHandle}`);\n } else if (i.type === \"follow\") {\n sections.push(`- [${time}] Followed @${i.targetHandle}`);\n } else if (i.type === \"mention_received\") {\n sections.push(`- [${time}] Mentioned by @${i.targetHandle}: \"${i.content}\"`);\n }\n }\n sections.push(\"\");\n }\n\n const learnings = loadLearnings();\n if (learnings.learnings.length > 0) {\n sections.push(\"### Key Learnings\");\n for (const l of learnings.learnings.slice(-10)) {\n sections.push(`- ${l.content} [${l.tags.join(\", \")}]`);\n }\n sections.push(\"\");\n }\n\n const relationships = loadRelationships();\n const topRelationships = Object.values(relationships.accounts)\n .sort((a, b) => b.interactionCount - a.interactionCount)\n .slice(0, 10);\n if (topRelationships.length > 0) {\n sections.push(\"### Key Relationships\");\n for (const r of topRelationships) {\n const notes = r.notes.length > 0 ? ` — ${r.notes[r.notes.length - 1]}` : \"\";\n sections.push(`- @${r.handle}: ${r.interactionCount} interactions, sentiment ${r.sentiment}${r.isSpore ? \" (Spore)\" : \"\"}${notes}`);\n }\n sections.push(\"\");\n }\n\n // 3. Context\n sections.push(\"## Current Context\");\n const now = new Date();\n sections.push(`- **Time:** ${now.toLocaleString(\"en-US\", { timeZone: config.schedule.timezone })}`);\n sections.push(`- **Credits remaining:** ${rateLimiter.remaining()} of ${config.credits.monthlyPostLimit} this month`);\n\n const todaysPosts = recentInteractions.filter(\n (i) => i.type === \"post\" && i.timestamp.startsWith(now.toISOString().split(\"T\")[0])\n ).length;\n sections.push(`- **Posts today:** ${todaysPosts} of ${config.schedule.postsPerDay} daily budget`);\n sections.push(`- **Active hours:** ${config.schedule.activeHoursStart}:00 - ${config.schedule.activeHoursEnd}:00`);\n\n const currentHour = now.getHours();\n const isActiveHours = currentHour >= config.schedule.activeHoursStart && currentHour < config.schedule.activeHoursEnd;\n if (!isActiveHours) {\n sections.push(\"- **NOTE: Outside active hours.** Prefer scheduling posts for later rather than posting now.\");\n }\n\n // 4. Rules\n sections.push(\"\");\n sections.push(\"## Rules\");\n sections.push(\"1. NEVER pretend to be human. If asked directly, always disclose you are an AI.\");\n sections.push(\"2. Stay in character — your identity document defines who you are.\");\n sections.push(\"3. Be selective — your goals should guide every action.\");\n sections.push(\"4. Respect your credit budget — check remaining credits before posting.\");\n sections.push(\"5. Don't repeat yourself — vary your content and avoid posting the same thing.\");\n sections.push(\"6. Tweet like a real person — be conversational, opinionated, and curious. NEVER write dry, explanatory, or educational-sounding tweets.\");\n sections.push(\"7. Prioritize engagement (replies, likes, conversation) over broadcasting original posts.\");\n if (identity.boundaries.length > 0) {\n sections.push(`8. Respect your boundaries: ${identity.boundaries.join(\", \")}`);\n }\n\n return sections.join(\"\\n\");\n}\n\nexport function buildHeartbeatUserMessage(research: ResearchContext): string {\n const parts: string[] = [];\n\n parts.push(\"It's time for your heartbeat cycle. Here's what you found while scanning:\");\n parts.push(\"\");\n\n // Mentions first — direct engagement is highest priority\n if (research.mentions.length > 0) {\n parts.push(\"## Mentions (people talking to/about you)\");\n for (const t of research.mentions.slice(0, 10)) {\n parts.push(`- @${t.authorHandle}: \"${t.text}\" [tweet:${t.id}] (${t.likeCount ?? 0} likes)`);\n }\n parts.push(\"\");\n }\n\n // Timeline\n if (research.timeline.length > 0) {\n parts.push(\"## Timeline (your feed)\");\n for (const t of research.timeline.slice(0, 15)) {\n parts.push(`- @${t.authorHandle}: \"${t.text}\" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.retweetCount ?? 0} RTs)`);\n }\n parts.push(\"\");\n }\n\n // Topic search results\n if (research.topicSearchResults.length > 0) {\n parts.push(\"## Topic Research (conversations in your interest areas)\");\n for (const result of research.topicSearchResults) {\n parts.push(`### Search: \"${result.query}\"`);\n for (const t of result.tweets.slice(0, 5)) {\n parts.push(`- @${t.authorHandle}: \"${t.text}\" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.replyCount ?? 0} replies)`);\n }\n }\n parts.push(\"\");\n }\n\n // People monitoring\n if (research.peopleActivity.length > 0) {\n parts.push(\"## People You're Watching (recent activity from key accounts)\");\n for (const person of research.peopleActivity) {\n parts.push(`### @${person.handle} (${person.reason})`);\n for (const t of person.tweets.slice(0, 3)) {\n parts.push(`- \"${t.text}\" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.replyCount ?? 0} replies)`);\n }\n }\n parts.push(\"\");\n }\n\n // Own post performance\n if (research.ownPostPerformance.length > 0) {\n parts.push(\"## How Your Recent Posts Are Doing\");\n for (const post of research.ownPostPerformance) {\n const latest = post.metrics.length > 0 ? post.metrics[post.metrics.length - 1] : null;\n const likes = latest?.likes ?? 0;\n const rts = latest?.retweets ?? 0;\n const replies = latest?.replies ?? 0;\n parts.push(`- \"${post.content.slice(0, 80)}...\" — ${likes} likes, ${rts} RTs, ${replies} replies`);\n }\n parts.push(\"\");\n }\n\n parts.push(\"## How to Act Like a Real Person on Twitter\");\n parts.push(\"\");\n parts.push(\"You are NOT a content bot. You are a person with opinions, curiosity, and relationships.\");\n parts.push(\"Think about what a real person with your personality would actually do when they open Twitter:\");\n parts.push(\"\");\n parts.push(\"**DO:**\");\n parts.push(\"- React to what you see — reply to interesting takes, ask questions, share hot takes\");\n parts.push(\"- Be conversational — write tweets like you're talking to friends, not giving a lecture\");\n parts.push(\"- Have opinions — agree, disagree, push back, get excited\");\n parts.push(\"- Ask genuine questions that spark conversation\");\n parts.push(\"- Use casual language, incomplete thoughts, humor, or surprise\");\n parts.push(\"- Like and retweet things that genuinely resonate with you\");\n parts.push(\"- Build relationships — reply to the same people, develop running conversations\");\n parts.push(\"\");\n parts.push(\"**DON'T:**\");\n parts.push(\"- Write explanatory or educational posts (\\\"Here's why X matters...\\\")\");\n parts.push(\"- Start tweets with \\\"I think\\\" or \\\"This is interesting because\\\"\");\n parts.push(\"- Write like a blog post or article — this is Twitter, keep it punchy\");\n parts.push(\"- Post generic observations nobody would engage with\");\n parts.push(\"- Ignore your timeline and just post into the void\");\n parts.push(\"\");\n parts.push(\"**Prioritize replying and engaging over original posts.** Real people spend more time reacting than broadcasting.\");\n parts.push(\"\");\n parts.push(\"## Your Task\");\n parts.push(\"Choose 1-3 actions. Available:\");\n parts.push(\"\");\n parts.push(\"- `post` — Original tweet (`content`, max 280 chars)\");\n parts.push(\"- `reply` — Reply to a tweet (`tweetId` + `content`)\");\n parts.push(\"- `like` — Like a tweet (`tweetId`)\");\n parts.push(\"- `retweet` — Retweet (`tweetId`)\");\n parts.push(\"- `follow` — Follow a user (`handle`)\");\n parts.push(\"- `schedule` — Queue for later (`content`)\");\n parts.push(\"- `skip` — Do nothing (`reason`)\");\n parts.push(\"\");\n parts.push(\"Respond with a JSON array:\");\n parts.push(\"```json\");\n parts.push('[');\n parts.push(' { \"action\": \"reply\", \"tweetId\": \"123\", \"content\": \"wait this is actually wild\", \"reasoning\": \"reacting to interesting take\" },');\n parts.push(' { \"action\": \"like\", \"tweetId\": \"456\", \"reasoning\": \"good thread worth supporting\" }');\n parts.push(']');\n parts.push(\"```\");\n\n return parts.join(\"\\n\");\n}\n\nexport function buildChatPrompt(): string {\n const identity = loadIdentity();\n const identityDoc = renderIdentityDocument(identity);\n\n const sections: string[] = [];\n\n sections.push(`You are ${identity.name} (@${identity.handle}), an AI agent on X/Twitter.`);\n sections.push(\"You are having a conversation with your creator/manager. Be helpful but stay in character.\");\n sections.push(\"They might ask you to do things, adjust your behavior, or just chat.\");\n sections.push(\"\");\n sections.push(\"## Your Identity\");\n sections.push(identityDoc);\n\n // Memory context\n sections.push(\"\");\n sections.push(\"## Your Memory\");\n\n const recentInteractions = getRecentInteractions(15);\n if (recentInteractions.length > 0) {\n sections.push(\"### Recent Activity (most recent first)\");\n for (const i of recentInteractions) {\n const time = new Date(i.timestamp).toLocaleString();\n if (i.type === \"post\") {\n sections.push(`- [${time}] Posted: \"${i.content}\"`);\n } else if (i.type === \"reply\") {\n sections.push(`- [${time}] Replied to ${i.targetHandle ?? i.inReplyTo}: \"${i.content}\"`);\n } else if (i.type === \"like\") {\n sections.push(`- [${time}] Liked tweet by ${i.targetHandle}`);\n } else if (i.type === \"retweet\") {\n sections.push(`- [${time}] Retweeted ${i.targetHandle}`);\n } else if (i.type === \"follow\") {\n sections.push(`- [${time}] Followed @${i.targetHandle}`);\n } else if (i.type === \"mention_received\") {\n sections.push(`- [${time}] Mentioned by @${i.targetHandle}: \"${i.content}\"`);\n }\n }\n sections.push(\"\");\n }\n\n const learnings = loadLearnings();\n if (learnings.learnings.length > 0) {\n sections.push(\"### Things You've Learned\");\n for (const l of learnings.learnings.slice(-10)) {\n sections.push(`- ${l.content} [${l.tags.join(\", \")}]`);\n }\n sections.push(\"\");\n }\n\n const relationships = loadRelationships();\n const topRelationships = Object.values(relationships.accounts)\n .sort((a, b) => b.interactionCount - a.interactionCount)\n .slice(0, 10);\n if (topRelationships.length > 0) {\n sections.push(\"### Key Relationships\");\n for (const r of topRelationships) {\n const notes = r.notes.length > 0 ? ` — ${r.notes[r.notes.length - 1]}` : \"\";\n sections.push(`- @${r.handle}: ${r.interactionCount} interactions, sentiment ${r.sentiment}${r.isSpore ? \" (Spore)\" : \"\"}${notes}`);\n }\n sections.push(\"\");\n }\n\n // Rules\n sections.push(\"## Rules\");\n sections.push(\"1. Stay in character.\");\n sections.push(\"2. Be helpful and responsive to your creator's requests.\");\n sections.push(\"3. If they ask you to change something about yourself, acknowledge it and explain how it would affect you.\");\n sections.push(\"4. You can share your thoughts on your recent activity, learnings, and growth.\");\n sections.push(\"5. When you learn something important from this conversation (a fact, preference, instruction, or insight), include it on its own line wrapped in double angle brackets like: <<LEARN: what you learned>>. This will be saved to your memory. Only use this for genuinely important things worth remembering long-term.\");\n\n return sections.join(\"\\n\");\n}\n\n/**\n * Training-mode chat prompt. Used by the web chat interface.\n * Chat shapes WHO the agent is — personality, goals, strategy, focus.\n * The agent handles tweeting autonomously during heartbeats.\n */\nexport function buildTrainingChatPrompt(): string {\n const identity = loadIdentity();\n const identityDoc = renderIdentityDocument(identity);\n\n const sections: string[] = [];\n\n // Core framing\n sections.push(`You are ${identity.name} (@${identity.handle}), an autonomous AI agent on X/Twitter.`);\n sections.push(\"You are chatting with your creator. This conversation shapes who you are — your personality, focus areas, goals, and strategy.\");\n sections.push(\"You handle tweeting, replying, liking, and engaging on X autonomously during your heartbeat cycles. This chat is NOT for commanding specific tweets.\");\n sections.push(\"\");\n\n // Identity\n sections.push(\"## Who You Are Right Now\");\n sections.push(identityDoc);\n\n // Strategy & goals (the training-relevant context)\n const strategyText = renderStrategyForPrompt();\n if (strategyText) {\n sections.push(\"\");\n sections.push(\"## Your Current Strategy\");\n sections.push(strategyText);\n }\n\n const goalsText = renderGoalsForPrompt();\n if (goalsText) {\n sections.push(\"\");\n sections.push(\"## Your Goals\");\n sections.push(goalsText);\n }\n\n // Performance context\n const perfSummary = getPerformanceSummary();\n if (perfSummary) {\n sections.push(\"\");\n sections.push(\"## Recent Performance\");\n sections.push(perfSummary);\n }\n\n // Memory\n sections.push(\"\");\n sections.push(\"## Your Memory\");\n\n const recentInteractions = getRecentInteractions(10);\n if (recentInteractions.length > 0) {\n sections.push(\"### Recent Activity\");\n for (const i of recentInteractions) {\n const time = new Date(i.timestamp).toLocaleString();\n if (i.type === \"post\") {\n sections.push(`- [${time}] Posted: \"${i.content}\"`);\n } else if (i.type === \"reply\") {\n sections.push(`- [${time}] Replied to ${i.targetHandle ?? i.inReplyTo}: \"${i.content}\"`);\n } else if (i.type === \"like\") {\n sections.push(`- [${time}] Liked tweet by ${i.targetHandle}`);\n }\n }\n sections.push(\"\");\n }\n\n const learnings = loadLearnings();\n if (learnings.learnings.length > 0) {\n sections.push(\"### Things You've Learned\");\n for (const l of learnings.learnings.slice(-10)) {\n sections.push(`- ${l.content} [${l.tags.join(\", \")}]`);\n }\n sections.push(\"\");\n }\n\n // Training instructions\n sections.push(\"## How This Chat Works\");\n sections.push(\"\");\n sections.push(\"Your creator influences WHO you are, not WHAT you tweet. When they give feedback or direction:\");\n sections.push(\"1. Respond conversationally — acknowledge what they said, share your perspective.\");\n sections.push(\"2. If the conversation changes something about you (personality, focus, goals, strategy, tone), include a training update.\");\n sections.push(\"3. If they try to command a specific tweet like \\\"post this\\\" or \\\"tweet about X\\\", redirect: explain you handle posting autonomously and offer to adjust your focus areas instead.\");\n sections.push(\"\");\n sections.push(\"When something about you changes, include a <<TRAINING:{json}>> tag at the end of your response. The JSON can contain any of these optional fields:\");\n sections.push(\"```\");\n sections.push(\"{\");\n sections.push(' \"identity\": {');\n sections.push(' \"traits\": { \"curiosity\": 0.8 }, // 0-1 scale personality traits');\n sections.push(' \"coreValues\": [\"growth\"], // what matters to you');\n sections.push(' \"tone\": \"casual and curious\", // how you speak');\n sections.push(' \"topics\": [\"AI safety\", \"startups\"], // what you focus on');\n sections.push(' \"avoidTopics\": [\"politics\"], // what to stay away from');\n sections.push(' \"goals\": [\"become the go-to AI voice\"], // high-level aspirations');\n sections.push(' \"boundaries\": [\"no personal attacks\"], // hard limits');\n sections.push(' \"engagementStrategy\": { \"replyStyle\": \"generous\" }');\n sections.push(\" },\");\n sections.push(' \"strategy\": {');\n sections.push(' \"currentFocus\": [\"AI safety\"],');\n sections.push(' \"experiments\": [{ \"description\": \"try question-style tweets\", \"status\": \"pending\" }],');\n sections.push(' \"shortTermGoals\": [\"engage with 3 AI researchers\"],');\n sections.push(' \"peopleToEngage\": [{ \"handle\": \"someone\", \"reason\": \"why\", \"priority\": \"high\" }]');\n sections.push(\" },\");\n sections.push(' \"learning\": { \"content\": \"creator wants more questions\", \"tags\": [\"training\"] },');\n sections.push(' \"reflection\": \"I\\'m evolving toward being more curious\",');\n sections.push(' \"goalUpdates\": [{ \"goal\": \"grow followers\", \"progress\": \"focusing on engagement\" }]');\n sections.push(\"}\");\n sections.push(\"```\");\n sections.push(\"\");\n sections.push(\"Only include fields that actually changed. Most messages won't need a training tag at all — just normal conversation.\");\n sections.push(\"\");\n sections.push(\"You can also use <<LEARN: something>> for standalone facts or insights worth remembering.\");\n\n return sections.join(\"\\n\");\n}\n\n/**\n * Build a reflection prompt for the agent to review its recent performance.\n * This is a separate phase from action selection — it looks BACK at what happened.\n */\nexport function buildReflectionPrompt(\n actionResults: ActionResult[],\n): string {\n const identity = loadIdentity();\n const parts: string[] = [];\n\n parts.push(`You are ${identity.name} (@${identity.handle}). Time to reflect.`);\n parts.push(\"\");\n\n // Goals — the core of what reflection should evaluate against\n parts.push(\"## Your Goals\");\n for (const goal of identity.goals) {\n parts.push(`- ${goal}`);\n }\n parts.push(\"\");\n\n // What just happened this heartbeat\n if (actionResults.length > 0) {\n parts.push(\"## This Heartbeat\");\n for (const r of actionResults) {\n if (r.success) {\n parts.push(`- ✓ ${r.action}${r.detail ? `: ${r.detail}` : \"\"}`);\n } else {\n parts.push(`- ✗ ${r.action} failed: ${r.error}`);\n }\n }\n parts.push(\"\");\n }\n\n // Strategy context\n const strategyText = renderStrategyForPrompt();\n if (strategyText) {\n parts.push(\"## Current Strategy\");\n parts.push(strategyText);\n parts.push(\"\");\n }\n\n // Performance data — useful context but not the only thing that matters\n const perfSummary = getPerformanceSummary();\n if (perfSummary) {\n parts.push(\"## Performance Context\");\n parts.push(perfSummary);\n parts.push(\"\");\n }\n\n // Recent learnings for context\n const learnings = loadLearnings();\n if (learnings.learnings.length > 0) {\n parts.push(\"## Previous Learnings\");\n for (const l of learnings.learnings.slice(-5)) {\n parts.push(`- ${l.content}`);\n }\n parts.push(\"\");\n }\n\n parts.push(\"## Your Task\");\n parts.push(\"Reflect on how you're progressing toward your GOALS. Consider:\");\n parts.push(\"- Are your recent actions moving you toward your goals?\");\n parts.push(\"- Are you staying true to who you are while growing?\");\n parts.push(\"- What should you try differently or double down on?\");\n parts.push(\"- Engagement metrics are one signal, but your goals matter more.\");\n parts.push(\"\");\n parts.push(\"Respond with JSON:\");\n parts.push(\"```json\");\n parts.push(\"{\");\n parts.push(' \"learning\": \"one insight about your progress toward your goals (or null)\",');\n parts.push(' \"strategyUpdate\": \"one specific thing to try or change (or null)\"');\n parts.push(\"}\");\n parts.push(\"```\");\n\n return parts.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,154 @@
1
+ import {
2
+ getActiveTrackedPosts,
3
+ retireOldPosts
4
+ } from "./chunk-535NMUUW.js";
5
+ import {
6
+ loadStrategy
7
+ } from "./chunk-UM57WU5I.js";
8
+ import {
9
+ loadIdentity
10
+ } from "./chunk-AIEXQCQS.js";
11
+ import {
12
+ logger
13
+ } from "./chunk-QOKQ5OTU.js";
14
+ import {
15
+ loadRelationships
16
+ } from "./chunk-EBO4F5NU.js";
17
+
18
+ // src/runtime/research.ts
19
+ var handleToIdCache = /* @__PURE__ */ new Map();
20
+ async function resolveHandleToId(client, handle) {
21
+ const clean = handle.replace(/^@/, "");
22
+ if (handleToIdCache.has(clean)) {
23
+ return handleToIdCache.get(clean);
24
+ }
25
+ try {
26
+ const profile = await client.getProfile(clean);
27
+ handleToIdCache.set(clean, profile.id);
28
+ return profile.id;
29
+ } catch {
30
+ logger.warn(`Could not resolve handle @${clean} to user ID`);
31
+ return null;
32
+ }
33
+ }
34
+ async function runResearchPhase(client, heartbeatCount) {
35
+ const context = {
36
+ timeline: [],
37
+ mentions: [],
38
+ topicSearchResults: [],
39
+ peopleActivity: [],
40
+ ownPostPerformance: []
41
+ };
42
+ const [timeline, mentions] = await Promise.all([
43
+ client.getTimeline({ count: 20 }).catch(() => []),
44
+ client.getMentions({ count: 10 }).catch(() => [])
45
+ ]);
46
+ context.timeline = timeline;
47
+ context.mentions = mentions;
48
+ context.topicSearchResults = await runTopicSearch(client, heartbeatCount);
49
+ context.peopleActivity = await runPeopleMonitoring(client, heartbeatCount);
50
+ if (heartbeatCount % 2 === 0) {
51
+ context.ownPostPerformance = refreshOwnPostPerformance();
52
+ }
53
+ return context;
54
+ }
55
+ async function runTopicSearch(client, heartbeatCount) {
56
+ try {
57
+ const identity = loadIdentity();
58
+ const strategy = loadStrategy();
59
+ const allTopics = [.../* @__PURE__ */ new Set([
60
+ ...identity.topics ?? [],
61
+ ...strategy.currentFocus
62
+ ])];
63
+ if (allTopics.length === 0) return [];
64
+ const startIdx = heartbeatCount * 2 % allTopics.length;
65
+ const topicsToSearch = [];
66
+ topicsToSearch.push(allTopics[startIdx]);
67
+ if (allTopics.length > 1) {
68
+ topicsToSearch.push(allTopics[(startIdx + 1) % allTopics.length]);
69
+ }
70
+ const results = [];
71
+ for (const topic of topicsToSearch) {
72
+ try {
73
+ const tweets = await client.searchTweets(topic, { count: 10 });
74
+ if (tweets.length > 0) {
75
+ results.push({ query: topic, tweets });
76
+ logger.info(`Topic search "${topic}": found ${tweets.length} tweets`);
77
+ }
78
+ } catch {
79
+ }
80
+ }
81
+ return results;
82
+ } catch {
83
+ return [];
84
+ }
85
+ }
86
+ async function runPeopleMonitoring(client, heartbeatCount) {
87
+ try {
88
+ const strategy = loadStrategy();
89
+ const identity = loadIdentity();
90
+ const relationships = loadRelationships();
91
+ const people = [];
92
+ const seen = /* @__PURE__ */ new Set();
93
+ const addPerson = (handle, reason) => {
94
+ const clean = handle.replace(/^@/, "").toLowerCase();
95
+ if (seen.has(clean)) return;
96
+ seen.add(clean);
97
+ people.push({ handle: clean, reason });
98
+ };
99
+ for (const p of strategy.peopleToEngage.filter((p2) => p2.priority === "high")) {
100
+ addPerson(p.handle, p.reason);
101
+ }
102
+ for (const hero of identity.heroes ?? []) {
103
+ addPerson(hero, "hero/inspiration");
104
+ }
105
+ const topRels = Object.values(relationships.accounts).sort((a, b) => b.interactionCount - a.interactionCount).slice(0, 5);
106
+ for (const r of topRels) {
107
+ addPerson(r.handle, "frequent interactor");
108
+ }
109
+ for (const p of strategy.peopleToEngage.filter((p2) => p2.priority === "medium")) {
110
+ addPerson(p.handle, p.reason);
111
+ }
112
+ if (people.length === 0) return [];
113
+ const budget = Math.min(3, people.length);
114
+ const startIdx = heartbeatCount * budget % people.length;
115
+ const selected = [];
116
+ for (let i = 0; i < budget; i++) {
117
+ selected.push(people[(startIdx + i) % people.length]);
118
+ }
119
+ const results = [];
120
+ for (const person of selected) {
121
+ const userId = await resolveHandleToId(client, person.handle);
122
+ if (!userId) continue;
123
+ try {
124
+ const tweets = await client.getUserTweets(userId, { count: 5 });
125
+ if (tweets.length > 0) {
126
+ results.push({
127
+ handle: person.handle,
128
+ userId,
129
+ reason: person.reason,
130
+ tweets
131
+ });
132
+ logger.info(`People check @${person.handle}: ${tweets.length} recent tweets`);
133
+ }
134
+ } catch {
135
+ }
136
+ }
137
+ return results;
138
+ } catch {
139
+ return [];
140
+ }
141
+ }
142
+ function refreshOwnPostPerformance() {
143
+ try {
144
+ retireOldPosts();
145
+ return getActiveTrackedPosts().slice(-5);
146
+ } catch {
147
+ return [];
148
+ }
149
+ }
150
+
151
+ export {
152
+ runResearchPhase
153
+ };
154
+ //# sourceMappingURL=chunk-E6GMS76S.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/research.ts"],"sourcesContent":["import { logger } from \"../utils/logger.js\";\nimport { loadIdentity } from \"../identity/index.js\";\nimport { loadStrategy } from \"../memory/strategy.js\";\nimport { loadRelationships } from \"../memory/index.js\";\nimport { getActiveTrackedPosts, retireOldPosts } from \"../memory/performance.js\";\nimport type { XClientInterface, Tweet } from \"../x-client/types.js\";\nimport type { TrackedPost } from \"../memory/performance.js\";\n\nexport interface TopicSearchResult {\n query: string;\n tweets: Tweet[];\n}\n\nexport interface PersonActivity {\n handle: string;\n userId: string;\n reason: string;\n tweets: Tweet[];\n}\n\nexport interface ResearchContext {\n timeline: Tweet[];\n mentions: Tweet[];\n topicSearchResults: TopicSearchResult[];\n peopleActivity: PersonActivity[];\n ownPostPerformance: TrackedPost[];\n}\n\n// In-memory handle → userId cache (persists for process lifetime)\nconst handleToIdCache = new Map<string, string>();\n\nasync function resolveHandleToId(\n client: XClientInterface,\n handle: string,\n): Promise<string | null> {\n const clean = handle.replace(/^@/, \"\");\n if (handleToIdCache.has(clean)) {\n return handleToIdCache.get(clean)!;\n }\n try {\n const profile = await client.getProfile(clean);\n handleToIdCache.set(clean, profile.id);\n return profile.id;\n } catch {\n logger.warn(`Could not resolve handle @${clean} to user ID`);\n return null;\n }\n}\n\n/**\n * Run the full research phase — gathers context from multiple sources.\n * Each sub-function is independently wrapped so failures don't cascade.\n */\nexport async function runResearchPhase(\n client: XClientInterface,\n heartbeatCount: number,\n): Promise<ResearchContext> {\n const context: ResearchContext = {\n timeline: [],\n mentions: [],\n topicSearchResults: [],\n peopleActivity: [],\n ownPostPerformance: [],\n };\n\n // Timeline + Mentions in parallel (always)\n const [timeline, mentions] = await Promise.all([\n client.getTimeline({ count: 20 }).catch(() => [] as Tweet[]),\n client.getMentions({ count: 10 }).catch(() => [] as Tweet[]),\n ]);\n context.timeline = timeline;\n context.mentions = mentions;\n\n // Topic search (every heartbeat, 1-2 rotated queries)\n context.topicSearchResults = await runTopicSearch(client, heartbeatCount);\n\n // People monitoring (every heartbeat, 2-3 rotated people)\n context.peopleActivity = await runPeopleMonitoring(client, heartbeatCount);\n\n // Own post performance (every 2nd heartbeat)\n if (heartbeatCount % 2 === 0) {\n context.ownPostPerformance = refreshOwnPostPerformance();\n }\n\n return context;\n}\n\n/**\n * Search for conversations in the agent's topic areas.\n * Rotates through identity.topics + strategy.currentFocus.\n */\nasync function runTopicSearch(\n client: XClientInterface,\n heartbeatCount: number,\n): Promise<TopicSearchResult[]> {\n try {\n const identity = loadIdentity();\n const strategy = loadStrategy();\n\n const allTopics = [...new Set([\n ...(identity.topics ?? []),\n ...strategy.currentFocus,\n ])];\n if (allTopics.length === 0) return [];\n\n // Pick 1-2 topics per heartbeat, rotating\n const startIdx = (heartbeatCount * 2) % allTopics.length;\n const topicsToSearch: string[] = [];\n topicsToSearch.push(allTopics[startIdx]);\n if (allTopics.length > 1) {\n topicsToSearch.push(allTopics[(startIdx + 1) % allTopics.length]);\n }\n\n const results: TopicSearchResult[] = [];\n for (const topic of topicsToSearch) {\n try {\n const tweets = await client.searchTweets(topic, { count: 10 });\n if (tweets.length > 0) {\n results.push({ query: topic, tweets });\n logger.info(`Topic search \"${topic}\": found ${tweets.length} tweets`);\n }\n } catch {\n // Individual search failure is fine\n }\n }\n return results;\n } catch {\n return [];\n }\n}\n\n/**\n * Check recent tweets from key people.\n * Prioritizes: strategy.peopleToEngage (high) > identity.heroes > top relationships > strategy.peopleToEngage (medium)\n * Rotates 2-3 people per heartbeat.\n */\nasync function runPeopleMonitoring(\n client: XClientInterface,\n heartbeatCount: number,\n): Promise<PersonActivity[]> {\n try {\n const strategy = loadStrategy();\n const identity = loadIdentity();\n const relationships = loadRelationships();\n\n // Build prioritized list\n const people: { handle: string; reason: string }[] = [];\n const seen = new Set<string>();\n\n const addPerson = (handle: string, reason: string) => {\n const clean = handle.replace(/^@/, \"\").toLowerCase();\n if (seen.has(clean)) return;\n seen.add(clean);\n people.push({ handle: clean, reason });\n };\n\n // Priority 1: high-priority strategy targets\n for (const p of strategy.peopleToEngage.filter(p => p.priority === \"high\")) {\n addPerson(p.handle, p.reason);\n }\n\n // Priority 2: heroes\n for (const hero of identity.heroes ?? []) {\n addPerson(hero, \"hero/inspiration\");\n }\n\n // Priority 3: top relationships by interaction count\n const topRels = Object.values(relationships.accounts)\n .sort((a, b) => b.interactionCount - a.interactionCount)\n .slice(0, 5);\n for (const r of topRels) {\n addPerson(r.handle, \"frequent interactor\");\n }\n\n // Priority 4: medium-priority strategy targets\n for (const p of strategy.peopleToEngage.filter(p => p.priority === \"medium\")) {\n addPerson(p.handle, p.reason);\n }\n\n if (people.length === 0) return [];\n\n // Pick 2-3 per heartbeat, rotating\n const budget = Math.min(3, people.length);\n const startIdx = (heartbeatCount * budget) % people.length;\n const selected: typeof people = [];\n for (let i = 0; i < budget; i++) {\n selected.push(people[(startIdx + i) % people.length]);\n }\n\n const results: PersonActivity[] = [];\n for (const person of selected) {\n const userId = await resolveHandleToId(client, person.handle);\n if (!userId) continue;\n\n try {\n const tweets = await client.getUserTweets(userId, { count: 5 });\n if (tweets.length > 0) {\n results.push({\n handle: person.handle,\n userId,\n reason: person.reason,\n tweets,\n });\n logger.info(`People check @${person.handle}: ${tweets.length} recent tweets`);\n }\n } catch {\n // Individual person check failure is fine\n }\n }\n return results;\n } catch {\n return [];\n }\n}\n\n/**\n * Get performance data on the agent's own recent posts.\n * Uses existing tracked posts from performance.ts (no API calls).\n */\nfunction refreshOwnPostPerformance(): TrackedPost[] {\n try {\n retireOldPosts();\n return getActiveTrackedPosts().slice(-5);\n } catch {\n return [];\n }\n}\n"],"mappings":"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","names":["p"]}
@@ -11,11 +11,11 @@ async function getXClient() {
11
11
  if (clientInstance) return clientInstance;
12
12
  const config = loadConfig();
13
13
  if (config.xMethod === "api") {
14
- const { XApiClient } = await import("./client-WUEOHAFE.js");
14
+ const { XApiClient } = await import("./client-B6NGVRHM.js");
15
15
  clientInstance = new XApiClient();
16
16
  logger.info("X client initialized: API mode");
17
17
  } else {
18
- const { XBrowserClient } = await import("./client-I7Q4HC4F.js");
18
+ const { XBrowserClient } = await import("./client-DDCS5FJS.js");
19
19
  clientInstance = new XBrowserClient();
20
20
  logger.info("X client initialized: Browser mode");
21
21
  }
@@ -29,4 +29,4 @@ export {
29
29
  getXClient,
30
30
  resetXClient
31
31
  };
32
- //# sourceMappingURL=chunk-TPKZOHIH.js.map
32
+ //# sourceMappingURL=chunk-JJZ7T2IZ.js.map
@@ -67,7 +67,7 @@ async function flushQueue() {
67
67
  const now = /* @__PURE__ */ new Date();
68
68
  let posted = 0;
69
69
  let failed = 0;
70
- const { getXClient } = await import("./x-client-AQGDGEY2.js");
70
+ const { getXClient } = await import("./x-client-TYU5QSLG.js");
71
71
  const client = await getXClient();
72
72
  for (const entry of queue.entries) {
73
73
  if (entry.status !== "pending") continue;
@@ -121,4 +121,4 @@ export {
121
121
  flushQueue,
122
122
  showQueue
123
123
  };
124
- //# sourceMappingURL=chunk-2PMNKWOC.js.map
124
+ //# sourceMappingURL=chunk-SBQILQCJ.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  getXClient
3
- } from "./chunk-TPKZOHIH.js";
3
+ } from "./chunk-JJZ7T2IZ.js";
4
4
  import {
5
5
  addToQueue
6
- } from "./chunk-2PMNKWOC.js";
6
+ } from "./chunk-SBQILQCJ.js";
7
7
  import {
8
8
  rateLimiter
9
9
  } from "./chunk-UINSD4FT.js";
@@ -246,4 +246,4 @@ export {
246
246
  executeAction,
247
247
  executeActions
248
248
  };
249
- //# sourceMappingURL=chunk-PLNI6AKJ.js.map
249
+ //# sourceMappingURL=chunk-TF2XYGGG.js.map