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.
- package/dist/agent/selfKnowledge.js +251 -0
- package/dist/agent/selfKnowledge.js.map +1 -0
- package/dist/skills/builtin/fb_autopilot.js +71 -76
- package/dist/skills/builtin/fb_autopilot.js.map +1 -1
- package/dist/skills/builtin/fb_truth_verifier.js +78 -0
- package/dist/skills/builtin/fb_truth_verifier.js.map +1 -0
- package/dist/utils/constants.js +1 -1
- package/dist/utils/constants.js.map +1 -1
- package/package.json +1 -1
- package/ui/dist/assets/{AuditPanel-yprjgSxp.js → AuditPanel-ChC-b5SP.js} +2 -2
- package/ui/dist/assets/{AutonomyPanel-uYRbGtru.js → AutonomyPanel-BCpdrVFw.js} +2 -2
- package/ui/dist/assets/{AutopilotPanel-l6gj_LV6.js → AutopilotPanel-B1f_JRtX.js} +1 -1
- package/ui/dist/assets/{AutoresearchPanel-D73YFdTk.js → AutoresearchPanel-CITPL0LA.js} +2 -2
- package/ui/dist/assets/BackupPanel-nxH6FApT.js +1 -0
- package/ui/dist/assets/{BrowserPanel-B2PIVcyy.js → BrowserPanel-Ddr7mS-x.js} +2 -2
- package/ui/dist/assets/CPActivity-I69H8j4x.js +1 -0
- package/ui/dist/assets/{CPAgentDetail-BcnUbGXB.js → CPAgentDetail-CbERciDC.js} +1 -1
- package/ui/dist/assets/CPAgents-B7xsnxuH.js +1 -0
- package/ui/dist/assets/CPApprovals-CWRvAUmU.js +1 -0
- package/ui/dist/assets/CPCosts-CB6jsAZF.js +1 -0
- package/ui/dist/assets/CPDashboard-BrIapvI1.js +1 -0
- package/ui/dist/assets/CPFiles-B6sdKdD2.js +1 -0
- package/ui/dist/assets/CPGoals-ClIa_lSv.js +1 -0
- package/ui/dist/assets/{CPInbox-D5J3KoZH.js → CPInbox-DP34PyKr.js} +2 -2
- package/ui/dist/assets/CPIssueDetail-B5XDvnNo.js +1 -0
- package/ui/dist/assets/CPIssues-mHcOqMxR.js +1 -0
- package/ui/dist/assets/CPLayout-COXF33Nc.js +2 -0
- package/ui/dist/assets/CPOrg-CrAXpcQv.js +8 -0
- package/ui/dist/assets/CPRuns-qhCxjMyH.js +1 -0
- package/ui/dist/assets/{CPSocial-Wjo7NdGw.js → CPSocial-Z_tlMxcf.js} +3 -3
- package/ui/dist/assets/CPVoice-KkHWnkzf.js +1 -0
- package/ui/dist/assets/ChannelsPanel-POcyuSof.js +1 -0
- package/ui/dist/assets/{CheckpointsPanel-BlemWfzE.js → CheckpointsPanel-V-9rpSua.js} +1 -1
- package/ui/dist/assets/CommandPostHub-P_tM-kjr.js +24 -0
- package/ui/dist/assets/{CronPanel-DITlv6pY.js → CronPanel-Cggo1M4b.js} +1 -1
- package/ui/dist/assets/{DataTable-D_Ha_hg9.js → DataTable-BqS0-Q13.js} +1 -1
- package/ui/dist/assets/{DreamPanel-C_vsucNi.js → DreamPanel-DBQ3PGPd.js} +4 -4
- package/ui/dist/assets/{EmptyState-B34O2R_p.js → EmptyState-M-gXHyMZ.js} +1 -1
- package/ui/dist/assets/{EvalHarnessPanel-CuhSE-zw.js → EvalHarnessPanel-IoTrhlAP.js} +2 -2
- package/ui/dist/assets/{EvalPanel-C3pPERMD.js → EvalPanel-CQq3ZXY_.js} +1 -1
- package/ui/dist/assets/{FileViewer-DWVXThSD.js → FileViewer-hBoZ1-0b.js} +1 -1
- package/ui/dist/assets/{FilesPanel-DiApOFhp.js → FilesPanel-CKd-Aypz.js} +2 -2
- package/ui/dist/assets/FleetPanel-DuOqhHhL.js +1 -0
- package/ui/dist/assets/{HomelabPanel-XjsdErR3.js → HomelabPanel-B3sehYqI.js} +2 -2
- package/ui/dist/assets/InfraView-CBPMt7Qu.js +2 -0
- package/ui/dist/assets/InlineEditableField-Cl8erGUX.js +1 -0
- package/ui/dist/assets/{Input-CynT1mu1.js → Input-DVOYwUp6.js} +1 -1
- package/ui/dist/assets/IntegrationsPanel-D3cD2B9D.js +1 -0
- package/ui/dist/assets/IntelligenceView-CP8EjWJa.js +2 -0
- package/ui/dist/assets/LearningPanel-DuR-b7JN.js +1 -0
- package/ui/dist/assets/{LogsPanel-Dcsum5Sf.js → LogsPanel-fjkC4y9g.js} +1 -1
- package/ui/dist/assets/McpPanel-CmTNwu1v.js +1 -0
- package/ui/dist/assets/{MemoryGraphPanel-BY2e6mfM.js → MemoryGraphPanel-BHvlmv6a.js} +1 -1
- package/ui/dist/assets/MemoryWikiPanel-BHlIOWgk.js +11 -0
- package/ui/dist/assets/{MeshPanel-BqoCNPoB.js → MeshPanel-CDOMhBSw.js} +1 -1
- package/ui/dist/assets/{MissionCanvas-DsRJk58m.js → MissionCanvas-D6Ksusvp.js} +3 -3
- package/ui/dist/assets/{MissionChat-ZnSSl8gc.js → MissionChat-C8tgZioW.js} +1 -1
- package/ui/dist/assets/{MissionLibrary-C1g-xMaj.js → MissionLibrary-zoNNuFfq.js} +1 -1
- package/ui/dist/assets/{MissionStart-D687lcOE.js → MissionStart-Cl-TNUwM.js} +1 -1
- package/ui/dist/assets/{Modal-DggltTtC.js → Modal-CrI6woDZ.js} +1 -1
- package/ui/dist/assets/{NvidiaPanel-DGKOH_5d.js → NvidiaPanel-DFFVLMcX.js} +1 -1
- package/ui/dist/assets/{OrganismPanel-DOfBCLU1.js → OrganismPanel-AZreSHGM.js} +1 -1
- package/ui/dist/assets/OverviewPanel-BPZqZt7W.js +1 -0
- package/ui/dist/assets/{PageHeader-C0rlQyU7.js → PageHeader-D0zki6c2.js} +1 -1
- package/ui/dist/assets/{PersonaProfilesPanel-BGxPAg-2.js → PersonaProfilesPanel-D6oFDKe0.js} +3 -3
- package/ui/dist/assets/{PersonasPanel-DXRE6koM.js → PersonasPanel-XsR9J5QK.js} +1 -1
- package/ui/dist/assets/RecipesPanel-DIzaTWEr.js +1 -0
- package/ui/dist/assets/{SecurityPanel-CAxyqD4i.js → SecurityPanel-ir9MHcLT.js} +1 -1
- package/ui/dist/assets/SelfImprovePanel-qYPMv78V.js +1 -0
- package/ui/dist/assets/{SelfProposalsPanel-BmPbk1ID.js → SelfProposalsPanel-CH1Y2atb.js} +2 -2
- package/ui/dist/assets/SessionsPanel-CCdc_C6t.js +1 -0
- package/ui/dist/assets/{SessionsTab-BiCfOITs.js → SessionsTab-C7PUd5SE.js} +1 -1
- package/ui/dist/assets/SettingsPanel-DfcDQBQt.js +1 -0
- package/ui/dist/assets/SettingsView-CII7Yww0.js +2 -0
- package/ui/dist/assets/{SkeletonLoader-D5oEHJJo.js → SkeletonLoader-sSfYo-Fz.js} +1 -1
- package/ui/dist/assets/{SkillsPanel-Ba069IpL.js → SkillsPanel-Tmqrdycy.js} +1 -1
- package/ui/dist/assets/{SomaView-UgcNHPOe.js → SomaView-BC9JpyMl.js} +1 -1
- package/ui/dist/assets/{StatCard-1ezaANF5.js → StatCard-SVWYzHIM.js} +1 -1
- package/ui/dist/assets/{StatusBadge-BBJ5EQaq.js → StatusBadge-BB7Tx0KG.js} +1 -1
- package/ui/dist/assets/{Tabs-B73fv4WC.js → Tabs-Dq9rfqV_.js} +1 -1
- package/ui/dist/assets/TeamsPanel-BwcZrYac.js +1 -0
- package/ui/dist/assets/TelemetryPanel-O4pGzY8E.js +1 -0
- package/ui/dist/assets/{TitanCanvas-CSUiusqg.js → TitanCanvas-CLwx89l2.js} +26 -81
- package/ui/dist/assets/ToolsView-Cxou2lHO.js +2 -0
- package/ui/dist/assets/{Tooltip-B6gn3X-e.js → Tooltip-D7oIArbx.js} +1 -1
- package/ui/dist/assets/TraceViewer-B7wBtNlu.js +1 -0
- package/ui/dist/assets/TrainingPanel-B_w0hutr.js +1 -0
- package/ui/dist/assets/{VoiceOverlay-BUmUQnVH.js → VoiceOverlay-BRfR3aax.js} +5 -15
- package/ui/dist/assets/{VramPanel-B6bz7SXa.js → VramPanel-CnHayj0l.js} +1 -1
- package/ui/dist/assets/{WatchView-TlR65a9N.js → WatchView-C5JexInX.js} +1 -1
- package/ui/dist/assets/WorkTab-DBdmjPyu.js +1 -0
- package/ui/dist/assets/{WorkflowsPanel-DFASgmHr.js → WorkflowsPanel-B20_l9zB.js} +3 -3
- package/ui/dist/assets/archive-b4MAXaqT.js +6 -0
- package/ui/dist/assets/{arrow-left-BYsiBeLD.js → arrow-left-CVNtxSv2.js} +1 -1
- package/ui/dist/assets/book-open-C01Cuvcr.js +6 -0
- package/ui/dist/assets/bot-B9_o_8Ao.js +6 -0
- package/ui/dist/assets/{chart-column-CS2cfm11.js → chart-column-GazTh38a.js} +1 -1
- package/ui/dist/assets/{check-B3cAKmN5.js → check-BLyCNJ5r.js} +1 -1
- package/ui/dist/assets/{chevron-right-BK_DfS2Z.js → chevron-right-C0fqPIeZ.js} +1 -1
- package/ui/dist/assets/{chevron-up-nvVN6RG5.js → chevron-up-Cuolucpe.js} +1 -1
- package/ui/dist/assets/{circle-check-big-CFOqr4iL.js → circle-check-big-Cu9C88pX.js} +1 -1
- package/ui/dist/assets/circle-stop-DDi4g3Z8.js +6 -0
- package/ui/dist/assets/{clock-rukpY_Oe.js → clock-3qGOTqaY.js} +1 -1
- package/ui/dist/assets/{dollar-sign-H1r95Esh.js → dollar-sign-Ceuo_See.js} +1 -1
- package/ui/dist/assets/{download-Ck8ONCke.js → download-zjVa__-G.js} +1 -1
- package/ui/dist/assets/{external-link-DqIplRhE.js → external-link-BGcox8dn.js} +1 -1
- package/ui/dist/assets/file-text-D5QL_N94.js +6 -0
- package/ui/dist/assets/flask-conical-y6Cy-6i3.js +6 -0
- package/ui/dist/assets/folder-4EFCpxV5.js +6 -0
- package/ui/dist/assets/{funnel-B_49OMiv.js → funnel-aaBOyHMd.js} +1 -1
- package/ui/dist/assets/{git-branch-BXbtPQU4.js → git-branch-BAviLfpT.js} +1 -1
- package/ui/dist/assets/index-BXK3Cqa2.js +322 -0
- package/ui/dist/assets/index-CoDvqC1m.css +1 -0
- package/ui/dist/assets/{layers-BF8CtoM5.js → layers-D5vIJhja.js} +1 -1
- package/ui/dist/assets/{legacy-JlbzNxjn.js → legacy-cJmQVDHK.js} +1 -1
- package/ui/dist/assets/{lightbulb-AaSBYG59.js → lightbulb-hxfajwJk.js} +1 -1
- package/ui/dist/assets/{list-todo-6HDCogwb.js → list-todo-B7VBfcd1.js} +1 -1
- package/ui/dist/assets/message-square-CQ3hYQbr.js +6 -0
- package/ui/dist/assets/mic-DTNzPnWH.js +6 -0
- package/ui/dist/assets/missions-CU4zDXVs.js +1 -0
- package/ui/dist/assets/{network-DbUTcR4O.js → network-CIXjczic.js} +1 -1
- package/ui/dist/assets/{pause-CChMZN2N.js → pause-CeiFt1tR.js} +1 -1
- package/ui/dist/assets/pencil-YPspIW8F.js +6 -0
- package/ui/dist/assets/{play-5lX0LsRr.js → play-DScs5qIy.js} +1 -1
- package/ui/dist/assets/{plug-BHc9XYgx.js → plug-C4ZWarwy.js} +1 -1
- package/ui/dist/assets/{plus-cRBC7ztV.js → plus-BceO4k1r.js} +1 -1
- package/ui/dist/assets/{proxy-BcAEe4-D.js → proxy-e5cYG9rZ.js} +1 -1
- package/ui/dist/assets/radio-B1lATllG.js +6 -0
- package/ui/dist/assets/{refresh-cw-fzP9o9Rx.js → refresh-cw-DIU4V0y3.js} +1 -1
- package/ui/dist/assets/{rotate-ccw-BFfyhtVm.js → rotate-ccw-7YrkxuUf.js} +1 -1
- package/ui/dist/assets/{save-3aooT5O1.js → save-BGrQ7Adz.js} +1 -1
- package/ui/dist/assets/{search-Df41RABJ.js → search-BJbRC2iK.js} +1 -1
- package/ui/dist/assets/{send-DCeeK62O.js → send-kVQXhRa0.js} +1 -1
- package/ui/dist/assets/server-_EOC2O2L.js +6 -0
- package/ui/dist/assets/{shield-check-BZQR9b0x.js → shield-check-Bhw8FUp0.js} +1 -1
- package/ui/dist/assets/terminal-Ba-zvC4N.js +6 -0
- package/ui/dist/assets/test-tube-DqCmCNDF.js +11 -0
- package/ui/dist/assets/{toggle-right-CNPtGqxb.js → toggle-right-BGgdvl64.js} +1 -1
- package/ui/dist/assets/{trash-2-nNLkUnFl.js → trash-2-DE2tk1lo.js} +1 -1
- package/ui/dist/assets/{trending-up-CZ57ONJ5.js → trending-up-C9lsLDad.js} +1 -1
- package/ui/dist/assets/{trophy-CE89gMmf.js → trophy-CMl1BT1j.js} +1 -1
- package/ui/dist/assets/volume-2-CGbWkkQ0.js +6 -0
- package/ui/dist/assets/wifi-off-CR1M6yEY.js +6 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/sw.js +1 -1
- package/ui/dist/assets/BackupPanel-C399wlfl.js +0 -1
- package/ui/dist/assets/CPActivity-CxIwdh5A.js +0 -1
- package/ui/dist/assets/CPAgents-CRNNvX7P.js +0 -1
- package/ui/dist/assets/CPApprovals-n-EwBTzt.js +0 -1
- package/ui/dist/assets/CPCosts-vKyv2IgA.js +0 -1
- package/ui/dist/assets/CPDashboard-DfrJDXIH.js +0 -1
- package/ui/dist/assets/CPFiles-DR7H1JKJ.js +0 -1
- package/ui/dist/assets/CPGoals-CXJvqD7-.js +0 -1
- package/ui/dist/assets/CPIssueDetail-Ceps7yHX.js +0 -1
- package/ui/dist/assets/CPIssues-CQBROgWu.js +0 -1
- package/ui/dist/assets/CPLayout-K1tx49gT.js +0 -17
- package/ui/dist/assets/CPOrg-ogYwckYg.js +0 -8
- package/ui/dist/assets/CPRuns-CBtH35hm.js +0 -1
- package/ui/dist/assets/ChannelsPanel-CecIa7DB.js +0 -1
- package/ui/dist/assets/CommandPostHub-CVlmR0QB.js +0 -24
- package/ui/dist/assets/FleetPanel-J2KqOAPo.js +0 -1
- package/ui/dist/assets/InfraView-BM5Hh-Ph.js +0 -2
- package/ui/dist/assets/InlineEditableField-DV4ZMlNT.js +0 -1
- package/ui/dist/assets/IntegrationsPanel-BwBAL4Mm.js +0 -1
- package/ui/dist/assets/IntelligenceView-CFRB_7h1.js +0 -2
- package/ui/dist/assets/LearningPanel-CLvCXlyX.js +0 -1
- package/ui/dist/assets/McpPanel-ZLSvzGaf.js +0 -1
- package/ui/dist/assets/MemoryWikiPanel-rN_HFem7.js +0 -11
- package/ui/dist/assets/OverviewPanel-CfD0O2n7.js +0 -1
- package/ui/dist/assets/RecipesPanel-DcX7l-dT.js +0 -1
- package/ui/dist/assets/SelfImprovePanel-CvlHcYH8.js +0 -1
- package/ui/dist/assets/SessionsPanel-D73nmmpu.js +0 -1
- package/ui/dist/assets/SettingsPanel-DbWCgc_k.js +0 -1
- package/ui/dist/assets/SettingsView-D4T87f-G.js +0 -2
- package/ui/dist/assets/TeamsPanel-CFwEgtjK.js +0 -1
- package/ui/dist/assets/TelemetryPanel-DyPn6MMU.js +0 -1
- package/ui/dist/assets/ToolsView-DPQwiQf8.js +0 -2
- package/ui/dist/assets/TraceViewer-Bnca4oPo.js +0 -1
- package/ui/dist/assets/TrainingPanel-Q9kTsTek.js +0 -1
- package/ui/dist/assets/WorkTab-BWt-Rmnx.js +0 -1
- package/ui/dist/assets/briefcase-DNCapwwn.js +0 -6
- package/ui/dist/assets/chevron-down-CwmUH3Wg.js +0 -6
- package/ui/dist/assets/folder-CtL9eBd6.js +0 -11
- package/ui/dist/assets/index-BrE4BVsB.js +0 -277
- package/ui/dist/assets/index-CVD3Q-nL.css +0 -1
- package/ui/dist/assets/missions-17O7FHUZ.js +0 -1
- package/ui/dist/assets/target-oWkFNXaP.js +0 -6
- package/ui/dist/assets/terminal-CDpsRBW8.js +0 -11
- package/ui/dist/assets/users-qVhbbxPL.js +0 -16
- 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
|
|
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 {
|
|
12
|
-
import {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
254
|
-
{ role: "user", content: `
|
|
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
|
-
|
|
257
|
-
${
|
|
258
|
-
|
|
259
|
-
|
|
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
|
}
|