/**
 * Semantic identity of a monitor — "are these two definitions the same watcher?"
 *
 * A monitor's NAME is not its identity. Two monitors can poll the same command
 * on the same interval and fire the same action under two different names, and
 * nothing today notices: `writeMonitor` overwrites by name and never compares
 * arguments. Observed on one box: `open-pr-watch`, `pr-ci-fail`, three stale
 * `pr2222-*` watchers, and an agent-added lander, all polling
 * `gh pr list ... phnx-labs/agents-cli` — four overlapping triggers on one PR
 * queue, added without a single warning.
 *
 * That is the double-trigger bug class. The fingerprint is what lets `add`
 * refuse it.
 *
 * WHAT IS HASHED: only fields that change what the monitor DOES — the source it
 * watches, how often, the condition that fires, and the action it takes. Name,
 * description, and `enabled` are deliberately excluded, because a duplicate
 * under a new name is exactly the case being caught, and a paused duplicate is
 * still a duplicate the moment someone resumes it.
 *
 * WHAT IS NOT HASHED: `device`/`devices`/`runOn`. Placement is who executes, not
 * what runs — and two boxes deliberately owning the same watcher is a different
 * decision (see `monitorRunsOnThisDevice`). Including placement would let the
 * same watcher be added N times by varying only the owner, which is the very
 * duplication this guards.
 */
import type { MonitorConfig } from './config.js';
/**
 * The behavioral identity of a monitor, as a short hex digest. Two monitors with
 * the same fingerprint watch the same thing and do the same thing on fire —
 * whatever they are called and wherever they are pinned.
 *
 * Pure: no I/O, so the duplicate check is unit-tested without a monitors dir.
 */
export declare function monitorFingerprint(config: Pick<MonitorConfig, 'source' | 'condition' | 'action'>): string;
/**
 * The already-existing monitor that this config would duplicate, or null.
 *
 * `existing` is the full monitor set (user + system layers), so a user monitor
 * duplicating a shipped built-in is caught too. A monitor never collides with
 * itself: a same-NAME entry is a rewrite of that monitor, which `add` reports
 * separately and more precisely than "duplicate".
 *
 * Pure — the caller supplies the list.
 */
export declare function findDuplicateMonitor(config: Pick<MonitorConfig, 'name' | 'source' | 'condition' | 'action'>, existing: Array<Pick<MonitorConfig, 'name' | 'source' | 'condition' | 'action'>>): string | null;
