Metadata-Version: 2.4
Name: bog-agents-daemon
Version: 0.8.7
Summary: Bog Agents Daemon — the patient watcher. Runs your bog-agents on cron schedules, file-change triggers, webhooks, and git push events; survives reboots, persists state, and reports back. Use it when you want an agent that wakes itself rather than waiting for you. Pass through in harmony.
Project-URL: Homepage, https://github.com/bogware/bog-agents
Project-URL: Repository, https://github.com/bogware/bog-agents
Project-URL: Issues, https://github.com/bogware/bog-agents/issues
Project-URL: Changelog, https://github.com/bogware/bog-agents/blob/main/libs/daemon/CHANGELOG.md
Author-email: bogware <support@bogware.com>
Maintainer-email: bogware <support@bogware.com>
License: MIT
Keywords: agents,ai,bog-agents,cron,daemon,langchain,langgraph,llm,scheduler
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: No Input/Output (Daemon)
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Distributed Computing
Requires-Python: <4.0,>=3.11
Requires-Dist: aiofiles<25.0.0,>=23.0.0
Requires-Dist: bog-agents<1.0.0,>=0.7.0
Requires-Dist: fastapi<1.0.0,>=0.115.0
Requires-Dist: uvicorn<1.0.0,>=0.30.0
Provides-Extra: acp
Requires-Dist: bog-agents-acp>=0.0.4; extra == 'acp'
Provides-Extra: all
Requires-Dist: bog-agents-acp>=0.0.4; extra == 'all'
Requires-Dist: daytona<1.0.0,>=0.113.0; extra == 'all'
Requires-Dist: langchain-anthropic<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-aws<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-baseten<1.0.0,>=0.1.9; extra == 'all'
Requires-Dist: langchain-cohere<1.0.0,>=0.5.0; extra == 'all'
Requires-Dist: langchain-deepseek<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-fireworks<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-google-genai<5.0.0,>=4.0.0; extra == 'all'
Requires-Dist: langchain-google-vertexai<4.0.0,>=3.0.0; extra == 'all'
Requires-Dist: langchain-groq<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-huggingface<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-ibm<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-litellm<2.0.0,>=0.6.1; extra == 'all'
Requires-Dist: langchain-mistralai<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-nvidia-ai-endpoints<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-ollama<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-openai<2.0.0,>=1.1.8; extra == 'all'
Requires-Dist: langchain-openrouter<2.0.0,>=0.1.0; extra == 'all'
Requires-Dist: langchain-perplexity<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langchain-xai<2.0.0,>=1.0.0; extra == 'all'
Requires-Dist: langsmith[sandbox]>=0.7.7; extra == 'all'
Requires-Dist: modal<2.0.0,>=0.65.0; extra == 'all'
Requires-Dist: runloop-api-client>=0.69.0; extra == 'all'
Requires-Dist: tavily-python<1.0.0,>=0.7.21; extra == 'all'
Provides-Extra: all-providers
Requires-Dist: langchain-anthropic<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-aws<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-baseten<1.0.0,>=0.1.9; extra == 'all-providers'
Requires-Dist: langchain-cohere<1.0.0,>=0.5.0; extra == 'all-providers'
Requires-Dist: langchain-deepseek<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-fireworks<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-google-genai<5.0.0,>=4.0.0; extra == 'all-providers'
Requires-Dist: langchain-google-vertexai<4.0.0,>=3.0.0; extra == 'all-providers'
Requires-Dist: langchain-groq<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-huggingface<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-ibm<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-litellm<2.0.0,>=0.6.1; extra == 'all-providers'
Requires-Dist: langchain-mistralai<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-nvidia-ai-endpoints<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-ollama<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-openai<2.0.0,>=1.1.8; extra == 'all-providers'
Requires-Dist: langchain-openrouter<2.0.0,>=0.1.0; extra == 'all-providers'
Requires-Dist: langchain-perplexity<2.0.0,>=1.0.0; extra == 'all-providers'
Requires-Dist: langchain-xai<2.0.0,>=1.0.0; extra == 'all-providers'
Provides-Extra: anthropic
Requires-Dist: langchain-anthropic<2.0.0,>=1.0.0; extra == 'anthropic'
Provides-Extra: baseten
Requires-Dist: langchain-baseten<1.0.0,>=0.1.9; extra == 'baseten'
Provides-Extra: bedrock
Requires-Dist: langchain-aws<2.0.0,>=1.0.0; extra == 'bedrock'
Provides-Extra: cohere
Requires-Dist: langchain-cohere<1.0.0,>=0.5.0; extra == 'cohere'
Provides-Extra: daytona-sandbox
Requires-Dist: daytona<1.0.0,>=0.113.0; extra == 'daytona-sandbox'
Provides-Extra: deepseek
Requires-Dist: langchain-deepseek<2.0.0,>=1.0.0; extra == 'deepseek'
Provides-Extra: fireworks
Requires-Dist: langchain-fireworks<2.0.0,>=1.0.0; extra == 'fireworks'
Provides-Extra: google-genai
Requires-Dist: langchain-google-genai<5.0.0,>=4.0.0; extra == 'google-genai'
Provides-Extra: groq
Requires-Dist: langchain-groq<2.0.0,>=1.0.0; extra == 'groq'
Provides-Extra: huggingface
Requires-Dist: langchain-huggingface<2.0.0,>=1.0.0; extra == 'huggingface'
Provides-Extra: ibm
Requires-Dist: langchain-ibm<2.0.0,>=1.0.0; extra == 'ibm'
Provides-Extra: langsmith-sandbox
Requires-Dist: langsmith[sandbox]>=0.7.7; extra == 'langsmith-sandbox'
Provides-Extra: litellm
Requires-Dist: langchain-litellm<2.0.0,>=0.6.1; extra == 'litellm'
Provides-Extra: mistralai
Requires-Dist: langchain-mistralai<2.0.0,>=1.0.0; extra == 'mistralai'
Provides-Extra: modal-sandbox
Requires-Dist: modal<2.0.0,>=0.65.0; extra == 'modal-sandbox'
Provides-Extra: nvidia
Requires-Dist: langchain-nvidia-ai-endpoints<2.0.0,>=1.0.0; extra == 'nvidia'
Provides-Extra: ollama
Requires-Dist: langchain-ollama<2.0.0,>=1.0.0; extra == 'ollama'
Provides-Extra: openai
Requires-Dist: langchain-openai<2.0.0,>=1.1.8; extra == 'openai'
Provides-Extra: openrouter
Requires-Dist: langchain-openrouter<2.0.0,>=0.1.0; extra == 'openrouter'
Provides-Extra: perplexity
Requires-Dist: langchain-perplexity<2.0.0,>=1.0.0; extra == 'perplexity'
Provides-Extra: runloop-sandbox
Requires-Dist: runloop-api-client>=0.69.0; extra == 'runloop-sandbox'
Provides-Extra: sandbox
Requires-Dist: daytona<1.0.0,>=0.113.0; extra == 'sandbox'
Requires-Dist: langsmith[sandbox]>=0.7.7; extra == 'sandbox'
Requires-Dist: modal<2.0.0,>=0.65.0; extra == 'sandbox'
Requires-Dist: runloop-api-client>=0.69.0; extra == 'sandbox'
Provides-Extra: vertexai
Requires-Dist: langchain-google-vertexai<4.0.0,>=3.0.0; extra == 'vertexai'
Provides-Extra: web-search
Requires-Dist: tavily-python<1.0.0,>=0.7.21; extra == 'web-search'
Provides-Extra: xai
Requires-Dist: langchain-xai<2.0.0,>=1.0.0; extra == 'xai'
Description-Content-Type: text/markdown

# Bog Agents Daemon

> *The patient watcher. Wakes itself. Pass through in harmony.*

Quiet ambient runner for [`bog-agents`](https://github.com/bogware/bog-agents).
Schedules. File watches. Inbound webhooks. Git pushes. Sits in the
background, fires the agent when something happens, writes the result
wherever you point it.

No terminal needed. No hand-holding. Goes the distance.

[![PyPI](https://img.shields.io/pypi/v/bog-agents-daemon)](https://pypi.org/project/bog-agents-daemon/)
[![Python](https://img.shields.io/pypi/pyversions/bog-agents-daemon)](https://pypi.org/project/bog-agents-daemon/)
[![License](https://img.shields.io/pypi/l/bog-agents-daemon)](https://opensource.org/licenses/MIT)

---

## Why a daemon

The CLI is great when you're at the keyboard. The daemon is what you reach
for when you want an agent that watches *for* you — and reports back when
something matters.

- **Five trigger types**: `cron`, `interval`, `file_change`, `webhook`,
  `git_push`.
- **Seven output targets**: `log`, `stdout`, `file`, `slack`, `webhook`,
  `email`, `github_comment`.
- **Auth + integrity**: token-authenticated REST API; HMAC-validated
  inbound webhooks; tokens stored with `0o600` permissions.
- **Durable**: `os.fsync()`-durable job persistence so a hard kill never
  loses a freshly-created job.
- **Cross-platform**: POSIX systemd, Windows Task Scheduler, or just
  `bog-agents-daemon run` in a shell. Same config either way.

If the CLI is *patient as still water*, the daemon is the still water that
keeps watch overnight.

---

## Install

```bash
pip install bog-agents-daemon
```

Pulls in [`bog-agents`](https://pypi.org/project/bog-agents/) automatically.
Add provider extras you need:

```bash
pip install "bog-agents-daemon[anthropic]"
pip install "bog-agents-daemon[openai]"
pip install "bog-agents-daemon[bedrock]"
```

---

## 30-second tour

Start the daemon (foreground for a quick test):

```bash
bog-agents-daemon run --port 7878
```

Add a job that runs every weekday morning:

```bash
bog-agents-daemon job add \
  --name morning-brief \
  --cron "0 9 * * 1-5" \
  --prompt "Summarize what changed in this repo since yesterday." \
  --output slack:#engineering
```

The job persists to `~/.bog-agents/daemon/jobs/`. The scheduler picks it up
on the next tick and fires it on the configured cadence.

---

## Triggers

```yaml
# A cron job
triggers:
  - type: cron
    cron: "0 9 * * 1-5"      # 9am Mon–Fri

# An interval
triggers:
  - type: interval
    seconds: 1800            # every 30 min

# A file watcher
triggers:
  - type: file_change
    patterns: ["src/**/*.py"]
    debounce_seconds: 5

# An inbound webhook (HMAC-validated)
triggers:
  - type: webhook
    path: /hooks/incident
    secret_env: WEBHOOK_SECRET

# A git push (post-receive on a remote)
triggers:
  - type: git_push
    repo: /srv/git/main.git
    branch: main
```

A job can have multiple triggers. The agent fires on any of them.

---

## Outputs

Where the result of an agent run goes. Configure one or many:

```yaml
outputs:
  - type: log              # systemd journal / stderr
  - type: file
    path: ~/.bog-agents/runs/morning-brief.md
  - type: slack
    channel: "#engineering"
    token_env: SLACK_BOT_TOKEN
  - type: webhook
    url: https://hooks.example.com/agent-output
    hmac_secret_env: OUTBOUND_WEBHOOK_SECRET
  - type: email
    to: oncall@example.com
    from: bog-agents@example.com
    smtp_env_prefix: SMTP_     # SMTP_HOST, SMTP_USER, SMTP_PASSWORD
  - type: github_comment
    repo: example/api
    issue: 1234
    token_env: GITHUB_TOKEN
```

---

## REST API

Once the daemon is running, you've got a small authenticated REST API for
managing jobs:

| Endpoint | Method | What |
|---|---|---|
| `/jobs` | GET | List all jobs |
| `/jobs` | POST | Create a job |
| `/jobs/{id}` | GET | Job detail |
| `/jobs/{id}` | DELETE | Delete a job |
| `/jobs/{id}/runs` | GET | Run history |
| `/jobs/{id}/run` | POST | Fire the job manually |
| `/health` | GET | Liveness probe |
| `/metrics` | GET | Counter snapshot |

Every endpoint requires `Authorization: Bearer <daemon_token>`. The token
is generated on first start, stored at `~/.bog-agents/daemon/.token`
with `0o600` permissions, and printed once to the foreground log so you
can copy it.

---

## Running as a service

### systemd (Linux)

```bash
bog-agents-daemon install-systemd --user
systemctl --user enable --now bog-agents-daemon
systemctl --user status bog-agents-daemon
```

### Windows Task Scheduler

```powershell
bog-agents-daemon install-windows-task
```

Creates a `bog-agents-daemon` task that starts at logon and restarts on
failure. Manage it with `schtasks` or the Task Scheduler GUI.

### macOS launchd

```bash
bog-agents-daemon install-launchd --user
launchctl load ~/Library/LaunchAgents/com.bogware.bog-agents-daemon.plist
```

---

## Security model

- **Token-authenticated API.** Every request needs `Authorization: Bearer`.
  Tokens generated with `secrets.token_urlsafe`, compared with
  `hmac.compare_digest`, stored at `0o600`.
- **HMAC-validated inbound webhooks.** Each webhook trigger declares an
  env-var holding its shared secret; the daemon verifies the HMAC-SHA256
  signature on every request.
- **Outbound webhook signing.** Outputs that POST to a webhook can sign
  the body with HMAC-SHA256 so the receiver can verify it came from
  this daemon.
- **No secrets on disk.** Provider keys, Slack tokens, and webhook
  secrets are all read from env vars — the daemon never persists them.
- **Resource limits.** Configurable per-job CPU / memory / wall-clock
  caps via the same FeatureConfig shape as the SDK.

---

## What's new in 0.8.0

Synced with `bog-agents` 0.8.0:

- **Patient by default.** Every agent run is wrapped by
  `ProviderRetryMiddleware` against transient provider failures.
- **Subprocess `stdin=/dev/null`** — interactive shell commands fired
  by the agent (e.g. Windows `date`) get an immediate EOF instead of
  hanging the daemon forever.
- **`virtual_mode=True` filesystem default** — agents launched by the
  daemon are confined to their working directory unless you explicitly
  opt out with `BOG_AGENTS_FS_UNSANDBOXED=1`.
- **Structured event logs** at every chokepoint, ready for shippers.

---

## When to use this vs. `/peat` in the CLI

| | Daemon | `/peat` |
|---|---|---|
| Survives reboot | ✓ | ✗ (only while CLI open) |
| Fires while you're asleep | ✓ | ✗ |
| Webhook / git-push triggers | ✓ | ✗ |
| Slack / email / GitHub-comment outputs | ✓ | ✗ |
| Reuses your interactive agent | ✗ | ✓ |
| Zero ops (no service install) | ✗ | ✓ |

`/peat` is the right tool when you're at the keyboard and want a personal
assistant for the duration of the session. The daemon is the right tool
when you want an agent that wakes itself.

---

## Documentation

- Repo: <https://github.com/bogware/bog-agents>
- Issues: <https://github.com/bogware/bog-agents/issues>
- Changelog: [`CHANGELOG.md`](https://github.com/bogware/bog-agents/blob/main/CHANGELOG.md)

---

## License

MIT. See [LICENSE](https://github.com/bogware/bog-agents/blob/main/LICENSE).

*Pass through in harmony.*
