Last active
March 12, 2026 11:38
-
-
Save lebbe/44d89f80af9a96a7d319cf2ff7b90425 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Provoke the Agent Protocol (PAP) | |
| **Version:** 0.2-slim | |
| **Status:** Working Draft | |
| **Author:** Lars-Erik Bruce | |
| --- | |
| ## 1. Purpose | |
| Most systems can monitor the world and detect when something crosses a threshold. Far fewer know what to do about it — because the right response requires reading context, applying judgment, and producing something meaningful. | |
| PAP connects those two worlds. | |
| The pattern is always the same: | |
| 1. **Something is monitored.** A metric, a feed, a file, a queue. | |
| 2. **A threshold is crossed.** A value, a condition, an absence. | |
| 3. **An agent is provoked.** It wakes up, reasons about what happened, and acts. | |
| The protocol defines three things and nothing else: | |
| 1. What a threshold-crossing looks like (**Event**) | |
| 2. What rules determine whether an agent is provoked (**Trigger**) | |
| 3. What an agent is and what it is allowed to do (**Agent Manifest**) | |
| Transport, authentication, observability, and orchestration are implementation choices outside of scope. | |
| --- | |
| ## 2. Concepts | |
| | Term | Definition | | |
| |---|---| | |
| | **Event** | A structured signal that a monitored condition has crossed a threshold | | |
| | **Trigger** | A rule that binds an event type to an agent manifest | | |
| | **Agent Manifest** | A description of an agent, its capabilities, and its constraints | | |
| | **Invocation** | A single run of an agent initiated by a trigger | | |
| | **Dispatcher** | The system that evaluates triggers and starts invocations | | |
| The distinction between an **Event** and a raw data stream matters: a PAP event is not a log line or a metric sample. It is a deliberate signal emitted by a monitoring system when it has determined that a threshold has been crossed. The monitoring system owns the detection; PAP owns what happens next. | |
| --- | |
| ## 3. Event | |
| ```json | |
| { | |
| "pap_version": "0.2", | |
| "id": "evt_a3f92b", | |
| "type": "energy.price.threshold_exceeded", | |
| "source": "monitoring.energy-price-tracker", | |
| "time": "2026-03-11T06:00:00Z", | |
| "data": { | |
| "price_nok_per_kwh": 4.82, | |
| "threshold_nok_per_kwh": 3.00, | |
| "region": "NO1", | |
| "forecast_hours": 6 | |
| } | |
| } | |
| ``` | |
| | Field | Type | Required | Description | | |
| |---|---|---|---| | |
| | `pap_version` | string | ✅ | Always `"0.2"` | | |
| | `id` | string | ✅ | Globally unique. Used for idempotency | | |
| | `type` | string | ✅ | Dot-notation: `domain.object.event` | | |
| | `source` | string | ✅ | The monitoring system that detected the threshold crossing | | |
| | `time` | ISO 8601 | ✅ | The time the threshold was crossed | | |
| | `data` | object | ✅ | Context about the condition, including the threshold and the observed value | | |
| Implementations using CloudEvents v1.0 are compatible with PAP — the fields map directly. This is not a requirement. | |
| ### Event type naming | |
| ``` | |
| [domain].[object].[condition] | |
| ``` | |
| The `[condition]` segment should describe what crossed a threshold, not merely what changed: | |
| | Instead of | Prefer | | |
| |---|---| | |
| | `energy.price.changed` | `energy.price.threshold_exceeded` | | |
| | `server.latency.updated` | `server.latency.sla_breached` | | |
| | `article.age.incremented` | `article.staleness.threshold_reached` | | |
| | `stock.price.changed` | `stock.price.dropped_significantly` | | |
| The prefix `pap.` is reserved for PAP system events such as `pap.agent.invocation.completed` and `pap.agent.invocation.failed`. | |
| --- | |
| ## 4. Trigger | |
| A trigger asks: given this event, should an agent be provoked? If yes — which one, and under what constraints? | |
| ```yaml | |
| pap_version: "0.2" | |
| trigger: | |
| id: energy-price-optimizer | |
| description: > | |
| When energy prices exceed threshold, provoke an agent to plan | |
| today's consumption across smart home devices. | |
| enabled: true | |
| match: | |
| type: "energy.price.threshold_exceeded" | |
| filter: | |
| - path: "$.data.price_nok_per_kwh" | |
| operator: gt | |
| value: 3.00 | |
| - path: "$.data.region" | |
| operator: eq | |
| value: "NO1" | |
| throttle: | |
| max_per_window: 1 | |
| window_key: "$.data.region" | |
| window_seconds: 3600 | |
| agent: energy-consumption-planner-v1 | |
| ``` | |
| The filter in a trigger is not doing detection — the monitoring system already did that. The filter is a *guard*: it ensures the right agent is provoked for the right subset of events of that type. | |
| ### Filter operators | |
| | Operator | Description | | |
| |---|---| | |
| | `eq` / `ne` | Equal / not equal | | |
| | `lt` / `lte` / `gt` / `gte` | Numeric comparison | | |
| | `in` / `not_in` | Present / not present in list | | |
| | `contains` | String contains substring | | |
| | `exists` | Field is present and not null | | |
| | `regex` | Regular expression match (RE2 syntax) | | |
| Filters are evaluated with implicit `and`. Compound `or` expressions are not supported in this version. | |
| ### Idempotency | |
| The dispatcher **MUST** deduplicate invocations: | |
| ``` | |
| key = SHA256(event.id + trigger.id) | |
| ``` | |
| The key is stored with a minimum TTL of 24 hours. The same event may match *different* triggers, but never the same trigger twice. | |
| ### Cascade control | |
| Events emitted by an agent **MUST** include the field `triggered_by` set to the originating invocation ID. The dispatcher **MUST** reject events where the `triggered_by` chain exceeds 3 levels deep. | |
| --- | |
| ## 5. Agent Manifest | |
| The agent manifest describes what the agent does, what it is allowed to do, and what happens when it finishes. | |
| An agent provoked by PAP is expected to do something a regular program cannot: read context, apply judgment, and produce output that requires reasoning. If the task can be done with an if-statement, it should be. | |
| ```yaml | |
| pap_version: "0.2" | |
| agent: | |
| id: energy-consumption-planner-v1 | |
| description: > | |
| Given current and forecast energy prices, reasons about the household's | |
| smart devices and produces an optimized consumption plan for the day. | |
| risk_level: medium # read_only | low | medium | high | |
| model: | |
| provider: anthropic | |
| model_id: claude-sonnet-4-20250514 | |
| max_tokens: 1000 | |
| temperature: 0.3 | |
| system_prompt: | | |
| You are an energy optimization agent for a smart home. | |
| You have access to current energy prices, a 6-hour price forecast, | |
| and the list of controllable devices in the home. | |
| Produce a concrete hour-by-hour consumption plan that minimizes cost | |
| while maintaining comfort. Respond only with JSON. | |
| tools: | |
| - name: get_device_list | |
| source: mcp://smarthome-hub | |
| risk_override: read_only | |
| - name: set_device_schedule | |
| source: mcp://smarthome-hub | |
| risk_override: medium | |
| limits: | |
| max_runtime_seconds: 30 | |
| max_tool_calls: 10 | |
| output: | |
| type: structured | |
| schema: | |
| plan: array | |
| estimated_saving_nok: number | |
| summary: string | |
| on_complete: | |
| emit_event: "pap.agent.invocation.completed" | |
| on_failure: | |
| retry: | |
| max_attempts: 2 | |
| backoff_seconds: 5 | |
| emit_event: "pap.agent.invocation.failed" | |
| ``` | |
| ### Risk levels | |
| The effective risk level of an invocation is `max(agent.risk_level, max(tool.risk_override))`. | |
| | Level | Typical use | Implementation requirement | | |
| |---|---|---| | |
| | `read_only` | Fetch data, generate a draft, write a summary | None | | |
| | `low` | Send an internal notification | None | | |
| | `medium` | Send a message to a customer, control a device, create a ticket | Audit log | | |
| | `high` | Modify financial data, permanently delete records | Audit log + manual approval | | |
| --- | |
| ## 6. Invocation flow | |
| ``` | |
| Monitoring system crosses threshold | |
| │ | |
| │ emits PAP Event | |
| ▼ | |
| [Dispatcher: evaluate triggers] | |
| ├── No match → discard | |
| └── Match found | |
| │ | |
| ▼ | |
| [Idempotency check] | |
| ├── Key known → discard | |
| └── New | |
| │ | |
| ▼ | |
| [Cascade check] | |
| ├── Depth ≥ 3 → reject | |
| └── OK | |
| │ | |
| ▼ | |
| [Throttle check] | |
| ├── Over threshold → discard | |
| └── OK | |
| │ | |
| ▼ | |
| [Risk check] | |
| ├── requires_approval → pause | |
| └── OK | |
| │ | |
| ▼ | |
| [Run agent] | |
| │ | |
| ┌───────┴───────┐ | |
| ▼ ▼ | |
| [Success] [Failure] | |
| │ │ | |
| [emit_event] [Retry → emit_event] | |
| ``` | |
| --- | |
| ## 7. Example scenarios | |
| The following scenarios illustrate the monitor → threshold → agent pattern across different domains. Each begins with a monitoring system that emits a PAP event when a threshold is crossed, and ends with an agent doing something that requires judgment — not just routing or filtering. | |
| --- | |
| ### Energy price spike | |
| **Monitored:** Spot electricity price per kWh in real time. | |
| **Threshold:** Price exceeds 3.00 NOK/kWh. | |
| **Agent provoked:** Reads the 6-hour price forecast and the list of controllable smart home devices. Produces an hour-by-hour consumption plan that shifts non-critical loads to cheaper windows while maintaining comfort. | |
| ```json | |
| { | |
| "pap_version": "0.2", | |
| "id": "evt_e1a9c3", | |
| "type": "energy.price.threshold_exceeded", | |
| "source": "monitoring.energy-price-tracker", | |
| "time": "2026-03-11T06:00:00Z", | |
| "data": { | |
| "price_nok_per_kwh": 4.82, | |
| "threshold_nok_per_kwh": 3.00, | |
| "region": "NO1", | |
| "forecast_hours": 6 | |
| } | |
| } | |
| ``` | |
| --- | |
| ### Stock drop | |
| **Monitored:** Price of a tracked stock ticker. | |
| **Threshold:** Price drops more than 5% within a single trading day. | |
| **Agent provoked:** Reads recent financial news, earnings reports, and sector performance. Produces a short analysis distinguishing whether the drop is company-specific or part of a broader market movement, with a recommended response. | |
| ```json | |
| { | |
| "pap_version": "0.2", | |
| "id": "evt_s7f01b", | |
| "type": "stock.price.dropped_significantly", | |
| "source": "monitoring.portfolio-tracker", | |
| "time": "2026-03-11T14:33:00Z", | |
| "data": { | |
| "ticker": "EQNR.OL", | |
| "drop_percent": 6.2, | |
| "threshold_percent": 5.0, | |
| "price_current": 284.10, | |
| "price_open": 302.90 | |
| } | |
| } | |
| ``` | |
| --- | |
| ### Knowledge article gone stale | |
| **Monitored:** Age of published knowledge base articles since last edit. | |
| **Threshold:** Article has not been updated in 180 days. | |
| **Agent provoked:** Reads the article in full and compares it against current product documentation. If the content is outdated, writes an updated draft. If it is still accurate, marks it as verified with a note explaining why no changes were needed. | |
| ```json | |
| { | |
| "pap_version": "0.2", | |
| "id": "evt_k3d72a", | |
| "type": "article.staleness.threshold_reached", | |
| "source": "monitoring.knowledge-base-crawler", | |
| "time": "2026-03-11T03:00:00Z", | |
| "data": { | |
| "article_id": "kb-4421", | |
| "title": "How to configure two-factor authentication", | |
| "last_edited": "2025-09-10T11:00:00Z", | |
| "days_since_edit": 182 | |
| } | |
| } | |
| ``` | |
| --- | |
| ### Support ticket unanswered | |
| **Monitored:** Time since last activity on open high-priority support tickets. | |
| **Threshold:** No response for 4 hours on a ticket marked high priority. | |
| **Agent provoked:** Reads the ticket thread and the customer's account history. Writes a complete, ready-to-send response draft that addresses the issue — not a template, but a reasoned reply grounded in the customer's specific situation. | |
| ```json | |
| { | |
| "pap_version": "0.2", | |
| "id": "evt_t9b44f", | |
| "type": "ticket.response.overdue", | |
| "source": "monitoring.support-queue", | |
| "time": "2026-03-11T15:00:00Z", | |
| "data": { | |
| "ticket_id": "tck-8812", | |
| "priority": "high", | |
| "hours_without_response": 4.2, | |
| "customer_id": "cust_4421" | |
| } | |
| } | |
| ``` | |
| --- | |
| ### Server latency SLA breach | |
| **Monitored:** p99 response time for a production API endpoint. | |
| **Threshold:** Latency exceeds SLA for more than 5 consecutive minutes. | |
| **Agent provoked:** Reads recent deployment history, error logs, and infrastructure metrics. Produces a prioritized hypothesis list for the on-call engineer — not a generic alert, but a reasoned first assessment of what likely changed and where to look. | |
| ```json | |
| { | |
| "pap_version": "0.2", | |
| "id": "evt_l2c88d", | |
| "type": "server.latency.sla_breached", | |
| "source": "monitoring.apm", | |
| "time": "2026-03-11T22:07:00Z", | |
| "data": { | |
| "endpoint": "/api/v2/orders", | |
| "p99_ms": 4800, | |
| "sla_ms": 1000, | |
| "duration_minutes": 6 | |
| } | |
| } | |
| ``` | |
| --- | |
| ### Competitor repricing | |
| **Monitored:** A competitor's public pricing page, scraped on a schedule. | |
| **Threshold:** A meaningful price change is detected relative to the last snapshot. | |
| **Agent provoked:** Compares the new prices against your own, identifies which products are now cheaper or more expensive relative to yours, and writes an internal briefing with a recommended response — whether to adjust, hold, or monitor further. | |
| ```json | |
| { | |
| "pap_version": "0.2", | |
| "id": "evt_c5e19b", | |
| "type": "competitor.pricing.changed", | |
| "source": "monitoring.web-scraper", | |
| "time": "2026-03-11T08:15:00Z", | |
| "data": { | |
| "competitor": "acme-rival.com", | |
| "products_changed": 4, | |
| "largest_change_percent": -12.0, | |
| "snapshot_diff_url": "s3://snapshots/diff_20260311.json" | |
| } | |
| } | |
| ``` | |
| --- | |
| ### New regulation published | |
| **Monitored:** A regulatory feed for a given legal domain (e.g. data privacy, financial compliance). | |
| **Threshold:** A new document is published matching a tracked category. | |
| **Agent provoked:** Reads the document and maps its requirements against the known architecture and data flows of the organization. Produces an internal memo listing which systems may be affected and what action, if any, is required. | |
| ```json | |
| { | |
| "pap_version": "0.2", | |
| "id": "evt_r8f55c", | |
| "type": "regulation.document.published", | |
| "source": "monitoring.regulatory-feed", | |
| "time": "2026-03-11T09:00:00Z", | |
| "data": { | |
| "title": "Amendment to GDPR Article 22 on automated decision-making", | |
| "category": "data-privacy", | |
| "url": "https://eur-lex.europa.eu/...", | |
| "effective_date": "2026-07-01" | |
| } | |
| } | |
| ``` | |
| --- | |
| ### Meeting notes uploaded | |
| **Monitored:** A shared folder where meeting recordings or transcripts are deposited. | |
| **Threshold:** A new file appears. | |
| **Agent provoked:** Reads the transcript, extracts decisions and action points, identifies owners by matching names to the org directory, and creates tasks in the project management tool — attributed to the right people, not dumped in a generic backlog. | |
| ```json | |
| { | |
| "pap_version": "0.2", | |
| "id": "evt_m1d30e", | |
| "type": "document.meeting_notes.uploaded", | |
| "source": "monitoring.shared-drive-watcher", | |
| "time": "2026-03-11T13:45:00Z", | |
| "data": { | |
| "file_id": "doc_9921", | |
| "filename": "Q1-planning-2026-03-11.txt", | |
| "uploader": "lars.erik@acme.com", | |
| "word_count": 3200 | |
| } | |
| } | |
| ``` | |
| --- | |
| ## 8. What PAP is not | |
| PAP does not specify how events are transported or queued. PAP does not specify how agents communicate with each other — use A2A or similar for that. PAP does not specify how agents access tools or context — use MCP for that. PAP does not mandate a registry implementation, an authentication scheme, or an observability format. These are all valid concerns, and all implementation decisions. | |
| PAP does not replace monitoring systems — it starts where they end. | |
| PAP specifies the moment between a threshold being crossed and an agent waking up to respond. That gap is what this protocol owns. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment