spora 0.7.2 → 0.7.4

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 (50) hide show
  1. package/dist/autonomy-XUKCAZM3.js +19 -0
  2. package/dist/{chunk-LXQNVVIY.js → chunk-4LNMA56H.js} +2 -2
  3. package/dist/{chunk-WIK74GGJ.js → chunk-EU4FMOKG.js} +104 -22
  4. package/dist/chunk-EU4FMOKG.js.map +1 -0
  5. package/dist/{chunk-OACD3HGE.js → chunk-MDOFAAZB.js} +3 -3
  6. package/dist/{chunk-E5NR6HT4.js → chunk-PN5A6MCV.js} +3 -3
  7. package/dist/{chunk-AOQ3WLZV.js → chunk-Q3YXJ2C6.js} +137 -28
  8. package/dist/chunk-Q3YXJ2C6.js.map +1 -0
  9. package/dist/{chunk-VZBHRUZS.js → chunk-QWEYVDLU.js} +1 -1
  10. package/dist/chunk-QWEYVDLU.js.map +1 -0
  11. package/dist/{chunk-KWWAIS3C.js → chunk-SUZUJGGW.js} +2 -2
  12. package/dist/{chunk-NPV3OV2K.js → chunk-YMGJQRKG.js} +13 -2
  13. package/dist/chunk-YMGJQRKG.js.map +1 -0
  14. package/dist/cli.js +33 -33
  15. package/dist/{client-57BQKVYF.js → client-Z5UQWPPI.js} +125 -41
  16. package/dist/client-Z5UQWPPI.js.map +1 -0
  17. package/dist/{colony-JPZC3R34.js → colony-NNX45EAV.js} +3 -3
  18. package/dist/{crypto-NOXNL4GP.js → crypto-B65ZH7KN.js} +2 -2
  19. package/dist/{heartbeat-TNEPE3ZP.js → heartbeat-ZCCOIZGU.js} +57 -13
  20. package/dist/heartbeat-ZCCOIZGU.js.map +1 -0
  21. package/dist/{init-ISSXETHY.js → init-SEJPTOOB.js} +6 -6
  22. package/dist/{llm-T33QTPVW.js → llm-OGOYCWBH.js} +3 -3
  23. package/dist/mcp-server.js +20 -20
  24. package/dist/{prompt-builder-5NYONN2W.js → prompt-builder-KJKFCGM7.js} +6 -4
  25. package/dist/{queue-G5PTE6R6.js → queue-2ZBKDFX3.js} +3 -3
  26. package/dist/web-chat/chat.html +190 -3
  27. package/dist/{web-chat-3HM35XM4.js → web-chat-ZZ65DUID.js} +148 -11
  28. package/dist/web-chat-ZZ65DUID.js.map +1 -0
  29. package/dist/{x-client-GY6XSPK6.js → x-client-YG7UCCNI.js} +3 -3
  30. package/package.json +1 -1
  31. package/dist/autonomy-DAV7X6QS.js +0 -19
  32. package/dist/chunk-AOQ3WLZV.js.map +0 -1
  33. package/dist/chunk-NPV3OV2K.js.map +0 -1
  34. package/dist/chunk-VZBHRUZS.js.map +0 -1
  35. package/dist/chunk-WIK74GGJ.js.map +0 -1
  36. package/dist/client-57BQKVYF.js.map +0 -1
  37. package/dist/heartbeat-TNEPE3ZP.js.map +0 -1
  38. package/dist/web-chat-3HM35XM4.js.map +0 -1
  39. /package/dist/{autonomy-DAV7X6QS.js.map → autonomy-XUKCAZM3.js.map} +0 -0
  40. /package/dist/{chunk-LXQNVVIY.js.map → chunk-4LNMA56H.js.map} +0 -0
  41. /package/dist/{chunk-OACD3HGE.js.map → chunk-MDOFAAZB.js.map} +0 -0
  42. /package/dist/{chunk-E5NR6HT4.js.map → chunk-PN5A6MCV.js.map} +0 -0
  43. /package/dist/{chunk-KWWAIS3C.js.map → chunk-SUZUJGGW.js.map} +0 -0
  44. /package/dist/{colony-JPZC3R34.js.map → colony-NNX45EAV.js.map} +0 -0
  45. /package/dist/{crypto-NOXNL4GP.js.map → crypto-B65ZH7KN.js.map} +0 -0
  46. /package/dist/{init-ISSXETHY.js.map → init-SEJPTOOB.js.map} +0 -0
  47. /package/dist/{llm-T33QTPVW.js.map → llm-OGOYCWBH.js.map} +0 -0
  48. /package/dist/{prompt-builder-5NYONN2W.js.map → prompt-builder-KJKFCGM7.js.map} +0 -0
  49. /package/dist/{queue-G5PTE6R6.js.map → queue-2ZBKDFX3.js.map} +0 -0
  50. /package/dist/{x-client-GY6XSPK6.js.map → x-client-YG7UCCNI.js.map} +0 -0
@@ -0,0 +1,19 @@
1
+ import {
2
+ runAutonomyCycle
3
+ } from "./chunk-EU4FMOKG.js";
4
+ import "./chunk-PN5A6MCV.js";
5
+ import "./chunk-MDOFAAZB.js";
6
+ import "./chunk-Q3YXJ2C6.js";
7
+ import "./chunk-P6KZIJYL.js";
8
+ import "./chunk-WN35MRMF.js";
9
+ import "./chunk-4LNMA56H.js";
10
+ import "./chunk-M6YOQVSI.js";
11
+ import "./chunk-SUZUJGGW.js";
12
+ import "./chunk-YMGJQRKG.js";
13
+ import "./chunk-NO3NQN67.js";
14
+ import "./chunk-JBYZ7K56.js";
15
+ import "./chunk-3RYCUGXE.js";
16
+ export {
17
+ runAutonomyCycle
18
+ };
19
+ //# sourceMappingURL=autonomy-XUKCAZM3.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-NPV3OV2K.js";
3
+ } from "./chunk-YMGJQRKG.js";
4
4
  import {
5
5
  loadConfig,
6
6
  saveConfig
@@ -54,4 +54,4 @@ var rateLimiter = new RateLimiter();
54
54
  export {
55
55
  rateLimiter
56
56
  };
57
- //# sourceMappingURL=chunk-LXQNVVIY.js.map
57
+ //# sourceMappingURL=chunk-4LNMA56H.js.map
@@ -1,23 +1,23 @@
1
1
  import {
2
2
  getXClient
3
- } from "./chunk-E5NR6HT4.js";
3
+ } from "./chunk-PN5A6MCV.js";
4
4
  import {
5
5
  addToQueue
6
- } from "./chunk-OACD3HGE.js";
6
+ } from "./chunk-MDOFAAZB.js";
7
7
  import {
8
8
  buildSystemPrompt,
9
9
  buildToolDecisionMessage
10
- } from "./chunk-AOQ3WLZV.js";
10
+ } from "./chunk-Q3YXJ2C6.js";
11
11
  import {
12
12
  loadIdentity,
13
13
  saveIdentity
14
14
  } from "./chunk-M6YOQVSI.js";
15
15
  import {
16
16
  generateResponse
17
- } from "./chunk-KWWAIS3C.js";
17
+ } from "./chunk-SUZUJGGW.js";
18
18
  import {
19
19
  logger
20
- } from "./chunk-NPV3OV2K.js";
20
+ } from "./chunk-YMGJQRKG.js";
21
21
  import {
22
22
  addLearning,
23
23
  getRecentInteractions
@@ -25,27 +25,76 @@ import {
25
25
 
26
26
  // src/runtime/decision-engine.ts
27
27
  function parseActions(llmResponse) {
28
- const jsonMatch = llmResponse.match(/\[[\s\S]*?\]/);
29
- if (!jsonMatch) {
30
- const objMatch = llmResponse.match(/\{[\s\S]*?\}/);
31
- if (objMatch) {
32
- try {
33
- return [JSON.parse(objMatch[0])];
34
- } catch {
35
- logger.warn("Could not parse LLM response as action object");
36
- return [];
28
+ const codeBlockMatch = llmResponse.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
29
+ if (codeBlockMatch) {
30
+ const inside = codeBlockMatch[1].trim();
31
+ try {
32
+ const parsed = JSON.parse(inside);
33
+ return Array.isArray(parsed) ? parsed : [parsed];
34
+ } catch {
35
+ }
36
+ }
37
+ let lastArrayStart = -1;
38
+ let lastArrayEnd = -1;
39
+ let depth = 0;
40
+ for (let i = 0; i < llmResponse.length; i++) {
41
+ if (llmResponse[i] === "[") {
42
+ if (depth === 0) lastArrayStart = i;
43
+ depth++;
44
+ } else if (llmResponse[i] === "]") {
45
+ depth--;
46
+ if (depth === 0) lastArrayEnd = i;
47
+ }
48
+ }
49
+ if (lastArrayStart >= 0 && lastArrayEnd > lastArrayStart) {
50
+ const arrayStr = llmResponse.slice(lastArrayStart, lastArrayEnd + 1);
51
+ try {
52
+ const parsed = JSON.parse(arrayStr);
53
+ if (Array.isArray(parsed) && parsed.length > 0 && parsed[0].action) {
54
+ return parsed;
55
+ }
56
+ } catch {
57
+ }
58
+ }
59
+ const objMatches = [...llmResponse.matchAll(/\{[^{}]*"action"\s*:\s*"[^"]+"/g)];
60
+ if (objMatches.length > 0) {
61
+ for (let i = objMatches.length - 1; i >= 0; i--) {
62
+ const start = objMatches[i].index;
63
+ let braceDepth = 0;
64
+ let end = -1;
65
+ for (let j = start; j < llmResponse.length; j++) {
66
+ if (llmResponse[j] === "{") braceDepth++;
67
+ else if (llmResponse[j] === "}") {
68
+ braceDepth--;
69
+ if (braceDepth === 0) {
70
+ end = j;
71
+ break;
72
+ }
73
+ }
74
+ }
75
+ if (end > start) {
76
+ try {
77
+ const obj = JSON.parse(llmResponse.slice(start, end + 1));
78
+ if (obj.action) return [obj];
79
+ } catch {
80
+ continue;
81
+ }
37
82
  }
38
83
  }
39
- logger.warn("No JSON found in LLM response");
40
- return [];
41
84
  }
42
85
  try {
43
- const actions = JSON.parse(jsonMatch[0]);
44
- return Array.isArray(actions) ? actions : [actions];
86
+ const parsed = JSON.parse(llmResponse.trim());
87
+ if (Array.isArray(parsed)) return parsed;
88
+ if (parsed.action) return [parsed];
45
89
  } catch {
46
- logger.warn("Failed to parse actions JSON from LLM response");
47
- return [];
48
90
  }
91
+ logger.warn("Failed to parse actions from LLM response");
92
+ if (llmResponse.length < 500) {
93
+ logger.warn(`Response was: ${llmResponse}`);
94
+ } else {
95
+ logger.warn(`Response starts with: ${llmResponse.slice(0, 200)}...`);
96
+ }
97
+ return [];
49
98
  }
50
99
  async function executeAction(action) {
51
100
  const { action: type } = action;
@@ -165,6 +214,22 @@ function hasStrongConversationOpportunity(timeline, mentions) {
165
214
  function wasInteractionAction(action) {
166
215
  return ["reply", "like", "retweet", "follow"].includes(action.action);
167
216
  }
217
+ function isWritingAction(action) {
218
+ return ["post", "reply", "schedule"].includes(action.action);
219
+ }
220
+ function executedWrittenContent(executedActions) {
221
+ return executedActions.filter((a) => isWritingAction(a) && typeof a.content === "string").map((a) => a.content?.trim() ?? "").filter((content) => content.length > 0);
222
+ }
223
+ function nearExactDuplicate(content, recent) {
224
+ const normalized = normalize(content);
225
+ if (!normalized) return false;
226
+ return recent.some((r) => {
227
+ const candidate = normalize(r);
228
+ if (!candidate) return false;
229
+ if (candidate === normalized) return true;
230
+ return jaccardSimilarity(content, r) >= 0.88;
231
+ });
232
+ }
168
233
  function isDuplicateTarget(action, executedActions) {
169
234
  if (!action.tweetId) return false;
170
235
  return executedActions.some((a) => a.tweetId === action.tweetId && a.action === action.action);
@@ -188,6 +253,15 @@ function evaluateActionPolicy(context) {
188
253
  if (isDuplicateTarget(action, executedActions)) {
189
254
  return { allowed: false, reason: `Action ${action.action} already executed for tweet ${action.tweetId} this heartbeat.` };
190
255
  }
256
+ if (action.content && isWritingAction(action)) {
257
+ const existingInHeartbeat = executedWrittenContent(executedActions);
258
+ if (nearExactDuplicate(action.content, existingInHeartbeat)) {
259
+ return {
260
+ allowed: false,
261
+ reason: "Rejected duplicate wording in this heartbeat. Write a distinct message before posting/replying again."
262
+ };
263
+ }
264
+ }
191
265
  const hasConversationOpportunity = hasStrongConversationOpportunity(timeline, mentions);
192
266
  const interactedAlready = executedActions.some(wasInteractionAction);
193
267
  if (action.action === "post" && hasConversationOpportunity && !interactedAlready && step < 2) {
@@ -196,14 +270,22 @@ function evaluateActionPolicy(context) {
196
270
  reason: "Engage first: reply/like/follow when mentions or active conversations are available before posting an original tweet."
197
271
  };
198
272
  }
199
- if ((action.action === "post" || action.action === "schedule") && action.content) {
273
+ if (isWritingAction(action) && action.content) {
200
274
  const recent = recentWrittenContent();
275
+ if (nearExactDuplicate(action.content, recent)) {
276
+ return {
277
+ allowed: false,
278
+ reason: "Rejected near-duplicate content from recent history. Tailor this message to the specific context."
279
+ };
280
+ }
201
281
  if (repeatedTemplate(action.content, recent)) {
202
282
  return {
203
283
  allowed: false,
204
284
  reason: "Rejected repetitive content pattern. Use a more novel structure or engage directly with timeline context."
205
285
  };
206
286
  }
287
+ }
288
+ if ((action.action === "post" || action.action === "schedule") && action.content) {
207
289
  const recentInteractions = getRecentInteractions(20);
208
290
  if (overusingAllCapsCadence(action.content, recentInteractions)) {
209
291
  return {
@@ -292,4 +374,4 @@ async function runAutonomyCycle(maxActions) {
292
374
  export {
293
375
  runAutonomyCycle
294
376
  };
295
- //# sourceMappingURL=chunk-WIK74GGJ.js.map
377
+ //# sourceMappingURL=chunk-EU4FMOKG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/decision-engine.ts","../src/runtime/policy.ts","../src/runtime/autonomy.ts"],"sourcesContent":["import { logger } from \"../utils/logger.js\";\nimport { getXClient } from \"../x-client/index.js\";\nimport { 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 tags?: string[];\n reason?: string;\n reasoning?: 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 // Strategy: try multiple extraction approaches, most specific first\n\n // 1. Try markdown code block first (```json ... ``` or ``` ... ```)\n const codeBlockMatch = llmResponse.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)```/);\n if (codeBlockMatch) {\n const inside = codeBlockMatch[1].trim();\n try {\n const parsed = JSON.parse(inside);\n return Array.isArray(parsed) ? parsed : [parsed];\n } catch {\n // Code block content wasn't valid JSON, continue to other strategies\n }\n }\n\n // 2. Find the last JSON array in the response (LLMs often put reasoning before the array)\n // Use a greedy approach to find the outermost array brackets\n let lastArrayStart = -1;\n let lastArrayEnd = -1;\n let depth = 0;\n for (let i = 0; i < llmResponse.length; i++) {\n if (llmResponse[i] === \"[\") {\n if (depth === 0) lastArrayStart = i;\n depth++;\n } else if (llmResponse[i] === \"]\") {\n depth--;\n if (depth === 0) lastArrayEnd = i;\n }\n }\n\n if (lastArrayStart >= 0 && lastArrayEnd > lastArrayStart) {\n const arrayStr = llmResponse.slice(lastArrayStart, lastArrayEnd + 1);\n try {\n const parsed = JSON.parse(arrayStr);\n if (Array.isArray(parsed) && parsed.length > 0 && parsed[0].action) {\n return parsed;\n }\n } catch {\n // Not valid JSON array, try next strategy\n }\n }\n\n // 3. Try to find a single action object (last one in the response)\n const objMatches = [...llmResponse.matchAll(/\\{[^{}]*\"action\"\\s*:\\s*\"[^\"]+\"/g)];\n if (objMatches.length > 0) {\n // Find the full object starting from this match\n for (let i = objMatches.length - 1; i >= 0; i--) {\n const start = objMatches[i].index!;\n let braceDepth = 0;\n let end = -1;\n for (let j = start; j < llmResponse.length; j++) {\n if (llmResponse[j] === \"{\") braceDepth++;\n else if (llmResponse[j] === \"}\") {\n braceDepth--;\n if (braceDepth === 0) { end = j; break; }\n }\n }\n if (end > start) {\n try {\n const obj = JSON.parse(llmResponse.slice(start, end + 1));\n if (obj.action) return [obj as AgentAction];\n } catch {\n continue;\n }\n }\n }\n }\n\n // 4. Last resort: try the whole response as JSON\n try {\n const parsed = JSON.parse(llmResponse.trim());\n if (Array.isArray(parsed)) return parsed;\n if (parsed.action) return [parsed as AgentAction];\n } catch {\n // Not JSON at all\n }\n\n logger.warn(\"Failed to parse actions from LLM response\");\n if (llmResponse.length < 500) {\n logger.warn(`Response was: ${llmResponse}`);\n } else {\n logger.warn(`Response starts with: ${llmResponse.slice(0, 200)}...`);\n }\n return [];\n}\n\nexport async function executeAction(action: AgentAction): Promise<ActionResult> {\n const { action: type } = action;\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) logger.info(`Posted: \"${action.content.slice(0, 50)}...\"`);\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 const client = await getXClient();\n const result = await client.replyToTweet(action.tweetId, action.content);\n if (result.success) logger.info(`Replied to ${action.tweetId}: \"${action.content.slice(0, 50)}...\"`);\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);\n logger.info(`Scheduled: \"${action.content.slice(0, 50)}...\" for ${entry.scheduledFor}`);\n return { action: type, success: true, detail: `Scheduled for ${entry.scheduledFor}` };\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 \"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 for (const action of actions) {\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","import { getRecentInteractions, type InteractionEntry } from \"../memory/index.js\";\nimport type { Tweet } from \"../x-client/types.js\";\nimport type { AgentAction } from \"./decision-engine.js\";\n\nexport interface PolicyContext {\n action: AgentAction;\n step: number;\n timeline: Tweet[];\n mentions: Tweet[];\n executedActions: AgentAction[];\n}\n\nexport interface PolicyDecision {\n allowed: boolean;\n reason?: string;\n}\n\nfunction normalize(text: string): string {\n return text\n .toLowerCase()\n .replace(/https?:\\/\\/\\S+/g, \"\")\n .replace(/[@#]\\w+/g, \"\")\n .replace(/[^a-z0-9\\s]/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction tokenSet(text: string): Set<string> {\n const tokens = normalize(text).split(\" \").filter(Boolean);\n return new Set(tokens);\n}\n\nfunction jaccardSimilarity(a: string, b: string): number {\n const aSet = tokenSet(a);\n const bSet = tokenSet(b);\n if (aSet.size === 0 || bSet.size === 0) return 0;\n\n let overlap = 0;\n for (const token of aSet) {\n if (bSet.has(token)) overlap += 1;\n }\n\n const union = aSet.size + bSet.size - overlap;\n return union === 0 ? 0 : overlap / union;\n}\n\nfunction firstWords(text: string, n: number): string {\n return normalize(text).split(\" \").filter(Boolean).slice(0, n).join(\" \");\n}\n\nfunction hasAllCapsEnding(text: string): boolean {\n const ending = text.split(/[.!?]/).map((s) => s.trim()).filter(Boolean).slice(-1)[0] ?? \"\";\n const words = ending.split(/\\s+/).filter(Boolean);\n if (words.length < 3) return false;\n return words.every((word) => /^[A-Z0-9]+$/.test(word));\n}\n\nfunction recentWrittenContent(): string[] {\n const recent = getRecentInteractions(40);\n return recent\n .filter((i) => i.type === \"post\" || i.type === \"reply\")\n .map((i) => i.content ?? \"\")\n .filter((content) => content.length > 0);\n}\n\nfunction hasStrongConversationOpportunity(timeline: Tweet[], mentions: Tweet[]): boolean {\n if (mentions.length > 0) return true;\n return timeline.some((tweet) => (tweet.replyCount ?? 0) > 0 || tweet.text.includes(\"?\"));\n}\n\nfunction wasInteractionAction(action: AgentAction): boolean {\n return [\"reply\", \"like\", \"retweet\", \"follow\"].includes(action.action);\n}\n\nfunction isWritingAction(action: AgentAction): boolean {\n return [\"post\", \"reply\", \"schedule\"].includes(action.action);\n}\n\nfunction executedWrittenContent(executedActions: AgentAction[]): string[] {\n return executedActions\n .filter((a) => isWritingAction(a) && typeof a.content === \"string\")\n .map((a) => a.content?.trim() ?? \"\")\n .filter((content) => content.length > 0);\n}\n\nfunction nearExactDuplicate(content: string, recent: string[]): boolean {\n const normalized = normalize(content);\n if (!normalized) return false;\n\n return recent.some((r) => {\n const candidate = normalize(r);\n if (!candidate) return false;\n if (candidate === normalized) return true;\n return jaccardSimilarity(content, r) >= 0.88;\n });\n}\n\nfunction isDuplicateTarget(action: AgentAction, executedActions: AgentAction[]): boolean {\n if (!action.tweetId) return false;\n return executedActions.some((a) => a.tweetId === action.tweetId && a.action === action.action);\n}\n\nfunction repeatedTemplate(content: string, recent: string[]): boolean {\n const prefix = firstWords(content, 7);\n if (!prefix) return false;\n\n return recent.some((r) => {\n const sameStart = firstWords(r, 7) === prefix;\n const similar = jaccardSimilarity(content, r) >= 0.62;\n return sameStart || similar;\n });\n}\n\nfunction overusingAllCapsCadence(content: string, recentEntries: InteractionEntry[]): boolean {\n if (!hasAllCapsEnding(content)) return false;\n const recentCaps = recentEntries\n .filter((i) => i.type === \"post\" && i.content)\n .slice(0, 8)\n .filter((i) => hasAllCapsEnding(i.content ?? \"\"));\n\n return recentCaps.length >= 2;\n}\n\nexport function evaluateActionPolicy(context: PolicyContext): PolicyDecision {\n const { action, step, timeline, mentions, executedActions } = context;\n\n if (isDuplicateTarget(action, executedActions)) {\n return { allowed: false, reason: `Action ${action.action} already executed for tweet ${action.tweetId} this heartbeat.` };\n }\n\n if (action.content && isWritingAction(action)) {\n const existingInHeartbeat = executedWrittenContent(executedActions);\n if (nearExactDuplicate(action.content, existingInHeartbeat)) {\n return {\n allowed: false,\n reason: \"Rejected duplicate wording in this heartbeat. Write a distinct message before posting/replying again.\",\n };\n }\n }\n\n const hasConversationOpportunity = hasStrongConversationOpportunity(timeline, mentions);\n const interactedAlready = executedActions.some(wasInteractionAction);\n\n if (\n action.action === \"post\" &&\n hasConversationOpportunity &&\n !interactedAlready &&\n step < 2\n ) {\n return {\n allowed: false,\n reason: \"Engage first: reply/like/follow when mentions or active conversations are available before posting an original tweet.\",\n };\n }\n\n if (isWritingAction(action) && action.content) {\n const recent = recentWrittenContent();\n if (nearExactDuplicate(action.content, recent)) {\n return {\n allowed: false,\n reason: \"Rejected near-duplicate content from recent history. Tailor this message to the specific context.\",\n };\n }\n\n if (repeatedTemplate(action.content, recent)) {\n return {\n allowed: false,\n reason: \"Rejected repetitive content pattern. Use a more novel structure or engage directly with timeline context.\",\n };\n }\n }\n\n if ((action.action === \"post\" || action.action === \"schedule\") && action.content) {\n const recentInteractions = getRecentInteractions(20);\n if (overusingAllCapsCadence(action.content, recentInteractions)) {\n return {\n allowed: false,\n reason: \"Rejected repetitive all-caps slogan cadence. Vary style and reduce monologue formatting.\",\n };\n }\n }\n\n if ((action.action === \"reply\" || action.action === \"like\" || action.action === \"retweet\") && action.tweetId) {\n const known = new Set([...timeline, ...mentions].map((tweet) => tweet.id));\n if (!known.has(action.tweetId)) {\n return {\n allowed: false,\n reason: `Tweet ${action.tweetId} is not in current observations. Choose a visible timeline/mention tweet for grounded engagement.`,\n };\n }\n }\n\n return { allowed: true };\n}\n","import { getXClient } from \"../x-client/index.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { getRecentInteractions } from \"../memory/index.js\";\nimport type { Tweet } from \"../x-client/types.js\";\nimport { buildSystemPrompt, buildToolDecisionMessage } from \"./prompt-builder.js\";\nimport { generateResponse } from \"./llm.js\";\nimport { parseActions, executeAction, type AgentAction, type ActionResult } from \"./decision-engine.js\";\nimport { evaluateActionPolicy } from \"./policy.js\";\n\nexport interface AutonomyCycleResult {\n timeline: Tweet[];\n mentions: Tweet[];\n actions: AgentAction[];\n results: ActionResult[];\n policyFeedback: string[];\n}\n\nexport async function runAutonomyCycle(maxActions: number): Promise<AutonomyCycleResult> {\n const client = await getXClient();\n\n let timeline: Tweet[] = [];\n let mentions: Tweet[] = [];\n\n try {\n timeline = await client.getTimeline({ count: 20 });\n } catch (error) {\n logger.warn(`Timeline read failed: ${(error as Error).message}`);\n }\n\n try {\n mentions = await client.getMentions({ count: 10 });\n } catch (error) {\n logger.warn(`Mentions read failed: ${(error as Error).message}`);\n }\n\n const systemPrompt = buildSystemPrompt();\n const actions: AgentAction[] = [];\n const results: ActionResult[] = [];\n const policyFeedback: string[] = [];\n\n // Keep short memory available in context by touching it before planner loop.\n getRecentInteractions(20);\n\n for (let step = 0; step < maxActions; step += 1) {\n const decisionPrompt = buildToolDecisionMessage({\n step,\n maxActions,\n timeline,\n mentions,\n executedActions: actions,\n policyFeedback,\n });\n\n const llmResponse = await generateResponse(systemPrompt, decisionPrompt);\n const parsed = parseActions(llmResponse.content);\n const proposedAction = parsed[0];\n\n if (!proposedAction) {\n logger.info(\"Planner returned no actionable tool call.\");\n break;\n }\n\n const policy = evaluateActionPolicy({\n action: proposedAction,\n step,\n timeline,\n mentions,\n executedActions: actions,\n });\n\n if (!policy.allowed) {\n const reason = policy.reason ?? \"Policy rejected action\";\n policyFeedback.push(reason);\n logger.info(`Policy rejected action ${proposedAction.action}: ${reason}`);\n continue;\n }\n\n const result = await executeAction(proposedAction);\n actions.push(proposedAction);\n results.push(result);\n\n if (proposedAction.action === \"skip\") {\n break;\n }\n }\n\n return {\n timeline,\n mentions,\n actions,\n results,\n policyFeedback,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-NPV3OV2K.js";
3
+ } from "./chunk-YMGJQRKG.js";
4
4
  import {
5
5
  loadConfig
6
6
  } from "./chunk-NO3NQN67.js";
@@ -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-GY6XSPK6.js");
70
+ const { getXClient } = await import("./x-client-YG7UCCNI.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-OACD3HGE.js.map
124
+ //# sourceMappingURL=chunk-MDOFAAZB.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-NPV3OV2K.js";
3
+ } from "./chunk-YMGJQRKG.js";
4
4
  import {
5
5
  loadConfig
6
6
  } from "./chunk-NO3NQN67.js";
@@ -13,7 +13,7 @@ async function getXClient() {
13
13
  if (config.xMethod !== "api") {
14
14
  throw new Error("Only X API mode is supported.");
15
15
  }
16
- const { XApiClient } = await import("./client-57BQKVYF.js");
16
+ const { XApiClient } = await import("./client-Z5UQWPPI.js");
17
17
  clientInstance = new XApiClient();
18
18
  logger.info("X client initialized: API mode");
19
19
  return clientInstance;
@@ -26,4 +26,4 @@ export {
26
26
  getXClient,
27
27
  resetXClient
28
28
  };
29
- //# sourceMappingURL=chunk-E5NR6HT4.js.map
29
+ //# sourceMappingURL=chunk-PN5A6MCV.js.map
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-WN35MRMF.js";
7
7
  import {
8
8
  rateLimiter
9
- } from "./chunk-LXQNVVIY.js";
9
+ } from "./chunk-4LNMA56H.js";
10
10
  import {
11
11
  loadIdentity,
12
12
  renderIdentityDocument
@@ -156,53 +156,103 @@ function buildSystemPrompt() {
156
156
  sections.push("3. Be selective \u2014 your goals should guide every action.");
157
157
  sections.push("4. Respect your credit budget \u2014 check remaining credits before posting.");
158
158
  sections.push("5. Don't repeat yourself \u2014 vary your content and avoid posting the same thing.");
159
+ sections.push("6. Tweet like a real person \u2014 be conversational, opinionated, and curious. NEVER write dry, explanatory, or educational-sounding tweets.");
160
+ sections.push("7. Prioritize engagement (replies, likes, conversation) over broadcasting original posts.");
159
161
  if (identity.boundaries.length > 0) {
160
- sections.push(`6. Respect your boundaries: ${identity.boundaries.join(", ")}`);
162
+ sections.push(`8. Respect your boundaries: ${identity.boundaries.join(", ")}`);
161
163
  }
162
164
  return sections.join("\n");
163
165
  }
164
- function buildHeartbeatUserMessage(timeline, mentions) {
166
+ function buildHeartbeatUserMessage(research) {
165
167
  const parts = [];
166
- parts.push("It's time for your heartbeat cycle. Here's what's happening on your timeline:");
168
+ parts.push("It's time for your heartbeat cycle. Here's what you found while scanning:");
167
169
  parts.push("");
168
- if (mentions.length > 0) {
170
+ if (research.mentions.length > 0) {
169
171
  parts.push("## Mentions (people talking to/about you)");
170
- for (const t of mentions.slice(0, 10)) {
172
+ for (const t of research.mentions.slice(0, 10)) {
171
173
  parts.push(`- @${t.authorHandle}: "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes)`);
172
174
  }
173
175
  parts.push("");
174
176
  }
175
- if (timeline.length > 0) {
176
- parts.push("## Timeline (recent posts from your feed)");
177
- for (const t of timeline.slice(0, 20)) {
177
+ if (research.timeline.length > 0) {
178
+ parts.push("## Timeline (your feed)");
179
+ for (const t of research.timeline.slice(0, 15)) {
178
180
  parts.push(`- @${t.authorHandle}: "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.retweetCount ?? 0} RTs)`);
179
181
  }
180
182
  parts.push("");
181
183
  }
184
+ if (research.topicSearchResults.length > 0) {
185
+ parts.push("## Topic Research (conversations in your interest areas)");
186
+ for (const result of research.topicSearchResults) {
187
+ parts.push(`### Search: "${result.query}"`);
188
+ for (const t of result.tweets.slice(0, 5)) {
189
+ parts.push(`- @${t.authorHandle}: "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.replyCount ?? 0} replies)`);
190
+ }
191
+ }
192
+ parts.push("");
193
+ }
194
+ if (research.peopleActivity.length > 0) {
195
+ parts.push("## People You're Watching (recent activity from key accounts)");
196
+ for (const person of research.peopleActivity) {
197
+ parts.push(`### @${person.handle} (${person.reason})`);
198
+ for (const t of person.tweets.slice(0, 3)) {
199
+ parts.push(`- "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.replyCount ?? 0} replies)`);
200
+ }
201
+ }
202
+ parts.push("");
203
+ }
204
+ if (research.ownPostPerformance.length > 0) {
205
+ parts.push("## How Your Recent Posts Are Doing");
206
+ for (const post of research.ownPostPerformance) {
207
+ const latest = post.metrics.length > 0 ? post.metrics[post.metrics.length - 1] : null;
208
+ const likes = latest?.likes ?? 0;
209
+ const rts = latest?.retweets ?? 0;
210
+ const replies = latest?.replies ?? 0;
211
+ parts.push(`- "${post.content.slice(0, 80)}..." \u2014 ${likes} likes, ${rts} RTs, ${replies} replies`);
212
+ }
213
+ parts.push("");
214
+ }
215
+ parts.push("## How to Act Like a Real Person on Twitter");
216
+ parts.push("");
217
+ parts.push("You are NOT a content bot. You are a person with opinions, curiosity, and relationships.");
218
+ parts.push("Think about what a real person with your personality would actually do when they open Twitter:");
219
+ parts.push("");
220
+ parts.push("**DO:**");
221
+ parts.push("- React to what you see \u2014 reply to interesting takes, ask questions, share hot takes");
222
+ parts.push("- Be conversational \u2014 write tweets like you're talking to friends, not giving a lecture");
223
+ parts.push("- Have opinions \u2014 agree, disagree, push back, get excited");
224
+ parts.push("- Ask genuine questions that spark conversation");
225
+ parts.push("- Use casual language, incomplete thoughts, humor, or surprise");
226
+ parts.push("- Like and retweet things that genuinely resonate with you");
227
+ parts.push("- Build relationships \u2014 reply to the same people, develop running conversations");
228
+ parts.push("");
229
+ parts.push("**DON'T:**");
230
+ parts.push(`- Write explanatory or educational posts ("Here's why X matters...")`);
231
+ parts.push('- Start tweets with "I think" or "This is interesting because"');
232
+ parts.push("- Write like a blog post or article \u2014 this is Twitter, keep it punchy");
233
+ parts.push("- Post generic observations nobody would engage with");
234
+ parts.push("- Ignore your timeline and just post into the void");
235
+ parts.push("");
236
+ parts.push("**Prioritize replying and engaging over original posts.** Real people spend more time reacting than broadcasting.");
237
+ parts.push("");
182
238
  parts.push("## Your Task");
183
- parts.push("Based on your identity, goals, and what you see above, decide what actions to take.");
184
- parts.push("You can take 1-3 actions. Choose from:");
239
+ parts.push("Choose 1-3 actions. Available:");
185
240
  parts.push("");
186
- parts.push("Available actions:");
187
- parts.push("- `post` \u2014 Write an original tweet (provide `content`, max 280 chars)");
188
- parts.push("- `reply` \u2014 Reply to a tweet (provide `tweetId` and `content`)");
189
- parts.push("- `like` \u2014 Like a tweet (provide `tweetId`)");
190
- parts.push("- `retweet` \u2014 Retweet (provide `tweetId`)");
191
- parts.push("- `follow` \u2014 Follow a user (provide `handle`)");
192
- parts.push("- `schedule` \u2014 Queue a post for later (provide `content`)");
193
- parts.push("- `learn` \u2014 Record a learning/observation (provide `content` and optional `tags`)");
194
- parts.push("- `reflect` \u2014 Add a journal entry about your growth (provide `content`)");
195
- parts.push("- `skip` \u2014 Do nothing this heartbeat (provide `reason`)");
241
+ parts.push("- `post` \u2014 Original tweet (`content`, max 280 chars)");
242
+ parts.push("- `reply` \u2014 Reply to a tweet (`tweetId` + `content`)");
243
+ parts.push("- `like` \u2014 Like a tweet (`tweetId`)");
244
+ parts.push("- `retweet` \u2014 Retweet (`tweetId`)");
245
+ parts.push("- `follow` \u2014 Follow a user (`handle`)");
246
+ parts.push("- `schedule` \u2014 Queue for later (`content`)");
247
+ parts.push("- `skip` \u2014 Do nothing (`reason`)");
196
248
  parts.push("");
197
- parts.push("Respond with a JSON array of actions:");
249
+ parts.push("Respond with a JSON array:");
198
250
  parts.push("```json");
199
251
  parts.push("[");
200
- parts.push(' { "action": "post", "content": "your tweet here", "reasoning": "why" },');
201
- parts.push(' { "action": "like", "tweetId": "123", "reasoning": "why" }');
252
+ parts.push(' { "action": "reply", "tweetId": "123", "content": "wait this is actually wild", "reasoning": "reacting to interesting take" },');
253
+ parts.push(' { "action": "like", "tweetId": "456", "reasoning": "good thread worth supporting" }');
202
254
  parts.push("]");
203
255
  parts.push("```");
204
- parts.push("");
205
- parts.push("Think carefully about what serves your goals. Be authentic to your personality.");
206
256
  return parts.join("\n");
207
257
  }
208
258
  function buildToolDecisionMessage(input) {
@@ -216,6 +266,7 @@ function buildToolDecisionMessage(input) {
216
266
  parts.push("2. Prefer context-aware replies/likes/follows when relevant.");
217
267
  parts.push("3. Avoid repetitive templates, slogans, and lecture-like formats.");
218
268
  parts.push("4. Ask questions sometimes. Curiosity beats certainty.");
269
+ parts.push("5. Never reuse the same wording across replies; tailor each reply to the specific tweet.");
219
270
  parts.push("");
220
271
  if (policyFeedback.length > 0) {
221
272
  parts.push("Policy feedback from previous attempts:");
@@ -410,12 +461,70 @@ function buildTrainingChatPrompt() {
410
461
  sections.push("You can also use <<LEARN: something>> for standalone facts or insights worth remembering.");
411
462
  return sections.join("\n");
412
463
  }
464
+ function buildReflectionPrompt(actionResults) {
465
+ const identity = loadIdentity();
466
+ const parts = [];
467
+ parts.push(`You are ${identity.name} (@${identity.handle}). Time to reflect.`);
468
+ parts.push("");
469
+ parts.push("## Your Goals");
470
+ for (const goal of identity.goals) {
471
+ parts.push(`- ${goal}`);
472
+ }
473
+ parts.push("");
474
+ if (actionResults.length > 0) {
475
+ parts.push("## This Heartbeat");
476
+ for (const r of actionResults) {
477
+ if (r.success) {
478
+ parts.push(`- \u2713 ${r.action}${r.detail ? `: ${r.detail}` : ""}`);
479
+ } else {
480
+ parts.push(`- \u2717 ${r.action} failed: ${r.error}`);
481
+ }
482
+ }
483
+ parts.push("");
484
+ }
485
+ const strategyText = renderStrategyForPrompt();
486
+ if (strategyText) {
487
+ parts.push("## Current Strategy");
488
+ parts.push(strategyText);
489
+ parts.push("");
490
+ }
491
+ const perfSummary = getPerformanceSummary();
492
+ if (perfSummary) {
493
+ parts.push("## Performance Context");
494
+ parts.push(perfSummary);
495
+ parts.push("");
496
+ }
497
+ const learnings = loadLearnings();
498
+ if (learnings.learnings.length > 0) {
499
+ parts.push("## Previous Learnings");
500
+ for (const l of learnings.learnings.slice(-5)) {
501
+ parts.push(`- ${l.content}`);
502
+ }
503
+ parts.push("");
504
+ }
505
+ parts.push("## Your Task");
506
+ parts.push("Reflect on how you're progressing toward your GOALS. Consider:");
507
+ parts.push("- Are your recent actions moving you toward your goals?");
508
+ parts.push("- Are you staying true to who you are while growing?");
509
+ parts.push("- What should you try differently or double down on?");
510
+ parts.push("- Engagement metrics are one signal, but your goals matter more.");
511
+ parts.push("");
512
+ parts.push("Respond with JSON:");
513
+ parts.push("```json");
514
+ parts.push("{");
515
+ parts.push(' "learning": "one insight about your progress toward your goals (or null)",');
516
+ parts.push(' "strategyUpdate": "one specific thing to try or change (or null)"');
517
+ parts.push("}");
518
+ parts.push("```");
519
+ return parts.join("\n");
520
+ }
413
521
 
414
522
  export {
415
523
  buildSystemPrompt,
416
524
  buildHeartbeatUserMessage,
417
525
  buildToolDecisionMessage,
418
526
  buildChatPrompt,
419
- buildTrainingChatPrompt
527
+ buildTrainingChatPrompt,
528
+ buildReflectionPrompt
420
529
  };
421
- //# sourceMappingURL=chunk-AOQ3WLZV.js.map
530
+ //# sourceMappingURL=chunk-Q3YXJ2C6.js.map