alvin-bot 5.7.0 → 5.8.1

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 (137) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +25 -31
  3. package/dist/claude.js +1 -102
  4. package/dist/config.js +1 -96
  5. package/dist/engine.js +1 -90
  6. package/dist/find-claude-binary.js +1 -98
  7. package/dist/handlers/async-agent-chunk-handler.js +1 -50
  8. package/dist/handlers/background-bypass.js +1 -75
  9. package/dist/handlers/commands.js +1 -2336
  10. package/dist/handlers/cron-progress.js +1 -52
  11. package/dist/handlers/document.js +1 -194
  12. package/dist/handlers/message.js +1 -959
  13. package/dist/handlers/photo.js +1 -154
  14. package/dist/handlers/platform-message.js +1 -360
  15. package/dist/handlers/stuck-timer.js +1 -54
  16. package/dist/handlers/video.js +1 -237
  17. package/dist/handlers/voice.js +1 -148
  18. package/dist/i18n.js +1 -805
  19. package/dist/index.js +1 -697
  20. package/dist/init-data-dir.js +1 -98
  21. package/dist/middleware/auth.js +1 -233
  22. package/dist/migrate.js +1 -162
  23. package/dist/paths.js +1 -146
  24. package/dist/platforms/discord.js +1 -175
  25. package/dist/platforms/index.js +1 -130
  26. package/dist/platforms/signal.js +1 -205
  27. package/dist/platforms/slack-slash-parser.js +1 -32
  28. package/dist/platforms/slack.js +1 -501
  29. package/dist/platforms/telegram.js +1 -111
  30. package/dist/platforms/types.js +1 -8
  31. package/dist/platforms/whatsapp-auth-helpers.js +1 -53
  32. package/dist/platforms/whatsapp.js +1 -707
  33. package/dist/providers/claude-sdk-provider.js +1 -565
  34. package/dist/providers/codex-cli-provider.js +1 -134
  35. package/dist/providers/index.js +1 -7
  36. package/dist/providers/ollama-provider.js +1 -32
  37. package/dist/providers/openai-compatible.js +1 -406
  38. package/dist/providers/registry.js +1 -352
  39. package/dist/providers/runtime-header.js +1 -45
  40. package/dist/providers/tool-executor.js +1 -475
  41. package/dist/providers/types.js +1 -227
  42. package/dist/services/access.js +1 -144
  43. package/dist/services/allowed-users-gate.js +1 -56
  44. package/dist/services/alvin-dispatch.js +1 -174
  45. package/dist/services/alvin-mcp-tools.js +1 -104
  46. package/dist/services/asset-index.js +1 -224
  47. package/dist/services/async-agent-parser.js +1 -418
  48. package/dist/services/async-agent-watcher.js +1 -583
  49. package/dist/services/auto-diagnostic.js +1 -228
  50. package/dist/services/broadcast.js +1 -52
  51. package/dist/services/browser-manager.js +1 -562
  52. package/dist/services/browser-webfetch.js +1 -127
  53. package/dist/services/browser.js +1 -121
  54. package/dist/services/cdp-bootstrap.js +1 -357
  55. package/dist/services/compaction.js +1 -144
  56. package/dist/services/critical-notify.js +1 -203
  57. package/dist/services/cron-resolver.js +1 -58
  58. package/dist/services/cron-scheduling.js +1 -310
  59. package/dist/services/cron.js +1 -861
  60. package/dist/services/custom-tools.js +1 -317
  61. package/dist/services/delivery-queue.js +1 -173
  62. package/dist/services/delivery-registry.js +1 -21
  63. package/dist/services/disk-cleanup.js +1 -203
  64. package/dist/services/elevenlabs.js +1 -58
  65. package/dist/services/embeddings/auto-detect.js +1 -74
  66. package/dist/services/embeddings/fts5.js +1 -108
  67. package/dist/services/embeddings/gemini.js +1 -65
  68. package/dist/services/embeddings/index.js +1 -496
  69. package/dist/services/embeddings/ollama.js +1 -78
  70. package/dist/services/embeddings/openai.js +1 -49
  71. package/dist/services/embeddings/provider.js +1 -22
  72. package/dist/services/embeddings/vector-base.js +1 -113
  73. package/dist/services/embeddings-migration.js +1 -193
  74. package/dist/services/embeddings.js +1 -9
  75. package/dist/services/env-file.js +1 -50
  76. package/dist/services/exec-guard.js +1 -71
  77. package/dist/services/fallback-order.js +1 -154
  78. package/dist/services/file-permissions.js +1 -93
  79. package/dist/services/heartbeat-file.js +1 -65
  80. package/dist/services/heartbeat.js +1 -313
  81. package/dist/services/hooks.js +1 -44
  82. package/dist/services/imagegen.js +1 -72
  83. package/dist/services/language-detect.js +1 -154
  84. package/dist/services/markdown.js +1 -63
  85. package/dist/services/mcp.js +1 -263
  86. package/dist/services/memory-extractor.js +1 -178
  87. package/dist/services/memory-inject-mode.js +1 -43
  88. package/dist/services/memory-layers.js +1 -156
  89. package/dist/services/memory.js +1 -146
  90. package/dist/services/ollama-manager.js +1 -339
  91. package/dist/services/permissions-wizard.js +1 -291
  92. package/dist/services/personality.js +1 -376
  93. package/dist/services/plugins.js +1 -171
  94. package/dist/services/preflight.js +1 -292
  95. package/dist/services/process-manager.js +1 -291
  96. package/dist/services/release-highlights.js +1 -79
  97. package/dist/services/reminders.js +1 -97
  98. package/dist/services/restart.js +1 -48
  99. package/dist/services/security-audit.js +1 -74
  100. package/dist/services/self-diagnosis.js +1 -272
  101. package/dist/services/self-search.js +1 -129
  102. package/dist/services/session-persistence.js +1 -237
  103. package/dist/services/session.js +1 -282
  104. package/dist/services/skills.js +1 -290
  105. package/dist/services/ssrf-guard.js +1 -162
  106. package/dist/services/standing-orders.js +1 -29
  107. package/dist/services/steer-channel.js +1 -46
  108. package/dist/services/stop-controller.js +1 -52
  109. package/dist/services/subagent-dedup.js +1 -86
  110. package/dist/services/subagent-delivery.js +1 -452
  111. package/dist/services/subagent-stats.js +1 -123
  112. package/dist/services/subagents.js +1 -814
  113. package/dist/services/sudo.js +1 -329
  114. package/dist/services/telegram.js +1 -158
  115. package/dist/services/timing-safe-bearer.js +1 -51
  116. package/dist/services/tool-discovery.js +1 -214
  117. package/dist/services/trends.js +1 -580
  118. package/dist/services/updater.js +1 -291
  119. package/dist/services/usage-tracker.js +1 -144
  120. package/dist/services/users.js +1 -271
  121. package/dist/services/voice.js +1 -104
  122. package/dist/services/watchdog-brake.js +1 -154
  123. package/dist/services/watchdog.js +1 -311
  124. package/dist/services/workspaces.js +1 -276
  125. package/dist/tui/index.js +1 -667
  126. package/dist/util/console-formatter.js +1 -109
  127. package/dist/util/debounce.js +1 -24
  128. package/dist/util/telegram-error-filter.js +1 -62
  129. package/dist/version.js +1 -24
  130. package/dist/web/bind-strategy.js +1 -42
  131. package/dist/web/canvas.js +1 -30
  132. package/dist/web/doctor-api.js +1 -604
  133. package/dist/web/openai-compat.js +1 -252
  134. package/dist/web/server.js +1 -1902
  135. package/dist/web/setup-api.js +1 -1101
  136. package/package.json +5 -2
  137. package/dist/.metadata_never_index +0 -0
@@ -1,580 +1 @@
1
- /**
2
- * Predictive Maintenance via Trends (Self-Preservation Phase 2, 3J).
3
- *
4
- * Mechanism:
5
- * 1. Once every 24 h: snapshot lightweight health metrics and append
6
- * one JSON line to ~/.alvin-bot/state/trends.jsonl
7
- * 2. After the file has ≥ 7 days of data: also run a daily AI
8
- * "anomaly detection" pass over the last 30 days of snapshots
9
- * 3. If the AI flags a concerning trend → DM operator via 1D
10
- *
11
- * Why daily and not continuous: trends are about slow degradation
12
- * (memory growth, error-rate increase, crash-frequency drift). A
13
- * 24-hour sampling cadence is right for these timescales and keeps
14
- * the storage + AI-call cost near zero.
15
- *
16
- * Storage format — JSONL, one line per day:
17
- *
18
- * {"ts": "2026-05-13T04:00:00Z", "uptime_s": 86400, "rss_mb": 105,
19
- * "crashes_24h": 0, "diag_24h": 0, "errors_24h": 3,
20
- * "provider": "claude-sdk", "version": "5.0.0"}
21
- *
22
- * The JSONL design is deliberate: easy to inspect with tail/head/awk,
23
- * easy to truncate to last N days, no parsing pitfalls. The AI gets
24
- * the whole tail as plain text — works with small-context models.
25
- *
26
- * Provider-agnostic — same engine.query() pipeline as 3I.
27
- *
28
- * Opt-out:
29
- * ALVIN_DISABLE_TRENDS=true → skip 3J entirely
30
- * ALVIN_DISABLE_SELF_PRESERVATION=true → skip all Phase-1/2
31
- *
32
- * Tunable for testing:
33
- * ALVIN_TRENDS_INTERVAL_HOURS=24 → snapshot cadence
34
- * ALVIN_TRENDS_AI_AFTER_DAYS=7 → days of data before AI analysis kicks in
35
- */
36
- import { appendFileSync, existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
37
- import { join, dirname } from "path";
38
- import { homedir } from "os";
39
- import { BOT_VERSION } from "../version.js";
40
- import { emitCritical } from "./critical-notify.js";
41
- const TRENDS_PATH = join(homedir(), ".alvin-bot", "state", "trends.jsonl");
42
- /**
43
- * B2 — peak-uptime high-water mark. The trends collector takes its FIRST
44
- * snapshot ~60s after every boot (startTrendsCollector schedules it at
45
- * 60_000ms). takeSnapshot() records uptime_s = process.uptime(), so the
46
- * first post-restart sample is structurally ≈ 62s. With deliberate
47
- * restarts (/update, launchctl reload) those ~62s samples dominate
48
- * trends.jsonl, so the 30-day AI pass perpetually concludes "restart
49
- * loop, never lives past ~62s" even when the process has actually been
50
- * continuously up for hours by the time the daily snapshot fires.
51
- *
52
- * Fix: persist the MAXIMUM real uptime this bot has ever observed (across
53
- * process generations) and record it on every snapshot as uptime_peak_s.
54
- * The peak only ever derives from process.uptime() — it is never
55
- * fabricated or extrapolated. The anomaly evaluation then keys on the
56
- * peak (hasRepresentativeUptime), so a process that genuinely lived for
57
- * hours is not flagged as a ~62s loop, while a genuine fast-restart loop
58
- * (peak never climbs past the startup transient) still fires.
59
- *
60
- * Stored next to trends.jsonl (state/), honoring ALVIN_DATA_DIR so tests
61
- * and non-default installs work. Survives restarts by design — that is
62
- * the whole point of a high-water mark.
63
- */
64
- function trendsStateDir() {
65
- const base = process.env.ALVIN_DATA_DIR || join(homedir(), ".alvin-bot");
66
- return join(base, "state");
67
- }
68
- function uptimePeakPath() {
69
- return join(trendsStateDir(), "uptime-peak.json");
70
- }
71
- /**
72
- * The startup transient: takeSnapshot's first sample is taken ~60s after
73
- * boot, so any uptime at/under this is indistinguishable from "just
74
- * restarted". An uptime ABOVE this proves the process actually lived past
75
- * the post-restart sampling window. 600s (10 min) is comfortably above
76
- * the 60s first-sample delay + scheduling jitter and far below the 24h
77
- * cron cadence, so a healthy bot trivially clears it while a real
78
- * crash-loop (exits within seconds/a couple minutes) never does.
79
- */
80
- export const STARTUP_TRANSIENT_S = 600;
81
- /**
82
- * Read the persisted peak uptime, fold in the CURRENT real uptime, persist
83
- * the (possibly larger) high-water mark, and return it. Pure w.r.t. time
84
- * sources: the only uptime input is process.uptime() — nothing invented.
85
- * Disk failures degrade gracefully to the current real uptime.
86
- */
87
- function bumpAndReadUptimePeak() {
88
- const currentReal = Math.round(process.uptime());
89
- let stored = 0;
90
- try {
91
- const raw = readFileSync(uptimePeakPath(), "utf-8");
92
- const parsed = JSON.parse(raw);
93
- if (typeof parsed.peak_s === "number" && Number.isFinite(parsed.peak_s) && parsed.peak_s > 0) {
94
- stored = parsed.peak_s;
95
- }
96
- }
97
- catch {
98
- // No file yet / unreadable — start the high-water mark from the
99
- // current real uptime. Not an error.
100
- }
101
- const peak = Math.max(stored, currentReal);
102
- try {
103
- mkdirSync(trendsStateDir(), { recursive: true });
104
- writeFileSync(uptimePeakPath(), JSON.stringify({ peak_s: peak }), "utf-8");
105
- }
106
- catch {
107
- // Disk full / permissions — non-fatal; we still return the in-memory peak.
108
- }
109
- return peak;
110
- }
111
- const DEFAULT_INTERVAL_HOURS = 24;
112
- const DEFAULT_AI_THRESHOLD_DAYS = 7;
113
- const MAX_RETAIN_DAYS = 90;
114
- /**
115
- * What counts as an "error" line in alvin-bot.err.log for the
116
- * errors_24h metric.
117
- *
118
- * stderr IS the bot's error channel, so the default is: count every
119
- * timestamped line. But a few subsystems deliberately write *benign*
120
- * operational diagnostics to stderr:
121
- *
122
- * - subagent-delivery's self-healing Markdown→plaintext retry
123
- * (a successful, expected fallback — not an error)
124
- * - critical-notify's own delivery-outcome line, kept on stderr on
125
- * purpose so it stays visible even in brake/crash context
126
- * - B3: subagent-delivery's "send failed … chat not found" line for a
127
- * stale/test async-agent whose delivery target chat no longer exists
128
- * (e.g. the recurring chat_id:1 test agent). This is benign noise,
129
- * not a real fault: the target chat is invalid, the watcher now
130
- * abandons such agents (see async-agent-watcher.ts), and counting it
131
- * made errors_24h creep upward indefinitely on every poll cycle.
132
- * The match is DELIBERATELY narrow — it requires BOTH the
133
- * `[subagent-delivery] send failed` prefix AND a `chat not found`
134
- * cause on the same line. A subagent-delivery failure for ANY other
135
- * reason (network, rate-limit, parse) is still counted, and a
136
- * `chat not found` from ANY OTHER subsystem (a real misconfigured
137
- * target) is still counted.
138
- *
139
- * Counting those turned this very monitor into a false-alarm generator:
140
- * it flagged its OWN log lines plus every release's restart churn, so
141
- * the alert kept firing even after the underlying issue was fixed.
142
- *
143
- * This is a BLACKLIST (count everything except the known benign
144
- * emitters), not a whitelist of error signatures — a health monitor
145
- * must never silently miss a novel real error. New benign emitters, if
146
- * any, get added here in one place instead of being chased across the
147
- * codebase.
148
- */
149
- export const ERR_LOG_PATTERN = /^(?!.*(?:\[critical-notify\]|\[subagent-delivery\] Markdown parse failed|\[subagent-delivery\] send failed.*chat not found)).+/;
150
- let trendsTimer = null;
151
- function isDisabled() {
152
- return (process.env.ALVIN_DISABLE_TRENDS === "true" ||
153
- process.env.ALVIN_DISABLE_SELF_PRESERVATION === "true");
154
- }
155
- function countLogLinesLast24h(filename, pattern) {
156
- const path = join(homedir(), ".alvin-bot", "logs", filename);
157
- if (!existsSync(path))
158
- return 0;
159
- try {
160
- const content = readFileSync(path, "utf-8");
161
- const cutoff = Date.now() - 24 * 60 * 60 * 1000;
162
- let count = 0;
163
- for (const line of content.split("\n")) {
164
- const tsMatch = line.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)/);
165
- if (!tsMatch)
166
- continue;
167
- const lineTs = new Date(tsMatch[1]).getTime();
168
- if (Number.isFinite(lineTs) && lineTs >= cutoff) {
169
- if (!pattern || pattern.test(line))
170
- count++;
171
- }
172
- }
173
- return count;
174
- }
175
- catch {
176
- return 0;
177
- }
178
- }
179
- function readWatchdogCrashes24h() {
180
- try {
181
- const path = join(homedir(), ".alvin-bot", "state", "watchdog.json");
182
- if (!existsSync(path))
183
- return 0;
184
- const data = JSON.parse(readFileSync(path, "utf-8"));
185
- return data.dailyCrashCount ?? 0;
186
- }
187
- catch {
188
- return 0;
189
- }
190
- }
191
- function countDiagnosticBundlesLast24h() {
192
- try {
193
- const dir = join(homedir(), ".alvin-bot", "diagnostics");
194
- if (!existsSync(dir))
195
- return 0;
196
- const { readdirSync, statSync } = require("fs");
197
- const cutoff = Date.now() - 24 * 60 * 60 * 1000;
198
- return readdirSync(dir)
199
- .filter((f) => f.endsWith(".md") && !f.endsWith(".analysis.md"))
200
- .filter((f) => {
201
- try {
202
- return statSync(join(dir, f)).mtimeMs >= cutoff;
203
- }
204
- catch {
205
- return false;
206
- }
207
- }).length;
208
- }
209
- catch {
210
- return 0;
211
- }
212
- }
213
- function takeSnapshot(activeProvider) {
214
- const mem = process.memoryUsage();
215
- return {
216
- ts: new Date().toISOString(),
217
- uptime_s: Math.round(process.uptime()),
218
- uptime_peak_s: bumpAndReadUptimePeak(),
219
- rss_mb: Math.round(mem.rss / 1024 / 1024),
220
- heap_mb: Math.round(mem.heapUsed / 1024 / 1024),
221
- crashes_24h: readWatchdogCrashes24h(),
222
- diag_24h: countDiagnosticBundlesLast24h(),
223
- errors_24h: countLogLinesLast24h("alvin-bot.err.log", ERR_LOG_PATTERN),
224
- provider: activeProvider,
225
- version: BOT_VERSION,
226
- };
227
- }
228
- function appendSnapshot(snap) {
229
- try {
230
- mkdirSync(dirname(TRENDS_PATH), { recursive: true });
231
- appendFileSync(TRENDS_PATH, JSON.stringify(snap) + "\n");
232
- }
233
- catch {
234
- // Disk full / permissions — non-fatal
235
- }
236
- }
237
- function readSnapshots(lastN = 30) {
238
- if (!existsSync(TRENDS_PATH))
239
- return [];
240
- try {
241
- const content = readFileSync(TRENDS_PATH, "utf-8");
242
- const lines = content.split("\n").filter((l) => l.trim());
243
- const recent = lines.slice(-lastN);
244
- return recent
245
- .map((l) => {
246
- try {
247
- return JSON.parse(l);
248
- }
249
- catch {
250
- return null;
251
- }
252
- })
253
- .filter((s) => s !== null);
254
- }
255
- catch {
256
- return [];
257
- }
258
- }
259
- const TREND_PROMPT_TEMPLATE = `You are an SRE monitoring an Alvin Bot instance over the last days.
260
- Below is one JSON line per day with the bot's daily health metrics.
261
-
262
- Detect any CONCERNING trend that suggests slow degradation — like:
263
- - Memory (rss_mb / heap_mb) growing day over day
264
- - Error rate (errors_24h) climbing
265
- - Crashes (crashes_24h) above 0 for multiple days
266
- - Diagnostic bundles (diag_24h) > 0 repeatedly
267
-
268
- If there is NO concerning trend, respond with EXACTLY this one line:
269
- ANOMALY: NONE
270
-
271
- If there IS a concerning trend, respond in this 3-line format — nothing else:
272
-
273
- ANOMALY: <one short sentence — what trend you noticed>
274
- SEVERITY: <warn | critical>
275
- SUGGESTION: <one shell command OR observation for the operator>
276
-
277
- --- LAST {N} DAYS OF SNAPSHOTS ---
278
- {SNAPSHOTS}
279
- --- END ---`;
280
- /**
281
- * V56 — Recent crash-evidence window.
282
- *
283
- * hasRealCrashEvidence keys the WARN-suppression gate on whether ANY
284
- * persisted snapshot recorded a real crash. Snapshots persist for up to
285
- * MAX_RETAIN_DAYS and the AI pass reads the last 30 (≈30 days at the 24h
286
- * cadence). If the WHOLE 30-day history is considered, a history briefly
287
- * poisoned by miscounted deliberate restarts (pre-v5.5.0 accounting bug,
288
- * fixed in v5.5.0 for NEW snapshots but the bad lines persist ~30 days)
289
- * keeps crash-evidence "true" — so the B2/B4 gate never suppresses and the
290
- * false WARN fires for ~a month instead of self-healing.
291
- *
292
- * Restricting the evidence check to the most recent ~48h means: once
293
- * v5.5.0's correct accounting produces clean recent snapshots
294
- * (crashes_24h=0), the false WARN clears within ~a day — while a GENUINE
295
- * crash loop (real crashes in the recent window) still returns true and
296
- * the WARN still fires (the protective purpose is intact).
297
- *
298
- * 48h (not 24h) is chosen because the snapshot cadence is ~24h
299
- * (DEFAULT_INTERVAL_HOURS): a 48h window reliably retains the last 1–2
300
- * daily snapshots even across day-boundary jitter / a skipped cron tick,
301
- * so a genuine recent crash loop is never missed, while crash evidence
302
- * older than ~2 days (the poisoned history) ages out and self-heals. A
303
- * timestamp window (not "last N snapshots") is used so self-healing keys
304
- * on real wall-clock time and is robust to cadence changes / test-tuned
305
- * ALVIN_TRENDS_INTERVAL_HOURS.
306
- */
307
- export const RECENT_CRASH_WINDOW_MS = 48 * 60 * 60 * 1000;
308
- /**
309
- * Returns true if at least one snapshot WITHIN THE RECENT WINDOW has a
310
- * non-zero crashes_24h value, meaning a REAL crash (not an
311
- * expected/deliberate restart) was recorded recently.
312
- *
313
- * After the B1 fix, deliberate restarts (SIGTERM / launchctl reload /
314
- * /restart / /update) write the expectedRestart beacon flag and are NOT
315
- * counted in dailyCrashCount. So crashes_24h === 0 across the recent
316
- * snapshots means the bot was only restarted intentionally — no real
317
- * crash evidence — even if OLDER snapshots were poisoned by the
318
- * pre-v5.5.0 miscount (those age out of the window and the false WARN
319
- * self-heals; see RECENT_CRASH_WINDOW_MS).
320
- *
321
- * Recency is determined from each snapshot's `ts` (ISO 8601, written by
322
- * takeSnapshot via new Date().toISOString()). FAIL-SAFE: a snapshot whose
323
- * `ts` is missing or unparseable is treated as in-window (counted) — a
324
- * health monitor must fail toward "visible", never go blind on bad data.
325
- *
326
- * Pure function, exported for unit testing.
327
- */
328
- export function hasRealCrashEvidence(snaps, nowMs = Date.now()) {
329
- const cutoff = nowMs - RECENT_CRASH_WINDOW_MS;
330
- return snaps.some((s) => {
331
- if (!(typeof s.crashes_24h === "number" && s.crashes_24h > 0))
332
- return false;
333
- // FAIL-SAFE: no/garbage ts → treat as recent (never silence on bad data).
334
- if (typeof s.ts !== "string")
335
- return true;
336
- const t = Date.parse(s.ts);
337
- if (!Number.isFinite(t))
338
- return true;
339
- return t >= cutoff;
340
- });
341
- }
342
- /**
343
- * B2 — Returns true if AT LEAST ONE snapshot proves the bot process
344
- * genuinely lived past the startup transient (i.e. it is NOT a ~62s
345
- * restart loop).
346
- *
347
- * The first per-boot snapshot is structurally taken ~60s after boot, so
348
- * its raw uptime_s is always ≈ 62 regardless of how long the process
349
- * subsequently runs. uptime_peak_s is the high-water mark of REAL
350
- * process.uptime() carried across process generations, so a single
351
- * snapshot whose peak exceeds STARTUP_TRANSIENT_S is hard evidence the
352
- * process did live for a representative duration. Legacy pre-B2 lines
353
- * have no uptime_peak_s — we fall back to their raw uptime_s, so a legacy
354
- * 24h cron snapshot still counts as representative on its own.
355
- *
356
- * A genuine fast-restart loop never lets the peak climb past the
357
- * transient, so it correctly returns false and the WARN still fires.
358
- *
359
- * Pure function, exported for unit testing.
360
- */
361
- export function hasRepresentativeUptime(snaps) {
362
- return snaps.some((s) => {
363
- const peak = typeof s.uptime_peak_s === "number" && Number.isFinite(s.uptime_peak_s)
364
- ? s.uptime_peak_s
365
- : typeof s.uptime_s === "number" && Number.isFinite(s.uptime_s)
366
- ? s.uptime_s
367
- : 0;
368
- return peak > STARTUP_TRANSIENT_S;
369
- });
370
- }
371
- /**
372
- * B2/B4 — Pure crash/restart WARN suppression decision.
373
- *
374
- * Encodes the SAME two gates, in the SAME precedence, that dailyTask
375
- * applies inline (B2 before B4). Extracted as a pure function purely so
376
- * the gate COMPOSITION (not just each helper in isolation) is unit
377
- * testable — the helpers are individually correct but the interaction
378
- * is where the real-crash-loop-after-a-healthy-period regression lives.
379
- *
380
- * Returns the suppression reason, or "none" when the WARN must fire.
381
- *
382
- * - "representative-uptime" (B2): a deliberate-restart / sampling
383
- * artifact — the AI saw ~62s uptimes but a snapshot peak proves the
384
- * process actually lived past the startup transient. ONLY applies
385
- * when there is no real crash evidence: a genuine crash loop after a
386
- * prior healthy period still carries the persisted high peak, so
387
- * without the crash-evidence guard B2 would permanently and silently
388
- * swallow it. With the guard, crashes_24h>0 falls through to B4.
389
- * - "no-crash-evidence" (B4): crash/restart pattern but crashes_24h===0
390
- * everywhere (deliberate-restart-only, not a real crash loop).
391
- * - "none": the WARN is real and must be emitted.
392
- *
393
- * Pure function, exported for unit testing.
394
- */
395
- export function evaluateCrashRestartSuppression(isCrashRestartPattern, snaps) {
396
- if (!isCrashRestartPattern)
397
- return "none";
398
- const realCrash = hasRealCrashEvidence(snaps);
399
- // B2: only the deliberate-restart / sampling-artifact case. A real
400
- // crash loop (crashes_24h>0) must NOT be suppressed here even though
401
- // the persisted uptime high-water mark still reads representative.
402
- if (!realCrash && hasRepresentativeUptime(snaps))
403
- return "representative-uptime";
404
- // B4: crash/restart pattern with zero real crash evidence.
405
- if (!realCrash)
406
- return "no-crash-evidence";
407
- return "none";
408
- }
409
- /** Test-only: take a snapshot without writing to trends.jsonl. */
410
- export function __takeSnapshotForTest(activeProvider) {
411
- return takeSnapshot(activeProvider);
412
- }
413
- function parseTrendResponse(text) {
414
- if (/^ANOMALY:\s*NONE/im.test(text)) {
415
- return {
416
- anomalyDetected: false,
417
- description: "no concerning trend detected",
418
- severity: "none",
419
- suggestion: "",
420
- raw: text,
421
- };
422
- }
423
- const get = (key) => {
424
- const m = text.match(new RegExp(`^${key}:\\s*(.+?)$`, "m"));
425
- return m ? m[1].trim() : "";
426
- };
427
- const sevRaw = get("SEVERITY").toLowerCase();
428
- const sev = sevRaw === "critical" ? "critical" : "warn";
429
- return {
430
- anomalyDetected: true,
431
- description: get("ANOMALY") || "(no description)",
432
- severity: sev,
433
- suggestion: get("SUGGESTION") || "(no suggestion)",
434
- raw: text,
435
- };
436
- }
437
- export async function analyzeTrends(registry) {
438
- if (isDisabled())
439
- return null;
440
- if (!registry)
441
- return null;
442
- const snaps = readSnapshots(30);
443
- const threshold = parseInt(process.env.ALVIN_TRENDS_AI_AFTER_DAYS || "", 10) || DEFAULT_AI_THRESHOLD_DAYS;
444
- if (snaps.length < threshold)
445
- return null;
446
- let provider;
447
- try {
448
- provider = registry.getActive();
449
- }
450
- catch {
451
- return null;
452
- }
453
- if (!provider)
454
- return null;
455
- const snapsBlock = snaps.map((s) => JSON.stringify(s)).join("\n");
456
- const prompt = TREND_PROMPT_TEMPLATE.replace("{N}", String(snaps.length)).replace("{SNAPSHOTS}", snapsBlock);
457
- const abortController = new AbortController();
458
- const timer = setTimeout(() => abortController.abort(), 60_000);
459
- let fullText = "";
460
- try {
461
- for await (const chunk of provider.query({
462
- prompt,
463
- systemPrompt: "You are a precise SRE assistant. Reply ONLY in the requested format.",
464
- abortSignal: abortController.signal,
465
- })) {
466
- if (chunk.type === "text") {
467
- if (chunk.delta)
468
- fullText += chunk.delta;
469
- else if (chunk.text)
470
- fullText = chunk.text;
471
- }
472
- else if (chunk.type === "error") {
473
- clearTimeout(timer);
474
- return null;
475
- }
476
- else if (chunk.type === "done") {
477
- if (chunk.text)
478
- fullText = chunk.text;
479
- break;
480
- }
481
- }
482
- }
483
- catch {
484
- clearTimeout(timer);
485
- return null;
486
- }
487
- clearTimeout(timer);
488
- if (!fullText.trim())
489
- return null;
490
- return parseTrendResponse(fullText);
491
- }
492
- async function dailyTask(registry) {
493
- try {
494
- // Snapshot first — always, regardless of AI being available
495
- const activeProviderKey = (() => {
496
- try {
497
- return registry?.getActiveKey() || "none";
498
- }
499
- catch {
500
- return "none";
501
- }
502
- })();
503
- const snap = takeSnapshot(activeProviderKey);
504
- appendSnapshot(snap);
505
- console.log(`📊 Trends snapshot taken: rss=${snap.rss_mb}MB errors=${snap.errors_24h} crashes=${snap.crashes_24h}`);
506
- // Then attempt AI analysis if we have enough history
507
- const result = await analyzeTrends(registry);
508
- if (!result)
509
- return;
510
- if (!result.anomalyDetected) {
511
- console.log(`📊 Trends AI: no anomaly detected`);
512
- return;
513
- }
514
- const recentSnaps = readSnapshots(30);
515
- const isCrashRestartPattern = /crash|restart|loop|uptime/i.test(result.description);
516
- // B2 gate: suppress an "uptime stuck at ~62s / restart loop" WARN when
517
- // the snapshots PROVE the process actually lived past the startup
518
- // transient. The first per-boot snapshot is structurally sampled ~60s
519
- // after boot, so raw uptime_s reads ≈62 even for a perfectly healthy
520
- // bot that has been up for hours by the time the daily snapshot fires.
521
- // uptime_peak_s is the high-water mark of real process.uptime() across
522
- // process generations: if ANY snapshot's peak exceeds the transient,
523
- // the "~62s loop" conclusion is factually false. A genuine fast-restart
524
- // loop never lets the peak climb, so it is NOT suppressed here.
525
- if (isCrashRestartPattern && !hasRealCrashEvidence(recentSnaps) && hasRepresentativeUptime(recentSnaps)) {
526
- console.log(`📊 Trends AI: suppressed WARN "${result.description}" — ` +
527
- `uptime/restart pattern flagged but at least one snapshot shows a ` +
528
- `representative peak uptime (>${STARTUP_TRANSIENT_S}s); the process ` +
529
- `did live well past the post-restart sampling window, not a ~62s loop`);
530
- return;
531
- }
532
- // B4 gate: suppress WARN when the AI flags a crash/restart-loop pattern
533
- // but the historical snapshots contain ZERO real crash evidence
534
- // (crashes_24h === 0 across the board). This happens when the bot was
535
- // restarted deliberately (launchctl reload / /update / /restart) — those
536
- // produce low uptimes that the AI reads as "restart loop", but the
537
- // crash counter stays at 0 because markExpectedRestart() was written
538
- // on each clean shutdown. A real crash loop WILL have crashes_24h > 0
539
- // in at least one snapshot and will still fire the WARN.
540
- if (isCrashRestartPattern && !hasRealCrashEvidence(recentSnaps)) {
541
- console.log(`📊 Trends AI: suppressed WARN "${result.description}" — ` +
542
- `crash/restart pattern detected but crashes_24h=0 across all snapshots ` +
543
- `(deliberate-restart-only, not a real crash loop)`);
544
- return;
545
- }
546
- console.log(`📊 Trends AI: ANOMALY (${result.severity}) — ${result.description}`);
547
- emitCritical({
548
- category: "custom",
549
- severity: result.severity === "critical" ? "critical" : "warn",
550
- title: `Trend anomaly detected: ${result.description}`,
551
- detail: `30-day trend analysis flagged a concerning pattern.\n\n` +
552
- `Suggestion: ${result.suggestion}\n\n` +
553
- `Trend data: ${TRENDS_PATH}`,
554
- suggestedAction: result.suggestion,
555
- });
556
- }
557
- catch (err) {
558
- console.warn(`📊 Trends daily task threw: ${err instanceof Error ? err.message : String(err)}`);
559
- }
560
- }
561
- export function startTrendsCollector(registry) {
562
- if (isDisabled())
563
- return;
564
- const intervalH = parseInt(process.env.ALVIN_TRENDS_INTERVAL_HOURS || "", 10) || DEFAULT_INTERVAL_HOURS;
565
- const intervalMs = intervalH * 60 * 60 * 1000;
566
- // Initial: take a first snapshot after 60 s to avoid measuring the
567
- // startup transient. Subsequent snapshots every intervalMs.
568
- setTimeout(() => {
569
- void dailyTask(registry);
570
- trendsTimer = setInterval(() => void dailyTask(registry), intervalMs);
571
- if (trendsTimer.unref)
572
- trendsTimer.unref();
573
- }, 60_000);
574
- }
575
- export function stopTrendsCollector() {
576
- if (trendsTimer) {
577
- clearInterval(trendsTimer);
578
- trendsTimer = null;
579
- }
580
- }
1
+ const _0x1e479d=_0x1e32,_0x257576=_0x1e32;(function(_0x5a52ec,_0x5312d9){const _0x242304=_0x1e32,_0x238cd7=_0x1e32,_0x3360f3=_0x5a52ec();while(!![]){try{const _0x235f85=-parseInt(_0x242304(0x19c))/(0x7be*-0x3+0x2*0xb6c+0x3*0x21)+-parseInt(_0x238cd7(0x230))/(0xc5b*0x1+-0x1d4e+-0x1*-0x10f5)*(parseInt(_0x242304(0x19f))/(-0x98a*0x2+-0x9*0x63+0x1692))+parseInt(_0x238cd7(0x233))/(-0x1*-0x151+-0x253e+-0x23f1*-0x1)*(-parseInt(_0x242304(0x1dd))/(0x1d9e+0x181a*0x1+-0x35b3))+-parseInt(_0x242304(0x1fa))/(0x23c3*-0x1+0x6*-0x1c+0x2471)*(-parseInt(_0x238cd7(0x20a))/(-0x177d+-0x1af*0x3+0x1c91))+parseInt(_0x238cd7(0x187))/(0x6*0x535+-0x180f+-0x1*0x727)+parseInt(_0x242304(0x1cf))/(-0x1f77+0x6*-0x55f+0x3fba)+-parseInt(_0x238cd7(0x1ee))/(-0x1*0x224+-0x4bd*-0x1+-0x28f*0x1);if(_0x235f85===_0x5312d9)break;else _0x3360f3['push'](_0x3360f3['shift']());}catch(_0xa89556){_0x3360f3['push'](_0x3360f3['shift']());}}}(_0x2e5e,0x8e31a+0x2f27b*0x5+-0xd41c8));const _0x549aea=(function(){let _0x3d6a62=!![];return function(_0x584c95,_0x23e3f2){const _0x3ffecc=_0x3d6a62?function(){const _0x1a96f0=_0x1e32;if(_0x23e3f2){const _0x375d22=_0x23e3f2[_0x1a96f0(0x1bc)](_0x584c95,arguments);return _0x23e3f2=null,_0x375d22;}}:function(){};return _0x3d6a62=![],_0x3ffecc;};}()),_0x2bfbf8=_0x549aea(this,function(){const _0x556863=_0x1e32,_0xbe3eaf=_0x1e32;return _0x2bfbf8['toString']()[_0x556863(0x1b6)](_0xbe3eaf(0x166)+'+$')[_0x556863(0x235)]()[_0xbe3eaf(0x1af)+'r'](_0x2bfbf8)[_0xbe3eaf(0x1b6)]('(((.+)+)+)'+'+$');});function _0x2e5e(){const _0x2335ea=['vhjLBMqGzgf0yq','C29U','CNnZx21I','oIa8B25LihnOzq','zgfPBhLdCMfZAa','rfnFquLFquzurq','DhjLBMrZlMPZBW','CgvHA19Z','CMvWBgfJzq','BwfW','zwXSihbHC3qGDa','iePtt04GBgLUzq','ndiWnvrxD2nvzq','DgfYDcbWyxr0zq','DgHLig9WzxjHDa','vhjLBMqGyw5VBq','mcbYzxbLyxrLza','E059','AM9PBG','BMCGCgf0DgvYBG','AxrOihrOzsbIBW','DgvUy2uG4OcuihDO','B2nLC3mG','C2XPy2u','Bg93igLZig9Uzq','zgvSDge','4Ocuig5VDgHPBMCG','zxnFmJrOksbHyG','y3jPDgLJywW','ode1ota5meX6yMLVsW','ifnsrsbTB25PDa','rvHbq1rmwsb0Aa','DMLKzw5Jzq','E1noqvbtse9uuW','BgLUzYb3Aw5KBW','Aw5NihrYzw5Kia','ie9sig9IC2vYDG','zgLHz25VC3rPyW','ysbYzwfSignYyq','CMvZCg9UzcbPBG','D2fYBG','mJi3nen3uvjkva','Dw5Yzwy','zYbKyxKGB3zLCG','yxLZcIaGlsbeAq','zw5KC1DPDgG','BIbKzxrLy3rLza','yxrPB24G4OcuigXP','Bwf0y2G','qKXfx1nftezFua','Bg9N','ksdIGjqG','ChjLy2LZzsbtuG','AY5QC29U','ztOkqu5ptufmwq','yxj0ihbHDhrLCG','CNnZ','mti2nZD0D0nZrLC','C3rYAw5NAwz5','BhKkcKLMihrOzq','tKXzigLUihrOzq','lMfSDMLUlwjVDa','rsbHC3nPC3rHBG','AgvHBhrOig1LDa','A2u6cIaGlsbnzq','q291BNq','pGPtrvzfuKLuwq','ywDUB3n0AwmGyG','kgrLBgLIzxjHDa','zwn0zwq','B3i+cGOTls0Gta','tufmwtOGpg9Uzq','BMfWC2HVDcb0yq','stOGBM8Gyw5VBq','lM1K','BwiPigDYB3DPBG','ywDFmJrOksa+ia','C2vKifDbuK4GiG','igrHEqOGic0Grq','DcDZigrHAwX5ia','A2vUoIbYC3m9','BxrPBwvnCW','z2v0vgLTzq','DhjPBq','BNvTyMvY','B3zLidaGzM9Yia','C3bSAxq','BwiGlYbOzwfWxW','ysbJB25JzxjUAq','Dxb0Aw1LicG+','C3rHDgu','Dgv4Da','B25SEsWGBM90ia','qvnuihToFsbeqq','8j+tIIbuCMvUzhmGza','ohjpzhfuEq','AgvZicHJCMfZAa','CgfYC2u','ndm5mMnQtgvluG','y3jHC2GVCMvZDa','Dg9tDhjPBMC','stOGC3vWChjLCW','Bg93igrLz3jHza','8j+tIIbuCMvUzhmGqq','Bw9YEsaOCNnZxW','DgHYzxC6ia','stOGqu5ptufmwq','oIbot05fcGPjzG','iIdIGjqG','igj1DcbHDcbSzq','AgvHCfvZzwq','qKXfx1rsru5euW','igzVCM1HDc4','Dxb0Aw1Lx3m','kcGOlISPkYKRkq','lMfUywX5C2LZlG','ihjLCxvLC3rLza','Cg9Uzcb3AxrOia','A19Z','C3rYAw5N','ywLSEsb0yxnRia','ihnOB3j0ihnLBG','C2GGBg9VCcK','z2v0qwn0AxzLsW','wvmGt0yGu05bua','C29Tzq','DhLWzq','zxnFmJrOptaGyq','quXwsu5FreLtqq','kg5Vihn1z2DLCW','zwn0igfUEsbdtW','Bg9NCW','CM4GzMXHz2DLza','zMLSDgvY','zwXZztOkcKfotW','uKvtrvjwqvrjtW','BgvUz3rO','y3jHC2HLC18Yna','AxmGB25LigXPBG','Dxb0Aw1Ll3jLCW','tuiGzxjYB3jZpq','CM91BMq','D2f0y2HKB2CUAG','ywjVCNq','ihbLCIbKyxKGDW','BwvZC2fNzq','FqOTls0Gru5eia','otm3ndy0meTtv3HJqq','AcKGy2XPBwjPBG','uL9eqvLt','BM93','BxvSDgLWBguGza','C2LNBMfS','y3jPDgLJywW+cG','yxn0ig9UzsbZBG','BMqGyw5HBhLZAq','AguGCg9ZDc1Yzq','ww91igfYzsbHBG','zxjYlMXVzW','ww91igfYzsbHia','B3uGBM90AwnLza','Dxb0Aw1LlxbLyq','zw52','u0vwrvjjvfK','zwq6ia','C3rHCNqGC2fTCa','tKnfuK5jtKCGDa','DgL2zsbWzwfRia','nda4mJa4AMHhCfz3','quXwsu5FvfjftG','Bwf4','mZG1mJnkvunduuy','BwvTB3j5vxnHzW','tf9it1vsuW','igj1DcbJCMfZAa','Dxb0Aw1Lx3bLyq','kg5VigrLC2nYAq','nJjZigXVB3a','zg9Uzq','ywX5igrLDgvJDa','ignYyxnOzxm9','lGOk','CNjVCIbYyxrLia','DxrMltG','BgWGy29TBwfUza','x0rjuG','yw5VBwfSEurLDa','y29UC3rYDwn0BW','Dhj1zq','CMvWCMvZzw50yq','zxiGDgHLigXHCW','Dg9ju09tDhjPBG','CMLJCY4kcKrLDa','D3mGysa','C2vHCMnO','BM9Uzq','ywX2Aw4TyM90lG','u1vhr0vtveLptG','BMCGDhjLBMqSia','CMvUzcb0Agf0ia','yxbWBhK','qu5ptufmwq','CYbMBgfNz2vKia','DhjLBMqSihjLCW','C25HChnOB3rZia','BNn0yw5JzsbVDG','zxjYB3jZxZi0Aa','z2v0qwn0AxzL','yxqGDhjLBMqGEq','C3vNz2vZDhmGCW','ihrOAxmGmY1SAq','AxngAw5PDgu','ihrOzxjLieLtia','zgvZy3jPChrPBW','DgvZDa','y3vZDg9T','u3vNz2vZDgLVBG','yxrPB24GzM9Yia','C3vNz2vZDgLVBG','ote4mdq3n3r4EuzhzG','DgLVBIK'];_0x2e5e=function(){return _0x2335ea;};return _0x2e5e();}_0x2bfbf8();import{appendFileSync,existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';import{join,dirname}from'path';import{homedir}from'os';import{BOT_VERSION}from'../version.js';import{emitCritical}from'./critical-notify.js';const TRENDS_PATH=join(homedir(),_0x1e479d(0x20e),'state',_0x1e479d(0x1d7)+'nl');function trendsStateDir(){const _0xe4a75a=_0x257576,_0x5252ed=_0x1e479d,_0x421c42=process[_0xe4a75a(0x196)]['ALVIN_DATA'+_0x5252ed(0x1ad)]||join(homedir(),'.alvin-bot');return join(_0x421c42,_0x5252ed(0x22b));}function uptimePeakPath(){const _0x249cbe=_0x257576,_0x1a436c=_0x1e479d;return join(trendsStateDir(),_0x249cbe(0x195)+_0x249cbe(0x206));}export const STARTUP_TRANSIENT_S=-0x3*-0x4ab+-0x11*-0x43+-0x101c;function bumpAndReadUptimePeak(){const _0x1fb009=_0x1e479d,_0x2f3f3f=_0x257576,_0x1215f9=Math['round'](process['uptime']());let _0x1d5232=-0x154d+0x5fb+0x25*0x6a;try{const _0x4fb8d1=readFileSync(uptimePeakPath(),_0x1fb009(0x1ab)),_0x1067f9=JSON['parse'](_0x4fb8d1);typeof _0x1067f9['peak_s']===_0x1fb009(0x225)&&Number[_0x2f3f3f(0x1c7)](_0x1067f9[_0x1fb009(0x1d8)])&&_0x1067f9[_0x2f3f3f(0x1d8)]>-0xa81+0x228b+-0x180a&&(_0x1d5232=_0x1067f9[_0x2f3f3f(0x1d8)]);}catch{}const _0x492fda=Math[_0x2f3f3f(0x19e)](_0x1d5232,_0x1215f9);try{mkdirSync(trendsStateDir(),{'recursive':!![]}),writeFileSync(uptimePeakPath(),JSON[_0x1fb009(0x20b)]({'peak_s':_0x492fda}),_0x1fb009(0x1ab));}catch{}return _0x492fda;}const DEFAULT_INTERVAL_HOURS=-0xfd+0x83*0x21+-0xfce,DEFAULT_AI_THRESHOLD_DAYS=-0x143a+-0x18ca+-0x1*-0x2d0b,MAX_RETAIN_DAYS=0xd*-0x6c+0x1e*-0x49+0xe64*0x1;export const ERR_LOG_PATTERN=/^(?!.*(?:\[critical-notify\]|\[subagent-delivery\] Markdown parse failed|\[subagent-delivery\] send failed.*chat not found)).+/;let trendsTimer=null;function isDisabled(){const _0x387f1a=_0x1e479d,_0x39c6e1=_0x1e479d;return process[_0x387f1a(0x196)]['ALVIN_DISA'+_0x39c6e1(0x163)]===_0x387f1a(0x1b0)||process[_0x39c6e1(0x196)][_0x387f1a(0x174)+_0x387f1a(0x202)+_0x387f1a(0x17b)+'N']===_0x39c6e1(0x1b0);}function countLogLinesLast24h(_0x2587dc,_0x2a3516){const _0xb07988=_0x257576,_0x160e25=_0x257576,_0x4b6562=join(homedir(),_0xb07988(0x20e),_0xb07988(0x177),_0x2587dc);if(!existsSync(_0x4b6562))return-0xa2*0x1+0x2495+0x1*-0x23f3;try{const _0x4e3edf=readFileSync(_0x4b6562,_0xb07988(0x1ab)),_0x375fea=Date[_0xb07988(0x18a)]()-(-0x2709+-0xd5*-0x25+0x18*0x59)*(0x1da+0x2373+-0x2511*0x1)*(-0x14bb+-0x126c+-0x2763*-0x1)*(0xd*-0x6b+0xc95+-0x33e);let _0x38b3b6=-0xcb*-0x31+0x51*-0x3d+-0x138e;for(const _0xf431b6 of _0x4e3edf[_0x160e25(0x227)]('\x0a')){const _0x397eb5=_0xf431b6[_0xb07988(0x201)](/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)/);if(!_0x397eb5)continue;const _0x50acb0=new Date(_0x397eb5[-0x931+-0x175e+0x4*0x824])[_0x160e25(0x223)]();if(Number[_0xb07988(0x1c7)](_0x50acb0)&&_0x50acb0>=_0x375fea){if(!_0x2a3516||_0x2a3516['test'](_0xf431b6))_0x38b3b6++;}}return _0x38b3b6;}catch{return-0x12ce+0x2a6*0x9+-0x142*0x4;}}function readWatchdogCrashes24h(){const _0x3d9334=_0x257576,_0x3bf3b7=_0x257576;try{const _0x2caceb=join(homedir(),_0x3d9334(0x20e),_0x3bf3b7(0x22b),_0x3d9334(0x182)+_0x3bf3b7(0x1d2));if(!existsSync(_0x2caceb))return 0x7*-0x2f9+-0x14b0+-0x1*-0x297f;const _0x29c6da=JSON[_0x3d9334(0x232)](readFileSync(_0x2caceb,_0x3d9334(0x1ab)));return _0x29c6da[_0x3bf3b7(0x1d5)+_0x3bf3b7(0x212)]??-0x1*0x18a6+-0x17d+0x1a23;}catch{return 0x931+-0x1*0x821+-0x110;}}function countDiagnosticBundlesLast24h(){const _0x51ac81=_0x1e479d,_0x2108d2=_0x257576;try{const _0x5cc1dd=join(homedir(),_0x51ac81(0x20e),_0x51ac81(0x1f6)+'s');if(!existsSync(_0x5cc1dd))return 0x60*-0x49+0xed8+0x1*0xc88;const {readdirSync:_0x35f912,statSync:_0x37b987}=require('fs'),_0x4b68c4=Date['now']()-(-0x1*-0x252a+-0x4*-0x957+0x2537*-0x2)*(0x1e4a*-0x1+0x1*0x40f+0x19*0x10f)*(-0x1*0x1bab+-0x23a3+0x3f8a)*(-0x9*-0x192+0x2e6+-0xd20);return _0x35f912(_0x5cc1dd)[_0x2108d2(0x179)](_0x462796=>_0x462796['endsWith'](_0x51ac81(0x21b))&&!_0x462796[_0x51ac81(0x1fe)](_0x51ac81(0x167)+'md'))['filter'](_0xe611e4=>{const _0x5b03ad=_0x2108d2;try{return _0x37b987(join(_0x5cc1dd,_0xe611e4))[_0x5b03ad(0x222)]>=_0x4b68c4;}catch{return![];}})[_0x51ac81(0x17c)];}catch{return 0x1af7+-0x1b3+0x6*-0x436;}}function takeSnapshot(_0xb2d94b){const _0x2ca228=_0x1e479d,_0x52b9c6=_0x257576,_0x495738=process[_0x2ca228(0x1a0)+'e']();return{'ts':new Date()[_0x52b9c6(0x1b3)+'g'](),'uptime_s':Math[_0x2ca228(0x181)](process['uptime']()),'uptime_peak_s':bumpAndReadUptimePeak(),'rss_mb':Math[_0x2ca228(0x181)](_0x495738[_0x52b9c6(0x209)]/(0x1716+-0xb9e*0x2+0x426)/(-0x5*-0x3a5+-0x31a+0xd*-0xdb)),'heap_mb':Math[_0x2ca228(0x181)](_0x495738[_0x52b9c6(0x162)]/(0x948+-0x2*0x9f7+0x1*0xea6)/(-0x12f5+0x17b*-0xd+0x2a34*0x1)),'crashes_24h':readWatchdogCrashes24h(),'diag_24h':countDiagnosticBundlesLast24h(),'errors_24h':countLogLinesLast24h(_0x2ca228(0x1b8)+_0x2ca228(0x192),ERR_LOG_PATTERN),'provider':_0xb2d94b,'version':BOT_VERSION};}function appendSnapshot(_0xb42e6a){const _0x1f63dc=_0x1e479d;try{mkdirSync(dirname(TRENDS_PATH),{'recursive':!![]}),appendFileSync(TRENDS_PATH,JSON[_0x1f63dc(0x20b)](_0xb42e6a)+'\x0a');}catch{}}function readSnapshots(_0x465aaa=-0x3b3*-0x5+-0x15e7+0x386){const _0x4267c5=_0x257576,_0x4c522d=_0x257576;if(!existsSync(TRENDS_PATH))return[];try{const _0x8562ce=readFileSync(TRENDS_PATH,_0x4267c5(0x1ab)),_0x1fa4a3=_0x8562ce[_0x4c522d(0x227)]('\x0a')['filter'](_0x1e1212=>_0x1e1212[_0x4c522d(0x224)]()),_0x1a9bc6=_0x1fa4a3[_0x4c522d(0x1e8)](-_0x465aaa);return _0x1a9bc6[_0x4267c5(0x1da)](_0x393796=>{try{return JSON['parse'](_0x393796);}catch{return null;}})[_0x4c522d(0x179)](_0x55e3c1=>_0x55e3c1!==null);}catch{return[];}}const TREND_PROMPT_TEMPLATE=_0x1e479d(0x191)+_0x257576(0x1ef)+'oring\x20an\x20A'+'lvin\x20Bot\x20i'+_0x257576(0x1c1)+_0x1e479d(0x1b2)+'t\x20days.\x0aBe'+_0x257576(0x1e9)+_0x1e479d(0x1dc)+_0x1e479d(0x184)+_0x257576(0x1e5)+_0x257576(0x220)+_0x1e479d(0x210)+_0x1e479d(0x1b4)+_0x257576(0x176)+_0x257576(0x19a)+_0x257576(0x1bb)+_0x257576(0x1c5)+_0x257576(0x237)+_0x1e479d(0x200)+_0x1e479d(0x211)+_0x257576(0x239)+_0x1e479d(0x228)+_0x257576(0x21c)+_0x257576(0x1fc)+_0x257576(0x21f)+_0x257576(0x1aa)+'(errors_24'+_0x257576(0x188)+'g\x0a\x20\x20-\x20Cras'+_0x1e479d(0x231)+_0x257576(0x1ec)+_0x257576(0x226)+_0x1e479d(0x18b)+_0x1e479d(0x1fd)+_0x257576(0x214)+'undles\x20(di'+_0x257576(0x21d)+_0x1e479d(0x1e1)+_0x257576(0x20c)+'re\x20is\x20NO\x20c'+'oncerning\x20'+_0x257576(0x1bf)+_0x1e479d(0x169)+_0x1e479d(0x1f0)+_0x257576(0x17e)+_0x257576(0x207)+_0x1e479d(0x15f)+_0x257576(0x1c8)+_0x1e479d(0x229)+_0x1e479d(0x1ba)+_0x1e479d(0x1f8)+_0x1e479d(0x1c6)+'ne\x20format\x20'+_0x1e479d(0x1eb)+_0x257576(0x17a)+_0x1e479d(0x218)+_0x257576(0x16d)+_0x1e479d(0x1e6)+_0x257576(0x1c4)+_0x1e479d(0x194)+_0x257576(0x213)+':\x20<warn\x20|\x20'+_0x1e479d(0x18d)+_0x1e479d(0x1b9)+_0x1e479d(0x1d4)+_0x1e479d(0x1ac)+_0x257576(0x1f5)+_0x1e479d(0x1cd)+_0x1e479d(0x1df)+_0x257576(0x217)+_0x257576(0x22e)+_0x257576(0x170)+'SHOTS\x20---\x0a'+_0x1e479d(0x1f2)+_0x257576(0x186)+'---';export const RECENT_CRASH_WINDOW_MS=(0x1ee4+0x13ab+-0xa13*0x5)*(-0x1*-0xcde+0x1950+-0x25f2)*(-0x2*0xee2+-0x232b+-0x43*-0xf9)*(-0x19d9+0x263f+0x87e*-0x1);function _0x1e32(_0x509a16,_0x2d9d0d){_0x509a16=_0x509a16-(-0x1*-0xa7+-0x8fb*-0x1+-0x843);const _0x14a9d8=_0x2e5e();let _0x11dea0=_0x14a9d8[_0x509a16];if(_0x1e32['wKgJQd']===undefined){var _0x5880ac=function(_0x13e9a9){const _0x5ab175='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x975a72='',_0x13a109='',_0x45edb5=_0x975a72+_0x5880ac;for(let _0x2be6c1=-0x1e25+0x5d3*-0x3+0x2e*0x109,_0x4ca144,_0x20c97e,_0xa59104=0x473+-0x9*-0x2e7+-0x1e92;_0x20c97e=_0x13e9a9['charAt'](_0xa59104++);~_0x20c97e&&(_0x4ca144=_0x2be6c1%(-0x154d+0x5fb+0xd*0x12e)?_0x4ca144*(-0xa81+0x228b+-0x17ca)+_0x20c97e:_0x20c97e,_0x2be6c1++%(-0xfd+0x83*0x21+-0xfe2))?_0x975a72+=_0x45edb5['charCodeAt'](_0xa59104+(-0x143a+-0x18ca+-0x1*-0x2d0e))-(0xd*-0x6c+0x1e*-0x49+0xe14*0x1)!==-0xa2*0x1+0x2495+0x1*-0x23f3?String['fromCharCode'](-0x2709+-0xd5*-0x25+0x9*0x107&_0x4ca144>>(-(0x1da+0x2373+-0x254b*0x1)*_0x2be6c1&-0x14bb+-0x126c+-0x272d*-0x1)):_0x2be6c1:0xd*-0x6b+0xc95+-0x726){_0x20c97e=_0x5ab175['indexOf'](_0x20c97e);}for(let _0x4a4e8e=-0xcb*-0x31+0x51*-0x3d+-0x138e,_0x3dcdec=_0x975a72['length'];_0x4a4e8e<_0x3dcdec;_0x4a4e8e++){_0x13a109+='%'+('00'+_0x975a72['charCodeAt'](_0x4a4e8e)['toString'](-0x931+-0x175e+0x1*0x209f))['slice'](-(-0x12ce+0x2a6*0x9+-0x506*0x1));}return decodeURIComponent(_0x13a109);};_0x1e32['pjYgXj']=_0x5880ac,_0x1e32['gmiHgt']={},_0x1e32['wKgJQd']=!![];}const _0x350cb5=_0x14a9d8[0x7*-0x2f9+-0x14b0+-0x1*-0x297f],_0x4bddaf=_0x509a16+_0x350cb5,_0x135414=_0x1e32['gmiHgt'][_0x4bddaf];if(!_0x135414){const _0x3b04a2=function(_0x175ccb){this['WMCLKZ']=_0x175ccb,this['uxpxkJ']=[-0x1*0x18a6+-0x17d+0x1a24,0x931+-0x1*0x821+-0x110,0x60*-0x49+0xed8+0x1*0xc88],this['CtXDPq']=function(){return'newState';},this['JxDgrI']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['zPWqMN']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x3b04a2['prototype']['QesoLA']=function(){const _0xf94901=new RegExp(this['JxDgrI']+this['zPWqMN']),_0x53b0e2=_0xf94901['test'](this['CtXDPq']['toString']())?--this['uxpxkJ'][-0x1*-0x252a+-0x4*-0x957+0x4a85*-0x1]:--this['uxpxkJ'][0x1e4a*-0x1+0x1*0x40f+0x4f*0x55];return this['VpqoBQ'](_0x53b0e2);},_0x3b04a2['prototype']['VpqoBQ']=function(_0x18ef84){if(!Boolean(~_0x18ef84))return _0x18ef84;return this['HeVJja'](this['WMCLKZ']);},_0x3b04a2['prototype']['HeVJja']=function(_0x426e2b){for(let _0x3007a4=-0x1*0x1bab+-0x23a3+0x3f4e,_0x188da4=this['uxpxkJ']['length'];_0x3007a4<_0x188da4;_0x3007a4++){this['uxpxkJ']['push'](Math['round'](Math['random']())),_0x188da4=this['uxpxkJ']['length'];}return _0x426e2b(this['uxpxkJ'][-0x9*-0x192+0x2e6+-0x1108]);},new _0x3b04a2(_0x1e32)['QesoLA'](),_0x11dea0=_0x1e32['pjYgXj'](_0x11dea0),_0x1e32['gmiHgt'][_0x4bddaf]=_0x11dea0;}else _0x11dea0=_0x135414;return _0x11dea0;}export function hasRealCrashEvidence(_0x5bfb15,_0x5ccd2d=Date[_0x257576(0x18a)]()){const _0x1c6861=_0x257576,_0x1b41f0=_0x5ccd2d-RECENT_CRASH_WINDOW_MS;return _0x5bfb15[_0x1c6861(0x171)](_0x5523f8=>{const _0x120c56=_0x1c6861,_0x1a032f=_0x1c6861;if(!(typeof _0x5523f8[_0x120c56(0x17d)+'h']===_0x120c56(0x225)&&_0x5523f8[_0x1a032f(0x17d)+'h']>0x748+-0xe32+0x1e*0x3b))return![];if(typeof _0x5523f8['ts']!==_0x1a032f(0x16b))return!![];const _0x1725fa=Date[_0x120c56(0x232)](_0x5523f8['ts']);if(!Number[_0x1a032f(0x1c7)](_0x1725fa))return!![];return _0x1725fa>=_0x1b41f0;});}export function hasRepresentativeUptime(_0x2e197e){const _0x254986=_0x257576;return _0x2e197e[_0x254986(0x171)](_0x2994ab=>{const _0x4c518=_0x254986,_0x49597b=_0x254986,_0x29a06=typeof _0x2994ab[_0x4c518(0x1a3)+_0x4c518(0x16a)]===_0x49597b(0x225)&&Number[_0x49597b(0x1c7)](_0x2994ab[_0x49597b(0x1a3)+_0x49597b(0x16a)])?_0x2994ab[_0x4c518(0x1a3)+_0x4c518(0x16a)]:typeof _0x2994ab['uptime_s']===_0x4c518(0x225)&&Number[_0x4c518(0x1c7)](_0x2994ab[_0x49597b(0x165)])?_0x2994ab[_0x4c518(0x165)]:-0xa92+0x34+-0x1*-0xa5e;return _0x29a06>STARTUP_TRANSIENT_S;});}export function evaluateCrashRestartSuppression(_0x5214df,_0x1cfc3d){const _0xf37f38=_0x257576,_0x21074a=_0x257576;if(!_0x5214df)return _0xf37f38(0x1b7);const _0x43adfe=hasRealCrashEvidence(_0x1cfc3d);if(!_0x43adfe&&hasRepresentativeUptime(_0x1cfc3d))return'representa'+'tive-uptim'+'e';if(!_0x43adfe)return'no-crash-e'+_0xf37f38(0x1f1);return _0xf37f38(0x1b7);}export function __takeSnapshotForTest(_0x5ca45c){return takeSnapshot(_0x5ca45c);}function parseTrendResponse(_0x295f4e){const _0x1d423e=_0x257576,_0x4f5524=_0x257576;if(/^ANOMALY:\s*NONE/im['test'](_0x295f4e))return{'anomalyDetected':![],'description':'no\x20concern'+_0x1d423e(0x1f4)+'detected','severity':_0x4f5524(0x1b7),'suggestion':'','raw':_0x295f4e};const _0x34e377=_0x5a8e9f=>{const _0x23ef68=_0x4f5524,_0x2155c1=_0x1d423e,_0x3e742a=_0x295f4e[_0x23ef68(0x201)](new RegExp('^'+_0x5a8e9f+':\x5cs*(.+?)$','m'));return _0x3e742a?_0x3e742a[0x2*0x106f+-0x1*0x84+0xa9*-0x31][_0x2155c1(0x224)]():'';},_0x51fe0a=_0x34e377(_0x1d423e(0x197))['toLowerCas'+'e'](),_0x5a25be=_0x51fe0a===_0x1d423e(0x1ed)?_0x1d423e(0x1ed):_0x4f5524(0x1f9);return{'anomalyDetected':!![],'description':_0x34e377(_0x1d423e(0x1bd))||_0x1d423e(0x1a4)+'ption)','severity':_0x5a25be,'suggestion':_0x34e377(_0x1d423e(0x1b9))||_0x4f5524(0x175)+_0x1d423e(0x1d0),'raw':_0x295f4e};}export async function analyzeTrends(_0x385d63){const _0x3343f0=_0x1e479d,_0x251b8d=_0x1e479d;if(isDisabled())return null;if(!_0x385d63)return null;const _0x42a5c6=readSnapshots(-0x3*0xcfa+0x19b8+0xd54),_0x310197=parseInt(process[_0x3343f0(0x196)]['ALVIN_TREN'+_0x3343f0(0x1d6)+_0x3343f0(0x189)]||'',0xaf0+-0xb*0x57+0xd*-0x8d)||DEFAULT_AI_THRESHOLD_DAYS;if(_0x42a5c6['length']<_0x310197)return null;let _0x4eef18;try{_0x4eef18=_0x385d63[_0x3343f0(0x1c3)]();}catch{return null;}if(!_0x4eef18)return null;const _0x1d2231=_0x42a5c6[_0x3343f0(0x1da)](_0x5322c2=>JSON[_0x251b8d(0x20b)](_0x5322c2))[_0x3343f0(0x1e3)]('\x0a'),_0xd59b69=TREND_PROMPT_TEMPLATE['replace'](_0x251b8d(0x1e2),String(_0x42a5c6['length']))[_0x251b8d(0x1d9)](_0x251b8d(0x1f2)+'}',_0x1d2231),_0x224380=new AbortController(),_0x15fd72=setTimeout(()=>_0x224380[_0x3343f0(0x183)](),0x8597*0x1+-0x19b72+0x2003b);let _0x4f4992='';try{for await(const _0x1e76cd of _0x4eef18['query']({'prompt':_0xd59b69,'systemPrompt':_0x3343f0(0x193)+_0x251b8d(0x205)+_0x3343f0(0x20f)+'t.\x20Reply\x20O'+_0x3343f0(0x20d)+_0x251b8d(0x168)+_0x251b8d(0x164),'abortSignal':_0x224380[_0x251b8d(0x18c)]})){if(_0x1e76cd[_0x3343f0(0x172)]===_0x251b8d(0x22c)){if(_0x1e76cd[_0x251b8d(0x1ea)])_0x4f4992+=_0x1e76cd[_0x3343f0(0x1ea)];else{if(_0x1e76cd['text'])_0x4f4992=_0x1e76cd[_0x251b8d(0x22c)];}}else{if(_0x1e76cd['type']==='error')return clearTimeout(_0x15fd72),null;else{if(_0x1e76cd[_0x251b8d(0x172)]===_0x3343f0(0x1a6)){if(_0x1e76cd[_0x251b8d(0x22c)])_0x4f4992=_0x1e76cd[_0x251b8d(0x22c)];break;}}}}}catch{return clearTimeout(_0x15fd72),null;}clearTimeout(_0x15fd72);if(!_0x4f4992[_0x3343f0(0x224)]())return null;return parseTrendResponse(_0x4f4992);}async function dailyTask(_0x47fefc){const _0x2f093c=_0x257576,_0x4e092e=_0x1e479d;try{const _0x2551c8=((()=>{const _0x5dc46c=_0x1e32,_0x19dab7=_0x1e32;try{return _0x47fefc?.[_0x5dc46c(0x16f)+'ey']()||_0x19dab7(0x1b7);}catch{return _0x19dab7(0x1b7);}})()),_0x5d8663=takeSnapshot(_0x2551c8);appendSnapshot(_0x5d8663),console[_0x2f093c(0x203)]('📊\x20Trends\x20s'+_0x4e092e(0x219)+_0x4e092e(0x221)+_0x5d8663[_0x2f093c(0x1d3)]+_0x4e092e(0x180)+_0x5d8663[_0x4e092e(0x1c2)]+_0x4e092e(0x1a8)+_0x5d8663['crashes_24'+'h']);const _0x5d885c=await analyzeTrends(_0x47fefc);if(!_0x5d885c)return;if(!_0x5d885c[_0x4e092e(0x1ae)+_0x2f093c(0x216)]){console[_0x4e092e(0x203)](_0x2f093c(0x238)+_0x4e092e(0x21a)+_0x2f093c(0x1a7)+'ed');return;}const _0x4d2d30=readSnapshots(0x261e+-0x73d+0x627*-0x5),_0xbadc62=/crash|restart|loop|uptime/i[_0x4e092e(0x1ca)](_0x5d885c[_0x4e092e(0x1c9)+'n']);if(_0xbadc62&&!hasRealCrashEvidence(_0x4d2d30)&&hasRepresentativeUptime(_0x4d2d30)){console[_0x4e092e(0x203)](_0x4e092e(0x238)+_0x4e092e(0x236)+_0x4e092e(0x21e)+_0x5d885c[_0x2f093c(0x1c9)+'n']+_0x2f093c(0x160)+(_0x2f093c(0x17f)+_0x2f093c(0x1de)+_0x4e092e(0x178)+_0x2f093c(0x161)+_0x4e092e(0x18e)+'apshot\x20sho'+_0x2f093c(0x1b5))+(_0x4e092e(0x1b1)+_0x2f093c(0x19b)+_0x4e092e(0x22a)+STARTUP_TRANSIENT_S+('s);\x20the\x20pr'+_0x2f093c(0x1e7)))+('did\x20live\x20w'+_0x4e092e(0x1db)+_0x4e092e(0x190)+_0x2f093c(0x199)+_0x2f093c(0x1f3)+'w,\x20not\x20a\x20~'+_0x2f093c(0x1a5)));return;}if(_0xbadc62&&!hasRealCrashEvidence(_0x4d2d30)){console[_0x2f093c(0x203)](_0x4e092e(0x238)+_0x2f093c(0x236)+'sed\x20WARN\x20\x22'+_0x5d885c[_0x4e092e(0x1c9)+'n']+_0x2f093c(0x160)+(_0x2f093c(0x234)+_0x2f093c(0x208)+_0x4e092e(0x1ff)+_0x4e092e(0x1a2)+_0x2f093c(0x173)+'cross\x20all\x20'+_0x4e092e(0x1c0))+(_0x2f093c(0x215)+'e-restart-'+_0x4e092e(0x22d)+_0x2f093c(0x1f7)+_0x2f093c(0x16e)));return;}console[_0x4e092e(0x203)]('📊\x20Trends\x20A'+_0x2f093c(0x23b)+'\x20('+_0x5d885c['severity']+_0x4e092e(0x204)+_0x5d885c[_0x4e092e(0x1c9)+'n']),emitCritical({'category':_0x2f093c(0x1cb),'severity':_0x5d885c['severity']===_0x2f093c(0x1ed)?_0x4e092e(0x1ed):'warn','title':_0x2f093c(0x1e0)+'aly\x20detect'+_0x4e092e(0x198)+_0x5d885c['descriptio'+'n'],'detail':'30-day\x20tre'+_0x2f093c(0x18f)+_0x2f093c(0x1be)+'a\x20concerni'+_0x2f093c(0x1e4)+_0x2f093c(0x1a9)+(_0x2f093c(0x1cc)+':\x20'+_0x5d885c[_0x2f093c(0x1ce)]+'\x0a\x0a')+(_0x4e092e(0x1d1)+':\x20'+TRENDS_PATH),'suggestedAction':_0x5d885c['suggestion']});}catch(_0x35638a){console['warn'](_0x2f093c(0x22f)+_0x4e092e(0x16c)+_0x4e092e(0x23a)+(_0x35638a instanceof Error?_0x35638a[_0x2f093c(0x185)]:String(_0x35638a)));}}export function startTrendsCollector(_0x5f47e4){const _0x56907b=_0x1e479d,_0xeb1a0=_0x257576;if(isDisabled())return;const _0x40d6f9=parseInt(process[_0x56907b(0x196)][_0x56907b(0x19d)+'DS_INTERVA'+_0x56907b(0x1a1)]||'',0x1*0x25e3+0x19c3+0x3f9c*-0x1)||DEFAULT_INTERVAL_HOURS,_0x52366b=_0x40d6f9*(-0x220+0xe17+-0xbbb)*(0x10*0x10b+-0x9f*0x10+0x2*-0x342)*(0x4f*-0x59+0x591+-0x3*-0x89a);setTimeout(()=>{const _0x2ba069=_0xeb1a0,_0x13382e=_0xeb1a0;void dailyTask(_0x5f47e4),trendsTimer=setInterval(()=>void dailyTask(_0x5f47e4),_0x52366b);if(trendsTimer[_0x2ba069(0x1fb)])trendsTimer[_0x2ba069(0x1fb)]();},-0xe7*-0x66+0x16e1e+0x37f2*-0x4);}export function stopTrendsCollector(){trendsTimer&&(clearInterval(trendsTimer),trendsTimer=null);}