Releases: Harsh-2002/Relay
v2.5.10 — Retry surfaced in /watch list
What's new
Follow-up to v2.5.9 (manual retry). The Retry button was only on the original failure notification card — if that scrolled off screen or got deleted, the pending analysis was stranded.
- `/watch` list now surfaces pending analyses. Any watch with a failed, un-retried analysis shows a warning row (`⚠ Analysis pending (attempt N): `) and gets an extra button row: ↻ Retry analysis / Dismiss.
- Dismiss from `/watch` refreshes the list in place. Dismiss from the failure card still edits that card to "Dismissed.".
- Failure card fallback. If the stored notification message was deleted or `editMessageText` fails, Retry now posts a fresh card instead of running invisibly. The pending record is updated with the new message id so subsequent retries edit in place.
Upgrade
`relay update && relay restart` — `[PROD]` banner should still read.
Tests
85 tests still pass (no schema change, the UI changes don't need new coverage beyond the smoke test already in place).
v2.5.9 — Manual retry for failed watch analysis
What's new
When a watch detects a page change, Relay kicks off an AI analysis session. If that session stalls (120 s timeout), hits a model-unavailable error, or gets rate-limited, the analysis used to fail and the change was effectively lost — the snapshot was already saved before analysis ran, so the next tick saw no hash change and didn't re-analyze.
This release adds a manual retry path. The failure notification now includes ↻ Retry and Dismiss inline buttons. Switch `/model` to a working provider, tap Retry, and the analysis runs against the stored content pair (not re-fetched, not stale).
How it works
- Analysis failures are classified (stall timeout, model-not-found, 429, 401, connection refused, fallback) into user-readable reasons.
- The `Analyzing...` card is edited in place into a failure card with two buttons.
- A new optional `pendingReanalysis` field on each `WatchJob` stores the content pair, attempt counter, last error, and model tried.
- Tap Retry — re-runs analysis with your current `/model` (so switching the model first works).
- Tap Dismiss — clears the pending state, edits the card to "Dismissed.", next tick resumes normally.
- If the watched page changes again while a retry is pending, the tick updates `currentContent` only — no auto-fire of a new analysis. When you eventually tap Retry, the diff covers everything from the last successful state through the latest content.
Backwards compatibility
Schema addition only. Old `watch.json` files load unchanged.
Not included (intentional)
- Auto-retry with backoff (user chose manual-only).
- Cron jobs — same failure mode exists in `src/cron.ts` but is out of scope for this release.
Upgrade
`relay update && relay restart` — the startup banner should read `Relay [PROD] data=~/.relay`.
Tests
75 → 85 (added 10 covering `classifyAnalysisError()` across all cases and `pendingReanalysis` schema round-trip).
v2.5.8
Reliable dev/prod separation + regression test + docs
The bug
In production, pm2 launches the daemon with `cwd=<wherever you ran `relay start` from>`. If that happened to be the Relay repo, three module-level path captures bound to `process.cwd()+".relay"` at import time — before `setDataDir()` had a chance to run. The result: config was read from `~/.relay/` but `cron.json`, `watch.json`, `session.json`, uploads, and a SKILL.md fallback all wrote to the repo's `.relay/` folder. Dev and prod silently cross-referenced each other's state.
The fix
All three leaks resolve the data dir lazily now:
- `src/utils/store.ts` — `JsonStore` no longer captures `filePath` in its constructor. Every `load`/`save` calls `getDataDir()` at access time.
- `src/utils/media.ts` — `UPLOAD_DIR` replaced by `getUploadDir()` which joins `getDataDir()` + "uploads" on each call.
- `src/utils/system-prompt.ts` — removed the `./SKILL.md` cwd fallback. SKILL.md now lives only at `{dataDir}/SKILL.md`, same in dev and prod.
Visibility
Relay now prints the active mode and directory at startup:
```
Relay [PROD] data=/home/alice/.relay
Relay [DEV] data=/home/alice/src/Relay/.relay
```
No guessing which one you're in.
Regression test
`tests/unit/utils/dataDir.test.ts` covers: JsonStore follows `setDataDir` between save/load, upload dir follows it too. 72 → 75 tests.
Documentation
New "Development vs Production" section in README. Expanded CLAUDE.md persistence section. Elevated "Data Directory" in `docs/configuration.md`. Unified `docs/features.md` on `{dataDir}`. New `docs/troubleshooting.md` entry with a safe migration recipe for users who have stale data stuck in a repo-local `.relay/`.
No new flag
`--dev` is already the right switch and remains the only one. We just made it impossible to accidentally ignore.
Upgrade
```
relay update
relay restart
```
Check the banner — you should see `[PROD] data=/.relay` going forward.
v2.5.7
Cross-platform portability audit pass
Follow-up to v2.5.6 after a full codebase audit. Resolves remaining portability issues so Relay runs cleanly on Linux (all major distros including Alpine, Void, Gentoo), macOS, BSD, and Windows.
Fixes
- update.ts:
relay updatefrom a git checkout now runs `git pull`, `npm install`, and `npm run build` as three separate `execCmd()` calls instead of one `execSync("a && b && c")` chain. Windows uses the `utils/shell.ts` wrapper to resolve npm's `.cmd` shim explicitly. Error messages also report which specific step failed. - providers/opencode.ts: Windows OpenCode server bootstrap parsed stdout with `split("\n")` which left trailing `\r` on each line when the child emitted CRLF. Switched to `split(/\r?\n/)`.
- config/setup.ts: `uv` installer hardcoded `curl -LsSf … | sh` on non-Windows. Minimal images (Alpine busybox, barebones containers) often ship with wget but not curl — the setup wizard now detects which is available and emits the matching command.
Not changed (intentional)
- `127.0.0.1` localhost binding in `relay-api.ts` and `opencode.ts` — works on every major desktop/server OS; only IPv6-only containers would be affected, which is out of scope.
- `{ mode: 0o600 }` on config/session files — Node silently ignores the flag on Windows (no crash), and POSIX permissions still apply where relevant.
Upgrade
`relay update`
v2.5.6
Universal pm2 startup registration
v2.5.5 only auto-registered on Linux+systemd because the idempotency check hardcoded /etc/systemd/system/pm2-\$USER.service. This release makes it portable to every OS pm2 supports.
Changes
- Portable fast-path — replaces the systemd-specific file check with a sentinel at `~/.pm2/.relay-startup-registered`. Works on macOS (launchd), BSD (rc.d), non-systemd Linux (OpenRC/runit), and anywhere else pm2 detects an init system. No platform-specific paths in Relay's code — pm2 does the detection, we just cache the result.
- Windows — `relay autostart` now prints an actionable hint pointing to `pm2-windows-startup` instead of silently returning. Still silent on routine `relay start` to avoid noise.
- Alpine / OpenBSD — the manual-hint message now notes that systems using `doas` (Alpine minimal, some OpenBSD setups) should substitute `doas` for `sudo` in the pm2-generated command, since pm2 always emits `sudo` regardless of platform.
- Generic copy — messages no longer say "systemd" specifically; they say "pm2 startup" / "init system".
Upgrade
relay update
No action needed beyond that — the sentinel gets created automatically on next start/restart.
v2.5.5
Fix for v2.5.4: pm2 startup exits with code 1 when action is required (by design), which caused the auto-registration in v2.5.4 to silently no-op. Now reads stdout regardless of exit status, so relay autostart actually registers the systemd unit.
v2.5.4
Auto-start on boot (survive reboots)
What changed
Relay daemons were silently dying on every system reboot — kernel upgrades and manual reboots would kill pm2, and nothing brought it back until someone manually ran relay start. This release fixes the root cause: the pm2 startup systemd registration is now handled automatically.
Changes
- Auto-register with systemd —
relay startnow checks for/etc/systemd/system/pm2-$USER.serviceand runspm2 startupautomatically when root or passwordless sudo is available. When sudo requires a password, a prominent bordered message prints the exact one-time command to paste. - Safety net for existing installs —
relay restartalso runs the check, so long-running installations pick up the fix on next restart. - New
relay autostartsubcommand — explicit opt-in for users who dismissed the hint or installed Relay before this release.
After upgrading
relay update
relay autostart # registers the systemd unit if not already done
After the one-time registration, Relay will auto-start on every reboot — including after kernel upgrades.
v2.5.3
Bug Fix
- Auto-restart daemon after
relay update— Previously the daemon wouldn't restart if the npm global update killed the pm2 process entry. Now captures daemon state before updating and falls back torelay startifpm2 restartfails.
v2.5.2
What's New
Test Suite & CI
- Added Vitest test framework with 72 tests across 6 test files
- Unit tests for
escapeHtml,formatCatchError,isNotModified, input state management, and question flows - Integration tests for cross-system input↔question clearing
- GitHub Actions CI pipeline running type-check + tests on Node 18, 20, 24
Bug Fix
- Cross-system clearing between input and question handlers prevents stale handler bugs
v2.5.1 — Web Monitoring & Deep Research
What's New
Web Monitoring (/watch)
URL change detection with AI-analyzed notifications. Monitor any webpage for changes — the scheduler fetches URLs on configurable intervals, compares SHA-256 content hashes, and only invokes AI when content actually changes.
/watch— Interactive list with inline keyboard (Enable/Disable, Check Now, Delete)/watch <url>— Start interactive creation flow (interval picker → task description)/watch add <url> <interval> Name: task— Direct one-command creation- Upfront URL validation with baseline snapshot capture
- Auto-disables after 5 consecutive fetch errors
- Full MCP tool support (
relay_watch_list,relay_watch_add,relay_watch_update,relay_watch_remove,relay_watch_toggle,relay_watch_run) - Persists to
~/.relay/watch.json
Deep Research (/research)
Run thorough multi-step research on any topic with live streaming to Telegram.
/research <topic>— Creates an isolated session, researches, streams results, cleans up/research— Prompts for topic interactively- Structured prompt using Anthropic's prompt engineering best practices (XML tags, role assignment, 5-step methodology, cross-source verification, self-critique)
- Includes current date for up-to-date results
Internal
src/utils/html-to-text.ts— Pure regex HTML-to-text converter (no JSDOM)- Watch HTTP API endpoints in
relay-api.ts - Watch MCP tools in
relay-server.ts - Watch system prompt instructions
- Updated all documentation (commands, features, configuration, troubleshooting)
Full Changelog: v2.5.0...v2.5.1