alvin-bot 4.26.0 → 5.1.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,121 @@
2
2
 
3
3
  All notable changes to Alvin Bot are documented here.
4
4
 
5
+ ## [5.1.0] — 2026-05-13
6
+
7
+ ### Permissions Wizard — guided one-and-done macOS setup
8
+
9
+ macOS' TCC framework architecturally **refuses** to let any app grant Full Disk Access / Automation / Accessibility programmatically — only the user can flip those switches in System Settings. There is no API, no AppleScript trick, no sudo bypass for a normal Node CLI.
10
+
11
+ What we can do — and what 5.1.0 does — is make the toggling experience painless:
12
+
13
+ 1. **Detect** every permission's current state (sudo + FDA + Automation + Accessibility)
14
+ 2. For each missing one: **open the exact right Settings pane**
15
+ 3. **Poll for the toggle** every 2 s, up to 60 s per permission
16
+ 4. **Verify** and move to the next
17
+ 5. End with a clear summary of granted / skipped / still-missing
18
+
19
+ ```bash
20
+ alvin-bot permissions status # quick state-of-the-world
21
+ alvin-bot permissions wizard # interactive guided setup
22
+ alvin-bot permissions open <id> # open one Settings pane
23
+ ```
24
+
25
+ The wizard also bundles **sudo-password storage** (Keychain on macOS, encrypted file on Linux) as the first step, so users get a single upfront onboarding flow instead of separate prompts later. Run-once intent: no more piecemeal permission requests at runtime.
26
+
27
+ ### New detections
28
+
29
+ - **Automation (Apple Events)** — probes via `osascript -e 'tell application "System Events" to ...'`, catches error code 1743 / "Not authorized" to distinguish denied vs granted. Used by Apple Mail, Apple Notes, Calendar skills.
30
+ - **Accessibility** — now distinguishes "cliclick not installed" from "permission denied", so the wizard suggests the actually-correct fix (install via brew vs. toggle in Settings).
31
+
32
+ ### Doctor integration
33
+
34
+ `alvin-bot doctor` now uses the same wizard service for its macOS-permissions section — shows all 4 permissions instead of just FDA, points to the wizard for any missing ones.
35
+
36
+ ### Telegram `/setup` integration
37
+
38
+ `/setup` keyboard gets a new **🛡️ Permissions Wizard (Mac)** button — surfaces the current 4-permission status and the CLI / WebUI commands to actually run the wizard. Mobile UX is intentionally read-only: the wizard needs to drive System Settings panes on the host, which only the local CLI/WebUI can do.
39
+
40
+ ### README — comprehensive CLI section refresh
41
+
42
+ Documented commands added: `tools`, `provider`, `permissions`, `browser`, `status`. Plus the full env-var opt-out table for Self-Preservation Phase 1 + 2 (`ALVIN_DISABLE_*`, `ALVIN_DEADMAN_THRESHOLD_SEC`, etc.). The README CLI section was missing six commands shipped in 4.23 — 5.0; now matches reality.
43
+
44
+ ### Honest about what we CAN'T do
45
+
46
+ The README and wizard are explicit about this: macOS TCC permissions cannot be granted by an app. The wizard opens panes and verifies after; the user toggles. No tricks, no entitlement bypass attempts.
47
+
48
+ ## [5.0.0] — 2026-05-13
49
+
50
+ ### Self-Preservation Phase 2 — the bot now reasons about its own failures
51
+
52
+ The bot now uses **its own AI provider** (whichever one you have configured — claude-sdk, codex-cli, groq, gemini, openai, ollama/gemma4, openrouter, nvidia-nim) to analyze why it failed and where it's heading. Two new features, both event-driven, both opt-out.
53
+
54
+ The major-version bump reflects a conceptual shift: AI is now part of the bot's **operational loop** about itself, not just the user-facing chat. The feature surface is backwards-compatible — existing setups keep running unchanged; everything new is additive and opt-out via env vars.
55
+
56
+ #### AI-driven Self-Diagnosis on bundles (feature 3I)
57
+
58
+ When the watchdog brake fires and 2F writes a forensic bundle, 3I picks up the analysis at **the next successful bot start**:
59
+
60
+ 1. Bot starts → Pre-Flight runs → 3I scans `~/.alvin-bot/diagnostics/` for unanalyzed bundles
61
+ 2. For each bundle without a `.analysis.md` sidecar, send it (clipped to ~12 KB, head+tail) to the active AI provider via `provider.query()`
62
+ 3. AI returns a structured 5-line response — `HYPOTHESIS / ROOT_CAUSE_CATEGORY / REMEDIATION / CONFIDENCE / EXPLANATION`
63
+ 4. Result is written as `.analysis.md` sidecar AND delivered to the operator via 1D Telegram DM
64
+
65
+ The 5-line plain-text format was chosen over JSON because **JSON parsing reliability is uneven across providers**, especially with smaller models. The format is hard to mess up — and we parse it with a tolerant regex.
66
+
67
+ Live verified on Apple Silicon with `claude-sdk`: bundle from the actual brake earlier that day was analyzed correctly — AI identified "skills-reload triggered repeated graceful restarts that tripped the brake", suggested the documented recovery command (`rm crash-loop.alert && alvin-bot launchd install`), all within ~9 s.
68
+
69
+ **Safety policy v1**: the AI's suggested remediation is shown to the operator but **NEVER auto-applied**. This is intentional — we want a track record of accurate suggestions before granting the bot any self-modifying power.
70
+
71
+ #### Predictive Maintenance via Trends (feature 3J)
72
+
73
+ A second daily timer writes a one-line JSON snapshot of bot health to `~/.alvin-bot/state/trends.jsonl`:
74
+
75
+ ```jsonl
76
+ {"ts":"2026-05-13T...","uptime_s":86400,"rss_mb":105,"heap_mb":33,"crashes_24h":0,"diag_24h":0,"errors_24h":3,"provider":"claude-sdk","version":"5.0.0"}
77
+ ```
78
+
79
+ After 7 days of data accumulate, every daily snapshot also triggers an AI **anomaly-detection pass** over the last 30 days. Output is a strict 3-line format — `ANOMALY: ... / SEVERITY: warn|critical / SUGGESTION: ...`, or just `ANOMALY: NONE` when nothing's concerning.
80
+
81
+ Live verified with synthetic 30-day memory-leak data (RSS climbing 100 → 220 MB linearly): `claude-sdk` correctly identified the leak, classified as `critical`, suggested heap-snapshot capture via `kill -USR2`. Confirmed end-to-end with file flag + Telegram DM delivered via 1D.
82
+
83
+ #### Provider-agnostic by design
84
+
85
+ Both 3I and 3J use the existing `provider.query()` abstraction — the same code path the bot uses for normal chat. Switching provider via `alvin-bot provider switch <key>` (added in 4.24.0) automatically retargets 3I + 3J as well. No provider-specific code in either feature.
86
+
87
+ **Tested provider**: `claude-sdk` (the "B1" test path). The `offline-gemma4` test path (B4) — stress-test of prompt design against a small-context local model — is deferred to a follow-up session; the deferral and its acceptance criteria are documented in the (gitignored) project `BACKLOG.md`.
88
+
89
+ #### Performance budget held
90
+
91
+ All new code runs detached, on long timers, or at startup-only. Steady-state cost: zero. The startup analyzer (3I) only runs if unanalyzed bundles exist — typically 0 on a healthy run. The trends collector (3J) runs once every 24 h with a 60 s warmup after startup.
92
+
93
+ Measured on Apple Silicon (vs. 4.26.0 baseline):
94
+ - RSS idle: **+0 MB** (modules loaded lazily via dynamic `import()`)
95
+ - Cold-start ready: **unchanged** (both modules load post-startup, fire-and-forget)
96
+ - 3I per-bundle latency on claude-sdk: ~9 s
97
+ - 3J per-analysis latency on claude-sdk: ~10 s
98
+
99
+ #### New env vars
100
+
101
+ ```
102
+ ALVIN_DISABLE_SELF_DIAGNOSIS=true # disable 3I
103
+ ALVIN_DISABLE_TRENDS=true # disable 3J
104
+ ALVIN_TRENDS_INTERVAL_HOURS=24 # default
105
+ ALVIN_TRENDS_AI_AFTER_DAYS=7 # min history before AI kicks in
106
+ ```
107
+
108
+ (All Phase-1 env vars from 4.26.0 continue to work — `ALVIN_DISABLE_SELF_PRESERVATION=true` still kills everything.)
109
+
110
+ #### Why a major version bump
111
+
112
+ Semantically, the bot is now **closing a loop on itself**: it observes its own forensics, asks an AI to interpret them, and reports back. That's a conceptual line worth marking. Nothing breaks — existing users update with `npm install -g alvin-bot@latest` and the new behaviour just appears in their next failure analysis.
113
+
114
+ #### Files added
115
+
116
+ - `src/services/self-diagnosis.ts` — 3I startup analyzer + analyzeBundle()
117
+ - `src/services/trends.ts` — 3J snapshot collector + analyzeTrends()
118
+ - `src/index.ts` — two fire-and-forget dynamic imports after Pre-Flight
119
+
5
120
  ## [4.26.0] — 2026-05-13
6
121
 
7
122
  ### Self-Preservation Phase 1 — four new resilience features, zero hot-path cost
package/README.md CHANGED
@@ -527,21 +527,105 @@ Drop your own `<name>/SKILL.md` into `~/.alvin-bot/skills/` for hot-reload. List
527
527
 
528
528
  ## 🛠️ CLI
529
529
 
530
+ ### Core lifecycle
531
+
532
+ ```bash
533
+ alvin-bot setup # Interactive setup wizard (Telegram + AI provider + tools)
534
+ alvin-bot start # Start the bot in background (launchd on macOS, pm2 elsewhere)
535
+ alvin-bot start -f # Start in foreground (for debugging)
536
+ alvin-bot stop # Stop the running bot
537
+ alvin-bot status # Show version + LaunchAgent / pm2 state (offline)
538
+ alvin-bot doctor # Health check — config, provider, memory, permissions
539
+ alvin-bot update # Pull latest from npm (or git if running from source)
540
+ alvin-bot version # Show version
541
+ ```
542
+
543
+ ### Interactive chat
544
+
545
+ ```bash
546
+ alvin-bot tui # Terminal chat UI with streaming + ANSI colors ✨
547
+ alvin-bot chat # Alias for tui
548
+ alvin-bot tui --lang de # Force German UI
549
+ ```
550
+
551
+ ### AI provider management (since 4.24.0)
552
+
553
+ Switch between Claude SDK / Codex CLI / Groq / Gemini / OpenAI / OpenRouter / NVIDIA NIM / offline Gemma 4 without re-running the full setup wizard. The switch command runs the same install + auth flow the wizard uses (CLI install + OAuth login for `claude-sdk` / `codex-cli`, API-key prompt + live validation for the rest), then does a byte-preserving merge of `~/.alvin-bot/.env` — the previous provider's API key is **parked, not deleted**, so rollback is one un-comment away.
554
+
555
+ ```bash
556
+ alvin-bot provider list # Show all providers + per-provider install/key status
557
+ alvin-bot provider show # Detailed info on the currently configured provider
558
+ alvin-bot provider switch <key> # Switch (interactive setup + .env merge + bot restart)
559
+ alvin-bot provider doctor # Validate current provider's auth against its API
560
+ ```
561
+
562
+ `<key>` accepts canonical slugs **or** short aliases: `claude`, `codex`, `gemini`, `nvidia`, `gpt`, `gemma`.
563
+
564
+ ### Optional tools — install / update (since 4.23.0)
565
+
566
+ A curated set of universally useful CLIs that unlock specific skills. Bootstrap tools (`yt-dlp`, `ffmpeg`, and `wacli` if WhatsApp is enabled) are auto-installed/updated by `setup` and `update`; the rest you opt into through the menu.
567
+
568
+ ```bash
569
+ alvin-bot tools list # Show installed / missing optional tools
570
+ alvin-bot tools install # Interactive menu — pick which to install
571
+ ```
572
+
573
+ ### macOS permissions wizard (since 5.1.0)
574
+
575
+ macOS' TCC framework refuses to let any app grant Full Disk Access / Automation / Accessibility programmatically — only the user can flip those switches. The wizard makes the toggling experience painless: it detects every permission's current state, opens the **exact right Settings pane** for each missing one, waits for you to toggle (polling every 2 s for up to 60 s per permission), verifies, and moves on. Bundles sudo-password storage in the same upfront flow.
576
+
577
+ ```bash
578
+ alvin-bot permissions status # Quick status: all 4 permissions + current state
579
+ alvin-bot permissions wizard # Interactive guided setup, one-and-done
580
+ alvin-bot permissions open <id> # Open one Settings pane (full-disk-access / automation / accessibility)
581
+ alvin-bot perms # alias for permissions
582
+ ```
583
+
584
+ ### LaunchAgent (macOS only)
585
+
586
+ ```bash
587
+ alvin-bot launchd install # Write ~/Library/LaunchAgents/com.alvinbot.app.plist + load
588
+ # (Also installs the dead-man-switch companion plist since 4.26.0)
589
+ alvin-bot launchd status # Show PID + recent stdout/stderr from the LaunchAgent
590
+ alvin-bot launchd uninstall # Unload + remove both plists
591
+ ```
592
+
593
+ ### Browser automation (bot-managed Chromium)
594
+
595
+ ```bash
596
+ alvin-bot browser start # Launch Chromium with CDP, persistent profile
597
+ alvin-bot browser start headful # Same, visible (for login flows)
598
+ alvin-bot browser goto <url> # Open URL, return JSON metadata
599
+ alvin-bot browser shot <url> [file] # Screenshot → ~/.alvin-bot/browser/screenshots/
600
+ alvin-bot browser eval <url> "<js>" # Run JS in page context
601
+ alvin-bot browser tabs # List open tabs
602
+ alvin-bot browser status # PID + CDP endpoint
603
+ alvin-bot browser stop # Quit Chromium
604
+ alvin-bot browser doctor # Diagnose Chromium / Playwright setup
605
+ ```
606
+
607
+ ### Maintenance & introspection
608
+
609
+ ```bash
610
+ alvin-bot audit # Security health check — permissions, secrets, config
611
+ alvin-bot search "<query>" # Search assets, memories, and skills index
612
+ ```
613
+
614
+ ### Environment-variable opt-outs (Self-Preservation features since 4.26.0 / 5.0.0)
615
+
616
+ Granular opt-out for the resilience subsystems — everything is enabled by default:
617
+
530
618
  ```bash
531
- alvin-bot setup # Interactive setup wizard
532
- alvin-bot tui # Terminal chat UI
533
- alvin-bot chat # Alias for tui
534
- alvin-bot doctor # Health check
535
- alvin-bot update # Pull latest & rebuild
536
- alvin-bot start # Start the bot (background via pm2)
537
- alvin-bot start -f # Start in foreground
538
- alvin-bot stop # Stop the bot
539
- alvin-bot launchd install # macOS only: install as LaunchAgent
540
- alvin-bot launchd status # macOS only: show LaunchAgent state
541
- alvin-bot launchd uninstall # macOS only: remove LaunchAgent
542
- alvin-bot audit # Security health check
543
- alvin-bot search # Search assets/memories/skills
544
- alvin-bot version # Show version
619
+ ALVIN_DISABLE_SELF_PRESERVATION=true # Kill ALL Phase-1 + Phase-2 features below
620
+ ALVIN_DISABLE_PREFLIGHT=true # Skip startup sanity check (Telegram, provider, SQLite, disk)
621
+ ALVIN_DISABLE_CRITICAL_NOTIFY=true # Skip cross-channel alerts (Telegram + macOS notif + file flag)
622
+ ALVIN_DISABLE_DEAD_MAN=true # Skip the zombie-detection heartbeat writer
623
+ ALVIN_DISABLE_AUTO_DIAGNOSTIC=true # Skip forensic-bundle writing on crash
624
+ ALVIN_DISABLE_SELF_DIAGNOSIS=true # Skip AI analysis of forensic bundles at startup
625
+ ALVIN_DISABLE_TRENDS=true # Skip daily trend snapshots + AI anomaly detection
626
+ ALVIN_DEADMAN_THRESHOLD_SEC=600 # Dead-man's-switch staleness threshold (default 10 min)
627
+ ALVIN_TRENDS_INTERVAL_HOURS=24 # Trend-snapshot cadence (default 24 h)
628
+ ALVIN_TRENDS_AI_AFTER_DAYS=7 # Days of history before AI anomaly detection kicks in
545
629
  ```
546
630
 
547
631
  ---
package/bin/cli.js CHANGED
@@ -2538,17 +2538,32 @@ async function doctor() {
2538
2538
  }
2539
2539
 
2540
2540
  // ── macOS permissions (TCC) ────────────────────────────────────────────
2541
+ // 5.1.0 — superseded by the permissions-wizard service, which checks
2542
+ // all four (sudo + FDA + Automation + Accessibility) and gives the
2543
+ // user a single command to fix anything missing. The legacy FDA-only
2544
+ // block stays as a fast-path summary for users who haven't yet run
2545
+ // the wizard.
2541
2546
  if (process.platform === "darwin") {
2542
2547
  console.log("\n macOS permissions:");
2543
- const fda = checkMacosFullDiskAccess(process.execPath);
2544
- if (fda.hasFDA) {
2545
- console.log(` ✅ Full Disk Access — granted to ${fda.realNodePath}`);
2546
- } else {
2547
- console.log(` ⚠️ Full Disk Access NOT granted to node (${fda.realNodePath})`);
2548
- console.log(` Spawned tools (codex CLI, file-reading skills) may silently fail`);
2549
- console.log(` to read protected directories under launchd. To fix:`);
2550
- console.log(` open "${fda.paneUrl}"`);
2551
- console.log(` and add the path above with the "+" button.`);
2548
+ try {
2549
+ const { readPermissionsSnapshot, formatPermissionsSnapshot } = await import("../dist/services/permissions-wizard.js");
2550
+ const snaps = await readPermissionsSnapshot();
2551
+ console.log(formatPermissionsSnapshot(snaps));
2552
+ const missing = snaps.filter(s => s.state === "missing" || s.state === "tool-missing").length;
2553
+ if (missing > 0) {
2554
+ console.log(`\n ${missing} permission${missing === 1 ? "" : "s"} need attention. Run:`);
2555
+ console.log(" alvin-bot permissions wizard");
2556
+ }
2557
+ } catch (err) {
2558
+ // Fall back to the legacy FDA-only check if the wizard module
2559
+ // is unavailable for any reason.
2560
+ const fda = checkMacosFullDiskAccess(process.execPath);
2561
+ if (fda.hasFDA) {
2562
+ console.log(` ✅ Full Disk Access — granted to ${fda.realNodePath}`);
2563
+ } else {
2564
+ console.log(` ⚠️ Full Disk Access NOT granted to node (${fda.realNodePath})`);
2565
+ console.log(` To fix: open "${fda.paneUrl}" → add ${fda.realNodePath}`);
2566
+ }
2552
2567
  }
2553
2568
  }
2554
2569
 
@@ -3102,6 +3117,76 @@ switch (cmd) {
3102
3117
  }
3103
3118
  break;
3104
3119
  }
3120
+ case "permissions":
3121
+ case "perms": {
3122
+ const sub = (process.argv[3] || "status").toLowerCase();
3123
+ (async () => {
3124
+ const { readPermissionsSnapshot, formatPermissionsSnapshot, runPermissionsWizard, PERMISSIONS } = await import("../dist/services/permissions-wizard.js");
3125
+ if (sub === "status" || sub === "ls" || sub === "list") {
3126
+ const snaps = await readPermissionsSnapshot();
3127
+ console.log("\n🛡️ Permissions status\n");
3128
+ console.log(formatPermissionsSnapshot(snaps));
3129
+ const missing = snaps.filter(s => s.state === "missing" || s.state === "tool-missing").length;
3130
+ console.log("");
3131
+ if (missing > 0) {
3132
+ console.log(` ${missing} missing — run: alvin-bot permissions wizard`);
3133
+ } else {
3134
+ console.log(" All applicable permissions granted.");
3135
+ }
3136
+ console.log("");
3137
+ } else if (sub === "wizard" || sub === "grant" || sub === "setup") {
3138
+ const yes = ["y","yes","j","ja"];
3139
+ await runPermissionsWizard({
3140
+ print: (t) => console.log(t),
3141
+ confirm: async (q) => {
3142
+ const a = (await ask(`${q} (y/n): `)).trim().toLowerCase();
3143
+ return yes.includes(a) || a === "";
3144
+ },
3145
+ promptPassword: async (q) => {
3146
+ // Best-effort: hide password if process.stdin is TTY (we can
3147
+ // toggle echo). Otherwise plain read — acceptable fallback.
3148
+ try {
3149
+ if (process.stdin.isTTY) {
3150
+ process.stdin.setRawMode?.(true);
3151
+ }
3152
+ } catch {}
3153
+ const a = await ask(q);
3154
+ try {
3155
+ process.stdin.setRawMode?.(false);
3156
+ } catch {}
3157
+ return a.trim();
3158
+ },
3159
+ });
3160
+ } else if (sub === "open") {
3161
+ const id = process.argv[4];
3162
+ const perm = PERMISSIONS.find(p => p.id === id);
3163
+ if (!perm || !perm.openPane) {
3164
+ console.log(`Unknown or non-openable permission: ${id}`);
3165
+ console.log("Available: " + PERMISSIONS.filter(p => p.openPane).map(p => p.id).join(", "));
3166
+ } else {
3167
+ const ok = perm.openPane();
3168
+ console.log(ok ? `Opened System Settings → ${perm.name}` : "Could not open Settings.");
3169
+ }
3170
+ } else {
3171
+ console.log("Usage: alvin-bot permissions <status|wizard|open <id>>");
3172
+ console.log("");
3173
+ console.log(" status (default) — List all permissions + current state");
3174
+ console.log(" wizard — Interactive guided setup: walks through each missing");
3175
+ console.log(" permission, opens the right Settings pane, waits for");
3176
+ console.log(" you to toggle, verifies. macOS-only TCC bits + sudo.");
3177
+ console.log(" open <id> — Open one specific Settings pane");
3178
+ console.log(" <id>: full-disk-access | automation | accessibility");
3179
+ console.log("");
3180
+ console.log("Note: macOS does NOT allow apps to grant TCC permissions programmatically.");
3181
+ console.log("The wizard opens the right Settings panes; YOU flip the switches; the wizard");
3182
+ console.log("verifies each one is actually granted before moving on.");
3183
+ }
3184
+ })().then(() => closeRL()).catch((err) => {
3185
+ console.error(err);
3186
+ closeRL();
3187
+ });
3188
+ break;
3189
+ }
3105
3190
  case "provider": {
3106
3191
  const sub = (process.argv[3] || "list").toLowerCase();
3107
3192
  const arg = process.argv[4];
@@ -3494,6 +3579,7 @@ ${t("cli.commands")}
3494
3579
  search Search your assets, memories, and skills
3495
3580
  tools List / install optional capability tools (ffmpeg, pandoc, …)
3496
3581
  provider List / switch AI provider (claude, codex, groq, gemini, …)
3582
+ permissions Status / wizard for macOS TCC + sudo permissions (alias: perms)
3497
3583
  browser Manage bot-owned Chromium (start/stop/goto/shot/doctor)
3498
3584
  update ${t("cli.updateDesc")}
3499
3585
  start ${t("cli.startDesc")} (background via PM2)
@@ -1698,6 +1698,7 @@ export function registerCommands(bot) {
1698
1698
  .text("🔑 Manage API Keys", "setup:keys").row()
1699
1699
  .text("📱 Platforms", "setup:platforms").row()
1700
1700
  .text("🔐 Sudo / Admin Access", "setup:sudo").row()
1701
+ .text("🛡️ Permissions Wizard (Mac)", "setup:permissions").row()
1701
1702
  .text("🔧 Open Web Dashboard", "setup:web").row();
1702
1703
  await ctx.reply(`⚙️ *Alvin Bot Setup*\n\n` +
1703
1704
  `*Active Model:* ${activeInfo.name}\n` +
@@ -1854,6 +1855,31 @@ export function registerCommands(bot) {
1854
1855
  `_The password is securely stored in ${status.storageMethod}._`, { parse_mode: "Markdown" });
1855
1856
  break;
1856
1857
  }
1858
+ case "permissions": {
1859
+ // 5.1.0 — Permissions Wizard summary. Mobile UX is intentionally
1860
+ // read-only: the wizard itself needs to open System Settings panes
1861
+ // on the host machine, which only the CLI/WebUI can drive. Here
1862
+ // we surface status + tell the user how to run the actual wizard.
1863
+ try {
1864
+ const { readPermissionsSnapshot } = await import("../services/permissions-wizard.js");
1865
+ const snaps = await readPermissionsSnapshot();
1866
+ const icons = { granted: "✅", missing: "❌", "tool-missing": "⚠️", "n/a": "·" };
1867
+ const lines = snaps.map(s => `${icons[s.state]} *${s.permission.name}* — ${s.state}${s.detail ? " _(" + s.detail + ")_" : ""}`);
1868
+ const missing = snaps.filter(s => s.state === "missing" || s.state === "tool-missing").length;
1869
+ const summary = missing === 0
1870
+ ? "*All applicable permissions granted.*"
1871
+ : `*${missing} permission${missing === 1 ? "" : "s"} need attention.*`;
1872
+ await ctx.editMessageText(`🛡️ *Permissions Status*\n\n${lines.join("\n")}\n\n${summary}\n\n` +
1873
+ `_macOS doesn't let any app grant TCC permissions on its own — you toggle the switch, the wizard verifies._\n\n` +
1874
+ `*To run the guided wizard:*\n` +
1875
+ `• CLI: \`alvin-bot permissions wizard\` (opens each Settings pane, waits, verifies)\n` +
1876
+ `• WebUI: http://localhost:${process.env.WEB_PORT || 3100} → Settings`, { parse_mode: "Markdown" });
1877
+ }
1878
+ catch (err) {
1879
+ await ctx.editMessageText("🛡️ *Permissions Status*\n\n_Could not load wizard module — try `alvin-bot permissions status` in a terminal._", { parse_mode: "Markdown" });
1880
+ }
1881
+ break;
1882
+ }
1857
1883
  case "web": {
1858
1884
  await ctx.editMessageText(`🌐 *Web Dashboard*\n\n` +
1859
1885
  `URL: \`http://localhost:${process.env.WEB_PORT || 3100}\`\n\n` +
package/dist/index.js CHANGED
@@ -215,6 +215,26 @@ import("./services/preflight.js")
215
215
  // Pre-Flight itself must never crash the bot.
216
216
  console.warn("⚠️ Pre-Flight check threw:", err?.message || err);
217
217
  });
218
+ // AI Self-Diagnosis startup analyzer (Self-Preservation Phase 2, 3I).
219
+ // Scans ~/.alvin-bot/diagnostics/ for forensic bundles without a
220
+ // .analysis.md sidecar and runs AI analysis on each. Findings land on
221
+ // the operator's phone via 1D Telegram channel within ~30 s of the
222
+ // bot recovering from a brake. Fire-and-forget, never blocks startup.
223
+ // Provider-agnostic: uses the active Provider's query() async generator.
224
+ import("./services/self-diagnosis.js")
225
+ .then(({ runStartupAnalyzer }) => runStartupAnalyzer(registry))
226
+ .catch((err) => {
227
+ console.warn("⚠️ Self-diagnosis analyzer threw:", err?.message || err);
228
+ });
229
+ // Predictive-Maintenance Trends collector (Self-Preservation Phase 2, 3J).
230
+ // Snapshots health metrics every 24 h (first one after 60 s warmup).
231
+ // After 7 days of data, also runs AI anomaly detection daily.
232
+ // If a concerning trend is flagged → DM operator via 1D channel.
233
+ import("./services/trends.js")
234
+ .then(({ startTrendsCollector }) => startTrendsCollector(registry))
235
+ .catch((err) => {
236
+ console.warn("⚠️ Trends collector threw:", err?.message || err);
237
+ });
218
238
  // Load plugins
219
239
  const pluginResult = await loadPlugins();
220
240
  if (pluginResult.loaded.length > 0) {