Skip to content
dsh.fish
Bundle

dsh-event-watch

Condition-driven session wake for DeepSeek Harness: durable file and GitHub watches that fire a configured prompt into a target session (cold-wake included), with transition-only dedupe and throttling

Source
dolcejust-spec
License
MIT
Updated
Updated 5 days ago

Readme

# dsh-event-watch

Condition-driven wake for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH): watch files or GitHub state, and when a condition **transitions**, inject a configured prompt into a target DSH session - cold-waking it if the session is closed. Think of it as `cron` for conditions instead of time.

v1 is server-side only. It exists as a deliberately minimal alternative to community sentinels: **zero runtime dependencies, zero process execution** (Node built-in `fetch` only - no `child_process`, no `exec`, no `spawn` anywhere in the shipped code), path-contained file scans, and a durable, audited, throttled fire path.

## Install

```sh
dsh plugin --profile web add dsh-event-watch
```

The npm tarball ships prebuilt `lib/`; consumer installs run no build scripts.

## Sensors (v1)

- **FILE** - one or more absolute `roots` plus glob patterns (`**/*.md`); fires on `created` or `changed` (mtime + size, confirmed by content hash). Metadata only: file contents are read for hashing, never executed or interpreted.
- **GITHUB** - polls the REST API with built-in `fetch` for (a) PR state changes (open/closed/merged/draft) on a repo (+optional branch filter), (b) check-run conclusion changes on a commit/branch, (c) new commits on a branch.

## Behavior

- Fires on **state transition only** - a condition that is already true at watch creation only establishes the baseline; it fires when it *changes* later. Re-observing the same state never re-fires (deduplicated by signature).
- **Throttled**: at most `maxWakePerWatchPerHour` deliveries per watch per rolling hour (default 3). Dropped conditions are recorded, not silently lost.
- **Durable**: watches live in a JSON file under the DSH home (`$DSH_HOME/event-watch/watches.json`), written atomically (tmp + rename), hot-reloaded across processes, guarded by a single-instance engine lock.
- **Audited**: every watch change, fire, throttle-drop, and sensor error appends an `event-watch/audit` event to the owning session's persisted log (marked `ignorable` so older builds skip it) and a capped per-watch ring in the store.
- **Cold wake**: a fire targeting a closed session resumes it with its recorded preset and model (adapted from dsh-cron) and delivers as the next turn.
- **Secrets**: the GitHub token is read from `tokenEnv` (default `GH_TOKEN`) once per poll, sent only as an Authorization header, never stored, logged, or echoed; all plugin output passes a redaction pass.
- **Misconfiguration fails loud** at load: unknown fields are rejected, paths must be absolute, poll interval >= 5000 ms.

## Configuration (cordis.yml)

```yaml
- id: event-watch
  name: dsh-event-watch
  config:
    pollIntervalMs: 30000        # minimum 5000
    maxWakePerWatchPerHour: 3
    trial: true                  # surfaced by /event-watch status
    targetSession: <session id>  # default target; per-watch overrides, then creating session
    watchers:                    # optional watches created at load
      - id: docs
        type: file
        spec:
          roots: ['D:\docs']
          globs: ['**/*.md']
          trigger: changed
        prompt: 'The docs changed; summarize what moved.'
```

## Tool and command

The agent-facing `event_watch` tool manages watches:

- `event_watch { action: "list" }`
- `event_watch { action: "add", type: "file", spec: { roots: [...], globs: [...], trigger: "created" }, prompt: "..." }`
- `event_watch { action: "add", type: "github", spec: { repo: "owner/name", mode: "pr" }, prompt: "..." }`
- `event_watch { action: "remove", id: "watch-1" }`
- `event_watch { action: "pause" | "resume", id: "watch-1" }`
- `event_watch { action: "status" }`

Humans get the `/event-watch` slash command (status view).

## Security posture

- No `child_process` / `exec` / `spawn` / `fork` - enforced by `pnpm run verify:zero-exec` in CI.
- File scans are contained: watched roots must be absolute, symlink escapes out of a root are blocked (fail closed), and scans are entry-count bounded.
- GitHub polling uses Node's built-in `fetch` only.
- Token handling: env-read per poll, bearer header only, redaction on every log/audit path.

## Development

```sh
pnpm install
pnpm test          # unit + integration (no network; GitHub mode uses recorded fixtures)
pnpm run typecheck
pnpm run build
pnpm run verify:zero-exec
```

## License

MIT

Install

dsh plugin --profile web add github:dolcejust-spec/dsh-event-watch

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source