alvin-bot 5.6.2 → 5.8.0

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 +29 -0
  2. package/README.md +1 -1
  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 -130
  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 -443
  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 -0
  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 -1831
  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 _0x1a279d=_0x5b7b,_0xe19cfe=_0x5b7b;(function(_0x55b561,_0x4c8558){const _0xd15737=_0x5b7b,_0x434edb=_0x5b7b,_0x67d8b0=_0x55b561();while(!![]){try{const _0x63b41b=parseInt(_0xd15737(0x14a))/(-0xef2+0x1b62+-0xc6f)*(parseInt(_0x434edb(0x163))/(-0x3a5+0x3c3+-0x1c))+-parseInt(_0xd15737(0x1bb))/(0x72*-0x8+-0x62*0x3b+0x1a29)+parseInt(_0x434edb(0x13e))/(-0x1a9e+-0x598+-0x113*-0x1e)*(parseInt(_0xd15737(0x1be))/(0x2*-0xdf9+0x1481*-0x1+-0xc1e*-0x4))+-parseInt(_0x434edb(0x118))/(0x27b+-0x1*-0x24a0+-0x2715)*(parseInt(_0xd15737(0x178))/(0x1*0x1a61+0x3*-0x95c+-0xdd*-0x2))+-parseInt(_0x434edb(0x18e))/(-0x19+0x6bf+-0x69e)+-parseInt(_0xd15737(0x1d0))/(-0xb1e+0x19*0xf4+-0x1*0xcad)*(-parseInt(_0xd15737(0x135))/(-0x2f9*-0x4+-0x463+-0x777*0x1))+parseInt(_0xd15737(0x1a1))/(-0x14d6+0x1c87+-0x7a6)*(parseInt(_0xd15737(0x1c2))/(0x5cb*0x1+-0x1*0x117d+0xbbe));if(_0x63b41b===_0x4c8558)break;else _0x67d8b0['push'](_0x67d8b0['shift']());}catch(_0x17485d){_0x67d8b0['push'](_0x67d8b0['shift']());}}}(_0x378c,-0xa1c11*-0x1+0x17dd8+-0x18290));const _0x5849d1=(function(){let _0x53b997=!![];return function(_0x26b15,_0x55d6f4){const _0xe20e7a=_0x53b997?function(){const _0x15e755=_0x5b7b;if(_0x55d6f4){const _0x48863d=_0x55d6f4[_0x15e755(0x13c)](_0x26b15,arguments);return _0x55d6f4=null,_0x48863d;}}:function(){};return _0x53b997=![],_0xe20e7a;};}()),_0x1701a4=_0x5849d1(this,function(){const _0x344090=_0x5b7b,_0x46d778=_0x5b7b;return _0x1701a4[_0x344090(0x1c4)]()[_0x344090(0x196)](_0x46d778(0x13d)+'+$')['toString']()[_0x46d778(0x15a)+'r'](_0x1701a4)[_0x46d778(0x196)](_0x46d778(0x13d)+'+$');});_0x1701a4();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(),_0x1a279d(0x12a),_0x1a279d(0x127),_0xe19cfe(0x1d6)+'nl');function trendsStateDir(){const _0x3489cb=_0xe19cfe,_0x13a317=_0x1a279d,_0x2c6c9e=process[_0x3489cb(0x1af)][_0x3489cb(0x142)+'_DIR']||join(homedir(),_0x3489cb(0x12a));return join(_0x2c6c9e,_0x3489cb(0x127));}function uptimePeakPath(){const _0x2340e5=_0xe19cfe,_0x2ae018=_0xe19cfe;return join(trendsStateDir(),_0x2340e5(0x1bf)+_0x2340e5(0x154));}function _0x5b7b(_0x4ccbc3,_0x2d63bf){_0x4ccbc3=_0x4ccbc3-(-0xfe9*-0x2+0x1*-0x24a6+-0x1f9*-0x3);const _0x37736e=_0x378c();let _0x151773=_0x37736e[_0x4ccbc3];if(_0x5b7b['EYXVLV']===undefined){var _0x5229d6=function(_0x989e78){const _0x41b4ec='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x12ae4a='',_0x110a5e='',_0x5d9f64=_0x12ae4a+_0x5229d6;for(let _0x423e60=0x19d7+-0x3*-0x33+-0x1a70,_0x296c30,_0x52ea41,_0x316bcc=-0x1*-0x17db+-0x26*0xa8+0x115;_0x52ea41=_0x989e78['charAt'](_0x316bcc++);~_0x52ea41&&(_0x296c30=_0x423e60%(-0x1c48+-0xde6+0x2a32)?_0x296c30*(0x248b*-0x1+0x1*-0x1177+0x1cf*0x1e)+_0x52ea41:_0x52ea41,_0x423e60++%(0x71*0x35+-0x527*0x2+-0xd13))?_0x12ae4a+=_0x5d9f64['charCodeAt'](_0x316bcc+(-0x2f9*0x5+-0x1631+-0x946*-0x4))-(-0x53*0x37+0xaf3+-0x1*-0x6ec)!==-0xc12+0x2c*0xd0+-0x17ae?String['fromCharCode'](-0x215b+-0xa*-0x377+-0x4c&_0x296c30>>(-(0x15ba+-0x71*-0x1e+-0x32*0xb3)*_0x423e60&0x14b+-0x13c0+0xf9*0x13)):_0x423e60:-0x93c+0x20db+-0x179f*0x1){_0x52ea41=_0x41b4ec['indexOf'](_0x52ea41);}for(let _0x488c4c=0xcb3+0x1*-0x89+-0xc2a,_0x7d882c=_0x12ae4a['length'];_0x488c4c<_0x7d882c;_0x488c4c++){_0x110a5e+='%'+('00'+_0x12ae4a['charCodeAt'](_0x488c4c)['toString'](0x170a*0x1+-0x2035+0x93b*0x1))['slice'](-(-0x101*0x21+0x1e10+-0x1*-0x313));}return decodeURIComponent(_0x110a5e);};_0x5b7b['cyXwFS']=_0x5229d6,_0x5b7b['fnXcNe']={},_0x5b7b['EYXVLV']=!![];}const _0xd7cd2e=_0x37736e[0x545+-0x3*0x607+0x28*0x52],_0xca22cd=_0x4ccbc3+_0xd7cd2e,_0x55109b=_0x5b7b['fnXcNe'][_0xca22cd];if(!_0x55109b){const _0x4bec3a=function(_0x3250e1){this['WAVLMg']=_0x3250e1,this['hjSxEV']=[-0x1009+-0xb5*0x27+-0x3f7*-0xb,-0x15f8+-0xe7f+-0x2477*-0x1,-0xa5e+0x15db+-0xb7d],this['CGESJP']=function(){return'newState';},this['BLUeJc']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['RSDWMH']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x4bec3a['prototype']['bLDQXe']=function(){const _0x47597d=new RegExp(this['BLUeJc']+this['RSDWMH']),_0x2d2369=_0x47597d['test'](this['CGESJP']['toString']())?--this['hjSxEV'][-0x1ba2+0x95c+0x1247]:--this['hjSxEV'][0x3e3+-0x1556*-0x1+-0x1939];return this['rzAIRs'](_0x2d2369);},_0x4bec3a['prototype']['rzAIRs']=function(_0x333d04){if(!Boolean(~_0x333d04))return _0x333d04;return this['GTtVAI'](this['WAVLMg']);},_0x4bec3a['prototype']['GTtVAI']=function(_0x4d2eb3){for(let _0xa6ee38=0x1640+0x225*0x3+-0x1caf,_0x29662e=this['hjSxEV']['length'];_0xa6ee38<_0x29662e;_0xa6ee38++){this['hjSxEV']['push'](Math['round'](Math['random']())),_0x29662e=this['hjSxEV']['length'];}return _0x4d2eb3(this['hjSxEV'][-0x249f+0x12e*-0x13+-0x7f*-0x77]);},new _0x4bec3a(_0x5b7b)['bLDQXe'](),_0x151773=_0x5b7b['cyXwFS'](_0x151773),_0x5b7b['fnXcNe'][_0xca22cd]=_0x151773;}else _0x151773=_0x55109b;return _0x151773;}export const STARTUP_TRANSIENT_S=-0x1e73+-0x1*-0x17db+-0x16*-0x68;function bumpAndReadUptimePeak(){const _0x325b72=_0x1a279d,_0xbf0e8d=_0x1a279d,_0x537087=Math['round'](process[_0x325b72(0x14f)]());let _0x1c9d2d=-0x12f7+-0x1c48+0x2f3f;try{const _0x355228=readFileSync(uptimePeakPath(),_0xbf0e8d(0x14e)),_0x27f3ba=JSON['parse'](_0x355228);typeof _0x27f3ba[_0x325b72(0x1cd)]===_0x325b72(0x173)&&Number['isFinite'](_0x27f3ba[_0xbf0e8d(0x1cd)])&&_0x27f3ba[_0xbf0e8d(0x1cd)]>-0x10dc+0x248b*-0x1+0x3*0x11cd&&(_0x1c9d2d=_0x27f3ba[_0x325b72(0x1cd)]);}catch{}const _0x5753a4=Math[_0x325b72(0x199)](_0x1c9d2d,_0x537087);try{mkdirSync(trendsStateDir(),{'recursive':!![]}),writeFileSync(uptimePeakPath(),JSON['stringify']({'peak_s':_0x5753a4}),_0xbf0e8d(0x14e));}catch{}return _0x5753a4;}const DEFAULT_INTERVAL_HOURS=-0x7*-0x4e5+0x1d7*0xb+-0x6cd*0x8,DEFAULT_AI_THRESHOLD_DAYS=-0x138b+-0x2f9*0x5+0x226f,MAX_RETAIN_DAYS=0x1cc5+0x1*-0x18bc+-0x3af;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 _0x220ab2=_0xe19cfe,_0x152b9f=_0x1a279d;return process['env'][_0x220ab2(0x17d)+_0x220ab2(0x150)]===_0x220ab2(0x153)||process[_0x220ab2(0x1af)]['ALVIN_DISA'+'BLE_SELF_P'+'RESERVATIO'+'N']===_0x220ab2(0x153);}function countLogLinesLast24h(_0x10cf3c,_0x546e88){const _0x525cc7=_0xe19cfe,_0x2fdd75=_0xe19cfe,_0x15c35c=join(homedir(),'.alvin-bot',_0x525cc7(0x12e),_0x10cf3c);if(!existsSync(_0x15c35c))return-0x176e+0x2436+-0x8*0x199;try{const _0x3c3e73=readFileSync(_0x15c35c,'utf-8'),_0x29e88d=Date[_0x2fdd75(0x164)]()-(0x1e57+0xed7+-0x168b*0x2)*(0x76d+0x2*-0x871+0x9b1)*(-0x61*-0x67+-0x6d*0x2f+-0x12c8)*(-0xb*0x1ea+-0x1*-0xae7+0xe0f);let _0x2252f1=-0x1*-0x1c0e+-0x225d+0x11*0x5f;for(const _0x81b0e3 of _0x3c3e73[_0x525cc7(0x158)]('\x0a')){const _0x361890=_0x81b0e3[_0x2fdd75(0x15d)](/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)/);if(!_0x361890)continue;const _0x428df4=new Date(_0x361890[-0x572+0x170a*0x1+-0x1197])[_0x525cc7(0x11d)]();if(Number['isFinite'](_0x428df4)&&_0x428df4>=_0x29e88d){if(!_0x546e88||_0x546e88[_0x525cc7(0x19f)](_0x81b0e3))_0x2252f1++;}}return _0x2252f1;}catch{return 0xf91+0x24ac*0x1+-0x343d;}}function readWatchdogCrashes24h(){const _0x14fad5=_0x1a279d,_0xa9fabd=_0xe19cfe;try{const _0x30b4da=join(homedir(),_0x14fad5(0x12a),_0xa9fabd(0x127),'watchdog.j'+_0x14fad5(0x128));if(!existsSync(_0x30b4da))return-0x22a8+-0x2*-0x12cd+-0x2f2;const _0x5b3e62=JSON[_0x14fad5(0x1d9)](readFileSync(_0x30b4da,'utf-8'));return _0x5b3e62['dailyCrash'+'Count']??0x1*0xa61+-0x1185+0x724;}catch{return-0xa3e+-0x3d6*-0x2+0x292;}}function countDiagnosticBundlesLast24h(){const _0x1586b6=_0x1a279d,_0x5a7a35=_0xe19cfe;try{const _0x3a60b5=join(homedir(),_0x1586b6(0x12a),'diagnostic'+'s');if(!existsSync(_0x3a60b5))return-0xe7f+-0xb89*0x1+0x1a08;const {readdirSync:_0x47d1d1,statSync:_0x281364}=require('fs'),_0x2ef247=Date[_0x1586b6(0x164)]()-(0x15db+-0x17bc+0x1f9)*(0x95c+-0x1c07+0x64d*0x3)*(-0xb7c+0x18d2+0x3*-0x45e)*(-0xb*0x15f+0x19d3+-0x6d6);return _0x47d1d1(_0x3a60b5)[_0x5a7a35(0x197)](_0x2ceb4f=>_0x2ceb4f[_0x5a7a35(0x1ac)](_0x5a7a35(0x162))&&!_0x2ceb4f[_0x1586b6(0x1ac)](_0x1586b6(0x11a)+'md'))[_0x1586b6(0x197)](_0x2d1ea5=>{try{return _0x281364(join(_0x3a60b5,_0x2d1ea5))['mtimeMs']>=_0x2ef247;}catch{return![];}})['length'];}catch{return 0x49*-0x6c+0x13c7+-0x1*-0xb05;}}function takeSnapshot(_0x37dcba){const _0x476162=_0xe19cfe,_0x3d4bbc=_0x1a279d,_0x159157=process[_0x476162(0x151)+'e']();return{'ts':new Date()[_0x3d4bbc(0x16d)+'g'](),'uptime_s':Math[_0x476162(0x152)](process[_0x476162(0x14f)]()),'uptime_peak_s':bumpAndReadUptimePeak(),'rss_mb':Math[_0x3d4bbc(0x152)](_0x159157[_0x3d4bbc(0x1a3)]/(-0x90f*0x4+-0x15cb+-0x14ad*-0x3)/(0xec3+-0x17c4+-0x1*-0xd01)),'heap_mb':Math[_0x3d4bbc(0x152)](_0x159157[_0x476162(0x125)]/(-0x1cdd*-0x1+0x1*0x16b+-0x1*0x1a48)/(0x2e9+0x182a+-0x1713)),'crashes_24h':readWatchdogCrashes24h(),'diag_24h':countDiagnosticBundlesLast24h(),'errors_24h':countLogLinesLast24h(_0x3d4bbc(0x11c)+_0x476162(0x119),ERR_LOG_PATTERN),'provider':_0x37dcba,'version':BOT_VERSION};}function appendSnapshot(_0x160bfe){const _0x1293be=_0x1a279d;try{mkdirSync(dirname(TRENDS_PATH),{'recursive':!![]}),appendFileSync(TRENDS_PATH,JSON[_0x1293be(0x1b1)](_0x160bfe)+'\x0a');}catch{}}function readSnapshots(_0x38003d=-0x10d6+-0x1e83*-0x1+0x1*-0xd8f){const _0x204c13=_0xe19cfe,_0x54e105=_0x1a279d;if(!existsSync(TRENDS_PATH))return[];try{const _0xabe21a=readFileSync(TRENDS_PATH,_0x204c13(0x14e)),_0x59bcca=_0xabe21a['split']('\x0a')[_0x204c13(0x197)](_0x57cfc5=>_0x57cfc5[_0x54e105(0x11e)]()),_0x4cb2c0=_0x59bcca['slice'](-_0x38003d);return _0x4cb2c0['map'](_0x1ed5d1=>{const _0x4ddfa8=_0x54e105;try{return JSON[_0x4ddfa8(0x1d9)](_0x1ed5d1);}catch{return null;}})[_0x54e105(0x197)](_0x598f75=>_0x598f75!==null);}catch{return[];}}const TREND_PROMPT_TEMPLATE=_0xe19cfe(0x120)+_0xe19cfe(0x1b7)+'oring\x20an\x20A'+'lvin\x20Bot\x20i'+_0xe19cfe(0x156)+_0xe19cfe(0x160)+_0xe19cfe(0x1c9)+_0xe19cfe(0x15c)+_0xe19cfe(0x123)+_0xe19cfe(0x13b)+_0xe19cfe(0x1b9)+_0x1a279d(0x1a4)+_0xe19cfe(0x11f)+_0x1a279d(0x18a)+_0x1a279d(0x145)+_0x1a279d(0x19c)+_0x1a279d(0x16a)+_0xe19cfe(0x15b)+_0xe19cfe(0x188)+_0x1a279d(0x1bd)+'ke:\x0a\x20\x20-\x20Me'+'mory\x20(rss_'+_0xe19cfe(0x17a)+_0xe19cfe(0x1b2)+_0x1a279d(0x159)+_0x1a279d(0x1dc)+_0xe19cfe(0x143)+'(errors_24'+'h)\x20climbin'+_0x1a279d(0x136)+_0xe19cfe(0x1a7)+_0x1a279d(0x1cb)+'ove\x200\x20for\x20'+_0xe19cfe(0x1c0)+'ays\x0a\x20\x20-\x20Di'+_0xe19cfe(0x1b6)+_0x1a279d(0x190)+_0x1a279d(0x137)+_0xe19cfe(0x186)+_0x1a279d(0x165)+_0x1a279d(0x1ba)+_0xe19cfe(0x141)+_0xe19cfe(0x1d7)+_0x1a279d(0x130)+'EXACTLY\x20th'+_0x1a279d(0x182)+'e:\x0aANOMALY'+_0xe19cfe(0x1a2)+'\x20there\x20IS\x20'+_0xe19cfe(0x192)+_0xe19cfe(0x1b5)+'respond\x20in'+'\x20this\x203-li'+_0xe19cfe(0x155)+'—\x20nothing\x20'+_0x1a279d(0x16b)+_0xe19cfe(0x198)+_0xe19cfe(0x144)+_0xe19cfe(0x1a8)+_0xe19cfe(0x122)+'ou\x20noticed'+_0x1a279d(0x18d)+':\x20<warn\x20|\x20'+_0x1a279d(0x18c)+'SUGGESTION'+_0xe19cfe(0x1b8)+_0x1a279d(0x195)+_0x1a279d(0x1b4)+_0x1a279d(0x1c8)+_0x1a279d(0x134)+_0xe19cfe(0x1ad)+'AST\x20{N}\x20DA'+_0x1a279d(0x129)+_0xe19cfe(0x146)+_0xe19cfe(0x1a9)+'}\x0a---\x20END\x20'+_0xe19cfe(0x15f);function _0x378c(){const _0x10e861=['DgHLig9WzxjHDa','mtbwCNnyEwy','zWOGic0Gq3jHCW','ywDFmJrOksa+ia','BMCGCgf0DgvYBG','zg9Uzq','oLXZkIGUkZ8Pja','ihbLCIbKyxKGDW','yxbWBhK','kcGOlISPkYKRkq','mZuYAgjNwM1Y','ywjVCNq','C3rHCNqGC2fTCa','B25JzxjUAw5Nia','quXwsu5Frefuqq','CNjVCIbYyxrLia','ihnOB3j0ihnLBG','zwn0igfUEsbdtW','u0HpvfmGls0TcG','BM8Gy29Uy2vYBG','C3rYAw5N','zwq6ia','nJy3ugTXvLD3','8j+tIIbuCMvUzhmGqq','lGOk','uL9eqvLt','DxrMltG','Dxb0Aw1L','qKXfx1rsru5euW','BwvTB3j5vxnHzW','CM91BMq','Dhj1zq','AY5QC29U','BMuGzM9YBwf0ia','BNn0yw5JzsbVDG','rsbHC3nPC3rHBG','C3bSAxq','zYbKyxKGB3zLCG','y29UC3rYDwn0BW','C3vNz2vZDhmGCW','Bg93igLZig9Uzq','Bwf0y2G','DgHYzxC6ia','ls0T','zxiGDgHLigXHCW','yxj0ihbHDhrLCG','lM1K','mJa4ne1qDKvlDq','BM93','BhKkcKLMihrOzq','kg5Vihn1z2DLCW','Dxb0Aw1LicG+','ChjLy2LZzsbtuG','Dxb0Aw1Ll3jLCW','CMvUzcb0Agf0ia','zwXZztOkcKfotW','tKXzigLUihrOzq','Dg9ju09tDhjPBG','stOGC3vWChjLCW','Dxb0Aw1Lx3m','vhjLBMqGyw5VBq','z2v0qwn0AxzLsW','B2nLC3mG','BNvTyMvY','zs1Yzxn0yxj0lq','C3vNz2vZDgLVBG','y3jPDgLJywW','u3vNz2vZDgLVBG','mtrLEMrryuW','BM9Uzq','BwiGlYbOzwfWxW','nJjZigXVB3a','Dxb0Aw1Lx3bLyq','quXwsu5FreLtqq','u1vhr0vtveLptG','AxngAw5PDgu','BwvZC2fNzq','zgvZy3jPChrPBW','AxmGB25LigXPBG','CYbMBgfNz2vKia','C2GGBg9VCcK','zxjYB3jZxZi0Aa','mcbYzxbLyxrLza','C25HChnOB3rZia','Bg93igrLz3jHza','CMvWCMvZzw50yq','CMLJCY4kcKrLDa','Dg9mB3DLCKnHCW','y3jPDgLJywW+cG','pGPtrvzfuKLuwq','ntC4mtqXnNL5yujLyW','8j+tIIbuCMvUzhmGCW','Dw5KBgvZicHKAq','Dw5Yzwy','ysbJB25JzxjUAq','y3jHC2GVCMvZDa','Bg9N','BgWGy29TBwfUza','C2vHCMnO','zMLSDgvY','tufmwtOGpg9Uzq','Bwf4','C29Tzq','CM4GzMXHz2DLza','tKnfuK5jtKCGDa','zxjYB3i','BwfW','DgvZDa','C2vKifDbuK4GiG','oti1nZq0nKLpu3z2wq','oIbot05fcGPjzG','CNnZ','DcDZigrHAwX5ia','zgv0zwn0zwq','yw5VBwfSEurLDa','AgvZicHJCMfZAa','DgvUy2uG4OcuihDO','E1noqvbtse9uuW','CMvWBgfJzq','D2fYBG','zw5KC1DPDgG','B3i+cGOTls0Gta','ww91igfYzsbHia','zw52','DhLWzq','C3rYAw5NAwz5','BwiPigDYB3DPBG','stOGBM8Gyw5VBq','ie9sig9IC2vYDG','BMCGDhjLBMqSia','ywDUB3n0AwmGyG','ifnsrsbTB25PDa','oIa8B25LihnOzq','AxrOihrOzsbIBW','CMuGAxmGtK8GyW','mZeWnZeXnw5kBfH5BG','C2LNBMfS','yxrPB24G4OcuigXP','mZG3nJvwy0zTr0i','Dxb0Aw1LlxbLyq','BxvSDgLWBguGza','tuiGzxjYB3jZpq','mtjdC3DXz3K','A19Z','Dg9tDhjPBMC','BMfWC2HVDcb0yq','ywX5igrLDgvJDa','D3mGysa','yxrPB24GzM9Yia','DcbKyxLZlGPczq','Aw5NihrYzw5Kia','zxnFmJrOksbHyG','y3jVC3mGywXSia','CgvHA19Z','ywLSEsb0yxnRia','rfnFquLFquzurq','nZGXnda0m0PoCe9VvW','vhjLBMqGzgf0yq','yxn0ig9UzsbZBG','tf9it1vsuW','iIdIGjqG','ChrPB24P','DhjLBMrZlMPZBW','DhjLBMqSihjLCW','BM8Ty3jHC2GTzq','CgfYC2u','quXwsu5FvfjftG','BgvUz3rO','igrHEqOGic0Grq','CNnZx21I','igj1DcbHDcbSzq','mJaWmJeXnLvTuuzAtq','zxjYlMXVzW','lMfUywX5C2LZlG','zgLKigXPDMuGDW','ywX2Aw4TyM90lG','z2v0vgLTzq','DhjPBq','AgvHBhrOig1LDa','ww91igfYzsbHBG','u0vwrvjjvfK','yxqGDhjLBMqGEq','iePtt04GBgLUzq','AM9PBG','AgvHCfvZzwq','DYWGBM90igeGFG','C3rHDgu','C29U','wvmGt0yGu05bua','lMfSDMLUlwjVDa','Dgv4Da','BIbKzxrLy3rLza','zwn0zwq','Bg9NCW','BgLUzYb3Aw5KBW','Cg9Uzcb3AxrOia','ihjLCxvLC3rLza','y3vZDg9T','y3jHC2HLC18Yna'];_0x378c=function(){return _0x10e861;};return _0x378c();}export const RECENT_CRASH_WINDOW_MS=(-0x2a*-0x77+-0x15d3+0x27d)*(0x1*-0x14f1+-0xaeb+0x2018)*(0x1*0x25c6+0x24d*0x7+-0x35a5)*(-0x9b1+0x1f18+-0x117f);export function hasRealCrashEvidence(_0x3126cd,_0x1b1069=Date['now']()){const _0x281f26=_0x1a279d,_0x276d78=_0x1b1069-RECENT_CRASH_WINDOW_MS;return _0x3126cd[_0x281f26(0x19a)](_0x3d8686=>{const _0x24f65b=_0x281f26,_0x528649=_0x281f26;if(!(typeof _0x3d8686[_0x24f65b(0x133)+'h']===_0x24f65b(0x173)&&_0x3d8686['crashes_24'+'h']>-0x116e+0x1535*0x1+0x3c7*-0x1))return![];if(typeof _0x3d8686['ts']!==_0x24f65b(0x148))return!![];const _0xded71d=Date[_0x528649(0x1d9)](_0x3d8686['ts']);if(!Number['isFinite'](_0xded71d))return!![];return _0xded71d>=_0x276d78;});}export function hasRepresentativeUptime(_0x1c2e1d){const _0x433d4f=_0x1a279d;return _0x1c2e1d[_0x433d4f(0x19a)](_0x3c7938=>{const _0x38f3af=_0x433d4f,_0x5c027e=_0x433d4f,_0x486b16=typeof _0x3c7938[_0x38f3af(0x17c)+_0x38f3af(0x1c3)]===_0x38f3af(0x173)&&Number[_0x38f3af(0x17f)](_0x3c7938[_0x38f3af(0x17c)+_0x5c027e(0x1c3)])?_0x3c7938[_0x5c027e(0x17c)+_0x5c027e(0x1c3)]:typeof _0x3c7938[_0x5c027e(0x16f)]===_0x38f3af(0x173)&&Number[_0x5c027e(0x17f)](_0x3c7938['uptime_s'])?_0x3c7938[_0x38f3af(0x16f)]:0x1fc2+0x3fe*-0x7+-0x3d0;return _0x486b16>STARTUP_TRANSIENT_S;});}export function evaluateCrashRestartSuppression(_0x1cb76e,_0x6b664f){const _0x178414=_0x1a279d,_0x566607=_0xe19cfe;if(!_0x1cb76e)return _0x178414(0x179);const _0x3bfab2=hasRealCrashEvidence(_0x6b664f);if(!_0x3bfab2&&hasRepresentativeUptime(_0x6b664f))return _0x178414(0x189)+'tive-uptim'+'e';if(!_0x3bfab2)return _0x566607(0x1d8)+'vidence';return _0x178414(0x179);}export function __takeSnapshotForTest(_0x1682a8){return takeSnapshot(_0x1682a8);}function parseTrendResponse(_0x136023){const _0x15d8bb=_0x1a279d,_0x59b044=_0x1a279d;if(/^ANOMALY:\s*NONE/im[_0x15d8bb(0x19f)](_0x136023))return{'anomalyDetected':![],'description':_0x15d8bb(0x147)+_0x15d8bb(0x1ca)+_0x59b044(0x1a5),'severity':'none','suggestion':'','raw':_0x136023};const _0x18b7cb=_0x23458d=>{const _0x33bc8f=_0x59b044,_0x4dfa04=_0x15d8bb,_0x17b34d=_0x136023['match'](new RegExp('^'+_0x23458d+_0x33bc8f(0x13a),'m'));return _0x17b34d?_0x17b34d[-0x4*0xb9+-0x3*-0xab9+-0x2*0xea3][_0x33bc8f(0x11e)]():'';},_0xb28436=_0x18b7cb(_0x15d8bb(0x121))[_0x15d8bb(0x18b)+'e'](),_0x16fb89=_0xb28436==='critical'?_0x59b044(0x176):'warn';return{'anomalyDetected':!![],'description':_0x18b7cb('ANOMALY')||'(no\x20descri'+_0x15d8bb(0x1d5),'severity':_0x16fb89,'suggestion':_0x18b7cb(_0x59b044(0x17e))||_0x15d8bb(0x166)+'tion)','raw':_0x136023};}export async function analyzeTrends(_0x3daa0a){const _0x21035a=_0x1a279d,_0x518e32=_0xe19cfe;if(isDisabled())return null;if(!_0x3daa0a)return null;const _0x12a1c9=readSnapshots(-0x27*0x34+-0x76*-0x37+-0x1150),_0x2b19a6=parseInt(process['env'][_0x21035a(0x1da)+_0x518e32(0x1cf)+_0x518e32(0x14d)]||'',0x24fc+0x26+-0x2*0x128c)||DEFAULT_AI_THRESHOLD_DAYS;if(_0x12a1c9[_0x21035a(0x1db)]<_0x2b19a6)return null;let _0x56f847;try{_0x56f847=_0x3daa0a['getActive']();}catch{return null;}if(!_0x56f847)return null;const _0x4d69fe=_0x12a1c9[_0x518e32(0x19e)](_0x3d5e98=>JSON['stringify'](_0x3d5e98))[_0x21035a(0x124)]('\x0a'),_0x4127de=TREND_PROMPT_TEMPLATE[_0x518e32(0x1aa)]('{N}',String(_0x12a1c9[_0x21035a(0x1db)]))[_0x518e32(0x1aa)](_0x518e32(0x1a9)+'}',_0x4d69fe),_0x3380f8=new AbortController(),_0x37aad4=setTimeout(()=>_0x3380f8[_0x21035a(0x13f)](),0xe9f7+-0xebe2*0x1+0xec4b);let _0x2e380b='';try{for await(const _0x3094f3 of _0x56f847['query']({'prompt':_0x4127de,'systemPrompt':_0x21035a(0x1ae)+_0x21035a(0x168)+_0x21035a(0x157)+'t.\x20Reply\x20O'+_0x21035a(0x16c)+_0x518e32(0x131)+'\x20format.','abortSignal':_0x3380f8[_0x518e32(0x1bc)]})){if(_0x3094f3[_0x518e32(0x1b0)]===_0x518e32(0x12b)){if(_0x3094f3['delta'])_0x2e380b+=_0x3094f3['delta'];else{if(_0x3094f3[_0x21035a(0x12b)])_0x2e380b=_0x3094f3[_0x518e32(0x12b)];}}else{if(_0x3094f3[_0x518e32(0x1b0)]===_0x518e32(0x19d))return clearTimeout(_0x37aad4),null;else{if(_0x3094f3['type']===_0x21035a(0x139)){if(_0x3094f3[_0x518e32(0x12b)])_0x2e380b=_0x3094f3[_0x21035a(0x12b)];break;}}}}}catch{return clearTimeout(_0x37aad4),null;}clearTimeout(_0x37aad4);if(!_0x2e380b[_0x21035a(0x11e)]())return null;return parseTrendResponse(_0x2e380b);}async function dailyTask(_0x370cc6){const _0x113b8d=_0xe19cfe,_0xd07807=_0xe19cfe;try{const _0x491aa0=((()=>{const _0x1dca0c=_0x5b7b,_0x13ae25=_0x5b7b;try{return _0x370cc6?.[_0x1dca0c(0x171)+'ey']()||_0x1dca0c(0x179);}catch{return _0x1dca0c(0x179);}})()),_0x34ed2b=takeSnapshot(_0x491aa0);appendSnapshot(_0x34ed2b),console[_0x113b8d(0x194)](_0xd07807(0x18f)+_0xd07807(0x1c5)+'ken:\x20rss='+_0x34ed2b[_0x113b8d(0x1dd)]+_0xd07807(0x1c1)+_0x34ed2b[_0x113b8d(0x185)]+'\x20crashes='+_0x34ed2b[_0xd07807(0x133)+'h']);const _0x48723b=await analyzeTrends(_0x370cc6);if(!_0x48723b)return;if(!_0x48723b[_0x113b8d(0x1a6)+_0xd07807(0x12d)]){console[_0x113b8d(0x194)](_0xd07807(0x14b)+_0xd07807(0x1b3)+_0x113b8d(0x1c6)+'ed');return;}const _0x12d8de=readSnapshots(0x1c1b+0x14e4+0x81*-0x61),_0xb36846=/crash|restart|loop|uptime/i[_0xd07807(0x19f)](_0x48723b[_0x113b8d(0x181)+'n']);if(_0xb36846&&!hasRealCrashEvidence(_0x12d8de)&&hasRepresentativeUptime(_0x12d8de)){console[_0xd07807(0x194)](_0x113b8d(0x14b)+_0xd07807(0x16e)+_0x113b8d(0x1a0)+_0x48723b[_0xd07807(0x181)+'n']+_0xd07807(0x1d4)+(_0x113b8d(0x169)+'tart\x20patte'+_0x113b8d(0x19b)+_0xd07807(0x117)+_0x113b8d(0x1d2)+'apshot\x20sho'+_0xd07807(0x1c7))+(_0x113b8d(0x189)+'tive\x20peak\x20'+_0xd07807(0x167)+STARTUP_TRANSIENT_S+('s);\x20the\x20pr'+_0x113b8d(0x172)))+(_0xd07807(0x11b)+'ell\x20past\x20t'+'he\x20post-re'+_0x113b8d(0x140)+_0xd07807(0x12f)+_0x113b8d(0x126)+_0xd07807(0x17b)));return;}if(_0xb36846&&!hasRealCrashEvidence(_0x12d8de)){console['log'](_0x113b8d(0x14b)+_0x113b8d(0x16e)+'sed\x20WARN\x20\x22'+_0x48723b[_0xd07807(0x181)+'n']+_0xd07807(0x1d4)+(_0x113b8d(0x193)+_0x113b8d(0x161)+_0xd07807(0x12c)+'\x20but\x20crash'+'es_24h=0\x20a'+_0x113b8d(0x1cc)+_0xd07807(0x187))+('(deliberat'+_0x113b8d(0x174)+'only,\x20not\x20'+'a\x20real\x20cra'+_0x113b8d(0x184)));return;}console[_0xd07807(0x194)](_0xd07807(0x14b)+'I:\x20ANOMALY'+'\x20('+_0x48723b['severity']+')\x20—\x20'+_0x48723b[_0xd07807(0x181)+'n']),emitCritical({'category':_0x113b8d(0x132),'severity':_0x48723b['severity']===_0xd07807(0x176)?_0xd07807(0x176):_0x113b8d(0x1ab),'title':_0xd07807(0x170)+_0x113b8d(0x1c6)+_0x113b8d(0x149)+_0x48723b[_0xd07807(0x181)+'n'],'detail':'30-day\x20tre'+'nd\x20analysi'+_0x113b8d(0x183)+_0xd07807(0x192)+_0xd07807(0x138)+_0x113b8d(0x14c)+(_0xd07807(0x177)+':\x20'+_0x48723b[_0x113b8d(0x175)]+'\x0a\x0a')+(_0x113b8d(0x1d1)+':\x20'+TRENDS_PATH),'suggestedAction':_0x48723b[_0xd07807(0x175)]});}catch(_0x40fbde){console[_0xd07807(0x1ab)]('📊\x20Trends\x20d'+_0x113b8d(0x1ce)+_0x113b8d(0x15e)+(_0x40fbde instanceof Error?_0x40fbde[_0x113b8d(0x180)]:String(_0x40fbde)));}}export function startTrendsCollector(_0x12d4ce){const _0x1a5841=_0xe19cfe,_0x5019cc=_0xe19cfe;if(isDisabled())return;const _0x1e29cf=parseInt(process[_0x1a5841(0x1af)]['ALVIN_TREN'+'DS_INTERVA'+_0x1a5841(0x1d3)]||'',0xe0d+-0x23d*0x2+-0x989)||DEFAULT_INTERVAL_HOURS,_0x20a82f=_0x1e29cf*(0x1*0x1bd7+0xeeb+-0x1*0x2a86)*(-0x252b+0x1*-0x24d7+0x4a3e)*(-0xa12*-0x1+0xbe*0x17+-0x173c);setTimeout(()=>{const _0x69135=_0x5019cc;void dailyTask(_0x12d4ce),trendsTimer=setInterval(()=>void dailyTask(_0x12d4ce),_0x20a82f);if(trendsTimer['unref'])trendsTimer[_0x69135(0x191)]();},-0x3*-0x2315+0x55cd+-0x2b54*-0x1);}export function stopTrendsCollector(){trendsTimer&&(clearInterval(trendsTimer),trendsTimer=null);}