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
@@ -0,0 +1,431 @@
1
+ import {
2
+ loadIdentity
3
+ } from "./chunk-IULO3GRE.js";
4
+ import {
5
+ paths
6
+ } from "./chunk-ZWKTKWS6.js";
7
+
8
+ // src/memory/strategy.ts
9
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
10
+
11
+ // src/memory/intents.ts
12
+ import { existsSync, readFileSync, writeFileSync } from "fs";
13
+ function defaultStore() {
14
+ return { intents: [] };
15
+ }
16
+ function normalizeHandle(handle) {
17
+ return handle.replace(/^@/, "").trim().toLowerCase();
18
+ }
19
+ function normalizeTopic(topic) {
20
+ return topic.trim().toLowerCase();
21
+ }
22
+ function normalizeDirective(text) {
23
+ return text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
24
+ }
25
+ function clip(text, max) {
26
+ if (text.length <= max) return text;
27
+ return `${text.slice(0, max - 3)}...`;
28
+ }
29
+ function unique(items) {
30
+ return [...new Set(items)];
31
+ }
32
+ function loadStore() {
33
+ if (!existsSync(paths.intents)) return defaultStore();
34
+ try {
35
+ const parsed = JSON.parse(readFileSync(paths.intents, "utf-8"));
36
+ return { intents: Array.isArray(parsed.intents) ? parsed.intents : [] };
37
+ } catch {
38
+ return defaultStore();
39
+ }
40
+ }
41
+ function saveStore(store) {
42
+ const now = Date.now();
43
+ store.intents = store.intents.filter((intent) => {
44
+ if (intent.status === "active") return true;
45
+ const endedAt = Date.parse(intent.updatedAt);
46
+ if (Number.isNaN(endedAt)) return false;
47
+ return now - endedAt < 21 * 24 * 60 * 60 * 1e3;
48
+ }).slice(-200);
49
+ writeFileSync(paths.intents, JSON.stringify(store, null, 2));
50
+ }
51
+ function markExpired(intents, nowIso) {
52
+ const nowMs = Date.parse(nowIso);
53
+ for (const intent of intents) {
54
+ if (intent.status !== "active") continue;
55
+ const expiresMs = Date.parse(intent.expiresAt);
56
+ if (!Number.isNaN(expiresMs) && expiresMs < nowMs) {
57
+ intent.status = "expired";
58
+ intent.updatedAt = nowIso;
59
+ }
60
+ }
61
+ }
62
+ function shouldAutoComplete(intent) {
63
+ if (intent.status !== "active") return false;
64
+ if (intent.targetHandles.length > 0) return intent.successes >= 1;
65
+ if (intent.focusTopics.length > 0) return intent.successes >= 2;
66
+ return intent.successes >= 1;
67
+ }
68
+ function upsertIntentFromSignal(signal) {
69
+ const store = loadStore();
70
+ const now = (/* @__PURE__ */ new Date()).toISOString();
71
+ markExpired(store.intents, now);
72
+ const directive = signal.directive.trim();
73
+ if (!directive) {
74
+ throw new Error("Intent directive cannot be empty.");
75
+ }
76
+ const normalizedDirective = normalizeDirective(directive);
77
+ const handles = unique((signal.targetHandles ?? []).map(normalizeHandle).filter(Boolean));
78
+ const topics = unique((signal.focusTopics ?? []).map(normalizeTopic).filter(Boolean));
79
+ const source = signal.source ?? "reflection";
80
+ const shortTermGoal = signal.shortTermGoal?.trim();
81
+ const existing = store.intents.find(
82
+ (intent2) => intent2.status === "active" && normalizeDirective(intent2.directive) === normalizedDirective
83
+ );
84
+ if (existing) {
85
+ existing.updatedAt = now;
86
+ existing.targetHandles = unique([...existing.targetHandles, ...handles]);
87
+ existing.focusTopics = unique([...existing.focusTopics, ...topics]);
88
+ if (shortTermGoal) existing.shortTermGoal = shortTermGoal;
89
+ saveStore(store);
90
+ return existing;
91
+ }
92
+ const createdAtMs = Date.parse(now);
93
+ const expiresAt = Number.isNaN(createdAtMs) ? now : new Date(createdAtMs + 72 * 60 * 60 * 1e3).toISOString();
94
+ const intent = {
95
+ id: `intent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
96
+ createdAt: now,
97
+ updatedAt: now,
98
+ expiresAt,
99
+ source,
100
+ directive,
101
+ targetHandles: handles,
102
+ focusTopics: topics,
103
+ shortTermGoal,
104
+ status: "active",
105
+ attempts: 0,
106
+ successes: 0,
107
+ failures: 0,
108
+ notes: []
109
+ };
110
+ store.intents.push(intent);
111
+ saveStore(store);
112
+ return intent;
113
+ }
114
+ function listIntents(options) {
115
+ const store = loadStore();
116
+ const now = (/* @__PURE__ */ new Date()).toISOString();
117
+ markExpired(store.intents, now);
118
+ saveStore(store);
119
+ if (options?.includeInactive) return store.intents;
120
+ return store.intents.filter((intent) => intent.status === "active");
121
+ }
122
+ function recordIntentExecution(intentId, input) {
123
+ const store = loadStore();
124
+ const now = (/* @__PURE__ */ new Date()).toISOString();
125
+ markExpired(store.intents, now);
126
+ const intent = store.intents.find((item) => item.id === intentId);
127
+ if (!intent || intent.status !== "active") {
128
+ saveStore(store);
129
+ return null;
130
+ }
131
+ intent.updatedAt = now;
132
+ intent.lastAttemptAt = now;
133
+ intent.attempts += 1;
134
+ if (input.success) {
135
+ intent.successes += 1;
136
+ intent.lastSuccessAt = now;
137
+ } else {
138
+ intent.failures += 1;
139
+ }
140
+ if (input.note) {
141
+ intent.notes.push(input.note);
142
+ if (intent.notes.length > 20) {
143
+ intent.notes = intent.notes.slice(-20);
144
+ }
145
+ }
146
+ if (!input.keepActive && shouldAutoComplete(intent)) {
147
+ intent.status = "completed";
148
+ } else if (intent.failures >= 6 && intent.successes === 0) {
149
+ intent.status = "failed";
150
+ }
151
+ saveStore(store);
152
+ return intent;
153
+ }
154
+ function renderActiveIntentsForPrompt(limit = 4) {
155
+ const active = listIntents().slice(0, limit);
156
+ if (active.length === 0) return "";
157
+ const summarize = (intent) => {
158
+ if (intent.targetHandles.length > 0 && intent.focusTopics.length > 0) {
159
+ return `Engage ${intent.targetHandles.slice(0, 2).map((h) => `@${h}`).join(" & ")} on ${intent.focusTopics.slice(0, 2).join(" / ")}`;
160
+ }
161
+ if (intent.targetHandles.length > 0) {
162
+ return `Engage ${intent.targetHandles.slice(0, 2).map((h) => `@${h}`).join(" & ")} directly`;
163
+ }
164
+ if (intent.focusTopics.length > 0) {
165
+ return `Engage active conversations on ${intent.focusTopics.slice(0, 2).join(" / ")}`;
166
+ }
167
+ if (intent.shortTermGoal && intent.shortTermGoal.trim().length > 0) {
168
+ return clip(intent.shortTermGoal.trim(), 96);
169
+ }
170
+ return clip(intent.directive, 96);
171
+ };
172
+ const lines = ["**Active mission intents:**"];
173
+ for (const intent of active) {
174
+ const extras = [];
175
+ if (intent.targetHandles.length > 0) {
176
+ extras.push(`targets: ${intent.targetHandles.map((h) => `@${h}`).join(", ")}`);
177
+ }
178
+ if (intent.focusTopics.length > 0) {
179
+ extras.push(`topics: ${intent.focusTopics.join(", ")}`);
180
+ }
181
+ lines.push(`- ${summarize(intent)}${extras.length > 0 ? ` (${extras.join(" | ")})` : ""}`);
182
+ }
183
+ return lines.join("\n");
184
+ }
185
+
186
+ // src/memory/strategy.ts
187
+ function defaultStrategy() {
188
+ return {
189
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
190
+ currentFocus: [],
191
+ contentInsights: [],
192
+ peopleToEngage: [],
193
+ experiments: [],
194
+ shortTermGoals: [],
195
+ currentMood: ""
196
+ };
197
+ }
198
+ var TEXT_HANDLE_HINTS = [
199
+ { handle: "elonmusk", phrases: ["elon musk", "musk", "elon"] },
200
+ { handle: "zuck", phrases: ["mark zuckerberg", "zuckerberg", "zuck"] },
201
+ { handle: "sama", phrases: ["sam altman", "altman", "sam altman s"] },
202
+ { handle: "satyanadella", phrases: ["satya nadella", "satya"] },
203
+ { handle: "sundarpichai", phrases: ["sundar pichai", "sundar"] },
204
+ { handle: "darioamodei", phrases: ["dario amodei", "amodei"] }
205
+ ];
206
+ function normalizeHandle2(handle) {
207
+ return handle.replace(/^@/, "").trim().toLowerCase();
208
+ }
209
+ function unique2(items) {
210
+ return [...new Set(items)];
211
+ }
212
+ function clip2(text, max) {
213
+ if (text.length <= max) return text;
214
+ return `${text.slice(0, max - 3)}...`;
215
+ }
216
+ var GENERIC_ABSTRACT_FOCUS = /* @__PURE__ */ new Set([
217
+ "infrastructure",
218
+ "scale",
219
+ "future of technology",
220
+ "implementation gaps",
221
+ "autonomy"
222
+ ]);
223
+ function pruneFocusTopics(topics, handles) {
224
+ if (topics.length === 0) return topics;
225
+ const concrete = topics.filter((topic) => !GENERIC_ABSTRACT_FOCUS.has(topic));
226
+ if (concrete.length > 0) return concrete;
227
+ return handles.length > 0 ? topics.slice(0, 2) : [];
228
+ }
229
+ function deriveShortTermGoal(text, handles, focusTopics) {
230
+ if (handles.length > 0 && focusTopics.length > 0) {
231
+ return `engage @${handles[0]} around ${focusTopics[0]}`;
232
+ }
233
+ if (handles.length > 0) {
234
+ return `engage @${handles[0]} directly`;
235
+ }
236
+ if (focusTopics.length > 0) {
237
+ return `engage around ${focusTopics[0]}`;
238
+ }
239
+ const imperative = text.match(/\b(today|this week|next|within|immediate|ship|execute|reply|post|engage)\b[\s\S]{0,80}/i);
240
+ if (imperative && imperative[0]) {
241
+ return clip2(imperative[0].trim(), 72).toLowerCase();
242
+ }
243
+ return void 0;
244
+ }
245
+ function normalizeForMatching(text) {
246
+ return ` ${text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim()} `;
247
+ }
248
+ function containsPhrase(normalized, phrase) {
249
+ const needle = normalizeForMatching(phrase);
250
+ return normalized.includes(needle);
251
+ }
252
+ function extractHandlesFromText(text) {
253
+ const handles = /* @__PURE__ */ new Set();
254
+ const mentionMatches = [...text.matchAll(/@([a-zA-Z0-9_]{1,15})/g)];
255
+ for (const match of mentionMatches) {
256
+ const handle = normalizeHandle2(match[1]);
257
+ if (handle) handles.add(handle);
258
+ }
259
+ const normalized = normalizeForMatching(text);
260
+ for (const hint of TEXT_HANDLE_HINTS) {
261
+ for (const phrase of hint.phrases) {
262
+ if (containsPhrase(normalized, phrase)) {
263
+ handles.add(hint.handle);
264
+ break;
265
+ }
266
+ }
267
+ }
268
+ return [...handles];
269
+ }
270
+ function extractFocusTopicsFromText(text) {
271
+ const topics = /* @__PURE__ */ new Set();
272
+ const quoted = [...text.matchAll(/"([^"]{3,48})"/g)].map((match) => match[1].trim());
273
+ for (const q of quoted) {
274
+ if (q.length >= 3) topics.add(q.toLowerCase());
275
+ }
276
+ const keywordHints = [
277
+ "ai",
278
+ "alignment",
279
+ "ethics",
280
+ "safety",
281
+ "spacex",
282
+ "neuralink",
283
+ "robotaxi",
284
+ "autonomy",
285
+ "infrastructure",
286
+ "regulation",
287
+ "scale",
288
+ "future of technology",
289
+ "implementation gaps",
290
+ "transhumanism"
291
+ ];
292
+ const lower = text.toLowerCase();
293
+ for (const keyword of keywordHints) {
294
+ if (lower.includes(keyword)) topics.add(keyword);
295
+ }
296
+ return [...topics].slice(0, 8);
297
+ }
298
+ function deriveStrategyIntent(text) {
299
+ const handles = extractHandlesFromText(text);
300
+ const focusTopics = pruneFocusTopics(extractFocusTopicsFromText(text), handles);
301
+ const shortTermGoal = deriveShortTermGoal(text, handles, focusTopics);
302
+ return {
303
+ handles,
304
+ focusTopics,
305
+ shortTermGoal
306
+ };
307
+ }
308
+ function upsertPersonOfInterest(people, handle, reason, priority = "high") {
309
+ const clean = normalizeHandle2(handle);
310
+ if (!clean) return people;
311
+ const existingIndex = people.findIndex((p) => normalizeHandle2(p.handle) === clean);
312
+ if (existingIndex >= 0) {
313
+ const existing = people[existingIndex];
314
+ people[existingIndex] = {
315
+ ...existing,
316
+ handle: clean,
317
+ reason,
318
+ priority: existing.priority === "high" ? "high" : priority
319
+ };
320
+ return people;
321
+ }
322
+ return [...people, { handle: clean, reason, priority }];
323
+ }
324
+ function applyStrategyUpdate(strategy, strategyUpdate) {
325
+ const trimmed = strategyUpdate.trim();
326
+ if (!trimmed) return strategy;
327
+ let selfHandle = "";
328
+ try {
329
+ selfHandle = normalizeHandle2(loadIdentity().handle);
330
+ } catch {
331
+ }
332
+ strategy.experiments.push({
333
+ description: trimmed,
334
+ status: "pending"
335
+ });
336
+ const intent = deriveStrategyIntent(trimmed);
337
+ for (const handle of intent.handles) {
338
+ if (selfHandle && normalizeHandle2(handle) === selfHandle) continue;
339
+ strategy.peopleToEngage = upsertPersonOfInterest(
340
+ strategy.peopleToEngage,
341
+ handle,
342
+ `Directive: ${clip2(trimmed, 120)}`,
343
+ "high"
344
+ );
345
+ }
346
+ if (intent.focusTopics.length > 0) {
347
+ strategy.currentFocus = unique2([...strategy.currentFocus, ...intent.focusTopics]).slice(-12);
348
+ }
349
+ if (intent.shortTermGoal) {
350
+ strategy.shortTermGoals = unique2([intent.shortTermGoal, ...strategy.shortTermGoals]).slice(0, 6);
351
+ }
352
+ if (intent.handles.length > 0 || intent.focusTopics.length > 0 || intent.shortTermGoal) {
353
+ upsertIntentFromSignal({
354
+ directive: trimmed,
355
+ source: "reflection",
356
+ targetHandles: intent.handles,
357
+ focusTopics: intent.focusTopics,
358
+ shortTermGoal: intent.shortTermGoal
359
+ });
360
+ }
361
+ strategy.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
362
+ return strategy;
363
+ }
364
+ function loadStrategy() {
365
+ if (!existsSync2(paths.strategy)) {
366
+ return defaultStrategy();
367
+ }
368
+ try {
369
+ return { ...defaultStrategy(), ...JSON.parse(readFileSync2(paths.strategy, "utf-8")) };
370
+ } catch {
371
+ return defaultStrategy();
372
+ }
373
+ }
374
+ function saveStrategy(strategy) {
375
+ strategy.contentInsights = strategy.contentInsights.slice(-15);
376
+ strategy.peopleToEngage = strategy.peopleToEngage.slice(-20);
377
+ strategy.experiments = strategy.experiments.slice(-10);
378
+ strategy.shortTermGoals = strategy.shortTermGoals.slice(-5);
379
+ writeFileSync2(paths.strategy, JSON.stringify(strategy, null, 2));
380
+ }
381
+ function renderStrategyForPrompt() {
382
+ const s = loadStrategy();
383
+ const lines = [];
384
+ if (s.currentFocus.length > 0) {
385
+ lines.push(`**Focus areas:** ${s.currentFocus.join(", ")}`);
386
+ }
387
+ if (s.contentInsights.length > 0) {
388
+ lines.push("**What's working:**");
389
+ for (const i of s.contentInsights.slice(-5)) {
390
+ lines.push(`- ${i.insight} (${i.confidence} confidence)`);
391
+ }
392
+ }
393
+ if (s.peopleToEngage.length > 0) {
394
+ lines.push("**People to engage with:**");
395
+ for (const p of s.peopleToEngage.slice(-5)) {
396
+ lines.push(`- @${p.handle}: ${p.reason} (${p.priority} priority)`);
397
+ }
398
+ }
399
+ if (s.experiments.length > 0) {
400
+ const pending = s.experiments.filter((e) => e.status === "pending");
401
+ if (pending.length > 0) {
402
+ lines.push("**Experiments to try:**");
403
+ for (const e of pending.slice(-3)) {
404
+ lines.push(`- ${e.description}`);
405
+ }
406
+ }
407
+ }
408
+ if (s.shortTermGoals.length > 0) {
409
+ lines.push("**Short-term goals:**");
410
+ for (const g of s.shortTermGoals) {
411
+ lines.push(`- ${g}`);
412
+ }
413
+ }
414
+ if (s.currentMood) {
415
+ lines.push(`**Current energy:** ${s.currentMood}`);
416
+ }
417
+ return lines.length > 0 ? lines.join("\n") : "";
418
+ }
419
+
420
+ export {
421
+ listIntents,
422
+ recordIntentExecution,
423
+ renderActiveIntentsForPrompt,
424
+ extractHandlesFromText,
425
+ deriveStrategyIntent,
426
+ applyStrategyUpdate,
427
+ loadStrategy,
428
+ saveStrategy,
429
+ renderStrategyForPrompt
430
+ };
431
+ //# sourceMappingURL=chunk-OTZNHIXT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/memory/strategy.ts","../src/memory/intents.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { paths } from \"../utils/paths.js\";\nimport { loadIdentity } from \"../identity/index.js\";\nimport { upsertIntentFromSignal } from \"./intents.js\";\n\nexport interface ContentInsight {\n insight: string;\n confidence: \"low\" | \"medium\" | \"high\";\n}\n\nexport interface PersonOfInterest {\n handle: string;\n reason: string;\n priority: \"high\" | \"medium\" | \"low\";\n}\n\nexport interface Experiment {\n description: string;\n status: \"pending\" | \"tried\" | \"successful\" | \"failed\";\n result?: string;\n}\n\nexport interface Strategy {\n lastUpdated: string;\n currentFocus: string[];\n contentInsights: ContentInsight[];\n peopleToEngage: PersonOfInterest[];\n experiments: Experiment[];\n shortTermGoals: string[];\n currentMood: string;\n}\n\nexport interface StrategyIntent {\n handles: string[];\n focusTopics: string[];\n shortTermGoal?: string;\n}\n\nfunction defaultStrategy(): Strategy {\n return {\n lastUpdated: new Date().toISOString(),\n currentFocus: [],\n contentInsights: [],\n peopleToEngage: [],\n experiments: [],\n shortTermGoals: [],\n currentMood: \"\",\n };\n}\n\nconst TEXT_HANDLE_HINTS: Array<{ handle: string; phrases: string[] }> = [\n { handle: \"elonmusk\", phrases: [\"elon musk\", \"musk\", \"elon\"] },\n { handle: \"zuck\", phrases: [\"mark zuckerberg\", \"zuckerberg\", \"zuck\"] },\n { handle: \"sama\", phrases: [\"sam altman\", \"altman\", \"sam altman s\"] },\n { handle: \"satyanadella\", phrases: [\"satya nadella\", \"satya\"] },\n { handle: \"sundarpichai\", phrases: [\"sundar pichai\", \"sundar\"] },\n { handle: \"darioamodei\", phrases: [\"dario amodei\", \"amodei\"] },\n];\n\nfunction normalizeHandle(handle: string): string {\n return handle.replace(/^@/, \"\").trim().toLowerCase();\n}\n\nfunction unique<T>(items: T[]): T[] {\n return [...new Set(items)];\n}\n\nfunction clip(text: string, max: number): string {\n if (text.length <= max) return text;\n return `${text.slice(0, max - 3)}...`;\n}\n\nconst GENERIC_ABSTRACT_FOCUS = new Set([\n \"infrastructure\",\n \"scale\",\n \"future of technology\",\n \"implementation gaps\",\n \"autonomy\",\n]);\n\nfunction pruneFocusTopics(topics: string[], handles: string[]): string[] {\n if (topics.length === 0) return topics;\n const concrete = topics.filter((topic) => !GENERIC_ABSTRACT_FOCUS.has(topic));\n if (concrete.length > 0) return concrete;\n return handles.length > 0 ? topics.slice(0, 2) : [];\n}\n\nfunction deriveShortTermGoal(\n text: string,\n handles: string[],\n focusTopics: string[],\n): string | undefined {\n if (handles.length > 0 && focusTopics.length > 0) {\n return `engage @${handles[0]} around ${focusTopics[0]}`;\n }\n if (handles.length > 0) {\n return `engage @${handles[0]} directly`;\n }\n if (focusTopics.length > 0) {\n return `engage around ${focusTopics[0]}`;\n }\n\n const imperative = text.match(/\\b(today|this week|next|within|immediate|ship|execute|reply|post|engage)\\b[\\s\\S]{0,80}/i);\n if (imperative && imperative[0]) {\n return clip(imperative[0].trim(), 72).toLowerCase();\n }\n\n return undefined;\n}\n\nfunction normalizeForMatching(text: string): string {\n return ` ${text.toLowerCase().replace(/[^a-z0-9\\s]/g, \" \").replace(/\\s+/g, \" \").trim()} `;\n}\n\nfunction containsPhrase(normalized: string, phrase: string): boolean {\n const needle = normalizeForMatching(phrase);\n return normalized.includes(needle);\n}\n\nexport function extractHandlesFromText(text: string): string[] {\n const handles = new Set<string>();\n const mentionMatches = [...text.matchAll(/@([a-zA-Z0-9_]{1,15})/g)];\n for (const match of mentionMatches) {\n const handle = normalizeHandle(match[1]);\n if (handle) handles.add(handle);\n }\n\n const normalized = normalizeForMatching(text);\n for (const hint of TEXT_HANDLE_HINTS) {\n for (const phrase of hint.phrases) {\n if (containsPhrase(normalized, phrase)) {\n handles.add(hint.handle);\n break;\n }\n }\n }\n\n return [...handles];\n}\n\nfunction extractFocusTopicsFromText(text: string): string[] {\n const topics = new Set<string>();\n const quoted = [...text.matchAll(/\"([^\"]{3,48})\"/g)].map((match) => match[1].trim());\n for (const q of quoted) {\n if (q.length >= 3) topics.add(q.toLowerCase());\n }\n\n const keywordHints = [\n \"ai\",\n \"alignment\",\n \"ethics\",\n \"safety\",\n \"spacex\",\n \"neuralink\",\n \"robotaxi\",\n \"autonomy\",\n \"infrastructure\",\n \"regulation\",\n \"scale\",\n \"future of technology\",\n \"implementation gaps\",\n \"transhumanism\",\n ];\n const lower = text.toLowerCase();\n for (const keyword of keywordHints) {\n if (lower.includes(keyword)) topics.add(keyword);\n }\n\n return [...topics].slice(0, 8);\n}\n\nexport function deriveStrategyIntent(text: string): StrategyIntent {\n const handles = extractHandlesFromText(text);\n const focusTopics = pruneFocusTopics(extractFocusTopicsFromText(text), handles);\n const shortTermGoal = deriveShortTermGoal(text, handles, focusTopics);\n\n return {\n handles,\n focusTopics,\n shortTermGoal,\n };\n}\n\nfunction upsertPersonOfInterest(\n people: PersonOfInterest[],\n handle: string,\n reason: string,\n priority: PersonOfInterest[\"priority\"] = \"high\",\n): PersonOfInterest[] {\n const clean = normalizeHandle(handle);\n if (!clean) return people;\n\n const existingIndex = people.findIndex((p) => normalizeHandle(p.handle) === clean);\n if (existingIndex >= 0) {\n const existing = people[existingIndex];\n people[existingIndex] = {\n ...existing,\n handle: clean,\n reason,\n priority: existing.priority === \"high\" ? \"high\" : priority,\n };\n return people;\n }\n\n return [...people, { handle: clean, reason, priority }];\n}\n\nexport function applyStrategyUpdate(strategy: Strategy, strategyUpdate: string): Strategy {\n const trimmed = strategyUpdate.trim();\n if (!trimmed) return strategy;\n let selfHandle = \"\";\n try {\n selfHandle = normalizeHandle(loadIdentity().handle);\n } catch {\n // Identity may not exist yet in setup flows.\n }\n\n strategy.experiments.push({\n description: trimmed,\n status: \"pending\",\n });\n\n const intent = deriveStrategyIntent(trimmed);\n for (const handle of intent.handles) {\n if (selfHandle && normalizeHandle(handle) === selfHandle) continue;\n strategy.peopleToEngage = upsertPersonOfInterest(\n strategy.peopleToEngage,\n handle,\n `Directive: ${clip(trimmed, 120)}`,\n \"high\",\n );\n }\n\n if (intent.focusTopics.length > 0) {\n strategy.currentFocus = unique([...strategy.currentFocus, ...intent.focusTopics]).slice(-12);\n }\n\n if (intent.shortTermGoal) {\n strategy.shortTermGoals = unique([intent.shortTermGoal, ...strategy.shortTermGoals]).slice(0, 6);\n }\n\n if (intent.handles.length > 0 || intent.focusTopics.length > 0 || intent.shortTermGoal) {\n upsertIntentFromSignal({\n directive: trimmed,\n source: \"reflection\",\n targetHandles: intent.handles,\n focusTopics: intent.focusTopics,\n shortTermGoal: intent.shortTermGoal,\n });\n }\n\n strategy.lastUpdated = new Date().toISOString();\n return strategy;\n}\n\nexport function loadStrategy(): Strategy {\n if (!existsSync(paths.strategy)) {\n return defaultStrategy();\n }\n try {\n return { ...defaultStrategy(), ...JSON.parse(readFileSync(paths.strategy, \"utf-8\")) };\n } catch {\n return defaultStrategy();\n }\n}\n\nexport function saveStrategy(strategy: Strategy): void {\n // Keep arrays bounded to prevent bloat\n strategy.contentInsights = strategy.contentInsights.slice(-15);\n strategy.peopleToEngage = strategy.peopleToEngage.slice(-20);\n strategy.experiments = strategy.experiments.slice(-10);\n strategy.shortTermGoals = strategy.shortTermGoals.slice(-5);\n writeFileSync(paths.strategy, JSON.stringify(strategy, null, 2));\n}\n\nexport function renderStrategyForPrompt(): string {\n const s = loadStrategy();\n const lines: string[] = [];\n\n if (s.currentFocus.length > 0) {\n lines.push(`**Focus areas:** ${s.currentFocus.join(\", \")}`);\n }\n\n if (s.contentInsights.length > 0) {\n lines.push(\"**What's working:**\");\n for (const i of s.contentInsights.slice(-5)) {\n lines.push(`- ${i.insight} (${i.confidence} confidence)`);\n }\n }\n\n if (s.peopleToEngage.length > 0) {\n lines.push(\"**People to engage with:**\");\n for (const p of s.peopleToEngage.slice(-5)) {\n lines.push(`- @${p.handle}: ${p.reason} (${p.priority} priority)`);\n }\n }\n\n if (s.experiments.length > 0) {\n const pending = s.experiments.filter(e => e.status === \"pending\");\n if (pending.length > 0) {\n lines.push(\"**Experiments to try:**\");\n for (const e of pending.slice(-3)) {\n lines.push(`- ${e.description}`);\n }\n }\n }\n\n if (s.shortTermGoals.length > 0) {\n lines.push(\"**Short-term goals:**\");\n for (const g of s.shortTermGoals) {\n lines.push(`- ${g}`);\n }\n }\n\n if (s.currentMood) {\n lines.push(`**Current energy:** ${s.currentMood}`);\n }\n\n return lines.length > 0 ? lines.join(\"\\n\") : \"\";\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { paths } from \"../utils/paths.js\";\n\nexport type IntentStatus = \"active\" | \"completed\" | \"expired\" | \"failed\";\n\nexport interface Intent {\n id: string;\n createdAt: string;\n updatedAt: string;\n expiresAt: string;\n source: \"reflection\" | \"training\" | \"manual\";\n directive: string;\n targetHandles: string[];\n focusTopics: string[];\n shortTermGoal?: string;\n status: IntentStatus;\n attempts: number;\n successes: number;\n failures: number;\n lastAttemptAt?: string;\n lastSuccessAt?: string;\n notes: string[];\n}\n\ninterface IntentStore {\n intents: Intent[];\n}\n\nexport interface IntentSignal {\n directive: string;\n source?: Intent[\"source\"];\n targetHandles?: string[];\n focusTopics?: string[];\n shortTermGoal?: string;\n}\n\nfunction defaultStore(): IntentStore {\n return { intents: [] };\n}\n\nfunction normalizeHandle(handle: string): string {\n return handle.replace(/^@/, \"\").trim().toLowerCase();\n}\n\nfunction normalizeTopic(topic: string): string {\n return topic.trim().toLowerCase();\n}\n\nfunction normalizeDirective(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction clip(text: string, max: number): string {\n if (text.length <= max) return text;\n return `${text.slice(0, max - 3)}...`;\n}\n\nfunction unique<T>(items: T[]): T[] {\n return [...new Set(items)];\n}\n\nfunction loadStore(): IntentStore {\n if (!existsSync(paths.intents)) return defaultStore();\n try {\n const parsed = JSON.parse(readFileSync(paths.intents, \"utf-8\")) as IntentStore;\n return { intents: Array.isArray(parsed.intents) ? parsed.intents : [] };\n } catch {\n return defaultStore();\n }\n}\n\nfunction saveStore(store: IntentStore): void {\n const now = Date.now();\n store.intents = store.intents\n .filter((intent) => {\n if (intent.status === \"active\") return true;\n const endedAt = Date.parse(intent.updatedAt);\n if (Number.isNaN(endedAt)) return false;\n return now - endedAt < 21 * 24 * 60 * 60 * 1000;\n })\n .slice(-200);\n writeFileSync(paths.intents, JSON.stringify(store, null, 2));\n}\n\nfunction markExpired(intents: Intent[], nowIso: string): void {\n const nowMs = Date.parse(nowIso);\n for (const intent of intents) {\n if (intent.status !== \"active\") continue;\n const expiresMs = Date.parse(intent.expiresAt);\n if (!Number.isNaN(expiresMs) && expiresMs < nowMs) {\n intent.status = \"expired\";\n intent.updatedAt = nowIso;\n }\n }\n}\n\nfunction shouldAutoComplete(intent: Intent): boolean {\n if (intent.status !== \"active\") return false;\n if (intent.targetHandles.length > 0) return intent.successes >= 1;\n if (intent.focusTopics.length > 0) return intent.successes >= 2;\n return intent.successes >= 1;\n}\n\nexport function upsertIntentFromSignal(signal: IntentSignal): Intent {\n const store = loadStore();\n const now = new Date().toISOString();\n markExpired(store.intents, now);\n\n const directive = signal.directive.trim();\n if (!directive) {\n throw new Error(\"Intent directive cannot be empty.\");\n }\n\n const normalizedDirective = normalizeDirective(directive);\n const handles = unique((signal.targetHandles ?? []).map(normalizeHandle).filter(Boolean));\n const topics = unique((signal.focusTopics ?? []).map(normalizeTopic).filter(Boolean));\n const source = signal.source ?? \"reflection\";\n const shortTermGoal = signal.shortTermGoal?.trim();\n\n const existing = store.intents.find((intent) =>\n intent.status === \"active\" && normalizeDirective(intent.directive) === normalizedDirective\n );\n\n if (existing) {\n existing.updatedAt = now;\n existing.targetHandles = unique([...existing.targetHandles, ...handles]);\n existing.focusTopics = unique([...existing.focusTopics, ...topics]);\n if (shortTermGoal) existing.shortTermGoal = shortTermGoal;\n saveStore(store);\n return existing;\n }\n\n const createdAtMs = Date.parse(now);\n const expiresAt = Number.isNaN(createdAtMs)\n ? now\n : new Date(createdAtMs + 72 * 60 * 60 * 1000).toISOString();\n const intent: Intent = {\n id: `intent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n createdAt: now,\n updatedAt: now,\n expiresAt,\n source,\n directive,\n targetHandles: handles,\n focusTopics: topics,\n shortTermGoal,\n status: \"active\",\n attempts: 0,\n successes: 0,\n failures: 0,\n notes: [],\n };\n store.intents.push(intent);\n saveStore(store);\n return intent;\n}\n\nexport function listIntents(options?: { includeInactive?: boolean }): Intent[] {\n const store = loadStore();\n const now = new Date().toISOString();\n markExpired(store.intents, now);\n saveStore(store);\n if (options?.includeInactive) return store.intents;\n return store.intents.filter((intent) => intent.status === \"active\");\n}\n\nexport function recordIntentExecution(\n intentId: string,\n input: { success: boolean; note?: string; keepActive?: boolean },\n): Intent | null {\n const store = loadStore();\n const now = new Date().toISOString();\n markExpired(store.intents, now);\n const intent = store.intents.find((item) => item.id === intentId);\n if (!intent || intent.status !== \"active\") {\n saveStore(store);\n return null;\n }\n\n intent.updatedAt = now;\n intent.lastAttemptAt = now;\n intent.attempts += 1;\n if (input.success) {\n intent.successes += 1;\n intent.lastSuccessAt = now;\n } else {\n intent.failures += 1;\n }\n\n if (input.note) {\n intent.notes.push(input.note);\n if (intent.notes.length > 20) {\n intent.notes = intent.notes.slice(-20);\n }\n }\n\n if (!input.keepActive && shouldAutoComplete(intent)) {\n intent.status = \"completed\";\n } else if (intent.failures >= 6 && intent.successes === 0) {\n intent.status = \"failed\";\n }\n\n saveStore(store);\n return intent;\n}\n\nexport function renderActiveIntentsForPrompt(limit: number = 4): string {\n const active = listIntents().slice(0, limit);\n if (active.length === 0) return \"\";\n\n const summarize = (intent: Intent): string => {\n if (intent.targetHandles.length > 0 && intent.focusTopics.length > 0) {\n return `Engage ${intent.targetHandles.slice(0, 2).map((h) => `@${h}`).join(\" & \")} on ${intent.focusTopics.slice(0, 2).join(\" / \")}`;\n }\n if (intent.targetHandles.length > 0) {\n return `Engage ${intent.targetHandles.slice(0, 2).map((h) => `@${h}`).join(\" & \")} directly`;\n }\n if (intent.focusTopics.length > 0) {\n return `Engage active conversations on ${intent.focusTopics.slice(0, 2).join(\" / \")}`;\n }\n if (intent.shortTermGoal && intent.shortTermGoal.trim().length > 0) {\n return clip(intent.shortTermGoal.trim(), 96);\n }\n return clip(intent.directive, 96);\n };\n\n const lines: string[] = [\"**Active mission intents:**\"];\n for (const intent of active) {\n const extras: string[] = [];\n if (intent.targetHandles.length > 0) {\n extras.push(`targets: ${intent.targetHandles.map((h) => `@${h}`).join(\", \")}`);\n }\n if (intent.focusTopics.length > 0) {\n extras.push(`topics: ${intent.focusTopics.join(\", \")}`);\n }\n lines.push(`- ${summarize(intent)}${extras.length > 0 ? ` (${extras.join(\" | \")})` : \"\"}`);\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["existsSync","readFileSync","writeFileSync","intent","normalizeHandle","unique","clip","existsSync","readFileSync","writeFileSync"]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ensureDirectories,
3
3
  paths
4
- } from "./chunk-3RYCUGXE.js";
4
+ } from "./chunk-ZWKTKWS6.js";
5
5
 
6
6
  // src/utils/config.ts
7
7
  import { readFileSync, writeFileSync, existsSync } from "fs";
@@ -87,4 +87,4 @@ export {
87
87
  saveConfig,
88
88
  createDefaultConfig
89
89
  };
90
- //# sourceMappingURL=chunk-NO3NQN67.js.map
90
+ //# sourceMappingURL=chunk-QYFNAGNI.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ensureDirectories,
3
3
  paths
4
- } from "./chunk-3RYCUGXE.js";
4
+ } from "./chunk-ZWKTKWS6.js";
5
5
 
6
6
  // src/utils/logger.ts
7
7
  import { appendFileSync } from "fs";
@@ -55,4 +55,4 @@ export {
55
55
  setLogLevel,
56
56
  logger
57
57
  };
58
- //# sourceMappingURL=chunk-YMGJQRKG.js.map
58
+ //# sourceMappingURL=chunk-RSNEVBEI.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ensureDirectories,
3
3
  paths
4
- } from "./chunk-3RYCUGXE.js";
4
+ } from "./chunk-ZWKTKWS6.js";
5
5
 
6
6
  // src/utils/crypto.ts
7
7
  import { createCipheriv, createDecipheriv, randomBytes, createHash } from "crypto";
@@ -53,4 +53,4 @@ export {
53
53
  saveCredentials,
54
54
  loadCredentials
55
55
  };
56
- //# sourceMappingURL=chunk-QWEYVDLU.js.map
56
+ //# sourceMappingURL=chunk-SXNZVKLJ.js.map
@@ -1,16 +1,19 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-YMGJQRKG.js";
3
+ } from "./chunk-RSNEVBEI.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-NO3NQN67.js";
6
+ } from "./chunk-QYFNAGNI.js";
7
7
  import {
8
8
  ensureDirectories,
9
9
  paths
10
- } from "./chunk-3RYCUGXE.js";
10
+ } from "./chunk-ZWKTKWS6.js";
11
11
 
12
12
  // src/scheduler/queue.ts
13
13
  import { readFileSync, writeFileSync, existsSync } from "fs";
14
+ function normalizeQueueContent(content) {
15
+ return content.toLowerCase().replace(/https?:\/\/\S+/g, "").replace(/[@#]\w+/g, "").replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
16
+ }
14
17
  function loadQueue() {
15
18
  if (!existsSync(paths.pendingPosts)) {
16
19
  return { entries: [] };
@@ -50,6 +53,15 @@ function nextScheduledTime() {
50
53
  }
51
54
  function addToQueue(content, scheduledFor) {
52
55
  const queue = loadQueue();
56
+ const normalized = normalizeQueueContent(content);
57
+ const existingPending = queue.entries.find((entry2) => {
58
+ if (entry2.status !== "pending") return false;
59
+ return normalizeQueueContent(entry2.content) === normalized;
60
+ });
61
+ if (existingPending) {
62
+ logger.warn(`Queue dedupe: skipping duplicate pending post (${existingPending.id})`);
63
+ return existingPending;
64
+ }
53
65
  const entry = {
54
66
  id: `post-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
55
67
  content,
@@ -67,7 +79,7 @@ async function flushQueue() {
67
79
  const now = /* @__PURE__ */ new Date();
68
80
  let posted = 0;
69
81
  let failed = 0;
70
- const { getXClient } = await import("./x-client-YG7UCCNI.js");
82
+ const { getXClient } = await import("./x-client-S2LUVEKV.js");
71
83
  const client = await getXClient();
72
84
  for (const entry of queue.entries) {
73
85
  if (entry.status !== "pending") continue;
@@ -121,4 +133,4 @@ export {
121
133
  flushQueue,
122
134
  showQueue
123
135
  };
124
- //# sourceMappingURL=chunk-MDOFAAZB.js.map
136
+ //# sourceMappingURL=chunk-ZLSDFYBR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scheduler/queue.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { paths, ensureDirectories } from \"../utils/paths.js\";\nimport { loadConfig, saveConfig } from \"../utils/config.js\";\nimport { logger } from \"../utils/logger.js\";\n\nexport interface QueueEntry {\n id: string;\n content: string;\n scheduledFor: string;\n status: \"pending\" | \"posted\" | \"failed\" | \"expired\";\n createdAt: string;\n postedAt?: string;\n error?: string;\n}\n\ninterface QueueData {\n entries: QueueEntry[];\n}\n\nfunction normalizeQueueContent(content: string): string {\n return content\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 loadQueue(): QueueData {\n if (!existsSync(paths.pendingPosts)) {\n return { entries: [] };\n }\n return JSON.parse(readFileSync(paths.pendingPosts, \"utf-8\")) as QueueData;\n}\n\nfunction saveQueue(data: QueueData): void {\n ensureDirectories();\n writeFileSync(paths.pendingPosts, JSON.stringify(data, null, 2));\n}\n\nfunction nextScheduledTime(): string {\n const config = loadConfig();\n const now = new Date();\n const queue = loadQueue();\n\n // Find the latest scheduled time in the queue\n const pendingEntries = queue.entries.filter((e) => e.status === \"pending\");\n let lastScheduled = now;\n\n if (pendingEntries.length > 0) {\n const latest = new Date(\n pendingEntries.reduce((max, e) =>\n new Date(e.scheduledFor) > new Date(max.scheduledFor) ? e : max\n ).scheduledFor\n );\n if (latest > lastScheduled) lastScheduled = latest;\n }\n\n // Add a random interval within the active hours\n const intervalMinutes = Math.floor(\n ((config.schedule.activeHoursEnd - config.schedule.activeHoursStart) * 60) /\n config.schedule.postsPerDay\n );\n\n const next = new Date(lastScheduled.getTime() + intervalMinutes * 60 * 1000);\n\n // Clamp to active hours\n if (next.getHours() >= config.schedule.activeHoursEnd) {\n next.setDate(next.getDate() + 1);\n next.setHours(config.schedule.activeHoursStart, Math.floor(Math.random() * 60), 0, 0);\n }\n if (next.getHours() < config.schedule.activeHoursStart) {\n next.setHours(config.schedule.activeHoursStart, Math.floor(Math.random() * 60), 0, 0);\n }\n\n return next.toISOString();\n}\n\nexport function addToQueue(content: string, scheduledFor?: string): QueueEntry {\n const queue = loadQueue();\n const normalized = normalizeQueueContent(content);\n\n const existingPending = queue.entries.find((entry) => {\n if (entry.status !== \"pending\") return false;\n return normalizeQueueContent(entry.content) === normalized;\n });\n\n if (existingPending) {\n logger.warn(`Queue dedupe: skipping duplicate pending post (${existingPending.id})`);\n return existingPending;\n }\n\n const entry: QueueEntry = {\n id: `post-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,\n content,\n scheduledFor: scheduledFor ?? nextScheduledTime(),\n status: \"pending\",\n createdAt: new Date().toISOString(),\n };\n\n queue.entries.push(entry);\n saveQueue(queue);\n\n logger.info(`Post queued: ${entry.id} scheduled for ${entry.scheduledFor}`);\n return entry;\n}\n\nexport async function flushQueue(): Promise<{\n posted: number;\n failed: number;\n remaining: number;\n}> {\n const queue = loadQueue();\n const now = new Date();\n let posted = 0;\n let failed = 0;\n\n const { getXClient } = await import(\"../x-client/index.js\");\n const client = await getXClient();\n\n for (const entry of queue.entries) {\n if (entry.status !== \"pending\") continue;\n if (new Date(entry.scheduledFor) > now) continue;\n\n try {\n const result = await client.postTweet(entry.content);\n if (result.success) {\n entry.status = \"posted\";\n entry.postedAt = new Date().toISOString();\n posted++;\n logger.info(`Posted: ${entry.id}`);\n } else {\n entry.status = \"failed\";\n entry.error = result.error;\n failed++;\n logger.warn(`Failed to post: ${entry.id} - ${result.error}`);\n }\n } catch (error) {\n entry.status = \"failed\";\n entry.error = (error as Error).message;\n failed++;\n }\n\n // Small delay between posts to avoid rate limits\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n\n saveQueue(queue);\n\n const remaining = queue.entries.filter((e) => e.status === \"pending\").length;\n return { posted, failed, remaining };\n}\n\nexport function showQueue(): void {\n const queue = loadQueue();\n const pending = queue.entries.filter((e) => e.status === \"pending\");\n\n if (pending.length === 0) {\n console.log(\"Queue is empty.\");\n return;\n }\n\n console.log(`\\n${pending.length} posts queued:\\n`);\n for (const entry of pending.sort(\n (a, b) => new Date(a.scheduledFor).getTime() - new Date(b.scheduledFor).getTime()\n )) {\n const time = new Date(entry.scheduledFor).toLocaleString();\n const preview = entry.content.length > 60 ? entry.content.slice(0, 60) + \"...\" : entry.content;\n console.log(` [${time}] ${preview}`);\n }\n console.log();\n}\n"],"mappings":"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","names":["entry"]}
@@ -16,7 +16,10 @@ var paths = {
16
16
  interactions: join(SPORA_DIR, "memory", "interactions"),
17
17
  learnings: join(SPORA_DIR, "memory", "learnings.json"),
18
18
  relationships: join(SPORA_DIR, "memory", "relationships.json"),
19
+ agentNetwork: join(SPORA_DIR, "memory", "agent-network.json"),
19
20
  strategy: join(SPORA_DIR, "memory", "strategy.json"),
21
+ intents: join(SPORA_DIR, "memory", "intents.json"),
22
+ bandit: join(SPORA_DIR, "memory", "bandit.json"),
20
23
  goals: join(SPORA_DIR, "memory", "goals.json"),
21
24
  performance: join(SPORA_DIR, "memory", "performance.json"),
22
25
  compacted: join(SPORA_DIR, "memory", "compacted"),
@@ -60,4 +63,4 @@ export {
60
63
  sporaExists,
61
64
  hasXCredentials
62
65
  };
63
- //# sourceMappingURL=chunk-3RYCUGXE.js.map
66
+ //# sourceMappingURL=chunk-ZWKTKWS6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/paths.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { mkdirSync, existsSync } from \"node:fs\";\n\nconst SPORA_DIR = join(homedir(), \".spora\");\n\nexport const paths = {\n root: SPORA_DIR,\n config: join(SPORA_DIR, \"config.json\"),\n credentials: join(SPORA_DIR, \"credentials.json\"),\n spore: join(SPORA_DIR, \"spore.json\"),\n identity: join(SPORA_DIR, \"identity.json\"),\n colonyToken: join(SPORA_DIR, \"colony-token.json\"),\n browser: join(SPORA_DIR, \"browser\"),\n browserAuth: join(SPORA_DIR, \"browser\", \"auth-state.json\"),\n memory: join(SPORA_DIR, \"memory\"),\n interactions: join(SPORA_DIR, \"memory\", \"interactions\"),\n learnings: join(SPORA_DIR, \"memory\", \"learnings.json\"),\n relationships: join(SPORA_DIR, \"memory\", \"relationships.json\"),\n agentNetwork: join(SPORA_DIR, \"memory\", \"agent-network.json\"),\n strategy: join(SPORA_DIR, \"memory\", \"strategy.json\"),\n intents: join(SPORA_DIR, \"memory\", \"intents.json\"),\n bandit: join(SPORA_DIR, \"memory\", \"bandit.json\"),\n goals: join(SPORA_DIR, \"memory\", \"goals.json\"),\n performance: join(SPORA_DIR, \"memory\", \"performance.json\"),\n compacted: join(SPORA_DIR, \"memory\", \"compacted\"),\n queue: join(SPORA_DIR, \"queue\"),\n pendingPosts: join(SPORA_DIR, \"queue\", \"pending-posts.json\"),\n logs: join(SPORA_DIR, \"logs\"),\n logFile: join(SPORA_DIR, \"logs\", \"spora.log\"),\n runtimeMetrics: join(SPORA_DIR, \"logs\", \"runtime-metrics.jsonl\"),\n dataDir: SPORA_DIR,\n llmKey: join(SPORA_DIR, \"llm-key\"),\n llmKeys: join(SPORA_DIR, \"llm-keys.json\"),\n runtimePid: join(SPORA_DIR, \"runtime.pid\"),\n stopSignal: join(SPORA_DIR, \"stop\"),\n connectionToken: join(SPORA_DIR, \"connection-token\"),\n} as const;\n\nexport function ensureDirectories(): void {\n const dirs = [\n paths.root,\n paths.memory,\n paths.interactions,\n paths.compacted,\n paths.queue,\n paths.logs,\n ];\n\n for (const dir of dirs) {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n}\n\nexport function sporaExists(): boolean {\n return existsSync(paths.config) && existsSync(paths.identity);\n}\n\nexport function hasXCredentials(): boolean {\n return existsSync(paths.credentials);\n}\n"],"mappings":";AAAA,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,WAAW,kBAAkB;AAEtC,IAAM,YAAY,KAAK,QAAQ,GAAG,QAAQ;AAEnC,IAAM,QAAQ;AAAA,EACnB,MAAM;AAAA,EACN,QAAQ,KAAK,WAAW,aAAa;AAAA,EACrC,aAAa,KAAK,WAAW,kBAAkB;AAAA,EAC/C,OAAO,KAAK,WAAW,YAAY;AAAA,EACnC,UAAU,KAAK,WAAW,eAAe;AAAA,EACzC,aAAa,KAAK,WAAW,mBAAmB;AAAA,EAChD,SAAS,KAAK,WAAW,SAAS;AAAA,EAClC,aAAa,KAAK,WAAW,WAAW,iBAAiB;AAAA,EACzD,QAAQ,KAAK,WAAW,QAAQ;AAAA,EAChC,cAAc,KAAK,WAAW,UAAU,cAAc;AAAA,EACtD,WAAW,KAAK,WAAW,UAAU,gBAAgB;AAAA,EACrD,eAAe,KAAK,WAAW,UAAU,oBAAoB;AAAA,EAC7D,cAAc,KAAK,WAAW,UAAU,oBAAoB;AAAA,EAC5D,UAAU,KAAK,WAAW,UAAU,eAAe;AAAA,EACnD,SAAS,KAAK,WAAW,UAAU,cAAc;AAAA,EACjD,QAAQ,KAAK,WAAW,UAAU,aAAa;AAAA,EAC/C,OAAO,KAAK,WAAW,UAAU,YAAY;AAAA,EAC7C,aAAa,KAAK,WAAW,UAAU,kBAAkB;AAAA,EACzD,WAAW,KAAK,WAAW,UAAU,WAAW;AAAA,EAChD,OAAO,KAAK,WAAW,OAAO;AAAA,EAC9B,cAAc,KAAK,WAAW,SAAS,oBAAoB;AAAA,EAC3D,MAAM,KAAK,WAAW,MAAM;AAAA,EAC5B,SAAS,KAAK,WAAW,QAAQ,WAAW;AAAA,EAC5C,gBAAgB,KAAK,WAAW,QAAQ,uBAAuB;AAAA,EAC/D,SAAS;AAAA,EACT,QAAQ,KAAK,WAAW,SAAS;AAAA,EACjC,SAAS,KAAK,WAAW,eAAe;AAAA,EACxC,YAAY,KAAK,WAAW,aAAa;AAAA,EACzC,YAAY,KAAK,WAAW,MAAM;AAAA,EAClC,iBAAiB,KAAK,WAAW,kBAAkB;AACrD;AAEO,SAAS,oBAA0B;AACxC,QAAM,OAAO;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AAEA,aAAW,OAAO,MAAM;AACtB,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,gBAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,IACpC;AAAA,EACF;AACF;AAEO,SAAS,cAAuB;AACrC,SAAO,WAAW,MAAM,MAAM,KAAK,WAAW,MAAM,QAAQ;AAC9D;AAEO,SAAS,kBAA2B;AACzC,SAAO,WAAW,MAAM,WAAW;AACrC;","names":[]}