spora 0.7.4 → 0.7.6

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 (88) hide show
  1. package/dist/autonomy-E3DWYRJM.js +20 -0
  2. package/dist/{chunk-SUZUJGGW.js → chunk-342ZX72W.js} +4 -4
  3. package/dist/{chunk-PN5A6MCV.js → chunk-5R4AJZHN.js} +4 -4
  4. package/dist/{chunk-PN5A6MCV.js.map → chunk-5R4AJZHN.js.map} +1 -1
  5. package/dist/{chunk-JBYZ7K56.js → chunk-BBXHECZ5.js} +2 -2
  6. package/dist/{chunk-WN35MRMF.js → chunk-CAWWG3MD.js} +2 -2
  7. package/dist/chunk-CP6JWCLY.js +171 -0
  8. package/dist/chunk-CP6JWCLY.js.map +1 -0
  9. package/dist/{chunk-T7L2L7ZL.js → chunk-D47OFTEK.js} +2 -2
  10. package/dist/{chunk-Q3YXJ2C6.js → chunk-E5PEY36J.js} +408 -63
  11. package/dist/chunk-E5PEY36J.js.map +1 -0
  12. package/dist/chunk-FBHLDOMC.js +2436 -0
  13. package/dist/chunk-FBHLDOMC.js.map +1 -0
  14. package/dist/{chunk-M6YOQVSI.js → chunk-IULO3GRE.js} +4 -4
  15. package/dist/chunk-IULO3GRE.js.map +1 -0
  16. package/dist/chunk-OTZNHIXT.js +431 -0
  17. package/dist/chunk-OTZNHIXT.js.map +1 -0
  18. package/dist/{chunk-NO3NQN67.js → chunk-QYFNAGNI.js} +2 -2
  19. package/dist/{chunk-YMGJQRKG.js → chunk-RSNEVBEI.js} +2 -2
  20. package/dist/{chunk-QWEYVDLU.js → chunk-SXNZVKLJ.js} +2 -2
  21. package/dist/{chunk-MDOFAAZB.js → chunk-ZLSDFYBR.js} +17 -5
  22. package/dist/chunk-ZLSDFYBR.js.map +1 -0
  23. package/dist/{chunk-3RYCUGXE.js → chunk-ZWKTKWS6.js} +4 -1
  24. package/dist/chunk-ZWKTKWS6.js.map +1 -0
  25. package/dist/cli.js +49 -49
  26. package/dist/{client-Z5UQWPPI.js → client-AR5ZD6S4.js} +48 -16
  27. package/dist/client-AR5ZD6S4.js.map +1 -0
  28. package/dist/{colony-NNX45EAV.js → colony-UGVYALOS.js} +7 -7
  29. package/dist/{config-FL4VJVKZ.js → config-MU2ODEO3.js} +3 -3
  30. package/dist/{crypto-B65ZH7KN.js → crypto-GDG5K3ZH.js} +3 -3
  31. package/dist/{goals-RBKLMILE.js → goals-QWX3A47Y.js} +3 -3
  32. package/dist/{heartbeat-ZCCOIZGU.js → heartbeat-GVBLNAFM.js} +18 -21
  33. package/dist/heartbeat-GVBLNAFM.js.map +1 -0
  34. package/dist/{identity-VDUW4I2K.js → identity-ASHVWIN5.js} +3 -3
  35. package/dist/{init-SEJPTOOB.js → init-C4OZPGUC.js} +108 -15
  36. package/dist/init-C4OZPGUC.js.map +1 -0
  37. package/dist/{llm-OGOYCWBH.js → llm-IJBRQ7O2.js} +5 -5
  38. package/dist/mcp-server.js +24 -24
  39. package/dist/{memory-PNW7SX7A.js → memory-AWKIW2KW.js} +3 -3
  40. package/dist/{memory-OIAH33G2.js → memory-DTSLVSQG.js} +3 -3
  41. package/dist/{paths-BYR6MEPR.js → paths-4V5OCB5F.js} +2 -2
  42. package/dist/prompt-builder-NTN4FCBD.js +27 -0
  43. package/dist/queue-QCGNDHH2.js +14 -0
  44. package/dist/strategy-R2BMRVJ3.js +19 -0
  45. package/dist/{web-chat-ZZ65DUID.js → web-chat-2N2RN6J7.js} +23 -28
  46. package/dist/web-chat-2N2RN6J7.js.map +1 -0
  47. package/dist/x-client-S2LUVEKV.js +12 -0
  48. package/package.json +1 -1
  49. package/dist/autonomy-XUKCAZM3.js +0 -19
  50. package/dist/chunk-3RYCUGXE.js.map +0 -1
  51. package/dist/chunk-4LNMA56H.js +0 -57
  52. package/dist/chunk-4LNMA56H.js.map +0 -1
  53. package/dist/chunk-EU4FMOKG.js +0 -377
  54. package/dist/chunk-EU4FMOKG.js.map +0 -1
  55. package/dist/chunk-M6YOQVSI.js.map +0 -1
  56. package/dist/chunk-MDOFAAZB.js.map +0 -1
  57. package/dist/chunk-P6KZIJYL.js +0 -79
  58. package/dist/chunk-P6KZIJYL.js.map +0 -1
  59. package/dist/chunk-Q3YXJ2C6.js.map +0 -1
  60. package/dist/client-Z5UQWPPI.js.map +0 -1
  61. package/dist/heartbeat-ZCCOIZGU.js.map +0 -1
  62. package/dist/init-SEJPTOOB.js.map +0 -1
  63. package/dist/prompt-builder-KJKFCGM7.js +0 -25
  64. package/dist/queue-2ZBKDFX3.js +0 -14
  65. package/dist/strategy-Z4JSFHSP.js +0 -12
  66. package/dist/web-chat-ZZ65DUID.js.map +0 -1
  67. package/dist/x-client-YG7UCCNI.js +0 -12
  68. /package/dist/{autonomy-XUKCAZM3.js.map → autonomy-E3DWYRJM.js.map} +0 -0
  69. /package/dist/{chunk-SUZUJGGW.js.map → chunk-342ZX72W.js.map} +0 -0
  70. /package/dist/{chunk-JBYZ7K56.js.map → chunk-BBXHECZ5.js.map} +0 -0
  71. /package/dist/{chunk-WN35MRMF.js.map → chunk-CAWWG3MD.js.map} +0 -0
  72. /package/dist/{chunk-T7L2L7ZL.js.map → chunk-D47OFTEK.js.map} +0 -0
  73. /package/dist/{chunk-NO3NQN67.js.map → chunk-QYFNAGNI.js.map} +0 -0
  74. /package/dist/{chunk-YMGJQRKG.js.map → chunk-RSNEVBEI.js.map} +0 -0
  75. /package/dist/{chunk-QWEYVDLU.js.map → chunk-SXNZVKLJ.js.map} +0 -0
  76. /package/dist/{colony-NNX45EAV.js.map → colony-UGVYALOS.js.map} +0 -0
  77. /package/dist/{config-FL4VJVKZ.js.map → config-MU2ODEO3.js.map} +0 -0
  78. /package/dist/{crypto-B65ZH7KN.js.map → crypto-GDG5K3ZH.js.map} +0 -0
  79. /package/dist/{goals-RBKLMILE.js.map → goals-QWX3A47Y.js.map} +0 -0
  80. /package/dist/{identity-VDUW4I2K.js.map → identity-ASHVWIN5.js.map} +0 -0
  81. /package/dist/{llm-OGOYCWBH.js.map → llm-IJBRQ7O2.js.map} +0 -0
  82. /package/dist/{memory-OIAH33G2.js.map → memory-AWKIW2KW.js.map} +0 -0
  83. /package/dist/{memory-PNW7SX7A.js.map → memory-DTSLVSQG.js.map} +0 -0
  84. /package/dist/{paths-BYR6MEPR.js.map → paths-4V5OCB5F.js.map} +0 -0
  85. /package/dist/{prompt-builder-KJKFCGM7.js.map → prompt-builder-NTN4FCBD.js.map} +0 -0
  86. /package/dist/{queue-2ZBKDFX3.js.map → queue-QCGNDHH2.js.map} +0 -0
  87. /package/dist/{strategy-Z4JSFHSP.js.map → strategy-R2BMRVJ3.js.map} +0 -0
  88. /package/dist/{x-client-YG7UCCNI.js.map → x-client-S2LUVEKV.js.map} +0 -0
@@ -1,97 +1,278 @@
1
1
  import {
2
+ loadStrategy,
3
+ renderActiveIntentsForPrompt,
2
4
  renderStrategyForPrompt
3
- } from "./chunk-P6KZIJYL.js";
5
+ } from "./chunk-OTZNHIXT.js";
4
6
  import {
5
7
  renderGoalsForPrompt
6
- } from "./chunk-WN35MRMF.js";
8
+ } from "./chunk-CAWWG3MD.js";
7
9
  import {
10
+ getPerformanceSummary,
8
11
  rateLimiter
9
- } from "./chunk-4LNMA56H.js";
12
+ } from "./chunk-CP6JWCLY.js";
10
13
  import {
11
14
  loadIdentity,
12
15
  renderIdentityDocument
13
- } from "./chunk-M6YOQVSI.js";
16
+ } from "./chunk-IULO3GRE.js";
14
17
  import {
15
18
  loadConfig
16
- } from "./chunk-NO3NQN67.js";
19
+ } from "./chunk-QYFNAGNI.js";
17
20
  import {
18
21
  getRecentInteractions,
19
22
  loadLearnings,
20
23
  loadRelationships
21
- } from "./chunk-JBYZ7K56.js";
22
- import {
23
- paths
24
- } from "./chunk-3RYCUGXE.js";
24
+ } from "./chunk-BBXHECZ5.js";
25
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
- }
26
+ // src/runtime/persona-constraints.ts
27
+ function normalizeHandle(handle) {
28
+ return handle.replace(/^@/, "").trim().toLowerCase();
37
29
  }
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
30
+ function unique(values) {
31
+ return [...new Set(values)];
32
+ }
33
+ function extractHandles(text) {
34
+ const fromMentions = [...text.matchAll(/@([a-zA-Z0-9_]{1,15})/g)].map((m) => m[1]);
35
+ const fromUrls = [...text.matchAll(/x\.com\/([a-zA-Z0-9_]{1,15})/gi)].map((m) => m[1]);
36
+ return unique(
37
+ [...fromMentions, ...fromUrls].map(normalizeHandle).filter((handle) => handle.length > 0)
44
38
  );
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)`);
39
+ }
40
+ function includesOnlyQualifier(text) {
41
+ return /\b(only|just|exclusively|strictly)\b/i.test(text);
42
+ }
43
+ function includesReplyIntent(text) {
44
+ return /\b(reply|respond|response|replying)\b/i.test(text);
45
+ }
46
+ function includesInteractionIntent(text) {
47
+ return /\b(interact|engage|talk|chat|speak|communicate|message)\b/i.test(text);
48
+ }
49
+ function impliesNoOriginalPosts(text) {
50
+ const lower = text.toLowerCase();
51
+ if (/\bonly\s+repl(?:y|ies)\b/.test(lower)) return true;
52
+ if (/\brepl(?:y|ies)\s+only\b/.test(lower)) return true;
53
+ if (/\breply-?only\b/.test(lower)) return true;
54
+ if (/\b(no|never|don['’]?t|dont|avoid)\s+(?:do\s+)?(?:original\s+)?posts?\b/.test(lower)) return true;
55
+ if (/\bno\s+tweets?\b/.test(lower)) return true;
56
+ return false;
57
+ }
58
+ function getPersonaConstraints(identityArg) {
59
+ const identity = identityArg ?? loadIdentity();
60
+ const strictReplyHandles = /* @__PURE__ */ new Set();
61
+ const strictInteractionHandles = /* @__PURE__ */ new Set();
62
+ const lines = [
63
+ ...identity.goals ?? [],
64
+ ...identity.boundaries ?? [],
65
+ identity.bio ?? "",
66
+ identity.originStory ?? "",
67
+ identity.worldview ?? "",
68
+ identity.tone ?? ""
69
+ ].filter((value) => typeof value === "string" && value.trim().length > 0);
70
+ let replyOnlyMode = false;
71
+ let noOriginalPosts = false;
72
+ for (const line of lines) {
73
+ const handles = extractHandles(line);
74
+ const hasOnly = includesOnlyQualifier(line);
75
+ const hasReply = includesReplyIntent(line);
76
+ const hasInteract = includesInteractionIntent(line);
77
+ if (impliesNoOriginalPosts(line)) {
78
+ replyOnlyMode = true;
79
+ noOriginalPosts = true;
63
80
  }
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)`);
81
+ if (hasOnly && hasReply) {
82
+ replyOnlyMode = true;
83
+ noOriginalPosts = true;
84
+ for (const handle of handles) {
85
+ strictReplyHandles.add(handle);
86
+ strictInteractionHandles.add(handle);
87
+ }
88
+ continue;
67
89
  }
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`);
90
+ if (hasOnly && hasInteract) {
91
+ noOriginalPosts = true;
92
+ for (const handle of handles) {
93
+ strictInteractionHandles.add(handle);
81
94
  }
82
95
  }
83
96
  }
84
- return lines.length > 0 ? lines.join("\n") : "";
97
+ if (strictReplyHandles.size > 0) {
98
+ replyOnlyMode = true;
99
+ noOriginalPosts = true;
100
+ }
101
+ if (strictInteractionHandles.size > 0) {
102
+ noOriginalPosts = true;
103
+ }
104
+ return {
105
+ onlyReplyToHandles: [...strictReplyHandles],
106
+ onlyInteractWithHandles: [...strictInteractionHandles],
107
+ noOriginalPosts,
108
+ replyOnlyMode
109
+ };
110
+ }
111
+ function personaConstraintHandles(constraints) {
112
+ return unique([
113
+ ...constraints.onlyReplyToHandles.map(normalizeHandle),
114
+ ...constraints.onlyInteractWithHandles.map(normalizeHandle)
115
+ ]).filter(Boolean);
116
+ }
117
+ function buildPersonaConstraintLines(constraints) {
118
+ const lines = [];
119
+ if (constraints.onlyReplyToHandles.length > 0) {
120
+ lines.push(`- Hard constraint: only reply to @${constraints.onlyReplyToHandles.join(", @")}`);
121
+ } else if (constraints.replyOnlyMode) {
122
+ lines.push("- Hard constraint: reply-only mode (no like/retweet/follow/post actions).");
123
+ }
124
+ const onlyInteract = constraints.onlyInteractWithHandles.filter(
125
+ (handle) => !constraints.onlyReplyToHandles.includes(handle)
126
+ );
127
+ if (onlyInteract.length > 0) {
128
+ lines.push(`- Hard constraint: only interact with @${onlyInteract.join(", @")}`);
129
+ }
130
+ if (constraints.noOriginalPosts) {
131
+ lines.push("- Hard constraint: do not create standalone original posts.");
132
+ }
133
+ return lines;
85
134
  }
86
135
 
87
136
  // src/runtime/prompt-builder.ts
137
+ var PROMPT_TOKEN_STOPWORDS = /* @__PURE__ */ new Set([
138
+ "about",
139
+ "after",
140
+ "again",
141
+ "against",
142
+ "among",
143
+ "because",
144
+ "being",
145
+ "between",
146
+ "could",
147
+ "every",
148
+ "first",
149
+ "from",
150
+ "going",
151
+ "have",
152
+ "having",
153
+ "into",
154
+ "just",
155
+ "like",
156
+ "more",
157
+ "most",
158
+ "only",
159
+ "other",
160
+ "over",
161
+ "really",
162
+ "same",
163
+ "some",
164
+ "than",
165
+ "that",
166
+ "their",
167
+ "there",
168
+ "these",
169
+ "they",
170
+ "this",
171
+ "those",
172
+ "through",
173
+ "very",
174
+ "what",
175
+ "when",
176
+ "where",
177
+ "which",
178
+ "while",
179
+ "with",
180
+ "would",
181
+ "your",
182
+ "youre",
183
+ "dont",
184
+ "cant",
185
+ "will",
186
+ "also",
187
+ "tweet",
188
+ "tweets",
189
+ "thread",
190
+ "threads",
191
+ "future",
192
+ "human",
193
+ "humans",
194
+ "technology",
195
+ "tech",
196
+ "agent",
197
+ "agents",
198
+ "build",
199
+ "building"
200
+ ]);
201
+ function normalizeForPrompt(text) {
202
+ return text.toLowerCase().replace(/https?:\/\/\S+/g, " ").replace(/[@#]\w+/g, " ").replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
203
+ }
204
+ function recentWrittenTextsForPrompt(limit = 12) {
205
+ return getRecentInteractions(60).filter((entry) => entry.type === "post" || entry.type === "reply").map((entry) => entry.content ?? "").filter((text) => text.trim().length > 0).slice(0, limit);
206
+ }
207
+ function findOverusedOpenings(texts) {
208
+ const counts = /* @__PURE__ */ new Map();
209
+ for (const text of texts) {
210
+ const opening = normalizeForPrompt(text).split(" ").filter(Boolean).slice(0, 4).join(" ");
211
+ if (!opening || opening.split(" ").length < 3) continue;
212
+ counts.set(opening, (counts.get(opening) ?? 0) + 1);
213
+ }
214
+ return [...counts.entries()].filter(([, count]) => count >= 2).sort((a, b) => b[1] - a[1]).slice(0, 4).map(([opening]) => opening);
215
+ }
216
+ function findOverusedTokens(texts) {
217
+ const counts = /* @__PURE__ */ new Map();
218
+ for (const text of texts) {
219
+ const tokens = new Set(
220
+ normalizeForPrompt(text).split(" ").filter((token) => token.length >= 5 && !PROMPT_TOKEN_STOPWORDS.has(token))
221
+ );
222
+ for (const token of tokens) {
223
+ counts.set(token, (counts.get(token) ?? 0) + 1);
224
+ }
225
+ }
226
+ return [...counts.entries()].filter(([, count]) => count >= 3).sort((a, b) => b[1] - a[1]).slice(0, 6).map(([token]) => token);
227
+ }
228
+ function buildVoiceLockLines(identity) {
229
+ const lines = [];
230
+ const { traits } = identity;
231
+ const assertive = traits.confidence >= 0.72 || traits.aggression >= 0.62 || identity.conflictStyle === "clap-back" || identity.conflictStyle === "debate";
232
+ lines.push(`- Tone anchor: ${identity.tone}`);
233
+ lines.push(`- Conflict style: ${identity.conflictStyle}`);
234
+ lines.push(`- Vocabulary: ${identity.vocabularyStyle}; tweet style: ${identity.tweetStyle}`);
235
+ if (assertive) {
236
+ lines.push("- Be stubborn and decisive. Take a side. Do not hedge or play both sides.");
237
+ }
238
+ if (traits.curiosity >= 0.72) {
239
+ lines.push("- Ask sharp questions when engaging. Curiosity should show up in replies.");
240
+ }
241
+ if (traits.verbosity <= 0.48) {
242
+ lines.push("- Keep replies punchy: 1-2 short sentences unless a longer reply is truly necessary.");
243
+ } else {
244
+ lines.push("- Keep replies compact and human; avoid essay-like paragraphs.");
245
+ }
246
+ if (identity.catchphrases.length > 0) {
247
+ lines.push(`- Your signature phrases: ${identity.catchphrases.slice(0, 3).join(" | ")} (use occasionally, not every post).`);
248
+ }
249
+ if (identity.framework !== "philosopher") {
250
+ lines.push("- Do NOT use philosopher framing: avoid lines like 'the real question', 'the deeper question', or abstract manifesto language.");
251
+ lines.push("- Ground every post/reply in concrete context: a person, a claim, an event, or a direct observation.");
252
+ }
253
+ lines.push("- If the reply could be posted by any generic AI account, rewrite it.");
254
+ return lines;
255
+ }
88
256
  function buildSystemPrompt() {
89
257
  const identity = loadIdentity();
258
+ const constraints = getPersonaConstraints(identity);
90
259
  const config = loadConfig();
91
260
  const identityDoc = renderIdentityDocument(identity);
92
261
  const sections = [];
93
262
  sections.push(`You are ${identity.name} (@${identity.handle}), an autonomous AI agent on X/Twitter.`);
94
263
  sections.push("");
264
+ sections.push("## Voice Lock (Non-Negotiable)");
265
+ for (const line of buildVoiceLockLines(identity)) {
266
+ sections.push(line);
267
+ }
268
+ const constraintLines = buildPersonaConstraintLines(constraints);
269
+ if (constraintLines.length > 0) {
270
+ sections.push("- Operational constraints override all other behavior:");
271
+ for (const line of constraintLines) {
272
+ sections.push(` ${line}`);
273
+ }
274
+ }
275
+ sections.push("");
95
276
  sections.push("## Your Identity");
96
277
  sections.push(identityDoc);
97
278
  sections.push("");
@@ -125,6 +306,15 @@ function buildSystemPrompt() {
125
306
  }
126
307
  sections.push("");
127
308
  }
309
+ const intentsText = renderActiveIntentsForPrompt();
310
+ if (intentsText) {
311
+ sections.push("### Active Mission Intents");
312
+ for (const line of intentsText.split("\n")) {
313
+ if (!line || line.startsWith("**Active mission intents:**")) continue;
314
+ sections.push(line);
315
+ }
316
+ sections.push("");
317
+ }
128
318
  const relationships = loadRelationships();
129
319
  const topRelationships = Object.values(relationships.accounts).sort((a, b) => b.interactionCount - a.interactionCount).slice(0, 10);
130
320
  if (topRelationships.length > 0) {
@@ -229,6 +419,8 @@ function buildHeartbeatUserMessage(research) {
229
419
  parts.push("**DON'T:**");
230
420
  parts.push(`- Write explanatory or educational posts ("Here's why X matters...")`);
231
421
  parts.push('- Start tweets with "I think" or "This is interesting because"');
422
+ parts.push('- Use philosopher opener templates like "The real question is..." or "The deeper question is..."');
423
+ parts.push("- Use colon/semicolon/em-dash manifesto framing in normal tweets");
232
424
  parts.push("- Write like a blog post or article \u2014 this is Twitter, keep it punchy");
233
425
  parts.push("- Post generic observations nobody would engage with");
234
426
  parts.push("- Ignore your timeline and just post into the void");
@@ -256,7 +448,20 @@ function buildHeartbeatUserMessage(research) {
256
448
  return parts.join("\n");
257
449
  }
258
450
  function buildToolDecisionMessage(input) {
259
- const { step, maxActions, timeline, mentions, executedActions, policyFeedback } = input;
451
+ const identity = loadIdentity();
452
+ const strategy = loadStrategy();
453
+ const {
454
+ step,
455
+ maxActions,
456
+ timeline,
457
+ mentions,
458
+ topicSearchResults = [],
459
+ peopleActivity = [],
460
+ executedActions,
461
+ policyFeedback,
462
+ blockedTweetIds = [],
463
+ disallowedActions = []
464
+ } = input;
260
465
  const parts = [];
261
466
  parts.push(`Heartbeat step ${step + 1} of ${maxActions}.`);
262
467
  parts.push("");
@@ -267,6 +472,18 @@ function buildToolDecisionMessage(input) {
267
472
  parts.push("3. Avoid repetitive templates, slogans, and lecture-like formats.");
268
473
  parts.push("4. Ask questions sometimes. Curiosity beats certainty.");
269
474
  parts.push("5. Never reuse the same wording across replies; tailor each reply to the specific tweet.");
475
+ parts.push("6. Never interact with your own tweets. No self-replies, self-likes, or self-retweets.");
476
+ parts.push("");
477
+ const priorityTargets = strategy.peopleToEngage.filter((person) => person.priority === "high").map((person) => `@${person.handle.replace(/^@/, "")}`);
478
+ if (priorityTargets.length > 0) {
479
+ parts.push(`Priority targets: ${priorityTargets.slice(0, 5).join(", ")}`);
480
+ parts.push("If any priority target appears in current context, prefer engaging them.");
481
+ parts.push("");
482
+ }
483
+ parts.push("Voice lock:");
484
+ for (const line of buildVoiceLockLines(identity)) {
485
+ parts.push(line);
486
+ }
270
487
  parts.push("");
271
488
  if (policyFeedback.length > 0) {
272
489
  parts.push("Policy feedback from previous attempts:");
@@ -282,6 +499,15 @@ function buildToolDecisionMessage(input) {
282
499
  }
283
500
  parts.push("");
284
501
  }
502
+ if (blockedTweetIds.length > 0) {
503
+ parts.push("Tweet IDs you must NOT use this heartbeat:");
504
+ parts.push(`- ${blockedTweetIds.slice(0, 20).join(", ")}`);
505
+ parts.push("");
506
+ }
507
+ if (disallowedActions.length > 0) {
508
+ parts.push(`Temporarily disallowed actions this heartbeat: ${disallowedActions.join(", ")}`);
509
+ parts.push("");
510
+ }
285
511
  if (mentions.length > 0) {
286
512
  parts.push("Mentions:");
287
513
  for (const t of mentions.slice(0, 10)) {
@@ -296,6 +522,26 @@ function buildToolDecisionMessage(input) {
296
522
  }
297
523
  parts.push("");
298
524
  }
525
+ if (topicSearchResults.length > 0) {
526
+ parts.push("Topic Search (fresh conversations beyond your home timeline):");
527
+ for (const result of topicSearchResults.slice(0, 2)) {
528
+ parts.push(`- Query: "${result.query}"`);
529
+ for (const t of result.tweets.slice(0, 4)) {
530
+ parts.push(` - @${t.authorHandle}: "${t.text}" [tweet:${t.id}]`);
531
+ }
532
+ }
533
+ parts.push("");
534
+ }
535
+ if (peopleActivity.length > 0) {
536
+ parts.push("People Monitoring (accounts you track):");
537
+ for (const person of peopleActivity.slice(0, 3)) {
538
+ parts.push(`- @${person.handle} (${person.reason})`);
539
+ for (const t of person.tweets.slice(0, 2)) {
540
+ parts.push(` - "${t.text}" [tweet:${t.id}]`);
541
+ }
542
+ }
543
+ parts.push("");
544
+ }
299
545
  parts.push("Available tools (choose one):");
300
546
  parts.push("- post { content, reasoning }");
301
547
  parts.push("- reply { tweetId, content, reasoning }");
@@ -312,6 +558,101 @@ function buildToolDecisionMessage(input) {
312
558
  parts.push('{"action":"reply","tweetId":"123","content":"Great point. Curious: what changed your mind?","reasoning":"Directly engages a relevant mention."}');
313
559
  return parts.join("\n");
314
560
  }
561
+ function buildOpportunityPortfolioMessage(input) {
562
+ const identity = loadIdentity();
563
+ const constraints = getPersonaConstraints(identity);
564
+ const strategy = loadStrategy();
565
+ const recentWritten = recentWrittenTextsForPrompt(12);
566
+ const overusedOpenings = findOverusedOpenings(recentWritten);
567
+ const overusedTokens = findOverusedTokens(recentWritten);
568
+ const { opportunities, maxActions, policyFeedback, executedActions } = input;
569
+ const parts = [];
570
+ parts.push(`Select a portfolio of up to ${maxActions} actions from ranked opportunities.`);
571
+ parts.push("");
572
+ parts.push("Portfolio goals:");
573
+ parts.push("1. Be socially immersed and grounded in real tweets.");
574
+ parts.push("2. Diversify actions and targets, avoid repetitive reply chains.");
575
+ parts.push("3. Prefer quality over quantity; skip weak opportunities.");
576
+ parts.push("4. Advance current strategy targets and focus areas from memory.");
577
+ parts.push("5. Use only listed candidate IDs.");
578
+ parts.push("");
579
+ const priorityTargets = strategy.peopleToEngage.filter((person) => person.priority === "high").map((person) => `@${person.handle.replace(/^@/, "")}`);
580
+ if (priorityTargets.length > 0) {
581
+ parts.push(`Priority targets: ${priorityTargets.slice(0, 5).join(", ")}`);
582
+ parts.push("When opportunities include these targets, prioritize them.");
583
+ parts.push("");
584
+ }
585
+ parts.push("Voice lock:");
586
+ for (const line of buildVoiceLockLines(identity)) {
587
+ parts.push(line);
588
+ }
589
+ const constraintLines = buildPersonaConstraintLines(constraints);
590
+ if (constraintLines.length > 0) {
591
+ parts.push("Operational constraints (hard):");
592
+ for (const line of constraintLines) {
593
+ parts.push(line);
594
+ }
595
+ }
596
+ parts.push("");
597
+ parts.push("Human style rotation:");
598
+ parts.push("- For each `reply`/`post`, pick one lane: quick reaction, curious question, friendly pushback, playful line, plain observation.");
599
+ parts.push("- Do not reuse the same lane for multiple content actions in this portfolio.");
600
+ parts.push("- Keep phrasing short and spoken; contractions and fragments are allowed.");
601
+ if (overusedOpenings.length > 0) {
602
+ parts.push(`- Avoid these repeated opening patterns: ${overusedOpenings.join(" | ")}`);
603
+ }
604
+ if (overusedTokens.length > 0) {
605
+ parts.push(`- Avoid overused anchor words this round: ${overusedTokens.join(", ")}`);
606
+ }
607
+ parts.push("");
608
+ if (policyFeedback.length > 0) {
609
+ parts.push("Latest policy feedback:");
610
+ for (const feedback of policyFeedback.slice(-6)) {
611
+ parts.push(`- ${feedback}`);
612
+ }
613
+ parts.push("");
614
+ }
615
+ if (executedActions.length > 0) {
616
+ parts.push("Actions already executed:");
617
+ for (const action of executedActions) {
618
+ parts.push(`- ${JSON.stringify(action)}`);
619
+ }
620
+ parts.push("");
621
+ }
622
+ parts.push("Opportunities (higher score = better):");
623
+ for (const opportunity of opportunities) {
624
+ parts.push(
625
+ `- ${opportunity.id} | action=${opportunity.actionType} | score=${opportunity.score.toFixed(2)} | source=${opportunity.source}${opportunity.authorHandle ? ` | author=@${opportunity.authorHandle}` : ""}${opportunity.tweetId ? ` | tweet=${opportunity.tweetId}` : ""}`
626
+ );
627
+ parts.push(` summary: ${opportunity.summary}`);
628
+ parts.push(` context: ${opportunity.context}`);
629
+ if (opportunity.requiresContent) {
630
+ parts.push(" content_required: true");
631
+ }
632
+ }
633
+ parts.push("");
634
+ parts.push("Return JSON only in this shape:");
635
+ parts.push("```json");
636
+ parts.push("{");
637
+ parts.push(' "selections": [');
638
+ parts.push(' { "candidateId": "opp-3", "content": "optional for reply/post", "reasoning": "short reason" }');
639
+ parts.push(" ]");
640
+ parts.push("}");
641
+ parts.push("```");
642
+ parts.push("Rules:");
643
+ parts.push("- For `reply` and `post`, include `content`.");
644
+ parts.push("- Keep content concise, natural, and non-repetitive.");
645
+ parts.push("- Avoid explanatory/essay voice. No lecture framing.");
646
+ parts.push("- Do NOT use abstract philosopher framing unless the identity framework is explicitly philosopher.");
647
+ parts.push("- Prefer short, human syntax: plain sentence fragments are fine.");
648
+ parts.push("- Avoid colon/semicolon/em-dash for non-philosopher personas.");
649
+ parts.push("- Prefer short conversational language: plain words, occasional question, and direct reaction.");
650
+ parts.push("- Reply content target: usually 8-24 words, max 2 short sentences.");
651
+ parts.push("- Prefer at least one non-reply action when viable.");
652
+ parts.push("- Do not target the same author repeatedly in one portfolio.");
653
+ parts.push("- Avoid consensus phrasing like 'you're both right' if your persona is assertive.");
654
+ return parts.join("\n");
655
+ }
315
656
  function buildChatPrompt() {
316
657
  const identity = loadIdentity();
317
658
  const identityDoc = renderIdentityDocument(identity);
@@ -520,11 +861,15 @@ function buildReflectionPrompt(actionResults) {
520
861
  }
521
862
 
522
863
  export {
864
+ getPersonaConstraints,
865
+ personaConstraintHandles,
866
+ buildPersonaConstraintLines,
523
867
  buildSystemPrompt,
524
868
  buildHeartbeatUserMessage,
525
869
  buildToolDecisionMessage,
870
+ buildOpportunityPortfolioMessage,
526
871
  buildChatPrompt,
527
872
  buildTrainingChatPrompt,
528
873
  buildReflectionPrompt
529
874
  };
530
- //# sourceMappingURL=chunk-Q3YXJ2C6.js.map
875
+ //# sourceMappingURL=chunk-E5PEY36J.js.map