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.
- package/dist/autonomy-XUKCAZM3.js +19 -0
- package/dist/{chunk-LXQNVVIY.js → chunk-4LNMA56H.js} +2 -2
- package/dist/{chunk-WIK74GGJ.js → chunk-EU4FMOKG.js} +104 -22
- package/dist/chunk-EU4FMOKG.js.map +1 -0
- package/dist/{chunk-OACD3HGE.js → chunk-MDOFAAZB.js} +3 -3
- package/dist/{chunk-E5NR6HT4.js → chunk-PN5A6MCV.js} +3 -3
- package/dist/{chunk-AOQ3WLZV.js → chunk-Q3YXJ2C6.js} +137 -28
- package/dist/chunk-Q3YXJ2C6.js.map +1 -0
- package/dist/{chunk-VZBHRUZS.js → chunk-QWEYVDLU.js} +1 -1
- package/dist/chunk-QWEYVDLU.js.map +1 -0
- package/dist/{chunk-KWWAIS3C.js → chunk-SUZUJGGW.js} +2 -2
- package/dist/{chunk-NPV3OV2K.js → chunk-YMGJQRKG.js} +13 -2
- package/dist/chunk-YMGJQRKG.js.map +1 -0
- package/dist/cli.js +33 -33
- package/dist/{client-57BQKVYF.js → client-Z5UQWPPI.js} +125 -41
- package/dist/client-Z5UQWPPI.js.map +1 -0
- package/dist/{colony-JPZC3R34.js → colony-NNX45EAV.js} +3 -3
- package/dist/{crypto-NOXNL4GP.js → crypto-B65ZH7KN.js} +2 -2
- package/dist/{heartbeat-TNEPE3ZP.js → heartbeat-ZCCOIZGU.js} +57 -13
- package/dist/heartbeat-ZCCOIZGU.js.map +1 -0
- package/dist/{init-ISSXETHY.js → init-SEJPTOOB.js} +6 -6
- package/dist/{llm-T33QTPVW.js → llm-OGOYCWBH.js} +3 -3
- package/dist/mcp-server.js +20 -20
- package/dist/{prompt-builder-5NYONN2W.js → prompt-builder-KJKFCGM7.js} +6 -4
- package/dist/{queue-G5PTE6R6.js → queue-2ZBKDFX3.js} +3 -3
- package/dist/web-chat/chat.html +190 -3
- package/dist/{web-chat-3HM35XM4.js → web-chat-ZZ65DUID.js} +148 -11
- package/dist/web-chat-ZZ65DUID.js.map +1 -0
- package/dist/{x-client-GY6XSPK6.js → x-client-YG7UCCNI.js} +3 -3
- package/package.json +1 -1
- package/dist/autonomy-DAV7X6QS.js +0 -19
- package/dist/chunk-AOQ3WLZV.js.map +0 -1
- package/dist/chunk-NPV3OV2K.js.map +0 -1
- package/dist/chunk-VZBHRUZS.js.map +0 -1
- package/dist/chunk-WIK74GGJ.js.map +0 -1
- package/dist/client-57BQKVYF.js.map +0 -1
- package/dist/heartbeat-TNEPE3ZP.js.map +0 -1
- package/dist/web-chat-3HM35XM4.js.map +0 -1
- /package/dist/{autonomy-DAV7X6QS.js.map → autonomy-XUKCAZM3.js.map} +0 -0
- /package/dist/{chunk-LXQNVVIY.js.map → chunk-4LNMA56H.js.map} +0 -0
- /package/dist/{chunk-OACD3HGE.js.map → chunk-MDOFAAZB.js.map} +0 -0
- /package/dist/{chunk-E5NR6HT4.js.map → chunk-PN5A6MCV.js.map} +0 -0
- /package/dist/{chunk-KWWAIS3C.js.map → chunk-SUZUJGGW.js.map} +0 -0
- /package/dist/{colony-JPZC3R34.js.map → colony-NNX45EAV.js.map} +0 -0
- /package/dist/{crypto-NOXNL4GP.js.map → crypto-B65ZH7KN.js.map} +0 -0
- /package/dist/{init-ISSXETHY.js.map → init-SEJPTOOB.js.map} +0 -0
- /package/dist/{llm-T33QTPVW.js.map → llm-OGOYCWBH.js.map} +0 -0
- /package/dist/{prompt-builder-5NYONN2W.js.map → prompt-builder-KJKFCGM7.js.map} +0 -0
- /package/dist/{queue-G5PTE6R6.js.map → queue-2ZBKDFX3.js.map} +0 -0
- /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-
|
|
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-
|
|
57
|
+
//# sourceMappingURL=chunk-4LNMA56H.js.map
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getXClient
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PN5A6MCV.js";
|
|
4
4
|
import {
|
|
5
5
|
addToQueue
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-MDOFAAZB.js";
|
|
7
7
|
import {
|
|
8
8
|
buildSystemPrompt,
|
|
9
9
|
buildToolDecisionMessage
|
|
10
|
-
} from "./chunk-
|
|
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-
|
|
17
|
+
} from "./chunk-SUZUJGGW.js";
|
|
18
18
|
import {
|
|
19
19
|
logger
|
|
20
|
-
} from "./chunk-
|
|
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
|
|
29
|
-
if (
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
44
|
-
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
124
|
+
//# sourceMappingURL=chunk-MDOFAAZB.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
logger
|
|
3
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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(`
|
|
162
|
+
sections.push(`8. Respect your boundaries: ${identity.boundaries.join(", ")}`);
|
|
161
163
|
}
|
|
162
164
|
return sections.join("\n");
|
|
163
165
|
}
|
|
164
|
-
function buildHeartbeatUserMessage(
|
|
166
|
+
function buildHeartbeatUserMessage(research) {
|
|
165
167
|
const parts = [];
|
|
166
|
-
parts.push("It's time for your heartbeat cycle. Here's what
|
|
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 (
|
|
177
|
-
for (const t of timeline.slice(0,
|
|
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("
|
|
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("
|
|
187
|
-
parts.push("- `
|
|
188
|
-
parts.push("- `
|
|
189
|
-
parts.push("- `
|
|
190
|
-
parts.push("- `
|
|
191
|
-
parts.push("- `
|
|
192
|
-
parts.push("- `
|
|
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
|
|
249
|
+
parts.push("Respond with a JSON array:");
|
|
198
250
|
parts.push("```json");
|
|
199
251
|
parts.push("[");
|
|
200
|
-
parts.push(' { "action": "
|
|
201
|
-
parts.push(' { "action": "like", "tweetId": "
|
|
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-
|
|
530
|
+
//# sourceMappingURL=chunk-Q3YXJ2C6.js.map
|