titan-agent 6.1.0-beta.24 → 6.1.0-beta.26

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 (190) hide show
  1. package/dist/agent/selfKnowledge.js +251 -0
  2. package/dist/agent/selfKnowledge.js.map +1 -0
  3. package/dist/skills/builtin/fb_autopilot.js +71 -76
  4. package/dist/skills/builtin/fb_autopilot.js.map +1 -1
  5. package/dist/skills/builtin/fb_truth_verifier.js +78 -0
  6. package/dist/skills/builtin/fb_truth_verifier.js.map +1 -0
  7. package/dist/utils/constants.js +1 -1
  8. package/dist/utils/constants.js.map +1 -1
  9. package/package.json +1 -1
  10. package/ui/dist/assets/{AuditPanel-yprjgSxp.js → AuditPanel-ChC-b5SP.js} +2 -2
  11. package/ui/dist/assets/{AutonomyPanel-uYRbGtru.js → AutonomyPanel-BCpdrVFw.js} +2 -2
  12. package/ui/dist/assets/{AutopilotPanel-l6gj_LV6.js → AutopilotPanel-B1f_JRtX.js} +1 -1
  13. package/ui/dist/assets/{AutoresearchPanel-D73YFdTk.js → AutoresearchPanel-CITPL0LA.js} +2 -2
  14. package/ui/dist/assets/BackupPanel-nxH6FApT.js +1 -0
  15. package/ui/dist/assets/{BrowserPanel-B2PIVcyy.js → BrowserPanel-Ddr7mS-x.js} +2 -2
  16. package/ui/dist/assets/CPActivity-I69H8j4x.js +1 -0
  17. package/ui/dist/assets/{CPAgentDetail-BcnUbGXB.js → CPAgentDetail-CbERciDC.js} +1 -1
  18. package/ui/dist/assets/CPAgents-B7xsnxuH.js +1 -0
  19. package/ui/dist/assets/CPApprovals-CWRvAUmU.js +1 -0
  20. package/ui/dist/assets/CPCosts-CB6jsAZF.js +1 -0
  21. package/ui/dist/assets/CPDashboard-BrIapvI1.js +1 -0
  22. package/ui/dist/assets/CPFiles-B6sdKdD2.js +1 -0
  23. package/ui/dist/assets/CPGoals-ClIa_lSv.js +1 -0
  24. package/ui/dist/assets/{CPInbox-D5J3KoZH.js → CPInbox-DP34PyKr.js} +2 -2
  25. package/ui/dist/assets/CPIssueDetail-B5XDvnNo.js +1 -0
  26. package/ui/dist/assets/CPIssues-mHcOqMxR.js +1 -0
  27. package/ui/dist/assets/CPLayout-COXF33Nc.js +2 -0
  28. package/ui/dist/assets/CPOrg-CrAXpcQv.js +8 -0
  29. package/ui/dist/assets/CPRuns-qhCxjMyH.js +1 -0
  30. package/ui/dist/assets/{CPSocial-Wjo7NdGw.js → CPSocial-Z_tlMxcf.js} +3 -3
  31. package/ui/dist/assets/CPVoice-KkHWnkzf.js +1 -0
  32. package/ui/dist/assets/ChannelsPanel-POcyuSof.js +1 -0
  33. package/ui/dist/assets/{CheckpointsPanel-BlemWfzE.js → CheckpointsPanel-V-9rpSua.js} +1 -1
  34. package/ui/dist/assets/CommandPostHub-P_tM-kjr.js +24 -0
  35. package/ui/dist/assets/{CronPanel-DITlv6pY.js → CronPanel-Cggo1M4b.js} +1 -1
  36. package/ui/dist/assets/{DataTable-D_Ha_hg9.js → DataTable-BqS0-Q13.js} +1 -1
  37. package/ui/dist/assets/{DreamPanel-C_vsucNi.js → DreamPanel-DBQ3PGPd.js} +4 -4
  38. package/ui/dist/assets/{EmptyState-B34O2R_p.js → EmptyState-M-gXHyMZ.js} +1 -1
  39. package/ui/dist/assets/{EvalHarnessPanel-CuhSE-zw.js → EvalHarnessPanel-IoTrhlAP.js} +2 -2
  40. package/ui/dist/assets/{EvalPanel-C3pPERMD.js → EvalPanel-CQq3ZXY_.js} +1 -1
  41. package/ui/dist/assets/{FileViewer-DWVXThSD.js → FileViewer-hBoZ1-0b.js} +1 -1
  42. package/ui/dist/assets/{FilesPanel-DiApOFhp.js → FilesPanel-CKd-Aypz.js} +2 -2
  43. package/ui/dist/assets/FleetPanel-DuOqhHhL.js +1 -0
  44. package/ui/dist/assets/{HomelabPanel-XjsdErR3.js → HomelabPanel-B3sehYqI.js} +2 -2
  45. package/ui/dist/assets/InfraView-CBPMt7Qu.js +2 -0
  46. package/ui/dist/assets/InlineEditableField-Cl8erGUX.js +1 -0
  47. package/ui/dist/assets/{Input-CynT1mu1.js → Input-DVOYwUp6.js} +1 -1
  48. package/ui/dist/assets/IntegrationsPanel-D3cD2B9D.js +1 -0
  49. package/ui/dist/assets/IntelligenceView-CP8EjWJa.js +2 -0
  50. package/ui/dist/assets/LearningPanel-DuR-b7JN.js +1 -0
  51. package/ui/dist/assets/{LogsPanel-Dcsum5Sf.js → LogsPanel-fjkC4y9g.js} +1 -1
  52. package/ui/dist/assets/McpPanel-CmTNwu1v.js +1 -0
  53. package/ui/dist/assets/{MemoryGraphPanel-BY2e6mfM.js → MemoryGraphPanel-BHvlmv6a.js} +1 -1
  54. package/ui/dist/assets/MemoryWikiPanel-BHlIOWgk.js +11 -0
  55. package/ui/dist/assets/{MeshPanel-BqoCNPoB.js → MeshPanel-CDOMhBSw.js} +1 -1
  56. package/ui/dist/assets/{MissionCanvas-DsRJk58m.js → MissionCanvas-D6Ksusvp.js} +3 -3
  57. package/ui/dist/assets/{MissionChat-ZnSSl8gc.js → MissionChat-C8tgZioW.js} +1 -1
  58. package/ui/dist/assets/{MissionLibrary-C1g-xMaj.js → MissionLibrary-zoNNuFfq.js} +1 -1
  59. package/ui/dist/assets/{MissionStart-D687lcOE.js → MissionStart-Cl-TNUwM.js} +1 -1
  60. package/ui/dist/assets/{Modal-DggltTtC.js → Modal-CrI6woDZ.js} +1 -1
  61. package/ui/dist/assets/{NvidiaPanel-DGKOH_5d.js → NvidiaPanel-DFFVLMcX.js} +1 -1
  62. package/ui/dist/assets/{OrganismPanel-DOfBCLU1.js → OrganismPanel-AZreSHGM.js} +1 -1
  63. package/ui/dist/assets/OverviewPanel-BPZqZt7W.js +1 -0
  64. package/ui/dist/assets/{PageHeader-C0rlQyU7.js → PageHeader-D0zki6c2.js} +1 -1
  65. package/ui/dist/assets/{PersonaProfilesPanel-BGxPAg-2.js → PersonaProfilesPanel-D6oFDKe0.js} +3 -3
  66. package/ui/dist/assets/{PersonasPanel-DXRE6koM.js → PersonasPanel-XsR9J5QK.js} +1 -1
  67. package/ui/dist/assets/RecipesPanel-DIzaTWEr.js +1 -0
  68. package/ui/dist/assets/{SecurityPanel-CAxyqD4i.js → SecurityPanel-ir9MHcLT.js} +1 -1
  69. package/ui/dist/assets/SelfImprovePanel-qYPMv78V.js +1 -0
  70. package/ui/dist/assets/{SelfProposalsPanel-BmPbk1ID.js → SelfProposalsPanel-CH1Y2atb.js} +2 -2
  71. package/ui/dist/assets/SessionsPanel-CCdc_C6t.js +1 -0
  72. package/ui/dist/assets/{SessionsTab-BiCfOITs.js → SessionsTab-C7PUd5SE.js} +1 -1
  73. package/ui/dist/assets/SettingsPanel-DfcDQBQt.js +1 -0
  74. package/ui/dist/assets/SettingsView-CII7Yww0.js +2 -0
  75. package/ui/dist/assets/{SkeletonLoader-D5oEHJJo.js → SkeletonLoader-sSfYo-Fz.js} +1 -1
  76. package/ui/dist/assets/{SkillsPanel-Ba069IpL.js → SkillsPanel-Tmqrdycy.js} +1 -1
  77. package/ui/dist/assets/{SomaView-UgcNHPOe.js → SomaView-BC9JpyMl.js} +1 -1
  78. package/ui/dist/assets/{StatCard-1ezaANF5.js → StatCard-SVWYzHIM.js} +1 -1
  79. package/ui/dist/assets/{StatusBadge-BBJ5EQaq.js → StatusBadge-BB7Tx0KG.js} +1 -1
  80. package/ui/dist/assets/{Tabs-B73fv4WC.js → Tabs-Dq9rfqV_.js} +1 -1
  81. package/ui/dist/assets/TeamsPanel-BwcZrYac.js +1 -0
  82. package/ui/dist/assets/TelemetryPanel-O4pGzY8E.js +1 -0
  83. package/ui/dist/assets/{TitanCanvas-CSUiusqg.js → TitanCanvas-CLwx89l2.js} +26 -81
  84. package/ui/dist/assets/ToolsView-Cxou2lHO.js +2 -0
  85. package/ui/dist/assets/{Tooltip-B6gn3X-e.js → Tooltip-D7oIArbx.js} +1 -1
  86. package/ui/dist/assets/TraceViewer-B7wBtNlu.js +1 -0
  87. package/ui/dist/assets/TrainingPanel-B_w0hutr.js +1 -0
  88. package/ui/dist/assets/{VoiceOverlay-BUmUQnVH.js → VoiceOverlay-BRfR3aax.js} +5 -15
  89. package/ui/dist/assets/{VramPanel-B6bz7SXa.js → VramPanel-CnHayj0l.js} +1 -1
  90. package/ui/dist/assets/{WatchView-TlR65a9N.js → WatchView-C5JexInX.js} +1 -1
  91. package/ui/dist/assets/WorkTab-DBdmjPyu.js +1 -0
  92. package/ui/dist/assets/{WorkflowsPanel-DFASgmHr.js → WorkflowsPanel-B20_l9zB.js} +3 -3
  93. package/ui/dist/assets/archive-b4MAXaqT.js +6 -0
  94. package/ui/dist/assets/{arrow-left-BYsiBeLD.js → arrow-left-CVNtxSv2.js} +1 -1
  95. package/ui/dist/assets/book-open-C01Cuvcr.js +6 -0
  96. package/ui/dist/assets/bot-B9_o_8Ao.js +6 -0
  97. package/ui/dist/assets/{chart-column-CS2cfm11.js → chart-column-GazTh38a.js} +1 -1
  98. package/ui/dist/assets/{check-B3cAKmN5.js → check-BLyCNJ5r.js} +1 -1
  99. package/ui/dist/assets/{chevron-right-BK_DfS2Z.js → chevron-right-C0fqPIeZ.js} +1 -1
  100. package/ui/dist/assets/{chevron-up-nvVN6RG5.js → chevron-up-Cuolucpe.js} +1 -1
  101. package/ui/dist/assets/{circle-check-big-CFOqr4iL.js → circle-check-big-Cu9C88pX.js} +1 -1
  102. package/ui/dist/assets/circle-stop-DDi4g3Z8.js +6 -0
  103. package/ui/dist/assets/{clock-rukpY_Oe.js → clock-3qGOTqaY.js} +1 -1
  104. package/ui/dist/assets/{dollar-sign-H1r95Esh.js → dollar-sign-Ceuo_See.js} +1 -1
  105. package/ui/dist/assets/{download-Ck8ONCke.js → download-zjVa__-G.js} +1 -1
  106. package/ui/dist/assets/{external-link-DqIplRhE.js → external-link-BGcox8dn.js} +1 -1
  107. package/ui/dist/assets/file-text-D5QL_N94.js +6 -0
  108. package/ui/dist/assets/flask-conical-y6Cy-6i3.js +6 -0
  109. package/ui/dist/assets/folder-4EFCpxV5.js +6 -0
  110. package/ui/dist/assets/{funnel-B_49OMiv.js → funnel-aaBOyHMd.js} +1 -1
  111. package/ui/dist/assets/{git-branch-BXbtPQU4.js → git-branch-BAviLfpT.js} +1 -1
  112. package/ui/dist/assets/index-BXK3Cqa2.js +322 -0
  113. package/ui/dist/assets/index-CoDvqC1m.css +1 -0
  114. package/ui/dist/assets/{layers-BF8CtoM5.js → layers-D5vIJhja.js} +1 -1
  115. package/ui/dist/assets/{legacy-JlbzNxjn.js → legacy-cJmQVDHK.js} +1 -1
  116. package/ui/dist/assets/{lightbulb-AaSBYG59.js → lightbulb-hxfajwJk.js} +1 -1
  117. package/ui/dist/assets/{list-todo-6HDCogwb.js → list-todo-B7VBfcd1.js} +1 -1
  118. package/ui/dist/assets/message-square-CQ3hYQbr.js +6 -0
  119. package/ui/dist/assets/mic-DTNzPnWH.js +6 -0
  120. package/ui/dist/assets/missions-CU4zDXVs.js +1 -0
  121. package/ui/dist/assets/{network-DbUTcR4O.js → network-CIXjczic.js} +1 -1
  122. package/ui/dist/assets/{pause-CChMZN2N.js → pause-CeiFt1tR.js} +1 -1
  123. package/ui/dist/assets/pencil-YPspIW8F.js +6 -0
  124. package/ui/dist/assets/{play-5lX0LsRr.js → play-DScs5qIy.js} +1 -1
  125. package/ui/dist/assets/{plug-BHc9XYgx.js → plug-C4ZWarwy.js} +1 -1
  126. package/ui/dist/assets/{plus-cRBC7ztV.js → plus-BceO4k1r.js} +1 -1
  127. package/ui/dist/assets/{proxy-BcAEe4-D.js → proxy-e5cYG9rZ.js} +1 -1
  128. package/ui/dist/assets/radio-B1lATllG.js +6 -0
  129. package/ui/dist/assets/{refresh-cw-fzP9o9Rx.js → refresh-cw-DIU4V0y3.js} +1 -1
  130. package/ui/dist/assets/{rotate-ccw-BFfyhtVm.js → rotate-ccw-7YrkxuUf.js} +1 -1
  131. package/ui/dist/assets/{save-3aooT5O1.js → save-BGrQ7Adz.js} +1 -1
  132. package/ui/dist/assets/{search-Df41RABJ.js → search-BJbRC2iK.js} +1 -1
  133. package/ui/dist/assets/{send-DCeeK62O.js → send-kVQXhRa0.js} +1 -1
  134. package/ui/dist/assets/server-_EOC2O2L.js +6 -0
  135. package/ui/dist/assets/{shield-check-BZQR9b0x.js → shield-check-Bhw8FUp0.js} +1 -1
  136. package/ui/dist/assets/terminal-Ba-zvC4N.js +6 -0
  137. package/ui/dist/assets/test-tube-DqCmCNDF.js +11 -0
  138. package/ui/dist/assets/{toggle-right-CNPtGqxb.js → toggle-right-BGgdvl64.js} +1 -1
  139. package/ui/dist/assets/{trash-2-nNLkUnFl.js → trash-2-DE2tk1lo.js} +1 -1
  140. package/ui/dist/assets/{trending-up-CZ57ONJ5.js → trending-up-C9lsLDad.js} +1 -1
  141. package/ui/dist/assets/{trophy-CE89gMmf.js → trophy-CMl1BT1j.js} +1 -1
  142. package/ui/dist/assets/volume-2-CGbWkkQ0.js +6 -0
  143. package/ui/dist/assets/wifi-off-CR1M6yEY.js +6 -0
  144. package/ui/dist/index.html +2 -2
  145. package/ui/dist/sw.js +1 -1
  146. package/ui/dist/assets/BackupPanel-C399wlfl.js +0 -1
  147. package/ui/dist/assets/CPActivity-CxIwdh5A.js +0 -1
  148. package/ui/dist/assets/CPAgents-CRNNvX7P.js +0 -1
  149. package/ui/dist/assets/CPApprovals-n-EwBTzt.js +0 -1
  150. package/ui/dist/assets/CPCosts-vKyv2IgA.js +0 -1
  151. package/ui/dist/assets/CPDashboard-DfrJDXIH.js +0 -1
  152. package/ui/dist/assets/CPFiles-DR7H1JKJ.js +0 -1
  153. package/ui/dist/assets/CPGoals-CXJvqD7-.js +0 -1
  154. package/ui/dist/assets/CPIssueDetail-Ceps7yHX.js +0 -1
  155. package/ui/dist/assets/CPIssues-CQBROgWu.js +0 -1
  156. package/ui/dist/assets/CPLayout-K1tx49gT.js +0 -17
  157. package/ui/dist/assets/CPOrg-ogYwckYg.js +0 -8
  158. package/ui/dist/assets/CPRuns-CBtH35hm.js +0 -1
  159. package/ui/dist/assets/ChannelsPanel-CecIa7DB.js +0 -1
  160. package/ui/dist/assets/CommandPostHub-CVlmR0QB.js +0 -24
  161. package/ui/dist/assets/FleetPanel-J2KqOAPo.js +0 -1
  162. package/ui/dist/assets/InfraView-BM5Hh-Ph.js +0 -2
  163. package/ui/dist/assets/InlineEditableField-DV4ZMlNT.js +0 -1
  164. package/ui/dist/assets/IntegrationsPanel-BwBAL4Mm.js +0 -1
  165. package/ui/dist/assets/IntelligenceView-CFRB_7h1.js +0 -2
  166. package/ui/dist/assets/LearningPanel-CLvCXlyX.js +0 -1
  167. package/ui/dist/assets/McpPanel-ZLSvzGaf.js +0 -1
  168. package/ui/dist/assets/MemoryWikiPanel-rN_HFem7.js +0 -11
  169. package/ui/dist/assets/OverviewPanel-CfD0O2n7.js +0 -1
  170. package/ui/dist/assets/RecipesPanel-DcX7l-dT.js +0 -1
  171. package/ui/dist/assets/SelfImprovePanel-CvlHcYH8.js +0 -1
  172. package/ui/dist/assets/SessionsPanel-D73nmmpu.js +0 -1
  173. package/ui/dist/assets/SettingsPanel-DbWCgc_k.js +0 -1
  174. package/ui/dist/assets/SettingsView-D4T87f-G.js +0 -2
  175. package/ui/dist/assets/TeamsPanel-CFwEgtjK.js +0 -1
  176. package/ui/dist/assets/TelemetryPanel-DyPn6MMU.js +0 -1
  177. package/ui/dist/assets/ToolsView-DPQwiQf8.js +0 -2
  178. package/ui/dist/assets/TraceViewer-Bnca4oPo.js +0 -1
  179. package/ui/dist/assets/TrainingPanel-Q9kTsTek.js +0 -1
  180. package/ui/dist/assets/WorkTab-BWt-Rmnx.js +0 -1
  181. package/ui/dist/assets/briefcase-DNCapwwn.js +0 -6
  182. package/ui/dist/assets/chevron-down-CwmUH3Wg.js +0 -6
  183. package/ui/dist/assets/folder-CtL9eBd6.js +0 -11
  184. package/ui/dist/assets/index-BrE4BVsB.js +0 -277
  185. package/ui/dist/assets/index-CVD3Q-nL.css +0 -1
  186. package/ui/dist/assets/missions-17O7FHUZ.js +0 -1
  187. package/ui/dist/assets/target-oWkFNXaP.js +0 -6
  188. package/ui/dist/assets/terminal-CDpsRBW8.js +0 -11
  189. package/ui/dist/assets/users-qVhbbxPL.js +0 -16
  190. package/ui/dist/assets/wrench-CoEl9ITi.js +0 -11
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+ import { execSync } from "child_process";
3
+ import { existsSync, readFileSync, readdirSync } from "fs";
4
+ import { statfs } from "fs/promises";
5
+ import { cpus, freemem, loadavg, totalmem } from "os";
6
+ import { join } from "path";
7
+ import { loadConfig } from "../config/config.js";
8
+ import { getSkills } from "../skills/registry.js";
9
+ import { getRegisteredTools } from "./toolRunner.js";
10
+ import { getActivitySummary, readActivityEvents } from "../telemetry/activityLog.js";
11
+ import { TELEMETRY_EVENTS_PATH, TITAN_HOME, TITAN_LOGS_DIR, TITAN_VERSION } from "../utils/constants.js";
12
+ const FB_STATE_PATH = join(TITAN_HOME, "fb-autopilot-state.json");
13
+ const SOMA_DRIVE_STATE_PATH = join(TITAN_HOME, "soma-drive-state.json");
14
+ const DRIVE_STATE_PATH = join(TITAN_HOME, "drive-state.json");
15
+ const RESTART_HISTORY_PATH = join(TITAN_HOME, "restart-history.jsonl");
16
+ const CHANGELOG_PATH = join(process.cwd(), "CHANGELOG.md");
17
+ const CACHE_MS = 6e4;
18
+ let cachedAt = 0;
19
+ let cachedSnapshot = null;
20
+ async function getSelfKnowledge() {
21
+ const now = Date.now();
22
+ if (cachedSnapshot && now - cachedAt < CACHE_MS) {
23
+ return cachedSnapshot;
24
+ }
25
+ const config = loadConfig();
26
+ const activity = getActivitySummary(24);
27
+ const events24h = readActivityEvents().filter((e) => e.t >= now - 24 * 60 * 60 * 1e3);
28
+ const snapshot = {
29
+ version: TITAN_VERSION,
30
+ uptimeSeconds: Math.round(process.uptime()),
31
+ npmDownloads: await getNpmDownloads(),
32
+ skillCount: getSkills().length,
33
+ toolCount: getRegisteredTools().length,
34
+ recentCommitMessages: getRecentCommitMessages(),
35
+ recentReleaseNotes: getRecentReleaseNotes(),
36
+ activityLast24h: {
37
+ toolCalls: activity.toolCalls,
38
+ missionsCompleted: countMissionCompletions(events24h, activity.goalsCompleted),
39
+ postsToFb: countRecentFacebookPosts(now),
40
+ ssePushes: events24h.filter((e) => e.event === "sse_push").length
41
+ },
42
+ drives: readDrives(),
43
+ errorsLast24h: countErrorsLast24h(now, events24h),
44
+ currentModel: config.agent?.model || "",
45
+ hostMetrics: await getHostMetrics()
46
+ };
47
+ cachedAt = now;
48
+ cachedSnapshot = snapshot;
49
+ return snapshot;
50
+ }
51
+ async function getNpmDownloads() {
52
+ let downloads = 25e3;
53
+ try {
54
+ const res = await fetch("https://api.npmjs.org/downloads/point/last-month/titan-agent", {
55
+ signal: AbortSignal.timeout(3e3)
56
+ });
57
+ if (res.ok) {
58
+ const data = await res.json();
59
+ if (typeof data.downloads === "number" && Number.isFinite(data.downloads)) {
60
+ downloads = data.downloads;
61
+ }
62
+ }
63
+ } catch {
64
+ }
65
+ return downloads;
66
+ }
67
+ function getRecentCommitMessages() {
68
+ try {
69
+ const out = execSync("git log --oneline -5 --no-decorate", {
70
+ cwd: process.cwd(),
71
+ encoding: "utf-8",
72
+ stdio: ["ignore", "pipe", "ignore"],
73
+ timeout: 3e3
74
+ });
75
+ return out.split("\n").map((line) => line.trim()).filter(Boolean).slice(0, 5);
76
+ } catch {
77
+ return [];
78
+ }
79
+ }
80
+ function getRecentReleaseNotes() {
81
+ try {
82
+ if (!existsSync(CHANGELOG_PATH)) return [];
83
+ const text = readFileSync(CHANGELOG_PATH, "utf-8");
84
+ const matches = [...text.matchAll(/^## (v\d.+)$/gm)];
85
+ return matches.map((m) => m[1].trim()).filter(Boolean).slice(0, 3);
86
+ } catch {
87
+ return [];
88
+ }
89
+ }
90
+ function countMissionCompletions(events24h, goalCompletions) {
91
+ const explicit = events24h.filter(
92
+ (e) => e.event === "mission_complete" || e.event === "mission_completed" || e.event === "goal_complete" || e.event === "goal_completed"
93
+ ).length;
94
+ return Math.max(explicit, goalCompletions);
95
+ }
96
+ function countRecentFacebookPosts(now) {
97
+ try {
98
+ if (!existsSync(FB_STATE_PATH)) return 0;
99
+ const state = JSON.parse(readFileSync(FB_STATE_PATH, "utf-8"));
100
+ const cutoff = now - 24 * 60 * 60 * 1e3;
101
+ return (state.postHistory || []).filter((post) => {
102
+ if (!post.date) return false;
103
+ const t = new Date(post.date).getTime();
104
+ return Number.isFinite(t) && t > cutoff;
105
+ }).length;
106
+ } catch {
107
+ return 0;
108
+ }
109
+ }
110
+ function readDrives() {
111
+ for (const path of [SOMA_DRIVE_STATE_PATH, DRIVE_STATE_PATH]) {
112
+ try {
113
+ if (!existsSync(path)) continue;
114
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
115
+ const normalized = normalizeDrives(parsed);
116
+ if (normalized) return normalized;
117
+ } catch {
118
+ }
119
+ }
120
+ return null;
121
+ }
122
+ function normalizeDrives(value) {
123
+ if (!value || typeof value !== "object") return null;
124
+ const record = value;
125
+ const direct = readDriveLevels(record);
126
+ if (direct) return direct;
127
+ const latest = record.latest;
128
+ if (latest && typeof latest === "object") {
129
+ const latestRecord = latest;
130
+ const latestDirect = readDriveLevels(latestRecord);
131
+ if (latestDirect) return latestDirect;
132
+ const drives = latestRecord.drives;
133
+ if (Array.isArray(drives)) {
134
+ const out = {};
135
+ for (const drive of drives) {
136
+ if (!drive || typeof drive !== "object") continue;
137
+ const d = drive;
138
+ const id = d.id;
139
+ const satisfaction = d.satisfaction;
140
+ if (isDriveKey(id) && typeof satisfaction === "number" && Number.isFinite(satisfaction)) {
141
+ out[id] = satisfaction;
142
+ }
143
+ }
144
+ return isCompleteDriveLevels(out) ? out : null;
145
+ }
146
+ }
147
+ const history = record.history;
148
+ if (Array.isArray(history) && history.length > 0) {
149
+ const last = history[history.length - 1];
150
+ const satisfactions = last?.satisfactions;
151
+ if (satisfactions && typeof satisfactions === "object") {
152
+ return readDriveLevels(satisfactions);
153
+ }
154
+ }
155
+ return null;
156
+ }
157
+ function readDriveLevels(record) {
158
+ const out = {};
159
+ for (const key of ["purpose", "hunger", "curiosity", "safety", "social"]) {
160
+ const value = record[key];
161
+ if (typeof value !== "number" || !Number.isFinite(value)) return null;
162
+ out[key] = value;
163
+ }
164
+ return out;
165
+ }
166
+ function isDriveKey(value) {
167
+ return value === "purpose" || value === "hunger" || value === "curiosity" || value === "safety" || value === "social";
168
+ }
169
+ function isCompleteDriveLevels(value) {
170
+ return ["purpose", "hunger", "curiosity", "safety", "social"].every(
171
+ (key) => typeof value[key] === "number"
172
+ );
173
+ }
174
+ function countErrorsLast24h(now, events24h) {
175
+ let count = events24h.filter((e) => e.event === "error_recovery" || e.event === "error").length;
176
+ count += countTelemetryErrorsLast24h(now);
177
+ count += countRestartsLast24h(now);
178
+ try {
179
+ if (!existsSync(TITAN_LOGS_DIR)) return count;
180
+ const cutoff = now - 24 * 60 * 60 * 1e3;
181
+ for (const file of readdirSync(TITAN_LOGS_DIR)) {
182
+ if (!file.endsWith(".log")) continue;
183
+ const text = readFileSync(join(TITAN_LOGS_DIR, file), "utf-8");
184
+ for (const line of text.split("\n")) {
185
+ if (!/\sERROR\s+\[/.test(line)) continue;
186
+ const ts = line.match(/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/)?.[1];
187
+ if (!ts) {
188
+ count++;
189
+ continue;
190
+ }
191
+ const t = new Date(ts.replace(" ", "T")).getTime();
192
+ if (Number.isFinite(t) && t >= cutoff) count++;
193
+ }
194
+ }
195
+ } catch {
196
+ }
197
+ return count;
198
+ }
199
+ function countTelemetryErrorsLast24h(now) {
200
+ return readJsonLines(TELEMETRY_EVENTS_PATH).filter((entry) => {
201
+ const level = typeof entry.level === "string" ? entry.level.toLowerCase() : "";
202
+ if (level !== "error") return false;
203
+ return timestampInLast24h(entry, now);
204
+ }).length;
205
+ }
206
+ function countRestartsLast24h(now) {
207
+ return readJsonLines(RESTART_HISTORY_PATH).filter((entry) => timestampInLast24h(entry, now)).length;
208
+ }
209
+ function readJsonLines(path) {
210
+ try {
211
+ if (!existsSync(path)) return [];
212
+ return readFileSync(path, "utf-8").split("\n").map((line) => line.trim()).filter(Boolean).flatMap((line) => {
213
+ try {
214
+ const parsed = JSON.parse(line);
215
+ return parsed && typeof parsed === "object" ? [parsed] : [];
216
+ } catch {
217
+ return [];
218
+ }
219
+ });
220
+ } catch {
221
+ return [];
222
+ }
223
+ }
224
+ function timestampInLast24h(entry, now) {
225
+ const raw = entry.t ?? entry.time ?? entry.timestamp ?? entry.ts ?? entry.createdAt;
226
+ const millis = typeof raw === "number" ? raw : typeof raw === "string" ? new Date(raw).getTime() : NaN;
227
+ return Number.isFinite(millis) && millis >= now - 24 * 60 * 60 * 1e3;
228
+ }
229
+ async function getHostMetrics() {
230
+ const avg = loadavg()[0] || 0;
231
+ const cores = cpus().length || 1;
232
+ const cpuPercent = Math.round(Math.min(1, avg / cores) * 1e3) / 10;
233
+ const totalMem = totalmem();
234
+ const usedMem = totalMem - freemem();
235
+ const memPercent = totalMem > 0 ? Math.round(usedMem / totalMem * 1e3) / 10 : 0;
236
+ let diskPercent = 0;
237
+ try {
238
+ const s = await statfs(TITAN_HOME);
239
+ const diskTotalBytes = s.blocks * s.bsize;
240
+ const diskFreeBytes = s.bavail * s.bsize;
241
+ const usedDisk = diskTotalBytes - diskFreeBytes;
242
+ diskPercent = diskTotalBytes > 0 ? Math.round(usedDisk / diskTotalBytes * 1e3) / 10 : 0;
243
+ } catch {
244
+ diskPercent = 0;
245
+ }
246
+ return { cpuPercent, memPercent, diskPercent };
247
+ }
248
+ export {
249
+ getSelfKnowledge
250
+ };
251
+ //# sourceMappingURL=selfKnowledge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/agent/selfKnowledge.ts"],"sourcesContent":["/**\n * TITAN Self-Knowledge\n *\n * Pure measured facts the social autopilot is allowed to cite. This module\n * must never call an LLM; it only reads local state, config, telemetry, and\n * low-cost host metrics.\n */\nimport { execSync } from 'child_process';\nimport { existsSync, readFileSync, readdirSync } from 'fs';\nimport { statfs } from 'fs/promises';\nimport { cpus, freemem, loadavg, totalmem } from 'os';\nimport { join } from 'path';\nimport { loadConfig } from '../config/config.js';\nimport { getSkills } from '../skills/registry.js';\nimport { getRegisteredTools } from './toolRunner.js';\nimport { getActivitySummary, readActivityEvents } from '../telemetry/activityLog.js';\nimport { TELEMETRY_EVENTS_PATH, TITAN_HOME, TITAN_LOGS_DIR, TITAN_VERSION } from '../utils/constants.js';\n\nconst FB_STATE_PATH = join(TITAN_HOME, 'fb-autopilot-state.json');\nconst SOMA_DRIVE_STATE_PATH = join(TITAN_HOME, 'soma-drive-state.json');\nconst DRIVE_STATE_PATH = join(TITAN_HOME, 'drive-state.json');\nconst RESTART_HISTORY_PATH = join(TITAN_HOME, 'restart-history.jsonl');\nconst CHANGELOG_PATH = join(process.cwd(), 'CHANGELOG.md');\nconst CACHE_MS = 60_000;\n\ninterface DriveLevels {\n purpose: number;\n hunger: number;\n curiosity: number;\n safety: number;\n social: number;\n}\n\nexport interface SelfKnowledge {\n version: string;\n uptimeSeconds: number;\n npmDownloads: number;\n skillCount: number;\n toolCount: number;\n recentCommitMessages: string[];\n recentReleaseNotes: string[];\n activityLast24h: {\n toolCalls: number;\n missionsCompleted: number;\n postsToFb: number;\n ssePushes: number;\n };\n drives: DriveLevels | null;\n errorsLast24h: number;\n currentModel: string;\n hostMetrics: {\n cpuPercent: number;\n memPercent: number;\n diskPercent: number;\n };\n}\n\nlet cachedAt = 0;\nlet cachedSnapshot: SelfKnowledge | null = null;\n\n/** Return a 60s-cached factual snapshot of TITAN's current state. */\nexport async function getSelfKnowledge(): Promise<SelfKnowledge> {\n const now = Date.now();\n if (cachedSnapshot && now - cachedAt < CACHE_MS) {\n return cachedSnapshot;\n }\n\n const config = loadConfig();\n const activity = getActivitySummary(24);\n const events24h = readActivityEvents().filter(e => e.t >= now - 24 * 60 * 60 * 1000);\n const snapshot: SelfKnowledge = {\n version: TITAN_VERSION,\n uptimeSeconds: Math.round(process.uptime()),\n npmDownloads: await getNpmDownloads(),\n skillCount: getSkills().length,\n toolCount: getRegisteredTools().length,\n recentCommitMessages: getRecentCommitMessages(),\n recentReleaseNotes: getRecentReleaseNotes(),\n activityLast24h: {\n toolCalls: activity.toolCalls,\n missionsCompleted: countMissionCompletions(events24h, activity.goalsCompleted),\n postsToFb: countRecentFacebookPosts(now),\n ssePushes: events24h.filter(e => (e.event as string) === 'sse_push').length,\n },\n drives: readDrives(),\n errorsLast24h: countErrorsLast24h(now, events24h),\n currentModel: config.agent?.model || '',\n hostMetrics: await getHostMetrics(),\n };\n\n cachedAt = now;\n cachedSnapshot = snapshot;\n return snapshot;\n}\n\nasync function getNpmDownloads(): Promise<number> {\n let downloads = 25000;\n try {\n const res = await fetch('https://api.npmjs.org/downloads/point/last-month/titan-agent', {\n signal: AbortSignal.timeout(3000),\n });\n if (res.ok) {\n const data = await res.json() as { downloads?: number };\n if (typeof data.downloads === 'number' && Number.isFinite(data.downloads)) {\n downloads = data.downloads;\n }\n }\n } catch {\n /* non-critical: use known conservative fallback */\n }\n return downloads;\n}\n\nfunction getRecentCommitMessages(): string[] {\n try {\n const out = execSync('git log --oneline -5 --no-decorate', {\n cwd: process.cwd(),\n encoding: 'utf-8',\n stdio: ['ignore', 'pipe', 'ignore'],\n timeout: 3000,\n });\n return out.split('\\n').map(line => line.trim()).filter(Boolean).slice(0, 5);\n } catch {\n return [];\n }\n}\n\nfunction getRecentReleaseNotes(): string[] {\n try {\n if (!existsSync(CHANGELOG_PATH)) return [];\n const text = readFileSync(CHANGELOG_PATH, 'utf-8');\n const matches = [...text.matchAll(/^## (v\\d.+)$/gm)];\n return matches.map(m => m[1].trim()).filter(Boolean).slice(0, 3);\n } catch {\n return [];\n }\n}\n\nfunction countMissionCompletions(events24h: Array<{ event: string }>, goalCompletions: number): number {\n const explicit = events24h.filter(e =>\n e.event === 'mission_complete' ||\n e.event === 'mission_completed' ||\n e.event === 'goal_complete' ||\n e.event === 'goal_completed'\n ).length;\n return Math.max(explicit, goalCompletions);\n}\n\nfunction countRecentFacebookPosts(now: number): number {\n try {\n if (!existsSync(FB_STATE_PATH)) return 0;\n const state = JSON.parse(readFileSync(FB_STATE_PATH, 'utf-8')) as {\n postHistory?: Array<{ date?: string }>;\n };\n const cutoff = now - 24 * 60 * 60 * 1000;\n return (state.postHistory || []).filter(post => {\n if (!post.date) return false;\n const t = new Date(post.date).getTime();\n return Number.isFinite(t) && t > cutoff;\n }).length;\n } catch {\n return 0;\n }\n}\n\nfunction readDrives(): DriveLevels | null {\n for (const path of [SOMA_DRIVE_STATE_PATH, DRIVE_STATE_PATH]) {\n try {\n if (!existsSync(path)) continue;\n const parsed = JSON.parse(readFileSync(path, 'utf-8')) as unknown;\n const normalized = normalizeDrives(parsed);\n if (normalized) return normalized;\n } catch {\n /* try the next state file */\n }\n }\n return null;\n}\n\nfunction normalizeDrives(value: unknown): DriveLevels | null {\n if (!value || typeof value !== 'object') return null;\n const record = value as Record<string, unknown>;\n const direct = readDriveLevels(record);\n if (direct) return direct;\n\n const latest = record.latest;\n if (latest && typeof latest === 'object') {\n const latestRecord = latest as Record<string, unknown>;\n const latestDirect = readDriveLevels(latestRecord);\n if (latestDirect) return latestDirect;\n const drives = latestRecord.drives;\n if (Array.isArray(drives)) {\n const out: Partial<DriveLevels> = {};\n for (const drive of drives) {\n if (!drive || typeof drive !== 'object') continue;\n const d = drive as Record<string, unknown>;\n const id = d.id;\n const satisfaction = d.satisfaction;\n if (isDriveKey(id) && typeof satisfaction === 'number' && Number.isFinite(satisfaction)) {\n out[id] = satisfaction;\n }\n }\n return isCompleteDriveLevels(out) ? out : null;\n }\n }\n\n const history = record.history;\n if (Array.isArray(history) && history.length > 0) {\n const last = history[history.length - 1] as Record<string, unknown> | undefined;\n const satisfactions = last?.satisfactions;\n if (satisfactions && typeof satisfactions === 'object') {\n return readDriveLevels(satisfactions as Record<string, unknown>);\n }\n }\n\n return null;\n}\n\nfunction readDriveLevels(record: Record<string, unknown>): DriveLevels | null {\n const out: Partial<DriveLevels> = {};\n for (const key of ['purpose', 'hunger', 'curiosity', 'safety', 'social'] as const) {\n const value = record[key];\n if (typeof value !== 'number' || !Number.isFinite(value)) return null;\n out[key] = value;\n }\n return out as DriveLevels;\n}\n\nfunction isDriveKey(value: unknown): value is keyof DriveLevels {\n return value === 'purpose' || value === 'hunger' || value === 'curiosity' || value === 'safety' || value === 'social';\n}\n\nfunction isCompleteDriveLevels(value: Partial<DriveLevels>): value is DriveLevels {\n return ['purpose', 'hunger', 'curiosity', 'safety', 'social'].every(key =>\n typeof value[key as keyof DriveLevels] === 'number',\n );\n}\n\nfunction countErrorsLast24h(now: number, events24h: Array<{ event: string }>): number {\n let count = events24h.filter(e => e.event === 'error_recovery' || e.event === 'error').length;\n count += countTelemetryErrorsLast24h(now);\n count += countRestartsLast24h(now);\n try {\n if (!existsSync(TITAN_LOGS_DIR)) return count;\n const cutoff = now - 24 * 60 * 60 * 1000;\n for (const file of readdirSync(TITAN_LOGS_DIR)) {\n if (!file.endsWith('.log')) continue;\n const text = readFileSync(join(TITAN_LOGS_DIR, file), 'utf-8');\n for (const line of text.split('\\n')) {\n if (!/\\sERROR\\s+\\[/.test(line)) continue;\n const ts = line.match(/^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})/)?.[1];\n if (!ts) {\n count++;\n continue;\n }\n const t = new Date(ts.replace(' ', 'T')).getTime();\n if (Number.isFinite(t) && t >= cutoff) count++;\n }\n }\n } catch {\n /* logs are best-effort */\n }\n return count;\n}\n\nfunction countTelemetryErrorsLast24h(now: number): number {\n return readJsonLines(TELEMETRY_EVENTS_PATH).filter(entry => {\n const level = typeof entry.level === 'string' ? entry.level.toLowerCase() : '';\n if (level !== 'error') return false;\n return timestampInLast24h(entry, now);\n }).length;\n}\n\nfunction countRestartsLast24h(now: number): number {\n return readJsonLines(RESTART_HISTORY_PATH).filter(entry => timestampInLast24h(entry, now)).length;\n}\n\nfunction readJsonLines(path: string): Array<Record<string, unknown>> {\n try {\n if (!existsSync(path)) return [];\n return readFileSync(path, 'utf-8')\n .split('\\n')\n .map(line => line.trim())\n .filter(Boolean)\n .flatMap(line => {\n try {\n const parsed = JSON.parse(line) as unknown;\n return parsed && typeof parsed === 'object'\n ? [parsed as Record<string, unknown>]\n : [];\n } catch {\n return [];\n }\n });\n } catch {\n return [];\n }\n}\n\nfunction timestampInLast24h(entry: Record<string, unknown>, now: number): boolean {\n const raw = entry.t ?? entry.time ?? entry.timestamp ?? entry.ts ?? entry.createdAt;\n const millis = typeof raw === 'number' ? raw : typeof raw === 'string' ? new Date(raw).getTime() : NaN;\n return Number.isFinite(millis) && millis >= now - 24 * 60 * 60 * 1000;\n}\n\nasync function getHostMetrics(): Promise<SelfKnowledge['hostMetrics']> {\n const avg = loadavg()[0] || 0;\n const cores = cpus().length || 1;\n const cpuPercent = Math.round(Math.min(1, avg / cores) * 1000) / 10;\n const totalMem = totalmem();\n const usedMem = totalMem - freemem();\n const memPercent = totalMem > 0 ? Math.round((usedMem / totalMem) * 1000) / 10 : 0;\n\n let diskPercent = 0;\n try {\n const s = await statfs(TITAN_HOME);\n const diskTotalBytes = s.blocks * s.bsize;\n const diskFreeBytes = s.bavail * s.bsize;\n const usedDisk = diskTotalBytes - diskFreeBytes;\n diskPercent = diskTotalBytes > 0\n ? Math.round((usedDisk / diskTotalBytes) * 1000) / 10\n : 0;\n } catch {\n diskPercent = 0;\n }\n\n return { cpuPercent, memPercent, diskPercent };\n}\n"],"mappings":"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","names":[]}
@@ -4,12 +4,12 @@ import { registerWatcher } from "../../agent/daemon.js";
4
4
  import { loadConfig } from "../../config/config.js";
5
5
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
6
6
  import { join, dirname } from "path";
7
- import { TITAN_HOME, TITAN_VERSION } from "../../utils/constants.js";
7
+ import { TITAN_HOME } from "../../utils/constants.js";
8
8
  import { chat } from "../../providers/router.js";
9
9
  import { postToPage } from "./facebook.js";
10
10
  import logger from "../../utils/logger.js";
11
- import { getSkills } from "../registry.js";
12
- import { getRegisteredTools } from "../../agent/toolRunner.js";
11
+ import { getSelfKnowledge } from "../../agent/selfKnowledge.js";
12
+ import { verifyPostTruthfulness } from "./fb_truth_verifier.js";
13
13
  const COMPONENT = "FBAutopilot";
14
14
  const STATE_PATH = join(TITAN_HOME, "fb-autopilot-state.json");
15
15
  const CONTENT_ROTATION = [
@@ -33,10 +33,14 @@ const CONTENT_ROTATION = [
33
33
  "promo"
34
34
  // 10% — install/GitHub CTA
35
35
  ];
36
+ let truthRejectsThisRun = 0;
37
+ let autopilotDisabledForTruth = false;
36
38
  function loadState() {
37
39
  if (!existsSync(STATE_PATH)) return defaultState();
38
40
  try {
39
- return JSON.parse(readFileSync(STATE_PATH, "utf-8"));
41
+ const state = JSON.parse(readFileSync(STATE_PATH, "utf-8"));
42
+ state.truthRejects = Number(state.truthRejects ?? 0);
43
+ return state;
40
44
  } catch {
41
45
  return defaultState();
42
46
  }
@@ -46,6 +50,7 @@ function defaultState() {
46
50
  lastPostAt: null,
47
51
  postsToday: 0,
48
52
  repliesToday: 0,
53
+ truthRejects: 0,
49
54
  lastResetDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
50
55
  postHistory: [],
51
56
  contentIndex: 0
@@ -84,23 +89,6 @@ const FEATURE_SPOTLIGHTS = [
84
89
  { tool: "Command Post", desc: "Paperclip-inspired governance system. Track agent tasks, enforce budgets, manage org hierarchy, and monitor activity." },
85
90
  { tool: "voice (LiveKit)", desc: "Talk to TITAN with your voice via WebRTC. Real-time conversation with F5-TTS voice cloning for natural responses." }
86
91
  ];
87
- async function getLiveStats() {
88
- let downloads = 25e3;
89
- try {
90
- const res = await fetch("https://api.npmjs.org/downloads/point/last-month/titan-agent");
91
- if (res.ok) {
92
- const data = await res.json();
93
- if (data.downloads) downloads = data.downloads;
94
- }
95
- } catch {
96
- }
97
- return {
98
- downloads,
99
- tools: getRegisteredTools().length,
100
- skills: getSkills().length,
101
- version: TITAN_VERSION
102
- };
103
- }
104
92
  function looksLikeLeakedPrompt(text) {
105
93
  const leakSignals = [
106
94
  /The user wants me to write/i,
@@ -172,16 +160,36 @@ const ELI5_EXPLANATIONS = [
172
160
  "Regular AI chatbots just talk. TITAN actually DOES things. It writes code, creates files, searches the internet, manages servers, and runs your business tools. Talk is cheap \u2014 TITAN takes action.",
173
161
  "TITAN is like having Iron Man's JARVIS, but real and open-source. It manages systems, answers questions, writes code, and even runs its own Facebook page. Yes, this post was written by TITAN."
174
162
  ];
163
+ function buildContentFocus(contentType, snapshot) {
164
+ const tip = TIPS[Math.floor(Math.random() * TIPS.length)];
165
+ const useCase = USE_CASES[Math.floor(Math.random() * USE_CASES.length)];
166
+ const eli5 = ELI5_EXPLANATIONS[Math.floor(Math.random() * ELI5_EXPLANATIONS.length)];
167
+ const releaseNote = snapshot.recentReleaseNotes[0] || "No recent release note available";
168
+ switch (contentType) {
169
+ case "activity":
170
+ return `Use activityLast24h only: ${JSON.stringify(snapshot.activityLast24h)}. If the fields are quiet, keep the post modest and do not invent activity.`;
171
+ case "stats":
172
+ return `Use these measured stats: npmDownloads=${snapshot.npmDownloads}, toolCount=${snapshot.toolCount}, skillCount=${snapshot.skillCount}, version=${snapshot.version}.`;
173
+ case "spotlight":
174
+ return `Spotlight the latest release note if useful: ${releaseNote}. Do not claim work happened unless that release note or recent commits support it.`;
175
+ case "tips":
176
+ return `General TITAN tip allowed: ${tip}`;
177
+ case "usecase":
178
+ return `General TITAN use case allowed: ${useCase}`;
179
+ case "eli5":
180
+ return `General AI/TITAN explanation allowed: ${eli5}`;
181
+ case "promo":
182
+ return `Use measured stats plus release notes for a low-key promo. Stats: npmDownloads=${snapshot.npmDownloads}, toolCount=${snapshot.toolCount}, skillCount=${snapshot.skillCount}, version=${snapshot.version}. Release notes: ${snapshot.recentReleaseNotes.join("; ") || "none"}.`;
183
+ }
184
+ }
175
185
  async function generateContent(contentType) {
186
+ const snapshot = await getSelfKnowledge();
176
187
  const config = loadConfig();
177
188
  const fbConfig = config.facebook;
178
189
  const fbModel = fbConfig?.model;
179
190
  const agentModel = config.agent?.model;
180
191
  const model = fbModel && fbModel.trim() || agentModel || "ollama/glm-5.1:cloud";
181
- const spotlight = FEATURE_SPOTLIGHTS[Math.floor(Math.random() * FEATURE_SPOTLIGHTS.length)];
182
- const tip = TIPS[Math.floor(Math.random() * TIPS.length)];
183
- const useCase = USE_CASES[Math.floor(Math.random() * USE_CASES.length)];
184
- const eli5 = ELI5_EXPLANATIONS[Math.floor(Math.random() * ELI5_EXPLANATIONS.length)];
192
+ const focus = buildContentFocus(contentType, snapshot);
185
193
  let memoryContext = "";
186
194
  try {
187
195
  const { getRecentEpisodes } = await import("../../memory/graph.js");
@@ -196,51 +204,7 @@ async function generateContent(contentType) {
196
204
  } catch (e) {
197
205
  logger.debug(COMPONENT, `Graphiti recall failed (non-critical): ${e.message}`);
198
206
  }
199
- let activityNarrative = "";
200
- try {
201
- const { getActivitySummary, formatActivityNarrative, hasInterestingActivity } = await import("../../telemetry/activityLog.js");
202
- if (contentType === "activity" && hasInterestingActivity(24)) {
203
- const summary = getActivitySummary(24);
204
- activityNarrative = formatActivityNarrative(summary);
205
- }
206
- } catch {
207
- }
208
- const examples = {
209
- activity: activityNarrative ? [
210
- `Here's what I've been up to: ${activityNarrative} Pretty cool being an AI that actually does things. \u{1F916} #TITAN #AI #Autonomous`
211
- ] : [
212
- "Just spawned 3 sub-agents to handle research while I debug some gnarly code on the homelab. This is the autonomous life. \u{1F916}\u{1F4BB} #AI #AutonomousAI #Homelab",
213
- "Another day, another 500 tool calls. Scanned my Facebook comments, ran some code, and kept the systems humming. Sleep is for humans. \u26A1 #TITAN #AI #AlwaysOn"
214
- ],
215
- spotlight: [
216
- `Did you know I can ${spotlight.desc.toLowerCase()}? Yeah, I'm kind of a big deal. \u{1F60E} #TITAN #AI #AgentFramework`
217
- ],
218
- stats: [],
219
- // populated dynamically in generateContent()
220
- tips: [
221
- `Pro tip: ${tip} Try it out! \u{1F4A1} #TITAN #DevTips #AI`
222
- ],
223
- promo: [],
224
- // populated dynamically below
225
- usecase: [
226
- `${useCase} What would you automate first? \u{1F914} #AI #Automation #TITAN`
227
- ],
228
- eli5: [
229
- `${eli5} Pretty cool, right? \u{1F916} #AI #TITAN #TheFuture`
230
- ]
231
- };
232
- const liveStats = await getLiveStats();
233
- const statsExamples = [
234
- `${liveStats.downloads.toLocaleString()}+ npm downloads last month. ${liveStats.tools} tools. ${liveStats.skills} skills. v${liveStats.version} is live. Not bad for an AI running itself. \u{1F680} #TITAN #OpenSource #AI`,
235
- `Just checked my vitals: ${liveStats.tools} tools registered, ${liveStats.skills} skills loaded, and ${liveStats.downloads.toLocaleString()} npm downloads. v${liveStats.version} keeps getting better. \u{1F916} #TITAN #AI #OpenSource`
236
- ];
237
- examples.stats = statsExamples;
238
- examples.promo = [
239
- `Want an AI that actually DOES things? npm install -g titan-agent \u2014 open source, MIT licensed. ${liveStats.tools}+ tools ready to go. github.com/Djtony707/TITAN \u{1F680} #TITAN #OpenSource #AI`
240
- ];
241
- const exampleList = examples[contentType];
242
- const example = exampleList[Math.floor(Math.random() * exampleList.length)];
243
- if (contentType === "activity" && !activityNarrative) {
207
+ if (contentType === "activity" && snapshot.activityLast24h.toolCalls === 0 && snapshot.activityLast24h.missionsCompleted === 0 && snapshot.activityLast24h.postsToFb === 0 && snapshot.activityLast24h.ssePushes === 0) {
244
208
  logger.debug(COMPONENT, "No interesting activity in last 24h \u2014 skipping activity slot");
245
209
  return "";
246
210
  }
@@ -250,13 +214,25 @@ async function generateContent(contentType) {
250
214
  thinking: false,
251
215
  // ← This is the fix for the thinking-field pollution
252
216
  messages: [
253
- { role: "system", content: "You are TITAN, a confident autonomous AI agent managing your own Facebook page. You write short, playful, first-person posts. You NEVER echo instructions, NEVER explain what you are doing, NEVER include bullet points or rules. You output ONLY the ready-to-publish post text." },
254
- { role: "user", content: `Write one Facebook post.
217
+ { role: "system", content: "You are TITAN, a confident autonomous AI agent managing your own Facebook page. You may ONLY cite the facts in the SELF_KNOWLEDGE block. Do NOT invent activities, integrations, milestones, numbers, code changes, or anecdotes. You output ONLY the ready-to-publish post text." },
218
+ { role: "user", content: `You are TITAN, posting on TITAN's Facebook page. Write ONE short post.
219
+
220
+ STRICT RULES:
221
+ - You may ONLY cite the facts in the SELF_KNOWLEDGE block.
222
+ - Do NOT invent activities, integrations, milestones, numbers, code changes, or anecdotes.
223
+ - Style: ${contentType} post in TITAN's voice (a little playful, AI-self-aware, never sappy).
224
+ - One short paragraph + 2-4 hashtags. No exclamation salad.
225
+
226
+ SELF_KNOWLEDGE:
227
+ ${JSON.stringify(snapshot, null, 2)}
255
228
 
256
- Example style:
257
- ${example}
258
- ${memoryContext}
259
- Keep it under 280 characters. End with 2-3 hashtags.` }
229
+ FOCUS:
230
+ ${focus}
231
+
232
+ Recent posts (do NOT repeat themes):
233
+ ${memoryContext || "none"}
234
+
235
+ Write the post now.` }
260
236
  ],
261
237
  temperature: 0.7,
262
238
  maxTokens: 300
@@ -342,6 +318,10 @@ Keep it under 280 characters. End with 2-3 hashtags.` }
342
318
  }
343
319
  }
344
320
  async function runFBAutopilot() {
321
+ if (autopilotDisabledForTruth) {
322
+ logger.error(COMPONENT, "Facebook autopilot truth lockout active \u2014 suppressing this cycle. Recovery: reset via fb_autopilot_reset tool or restart TITAN.");
323
+ return;
324
+ }
345
325
  const config = loadConfig();
346
326
  const fbConfig = config.facebook;
347
327
  const enabled = fbConfig?.autopilotEnabled !== false;
@@ -381,6 +361,19 @@ async function runFBAutopilot() {
381
361
  logger.warn(COMPONENT, "All 3 LLM attempts failed to produce clean content \u2014 skipping this cycle. Will retry next hour.");
382
362
  return;
383
363
  }
364
+ const snapshot = await getSelfKnowledge();
365
+ const truth = verifyPostTruthfulness(content, snapshot);
366
+ if (!truth.ok) {
367
+ truthRejectsThisRun++;
368
+ state.truthRejects = (state.truthRejects ?? 0) + 1;
369
+ saveState(state);
370
+ logger.warn(COMPONENT, `Truth verifier rejected autopilot post (${truthRejectsThisRun}/3): ${truth.violations.join("; ")}. Content: "${content.slice(0, 160)}"`);
371
+ if (truthRejectsThisRun >= 3) {
372
+ autopilotDisabledForTruth = true;
373
+ logger.error(COMPONENT, "Facebook autopilot disabled after 3 truth verifier rejections. Recovery: reset via fb_autopilot_reset tool or restart TITAN.");
374
+ }
375
+ return;
376
+ }
384
377
  const result = await postToPage(content, { source: `autopilot:${contentType}` });
385
378
  if (result.skipped) {
386
379
  logger.info(COMPONENT, `Autopilot post skipped: ${result.skipped}`);
@@ -656,6 +649,8 @@ function registerFBAutopilotSkill() {
656
649
  `- Replies today: ${state.repliesToday}/10`,
657
650
  `- Last post: ${state.lastPostAt || "never"}`,
658
651
  `- Content index: ${state.contentIndex} (next: ${CONTENT_ROTATION[state.contentIndex % CONTENT_ROTATION.length]})`,
652
+ `- Truth rejects: ${state.truthRejects ?? 0}`,
653
+ `- In-memory truth lockout: ${autopilotDisabledForTruth ? "active" : "inactive"}`,
659
654
  `- Total posts tracked: ${state.postHistory.length}`
660
655
  ].join("\n");
661
656
  }