feat: add durable heartbeat monitoring by MacroModel · Pull Request #47193 · anomalyco/opencode · GitHub
Skip to content

feat: add durable heartbeat monitoring - #47193

Closed
MacroModel wants to merge 1 commit into
anomalyco:devfrom
MacroModel:codex/durable-heartbeat
Closed

MacroModel wants to merge 1 commit into
anomalyco:devfrom
MacroModel:codex/durable-heartbeat

Conversation

@MacroModel

Copy link
Copy Markdown

Persist heartbeat schedules and policy across restarts, recover pending checks on startup, and present heartbeat turns as collapsible timeline cards in the web app.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds durable heartbeat monitoring for long-running external commands.

Heartbeat checks can be scheduled, inspected, or cancelled. Their timing, check count, and backoff policy are stored in SQLite, so pending checks survive an OpenCode restart. A revision check prevents stale timers from firing twice.

Heartbeat is enabled by default and can be disabled with experimental.heartbeat.enabled: false. Disabling it hides the tool and its shell guidance, and automatically cancels pending heartbeat jobs.

The web timeline groups each heartbeat turn into a collapsible card showing its task, check count, state, and latest progress.

How did you verify your code works?

  • Ran 103 backend tests successfully.
  • Ran 15 web timeline tests successfully.
  • Ran backend and web type checks.
  • Built and smoke-tested the OpenCode binary.
  • Restarted OpenCode with a scheduled heartbeat and verified its timing and count were preserved.
  • Verified collapsed and expanded heartbeat cards in the web UI.

Screenshots / recordings

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Persist heartbeat schedules and policy across restarts, recover pending checks on startup, and present heartbeat turns as collapsible timeline cards in the web app.
@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Sep 4, 2026
@github-actions github-actions Bot closed this Sep 4, 2026
hunterchristian added a commit to chipp-ai/opencode that referenced this pull request Sep 23, 2026
A monitor tool (plus monitor_list/monitor_stop) lets the model watch
a long-running background command's output for a pattern, or until it
exits, and get notified back into the session as new messages -- a
standing watch, not just a one-shot check. Analogous to Claude Code's
own Monitor tool. Gated behind OPENCODE_EXPERIMENTAL_MONITOR, matching
the existing experimentalBackgroundSubagents convention.

Ported from upstream anomalyco/opencode PR anomalyco#33806 "feat: add Monitor
tool" (1,398 lines, closed by the automated PR-cleanup bot, never
reviewed) -- built directly on this fork's existing BackgroundJob
primitive rather than modifying it. Reused nearly word-for-word: the
re-arm/replace parameter shape, session-scoped list/stop logic, the
untrusted-output fence with an unpredictable id so watched output
can't forge its own closing tag, control-character stripping, the
never-grant-"always"-for-a-glob-command permission rule, the
detached-process-plus-parent-death-watchdog design, the flood guard,
and most of the tool description and test scenarios. Rewritten rather
than ported: avoided anomalyco#33806's own change to core BackgroundJob (a
WakeScope to dodge a self-re-arm deadlock) by forking notifications
into the tool's own scope instead, the same pattern task.ts already
uses; its hand-written reader/debounce became an Effect Stream
pipeline; its base64 command-wrapper isn't needed since this repo's
bash tool already runs commands through plain ChildProcess.make;
permission goes through the existing bash permission rather than a
new one, so existing bash rules govern monitors too.

Also considered anomalyco#44191 (cron scheduler, closed by the same bot the
same week, praised by a human reviewer first) and the
anomalyco#47166/anomalyco#47193/anomalyco#47284 durable-heartbeat resubmits (each closed within
hours by a different, 2-hour PR-template-compliance bot, never
reviewed) -- neither ported; see known gaps.

Found and fixed a real, generalizable bug along the way: a command
that writes output continuously with no pause (e.g. `yes`) hung
teardown and starved timers, because the stdout read pipeline never
yielded control back to the scheduler while data kept arriving.
Fixed with a single Stream.tap(() => Effect.yieldNow), documented
inline -- the same issue could affect other handle.stdout readers
facing a non-pausing producer.

Known gaps (deliberately scoped): not durable, in-memory only; no
scheduler (only a shell poll-loop inside the watched command); opencode
run's non-interactive mode exits once idle, killing active monitors;
no UI (model-only via monitor_list); stdout only, 4000-char line cap,
5000-line flood guard; parent-death watchdog is POSIX-only; no
external/webhook delivery.

New packages/opencode/src/tool/monitor.ts (~360 lines) + monitor.txt.
New packages/opencode/test/tool/monitor.test.ts (13 tests: self-cancel
re-arm without deadlock, concurrency, list/stop, byte-flood guard,
multi-line commands) -- passes 3 runs in a row. Verified against this
fork's current dev (not just the agent's own stale worktree base):
bun typecheck clean; full packages/opencode suite 3600 pass, only the
4 pre-existing, confirmed-unrelated cf-ai-gateway failures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant