{
  "$defs": {
    "AuthUser": {
      "description": "The authenticated user returned by `/v1/auth` (me / signup / login, and\nthe CLI device-session completed bag).\n\n`account_id` is REQUIRED: a user belongs to an account (signup creates one\nand binds the user), so a user response without an account is an invariant\nviolation, not a valid state. `created_at` is the user's join time.",
      "properties": {
        "account_id": {
          "title": "Account Id",
          "type": "string"
        },
        "created_at": {
          "format": "date-time",
          "title": "Created At",
          "type": "string"
        },
        "email": {
          "title": "Email",
          "type": "string"
        },
        "id": {
          "title": "Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "email",
        "account_id",
        "created_at"
      ],
      "title": "AuthUser",
      "type": "object"
    },
    "CuratedFeedConfig": {
      "description": "Schema for Feed.data when Feed.kind == 'curated'.\n\nSources are user-maintained ; one Source row per place data comes\nfrom, regardless of whether they were typed in by hand or generated\nby an external script. Watermarks + per-source metadata live on the\nSource rows; this config only carries feed-level knobs.\n\n`default_field_map` is a connector-readable hint dictionary that\napplies to every source in the feed; per-source overrides on a\nSource row's `field_map` take precedence. Keys a connector doesn't\nrecognise are ignored.",
      "properties": {
        "default_field_map": {
          "additionalProperties": {
            "type": "string"
          },
          "title": "Default Field Map",
          "type": "object"
        },
        "retention_days": {
          "default": 30,
          "title": "Retention Days",
          "type": "integer"
        }
      },
      "title": "CuratedFeedConfig",
      "type": "object"
    },
    "DeliveryCadence": {
      "description": "Cadence of a DELIVERY action (webhook / log): emit per item, or\nbatch a window into one emission. A field in the action's config, not\na separate kind.",
      "enum": [
        "instant",
        "digest"
      ],
      "title": "DeliveryCadence",
      "type": "string"
    },
    "EngineListResponse": {
      "description": "`GET /v1/engines` envelope. One entry per registered engine kind.",
      "properties": {
        "items": {
          "default": [],
          "items": {
            "$ref": "#/$defs/EngineStatus"
          },
          "title": "Items",
          "type": "array"
        }
      },
      "title": "EngineListResponse",
      "type": "object"
    },
    "EngineSpec": {
      "description": "Which engine + model an LLM-backed action uses.\n\n`kind == \"\"` means \"use the server default\" ; the server fills it\nfrom settings and rejects an unregistered kind (policy ; the pure\npackage can't know the registry). `model`, when non-empty, is the\nper-call model override the engine runs with (else the engine's\nserver-side default).",
      "properties": {
        "kind": {
          "default": "",
          "title": "Kind",
          "type": "string"
        },
        "model": {
          "default": "",
          "title": "Model",
          "type": "string"
        }
      },
      "title": "EngineSpec",
      "type": "object"
    },
    "EngineStatus": {
      "description": "Per-engine reachability snapshot.\n\n`default_model` is what the engine would use when a config leaves\n`engine.model` empty (the server-side default). It may be empty when\nthe server has no default model configured.\n\n`available_models` is the upstream's loaded set when reachable;\nwhen unreachable it's an empty list (the server didn't see anything\nand shouldn't pretend it did).",
      "properties": {
        "available": {
          "title": "Available",
          "type": "boolean"
        },
        "available_models": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Available Models",
          "type": "array"
        },
        "default_model": {
          "default": "",
          "title": "Default Model",
          "type": "string"
        },
        "how_to_fix": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "How To Fix"
        },
        "kind": {
          "title": "Kind",
          "type": "string"
        },
        "unreachable_reason": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Unreachable Reason"
        }
      },
      "required": [
        "kind",
        "available"
      ],
      "title": "EngineStatus",
      "type": "object"
    },
    "ExternalContentStatus": {
      "description": "How the linked-article enrichment fared for one run, recorded on the result\nso a finished run carries its own provenance: run WITH the article, or without\nit (none to fetch / disabled / the fetch yielded nothing). A run is never failed\nfor missing enrichment ; this is the status of it.",
      "enum": [
        "not_applicable",
        "disabled",
        "included",
        "unavailable",
        "missing"
      ],
      "title": "ExternalContentStatus",
      "type": "string"
    },
    "ExtractActionInput": {
      "properties": {
        "config": {
          "$ref": "#/$defs/ExtractConfig"
        },
        "id": {
          "default": "",
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "extract",
          "default": "extract",
          "title": "Kind",
          "type": "string"
        },
        "rank": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Rank"
        }
      },
      "required": [
        "config"
      ],
      "title": "ExtractActionInput",
      "type": "object"
    },
    "ExtractActionWire": {
      "properties": {
        "config": {
          "anyOf": [
            {
              "$ref": "#/$defs/ExtractConfig"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "id": {
          "default": "",
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "extract",
          "default": "extract",
          "title": "Kind",
          "type": "string"
        },
        "rank": {
          "title": "Rank",
          "type": "integer"
        },
        "summary": {
          "$ref": "#/$defs/WatchActionConfigSummary"
        }
      },
      "required": [
        "rank"
      ],
      "title": "ExtractActionWire",
      "type": "object"
    },
    "ExtractConfig": {
      "description": "Config for a WatchAction with kind == 'extract'.\n\nHydration only: the engine reads each item (optionally with its fetched\nlinked-article text) and returns a value for every declared `field`,\npersisted under `result.extracted`. No threshold, no gate ; the run\nalways SUCCEEDS so the chain advances. Domain-agnostic ; the field set\nand `instructions` are entirely operator-supplied.",
      "properties": {
        "engine": {
          "$ref": "#/$defs/EngineSpec"
        },
        "fetch_external_content": {
          "default": true,
          "title": "Fetch External Content",
          "type": "boolean"
        },
        "fields": {
          "items": {
            "$ref": "#/$defs/ExtractField"
          },
          "maxItems": 64,
          "title": "Fields",
          "type": "array"
        },
        "instructions": {
          "default": "",
          "maxLength": 2048,
          "title": "Instructions",
          "type": "string"
        }
      },
      "required": [
        "fields"
      ],
      "title": "ExtractConfig",
      "type": "object"
    },
    "ExtractField": {
      "description": "One field to pull out of an item. `name` is the key under\n`extracted` (and the `result.extracted.<name>` report column) ; `description`\nis what the engine should put there (e.g. \"a one-line summary of the item\").\nA typed list (not a free dict) so the engine can build a strict JSON schema\nfrom it.",
      "properties": {
        "description": {
          "maxLength": 256,
          "minLength": 1,
          "title": "Description",
          "type": "string"
        },
        "name": {
          "maxLength": 64,
          "title": "Name",
          "type": "string"
        }
      },
      "required": [
        "name",
        "description"
      ],
      "title": "ExtractField",
      "type": "object"
    },
    "ExtractResult": {
      "description": "Result an extract run writes to WatchActionRun.result.\n\n`extracted` is the `{field name: value}` map (string values in v1 ; the\nreport renders them as cells and projects `result.extracted.<name>`).\n`status` is how complete the extraction was: COMPLETE (every declared field\nfilled), PARTIAL (some), EMPTY (none). The action always sets it explicitly ;\nthe EMPTY default just covers a result built without one. `enrichment_status`\nrecords how the linked-article fetch fared (the article TEXT is never stored ;\nthe fetch is lazy + ephemeral).",
      "properties": {
        "enrichment_status": {
          "$ref": "#/$defs/ExternalContentStatus",
          "default": "not_applicable"
        },
        "extracted": {
          "additionalProperties": {
            "type": "string"
          },
          "title": "Extracted",
          "type": "object"
        },
        "status": {
          "$ref": "#/$defs/ExtractStatus",
          "default": "empty"
        }
      },
      "title": "ExtractResult",
      "type": "object"
    },
    "ExtractRunWire": {
      "properties": {
        "action_id": {
          "title": "Action Id",
          "type": "string"
        },
        "completed_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Completed At"
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "error": {
          "default": "",
          "title": "Error",
          "type": "string"
        },
        "feed_item_id": {
          "title": "Feed Item Id",
          "type": "string"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "extract",
          "default": "extract",
          "title": "Kind",
          "type": "string"
        },
        "result": {
          "anyOf": [
            {
              "$ref": "#/$defs/ExtractResult"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "scheduled_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Scheduled At"
        },
        "started_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Started At"
        },
        "state": {
          "$ref": "#/$defs/WatchActionRunState"
        },
        "watch_id": {
          "title": "Watch Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "watch_id",
        "action_id",
        "feed_item_id",
        "state"
      ],
      "title": "ExtractRunWire",
      "type": "object"
    },
    "ExtractStatus": {
      "description": "Outcome of one hydration, recorded on the result so a SUCCEEDED run\ncarries how complete its extraction was.",
      "enum": [
        "complete",
        "partial",
        "empty"
      ],
      "title": "ExtractStatus",
      "type": "string"
    },
    "FeedConfigSummary": {
      "description": "Display-only projection of a feed config for the CLI preview.\n\nBuilt server-side from the typed config (the only place that knows\nthe schema) so the CLI prints it without parsing the `data` blob.\nCurated feeds emit an empty summary because all per-source state\nsurfaces via FeedView.sources / source_count; the class stays as\na hook for future kinds that have non-source-shaped config to\nproject for the CLI.",
      "properties": {},
      "title": "FeedConfigSummary",
      "type": "object"
    },
    "FeedInput": {
      "description": "The envelope the CLI constructs for a feed write (request side). CLI-owned,\ndistinct from the server-emitted models (mirrors WatchInput). `data` carries\nthe kind-specific config (retention + default_field_map), opaque here; the\nserver validates it. `sources` is the optional starter source list for curated\nfeeds (the server creates Source rows atomically with the Feed). Extra keys\nignored so an edit seed's read-only fields drop on round-trip.",
      "properties": {
        "data": {
          "$ref": "#/$defs/CuratedFeedConfig"
        },
        "is_active": {
          "default": true,
          "title": "Is Active",
          "type": "boolean"
        },
        "kind": {
          "default": "curated",
          "title": "Kind",
          "type": "string"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "poll_interval_seconds": {
          "default": 300,
          "title": "Poll Interval Seconds",
          "type": "integer"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/SourceInput"
          },
          "title": "Sources",
          "type": "array"
        }
      },
      "required": [
        "name"
      ],
      "title": "FeedInput",
      "type": "object"
    },
    "FeedItemListResponse": {
      "description": "`GET /v1/feeds/<id>/items` -> `{\"items\": [...], \"next_cursor\": <id>|None}`.\n\nCursor-paginated by ULID pk, newest-first. Pass `?after=<id>` to fetch the\nnext page; `next_cursor` is the id to send back, or null when the page wasn't\nfull (= no more rows).",
      "properties": {
        "items": {
          "items": {
            "$ref": "#/$defs/FeedItemWire"
          },
          "title": "Items",
          "type": "array"
        },
        "next_cursor": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Next Cursor"
        }
      },
      "title": "FeedItemListResponse",
      "type": "object"
    },
    "FeedItemPayload": {
      "additionalProperties": true,
      "description": "Base payload: the canonical engine-input fields every connector maps\nonto, plus a catch-all for keys this build doesn't model. `extra=\"allow\"`\nkeeps unmodeled connector fields readable, and this is also the FALLBACK\nunion member: a payload whose `kind` matches no known variant (a newer\nconnector, or older data) validates here instead of breaking the read.",
      "properties": {
        "content": {
          "default": "",
          "title": "Content",
          "type": "string"
        },
        "external_id": {
          "default": "",
          "title": "External Id",
          "type": "string"
        },
        "external_url": {
          "default": "",
          "title": "External Url",
          "type": "string"
        },
        "kind": {
          "default": "",
          "title": "Kind",
          "type": "string"
        },
        "occurred_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Occurred At"
        },
        "parent_external_id": {
          "default": "",
          "title": "Parent External Id",
          "type": "string"
        },
        "source": {
          "default": "",
          "title": "Source",
          "type": "string"
        },
        "title": {
          "default": "",
          "title": "Title",
          "type": "string"
        },
        "url": {
          "default": "",
          "title": "Url",
          "type": "string"
        }
      },
      "title": "FeedItemPayload",
      "type": "object"
    },
    "FeedItemWire": {
      "description": "One persisted FeedItem on the wire ; the \"sort by new and go\" unit.\n\n`data` is the connector SourcePayload's dump, parsed into a typed\n`FeedItemData` (canonical fields typed for every kind; connector-specific\nfields typed per known kind; unknown kinds fall to the permissive base).\nDatetimes stay real; the renderer ISO-encodes them.\n\n`source_kind` is the connector kind (e.g. `\"reddit_subreddit\"`),\ndenormalized from the producing Source. `source_label` is the\noperator-visible display string (e.g. `\"r/ClaudeCowork\"`), set on\nthe FeedItem row at record time from the SourceSpec's `.display()`.",
      "properties": {
        "data": {
          "anyOf": [
            {
              "$ref": "#/$defs/RssEntryPayload"
            },
            {
              "$ref": "#/$defs/NewRedditPostPayload"
            },
            {
              "$ref": "#/$defs/HackerNewsFeedPayload"
            },
            {
              "$ref": "#/$defs/HackerNewsCommentPayload"
            },
            {
              "$ref": "#/$defs/FeedItemPayload"
            }
          ],
          "title": "Data"
        },
        "external_id": {
          "title": "External Id",
          "type": "string"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "occurred_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Occurred At"
        },
        "source_kind": {
          "title": "Source Kind",
          "type": "string"
        },
        "source_label": {
          "default": "",
          "title": "Source Label",
          "type": "string"
        }
      },
      "required": [
        "id",
        "source_kind",
        "external_id"
      ],
      "title": "FeedItemWire",
      "type": "object"
    },
    "FeedListResponse": {
      "description": "`GET /v1/feeds` -> `{\"items\": [...], \"next_cursor\": <id>|None}`.\n\nCursor-paginated by ULID pk, newest-first. Pass `?after=<id>` to fetch\nthe next page; `next_cursor` is the id to send back, or null when the\npage wasn't full (= no more rows).",
      "properties": {
        "items": {
          "items": {
            "$ref": "#/$defs/FeedWire"
          },
          "title": "Items",
          "type": "array"
        },
        "next_cursor": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Next Cursor"
        }
      },
      "title": "FeedListResponse",
      "type": "object"
    },
    "FeedMutationResponse": {
      "description": "Create / edit response (POST + PUT, real and `?dry_run=true`).\n`id` absent on a create dry-run preview; `dry_run` True for preview.\nCarries the same `sources` + `source_count` enrichment as FeedView\nso the CLI's confirm-preview shows the operator the post-mutation\nsource list without an extra GET.",
      "properties": {
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "data": {
          "$ref": "#/$defs/CuratedFeedConfig"
        },
        "dry_run": {
          "title": "Dry Run",
          "type": "boolean"
        },
        "id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Id"
        },
        "is_active": {
          "title": "Is Active",
          "type": "boolean"
        },
        "kind": {
          "title": "Kind",
          "type": "string"
        },
        "last_polled_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Last Polled At"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "next_poll_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Next Poll At"
        },
        "poll_interval_seconds": {
          "title": "Poll Interval Seconds",
          "type": "integer"
        },
        "source_count": {
          "default": 0,
          "title": "Source Count",
          "type": "integer"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/SourceWire"
          },
          "title": "Sources",
          "type": "array"
        },
        "summary": {
          "$ref": "#/$defs/FeedConfigSummary"
        },
        "user_id": {
          "title": "User Id",
          "type": "string"
        }
      },
      "required": [
        "name",
        "kind",
        "is_active",
        "poll_interval_seconds",
        "user_id",
        "dry_run"
      ],
      "title": "FeedMutationResponse",
      "type": "object"
    },
    "FeedView": {
      "description": "`GET /v1/feeds/<id>` - the feed's CONFIG detail: the kind-independent\nenvelope + display `summary` + its currently-attached Source rows. The item\nlog is NOT carried here; it has its own paginated route\n(`GET /v1/feeds/<id>/items`, the `feed item list` reader).",
      "properties": {
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "data": {
          "$ref": "#/$defs/CuratedFeedConfig"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "is_active": {
          "title": "Is Active",
          "type": "boolean"
        },
        "kind": {
          "title": "Kind",
          "type": "string"
        },
        "last_polled_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Last Polled At"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "next_poll_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Next Poll At"
        },
        "poll_interval_seconds": {
          "title": "Poll Interval Seconds",
          "type": "integer"
        },
        "source_count": {
          "default": 0,
          "title": "Source Count",
          "type": "integer"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/SourceWire"
          },
          "title": "Sources",
          "type": "array"
        },
        "summary": {
          "$ref": "#/$defs/FeedConfigSummary"
        },
        "user_id": {
          "title": "User Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "name",
        "kind",
        "is_active",
        "poll_interval_seconds",
        "user_id"
      ],
      "title": "FeedView",
      "type": "object"
    },
    "FeedWire": {
      "description": "The kind-independent envelope every `/v1/feeds` response item\ncarries. List-item shape and base for detail / mutation responses.",
      "properties": {
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "data": {
          "$ref": "#/$defs/CuratedFeedConfig"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "is_active": {
          "title": "Is Active",
          "type": "boolean"
        },
        "kind": {
          "title": "Kind",
          "type": "string"
        },
        "last_polled_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Last Polled At"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "next_poll_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Next Poll At"
        },
        "poll_interval_seconds": {
          "title": "Poll Interval Seconds",
          "type": "integer"
        },
        "user_id": {
          "title": "User Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "name",
        "kind",
        "is_active",
        "poll_interval_seconds",
        "user_id"
      ],
      "title": "FeedWire",
      "type": "object"
    },
    "HackerNewsCommentPayload": {
      "additionalProperties": true,
      "description": "`hn_comment`: one Hacker News comment (HackerNewsCommentConnector).\n\n`title` carries the parent story's headline (the relevance engine scores\nonly title+content, so a bare comment is unjudgeable without it); `content`\nis the comment body; `parent_external_id` is the root story id.",
      "properties": {
        "author": {
          "default": "",
          "title": "Author",
          "type": "string"
        },
        "content": {
          "default": "",
          "title": "Content",
          "type": "string"
        },
        "external_id": {
          "default": "",
          "title": "External Id",
          "type": "string"
        },
        "external_url": {
          "default": "",
          "title": "External Url",
          "type": "string"
        },
        "feed": {
          "default": "",
          "title": "Feed",
          "type": "string"
        },
        "kind": {
          "const": "hn_comment",
          "title": "Kind",
          "type": "string"
        },
        "occurred_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Occurred At"
        },
        "parent_external_id": {
          "default": "",
          "title": "Parent External Id",
          "type": "string"
        },
        "source": {
          "default": "",
          "title": "Source",
          "type": "string"
        },
        "story_title": {
          "default": "",
          "title": "Story Title",
          "type": "string"
        },
        "title": {
          "default": "",
          "title": "Title",
          "type": "string"
        },
        "url": {
          "default": "",
          "title": "Url",
          "type": "string"
        }
      },
      "required": [
        "kind"
      ],
      "title": "HackerNewsCommentPayload",
      "type": "object"
    },
    "HackerNewsCommentSourceSpec": {
      "description": "Identity of one Hacker News COMMENT stream. Bound to HackerNewsCommentConnector.\n\n`tags=comment` unfiltered is the site-wide comment firehose (~20k/day on\naverage, bursty), so `query` is REQUIRED and NON-BLANK here (it overrides the\nbase default away). That is the structural guard that keeps an unfiltered\nfirehose from ever reaching the per-item relevance engine -- a blank or\nwhitespace-only query would strip to no pre-filter, so it is rejected at the\nspec layer, not merely required-to-be-present. Volume past the keyword filter\nis further bounded by the connector's page cap.",
      "properties": {
        "kind": {
          "const": "hn_comment",
          "default": "hn_comment",
          "title": "Kind",
          "type": "string"
        },
        "match": {
          "default": "all",
          "enum": [
            "all",
            "any"
          ],
          "title": "Match",
          "type": "string"
        },
        "query": {
          "minLength": 1,
          "title": "Query",
          "type": "string"
        }
      },
      "required": [
        "query"
      ],
      "title": "HackerNewsCommentSourceSpec",
      "type": "object"
    },
    "HackerNewsFeedPayload": {
      "additionalProperties": true,
      "description": "`hn_feed`: one Hacker News story / Show HN / Ask HN post (HackerNewsFeedConnector).",
      "properties": {
        "author": {
          "default": "",
          "title": "Author",
          "type": "string"
        },
        "content": {
          "default": "",
          "title": "Content",
          "type": "string"
        },
        "external_id": {
          "default": "",
          "title": "External Id",
          "type": "string"
        },
        "external_url": {
          "default": "",
          "title": "External Url",
          "type": "string"
        },
        "feed": {
          "default": "",
          "title": "Feed",
          "type": "string"
        },
        "kind": {
          "const": "hn_feed",
          "title": "Kind",
          "type": "string"
        },
        "num_comments": {
          "default": 0,
          "title": "Num Comments",
          "type": "integer"
        },
        "occurred_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Occurred At"
        },
        "parent_external_id": {
          "default": "",
          "title": "Parent External Id",
          "type": "string"
        },
        "points": {
          "default": 0,
          "title": "Points",
          "type": "integer"
        },
        "source": {
          "default": "",
          "title": "Source",
          "type": "string"
        },
        "title": {
          "default": "",
          "title": "Title",
          "type": "string"
        },
        "url": {
          "default": "",
          "title": "Url",
          "type": "string"
        }
      },
      "required": [
        "kind"
      ],
      "title": "HackerNewsFeedPayload",
      "type": "object"
    },
    "HackerNewsFeedSourceSpec": {
      "description": "Identity of one Hacker News story feed. Bound to HackerNewsFeedConnector.\n\n`feed` selects which posts the connector pulls; it maps to an Algolia\nHN Search `tags` value (new -> story, show -> show_hn, ask -> ask_hn).\nThe set is closed to the feeds that map to a single Algolia tag and a\nnewest-first date order; the ranked Firebase feeds (top / best) have no\nAlgolia equivalent and are intentionally out of scope here.",
      "properties": {
        "feed": {
          "default": "new",
          "enum": [
            "new",
            "show",
            "ask"
          ],
          "title": "Feed",
          "type": "string"
        },
        "kind": {
          "const": "hn_feed",
          "default": "hn_feed",
          "title": "Kind",
          "type": "string"
        },
        "match": {
          "default": "all",
          "enum": [
            "all",
            "any"
          ],
          "title": "Match",
          "type": "string"
        },
        "query": {
          "default": "",
          "title": "Query",
          "type": "string"
        }
      },
      "title": "HackerNewsFeedSourceSpec",
      "type": "object"
    },
    "LogActionInput": {
      "properties": {
        "config": {
          "$ref": "#/$defs/LogConfig"
        },
        "id": {
          "default": "",
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "log",
          "default": "log",
          "title": "Kind",
          "type": "string"
        },
        "rank": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Rank"
        }
      },
      "required": [
        "config"
      ],
      "title": "LogActionInput",
      "type": "object"
    },
    "LogActionWire": {
      "properties": {
        "config": {
          "anyOf": [
            {
              "$ref": "#/$defs/LogConfig"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "id": {
          "default": "",
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "log",
          "default": "log",
          "title": "Kind",
          "type": "string"
        },
        "rank": {
          "title": "Rank",
          "type": "integer"
        },
        "summary": {
          "$ref": "#/$defs/WatchActionConfigSummary"
        }
      },
      "required": [
        "rank"
      ],
      "title": "LogActionWire",
      "type": "object"
    },
    "LogConfig": {
      "description": "Config for a WatchAction with kind == 'log'.\n\nWrites the item to the server log under `prefix`. `include_fields`\nwhitelists item fields (empty = all). `delivery` /\n`digest_interval_seconds` (from the base) pick instant vs batched. No\nsecrets, so the dump is plain and an edit replaces wholesale.\n\nDigest log delivery is best-effort AT-LEAST-ONCE: a crash after the batch\nis logged but before it's marked done re-logs the line on the next flush\n(a log line carries no idempotency key to dedup on, unlike webhook). A\nduplicate log line is harmless ; don't treat the log as an exactly-once\nrecord.",
      "properties": {
        "delivery": {
          "$ref": "#/$defs/DeliveryCadence",
          "default": "instant"
        },
        "digest_interval_seconds": {
          "default": 0,
          "minimum": 0,
          "title": "Digest Interval Seconds",
          "type": "integer"
        },
        "include_fields": {
          "items": {
            "type": "string"
          },
          "title": "Include Fields",
          "type": "array"
        },
        "prefix": {
          "default": "[watch]",
          "title": "Prefix",
          "type": "string"
        }
      },
      "title": "LogConfig",
      "type": "object"
    },
    "LogResult": {
      "description": "Result a log run writes: the line it emitted (kept for the audit).",
      "properties": {
        "rendered": {
          "default": "",
          "title": "Rendered",
          "type": "string"
        }
      },
      "title": "LogResult",
      "type": "object"
    },
    "LogRunWire": {
      "properties": {
        "action_id": {
          "title": "Action Id",
          "type": "string"
        },
        "completed_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Completed At"
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "error": {
          "default": "",
          "title": "Error",
          "type": "string"
        },
        "feed_item_id": {
          "title": "Feed Item Id",
          "type": "string"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "log",
          "default": "log",
          "title": "Kind",
          "type": "string"
        },
        "result": {
          "anyOf": [
            {
              "$ref": "#/$defs/LogResult"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "scheduled_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Scheduled At"
        },
        "started_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Started At"
        },
        "state": {
          "$ref": "#/$defs/WatchActionRunState"
        },
        "watch_id": {
          "title": "Watch Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "watch_id",
        "action_id",
        "feed_item_id",
        "state"
      ],
      "title": "LogRunWire",
      "type": "object"
    },
    "NewRedditPostPayload": {
      "additionalProperties": true,
      "description": "`new_post`: one post off a subreddit's /new (RedditSubredditConnector).",
      "properties": {
        "author": {
          "default": "",
          "title": "Author",
          "type": "string"
        },
        "content": {
          "default": "",
          "title": "Content",
          "type": "string"
        },
        "external_id": {
          "default": "",
          "title": "External Id",
          "type": "string"
        },
        "external_url": {
          "default": "",
          "title": "External Url",
          "type": "string"
        },
        "kind": {
          "const": "new_post",
          "title": "Kind",
          "type": "string"
        },
        "occurred_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Occurred At"
        },
        "parent_external_id": {
          "default": "",
          "title": "Parent External Id",
          "type": "string"
        },
        "permalink": {
          "default": "",
          "title": "Permalink",
          "type": "string"
        },
        "source": {
          "default": "",
          "title": "Source",
          "type": "string"
        },
        "subreddit": {
          "default": "",
          "title": "Subreddit",
          "type": "string"
        },
        "title": {
          "default": "",
          "title": "Title",
          "type": "string"
        },
        "url": {
          "default": "",
          "title": "Url",
          "type": "string"
        }
      },
      "required": [
        "kind"
      ],
      "title": "NewRedditPostPayload",
      "type": "object"
    },
    "RedditSubredditSourceSpec": {
      "description": "Identity of one subreddit source. Bound to RedditSubRedditConnector.",
      "properties": {
        "kind": {
          "const": "reddit_subreddit",
          "default": "reddit_subreddit",
          "title": "Kind",
          "type": "string"
        },
        "subreddit": {
          "title": "Subreddit",
          "type": "string"
        }
      },
      "required": [
        "subreddit"
      ],
      "title": "RedditSubredditSourceSpec",
      "type": "object"
    },
    "RssEntryPayload": {
      "additionalProperties": true,
      "description": "`rss_entry`: one RSS/Atom entry (RssEntryConnector).",
      "properties": {
        "author": {
          "default": "",
          "title": "Author",
          "type": "string"
        },
        "categories": {
          "items": {
            "type": "string"
          },
          "title": "Categories",
          "type": "array"
        },
        "content": {
          "default": "",
          "title": "Content",
          "type": "string"
        },
        "external_id": {
          "default": "",
          "title": "External Id",
          "type": "string"
        },
        "external_url": {
          "default": "",
          "title": "External Url",
          "type": "string"
        },
        "feed_url": {
          "default": "",
          "title": "Feed Url",
          "type": "string"
        },
        "kind": {
          "const": "rss_entry",
          "title": "Kind",
          "type": "string"
        },
        "occurred_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Occurred At"
        },
        "parent_external_id": {
          "default": "",
          "title": "Parent External Id",
          "type": "string"
        },
        "source": {
          "default": "",
          "title": "Source",
          "type": "string"
        },
        "title": {
          "default": "",
          "title": "Title",
          "type": "string"
        },
        "url": {
          "default": "",
          "title": "Url",
          "type": "string"
        }
      },
      "required": [
        "kind"
      ],
      "title": "RssEntryPayload",
      "type": "object"
    },
    "RssSourceSpec": {
      "description": "Identity of one RSS/Atom source by URL. Bound to a generic RSS connector.",
      "properties": {
        "kind": {
          "const": "rss",
          "default": "rss",
          "title": "Kind",
          "type": "string"
        },
        "name": {
          "default": "",
          "title": "Name",
          "type": "string"
        },
        "url": {
          "title": "Url",
          "type": "string"
        }
      },
      "required": [
        "url"
      ],
      "title": "RssSourceSpec",
      "type": "object"
    },
    "RunFeed": {
      "description": "A feed referenced by the audited runs, returned once in the response's\n`feeds` map (keyed by id). Feeds are far fewer than runs, so this normalizes\nthe many runs -> few feeds relationship instead of repeating the feed per\nrow.",
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "name": {
          "default": "",
          "title": "Name",
          "type": "string"
        }
      },
      "required": [
        "id"
      ],
      "title": "RunFeed",
      "type": "object"
    },
    "RunFeedItem": {
      "description": "The feed item a run was judged against, narrowed for the audit log.\n\nThe `Run*` prefix (vs the package's `*Wire` suffix) marks an audit-narrowed\nprojection: a deliberately smaller view of `FeedItem` for the runs response's\nside tables, not the full `FeedItemWire`. `RunFeed` is the same idea for Feed.\n\n`title` / `url` / `external_url` come from the connector payload\n(`FeedItem.data`, a SourcePayload dump where all three live on the base ;\n`external_url` is the off-site link, empty when there's none). `source_label`\nis the operator-visible source string. `feed_id` keys into the response's `feeds`\nmap and is REQUIRED (the structural join key; the display fields default to\nempty, the join key never does). NOT embedded on each run row: returned once\nper item in the response's `feed_items` map (items are ~1:1 with runs, but the\nrun row stays pure ids and the shape matches `action` / `feeds`).",
      "properties": {
        "external_url": {
          "default": "",
          "title": "External Url",
          "type": "string"
        },
        "feed_id": {
          "title": "Feed Id",
          "type": "string"
        },
        "occurred_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Occurred At"
        },
        "source_label": {
          "default": "",
          "title": "Source Label",
          "type": "string"
        },
        "title": {
          "default": "",
          "title": "Title",
          "type": "string"
        },
        "url": {
          "default": "",
          "title": "Url",
          "type": "string"
        }
      },
      "required": [
        "feed_id"
      ],
      "title": "RunFeedItem",
      "type": "object"
    },
    "SemanticFilterActionInput": {
      "properties": {
        "config": {
          "$ref": "#/$defs/SemanticFilterConfig"
        },
        "id": {
          "default": "",
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "semantic_filter",
          "default": "semantic_filter",
          "title": "Kind",
          "type": "string"
        },
        "rank": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Rank"
        }
      },
      "required": [
        "config"
      ],
      "title": "SemanticFilterActionInput",
      "type": "object"
    },
    "SemanticFilterActionWire": {
      "properties": {
        "config": {
          "anyOf": [
            {
              "$ref": "#/$defs/SemanticFilterConfig"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "id": {
          "default": "",
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "semantic_filter",
          "default": "semantic_filter",
          "title": "Kind",
          "type": "string"
        },
        "rank": {
          "title": "Rank",
          "type": "integer"
        },
        "summary": {
          "$ref": "#/$defs/WatchActionConfigSummary"
        }
      },
      "required": [
        "rank"
      ],
      "title": "SemanticFilterActionWire",
      "type": "object"
    },
    "SemanticFilterConfig": {
      "description": "Config for a WatchAction with kind == 'semantic_filter'.\n\nThe LLM relevance gate: scores each item against `instructions` with\n`engine`, and the run GATES the chain when the score is below\n`threshold` (a pass=false). It is the v2 home for what the old\nListener carried as instructions + engine + hit_threshold ; it owns\nno feed (the Watch subscribes to feeds) and no delivery (that's the\ndelivery actions).",
      "properties": {
        "engine": {
          "$ref": "#/$defs/EngineSpec"
        },
        "fetch_external_content": {
          "default": true,
          "title": "Fetch External Content",
          "type": "boolean"
        },
        "instructions": {
          "title": "Instructions",
          "type": "string"
        },
        "threshold": {
          "default": 0.8,
          "exclusiveMinimum": 0.0,
          "maximum": 1.0,
          "title": "Threshold",
          "type": "number"
        }
      },
      "required": [
        "instructions"
      ],
      "title": "SemanticFilterConfig",
      "type": "object"
    },
    "SemanticFilterResult": {
      "description": "Result a semantic-filter run writes to WatchActionRun.result.\n\n`passed` is the gate decision (False -> the run is GATED, the chain\nstops) ; `score` is the engine's relevance in [0.0, 1.0] (kept for the\naudit log + threshold tuning), bounded to match the engine contract +\n`SemanticFilterConfig.threshold` so an out-of-range score is rejected\nat the write boundary, not silently logged. `passed` not `pass`\nbecause `pass` is a Python keyword. `enrichment_status` records the STATUS of\nthe linked-article fetch (provenance for the score) ; the article TEXT itself\nis never stored here, the fetch is lazy and ephemeral.",
      "properties": {
        "enrichment_status": {
          "$ref": "#/$defs/ExternalContentStatus",
          "default": "not_applicable"
        },
        "passed": {
          "title": "Passed",
          "type": "boolean"
        },
        "reason": {
          "default": "",
          "title": "Reason",
          "type": "string"
        },
        "score": {
          "maximum": 1.0,
          "minimum": 0.0,
          "title": "Score",
          "type": "number"
        }
      },
      "required": [
        "passed",
        "score"
      ],
      "title": "SemanticFilterResult",
      "type": "object"
    },
    "SemanticFilterRunWire": {
      "properties": {
        "action_id": {
          "title": "Action Id",
          "type": "string"
        },
        "completed_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Completed At"
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "error": {
          "default": "",
          "title": "Error",
          "type": "string"
        },
        "feed_item_id": {
          "title": "Feed Item Id",
          "type": "string"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "semantic_filter",
          "default": "semantic_filter",
          "title": "Kind",
          "type": "string"
        },
        "result": {
          "anyOf": [
            {
              "$ref": "#/$defs/SemanticFilterResult"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "scheduled_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Scheduled At"
        },
        "started_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Started At"
        },
        "state": {
          "$ref": "#/$defs/WatchActionRunState"
        },
        "watch_id": {
          "title": "Watch Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "watch_id",
        "action_id",
        "feed_item_id",
        "state"
      ],
      "title": "SemanticFilterRunWire",
      "type": "object"
    },
    "SourceInput": {
      "description": "One source on a feed-create or `feed source set` payload. `spec` / `meta` /\n`field_map` come from `SourceFields`.\n\n`last_event_at` is the optional starting watermark. None means\n\"live mode from now\" (server-policy defaulted at save time); a\npast datetime means \"fetch items newer than this\" ; the operator's\nbackfill knob. Server policy rejects future values.",
      "properties": {
        "field_map": {
          "additionalProperties": {
            "type": "string"
          },
          "title": "Field Map",
          "type": "object"
        },
        "last_event_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Last Event At"
        },
        "meta": {
          "additionalProperties": {
            "type": "string"
          },
          "title": "Meta",
          "type": "object"
        },
        "spec": {
          "discriminator": {
            "mapping": {
              "hn_comment": "#/$defs/HackerNewsCommentSourceSpec",
              "hn_feed": "#/$defs/HackerNewsFeedSourceSpec",
              "reddit_subreddit": "#/$defs/RedditSubredditSourceSpec",
              "rss": "#/$defs/RssSourceSpec"
            },
            "propertyName": "kind"
          },
          "oneOf": [
            {
              "$ref": "#/$defs/RedditSubredditSourceSpec"
            },
            {
              "$ref": "#/$defs/RssSourceSpec"
            },
            {
              "$ref": "#/$defs/HackerNewsFeedSourceSpec"
            },
            {
              "$ref": "#/$defs/HackerNewsCommentSourceSpec"
            }
          ],
          "title": "Spec"
        }
      },
      "required": [
        "spec"
      ],
      "title": "SourceInput",
      "type": "object"
    },
    "SourceSetPayload": {
      "description": "Round-trip file format for `magpie feed source export` /\n`magpie feed source set`. Operators or their scrape scripts can\nconstruct this directly; the bare `list[SourceInput]` shape is\nalso accepted on input for hand-rolled cases.",
      "properties": {
        "sources": {
          "items": {
            "$ref": "#/$defs/SourceInput"
          },
          "title": "Sources",
          "type": "array"
        },
        "version": {
          "const": "v1",
          "default": "v1",
          "title": "Version",
          "type": "string"
        }
      },
      "title": "SourceSetPayload",
      "type": "object"
    },
    "SourceSetResult": {
      "description": "POST /v1/feeds/{id}/sources (PUT collection) ; replace-mode reconcile result.\n\n`added` / `removed` / `persisted` describe the diff this call\napplied; `source_count` is the total after the diff. A no-op set\n(nothing changed) reports `added=0, removed=0, persisted=total`.",
      "properties": {
        "added": {
          "title": "Added",
          "type": "integer"
        },
        "persisted": {
          "title": "Persisted",
          "type": "integer"
        },
        "removed": {
          "title": "Removed",
          "type": "integer"
        },
        "source_count": {
          "title": "Source Count",
          "type": "integer"
        }
      },
      "required": [
        "added",
        "removed",
        "persisted",
        "source_count"
      ],
      "title": "SourceSetResult",
      "type": "object"
    },
    "SourceWire": {
      "description": "One Source row on the read path. `spec` / `meta` / `field_map` come from\n`SourceFields`.\n\nRelies on pydantic's DEFAULT `extra=\"ignore\"` (no explicit model_config): the\noutput-only `display` computed field is absent from the input schema, so\nre-validating a `model_dump()` (which includes `display`) silently drops it and\nrecomputes, rather than erroring as it would under `extra=\"forbid\"`.",
      "properties": {
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "display": {
          "description": "The source's human label, kind-polymorphic (e.g. `r/foo`, or an RSS\nfeed's name/url). Provided on the wire so a consumer reads one labeled\nfield instead of re-deriving it per kind from `spec`; it is the same\n`SourceSpec.display()` the server records onto FeedItem.source_label.",
          "readOnly": true,
          "title": "Display",
          "type": "string"
        },
        "field_map": {
          "additionalProperties": {
            "type": "string"
          },
          "title": "Field Map",
          "type": "object"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "last_event_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Last Event At"
        },
        "meta": {
          "additionalProperties": {
            "type": "string"
          },
          "title": "Meta",
          "type": "object"
        },
        "spec": {
          "discriminator": {
            "mapping": {
              "hn_comment": "#/$defs/HackerNewsCommentSourceSpec",
              "hn_feed": "#/$defs/HackerNewsFeedSourceSpec",
              "reddit_subreddit": "#/$defs/RedditSubredditSourceSpec",
              "rss": "#/$defs/RssSourceSpec"
            },
            "propertyName": "kind"
          },
          "oneOf": [
            {
              "$ref": "#/$defs/RedditSubredditSourceSpec"
            },
            {
              "$ref": "#/$defs/RssSourceSpec"
            },
            {
              "$ref": "#/$defs/HackerNewsFeedSourceSpec"
            },
            {
              "$ref": "#/$defs/HackerNewsCommentSourceSpec"
            }
          ],
          "title": "Spec"
        }
      },
      "required": [
        "spec",
        "id",
        "display"
      ],
      "title": "SourceWire",
      "type": "object"
    },
    "TelemetryState": {
      "description": "The instance's telemetry mode, whether this user may change it (`can_set`), and\nthe server-computed `emitting` (will an event actually send right now; see the field).\n\nThe `is_*` helpers keep callers off the raw `mode` string -- the enum\ncomparison happens once, here, so a consumer reads `state.is_unset` rather\nthan `state.mode == \"unset\"`.",
      "properties": {
        "can_set": {
          "title": "Can Set",
          "type": "boolean"
        },
        "emitting": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Emitting"
        },
        "mode": {
          "title": "Mode",
          "type": "string"
        }
      },
      "required": [
        "mode",
        "can_set"
      ],
      "title": "TelemetryState",
      "type": "object"
    },
    "WatchActionConfigSummary": {
      "description": "Display-only projection of an action config for the CLI preview.\n\nBuilt server-side from the typed config (the only place that knows\nthe schema), so the CLI prints it without parsing the `config` blob ;\nno shadow schema on the client. `detail` is presentation, not a\ncontract ; the action's `kind` is carried separately (the column /\nwire), so it isn't repeated here.",
      "properties": {
        "detail": {
          "default": "",
          "title": "Detail",
          "type": "string"
        }
      },
      "title": "WatchActionConfigSummary",
      "type": "object"
    },
    "WatchActionDeliveryListResponse": {
      "description": "`GET /v1/actions/<action_id>/deliveries` envelope. Cursor-paginated by\nULID pk, newest-first. `?after=<id>` for the next page; `next_cursor` null\nwhen the page wasn't full. Filter by `?state=` (a WatchActionDeliveryState\nvalue).",
      "properties": {
        "items": {
          "items": {
            "$ref": "#/$defs/WatchActionDeliveryWire"
          },
          "title": "Items",
          "type": "array"
        },
        "next_cursor": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Next Cursor"
        }
      },
      "title": "WatchActionDeliveryListResponse",
      "type": "object"
    },
    "WatchActionDeliveryState": {
      "description": "Lifecycle of one WatchActionDelivery (one outbound HTTP call attempt).\n\nPENDING is set when the row is created, just before the call; the\nresponse moves it terminal. Mirrors the delivery half of\n`WatchActionRunState`:\n  - SUCCEEDED : a 2xx response.\n  - ERRORED   : a PERMANENT failure (blocked destination, redirect, a\n                non-retryable 4xx) ; the call won't be retried.\n  - FAILED    : a TRANSIENT failure (5xx / 408 / 429 / connect / timeout).\n                The owning run stays retryable, so a later attempt makes a\n                NEW delivery row.",
      "enum": [
        "pending",
        "succeeded",
        "errored",
        "failed"
      ],
      "title": "WatchActionDeliveryState",
      "type": "string"
    },
    "WatchActionDeliveryView": {
      "description": "`GET /v1/action-deliveries/<delivery_id>`, the detail: the list row plus the\nexact `request_payload` we sent (a WebhookPayload dump), stored\npoint-in-time. Opaque here ; headers are NEVER included (auth tokens). Kept\noff the list wire so a list call doesn't ship every batch body.",
      "properties": {
        "action_id": {
          "title": "Action Id",
          "type": "string"
        },
        "attempt": {
          "default": 0,
          "title": "Attempt",
          "type": "integer"
        },
        "completed_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Completed At"
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "delivery": {
          "$ref": "#/$defs/DeliveryCadence"
        },
        "error": {
          "default": "",
          "title": "Error",
          "type": "string"
        },
        "http_status": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Http Status"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "item_count": {
          "default": 0,
          "title": "Item Count",
          "type": "integer"
        },
        "method": {
          "$ref": "#/$defs/WebhookMethod"
        },
        "request_payload": {
          "additionalProperties": true,
          "title": "Request Payload",
          "type": "object"
        },
        "started_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Started At"
        },
        "state": {
          "$ref": "#/$defs/WatchActionDeliveryState"
        },
        "target_host": {
          "default": "",
          "title": "Target Host",
          "type": "string"
        },
        "watch_id": {
          "title": "Watch Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "watch_id",
        "action_id",
        "delivery",
        "method",
        "state"
      ],
      "title": "WatchActionDeliveryView",
      "type": "object"
    },
    "WatchActionDeliveryWire": {
      "description": "One WatchActionDelivery on the LIST wire\n(`GET /v1/actions/<action_id>/deliveries`).\n\nOne outbound HTTP call ATTEMPT (a digest that retries makes several, one\nrow each). `target_host` is the redacted destination host (never the full\nURL or any secret). `http_status` is null until the call returns. The full\n`request_payload` is NOT here (it can be a large batch body) ; fetch one\ndelivery's detail (WatchActionDeliveryView) for it. Datetimes real; renderer\nencodes.",
      "properties": {
        "action_id": {
          "title": "Action Id",
          "type": "string"
        },
        "attempt": {
          "default": 0,
          "title": "Attempt",
          "type": "integer"
        },
        "completed_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Completed At"
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "delivery": {
          "$ref": "#/$defs/DeliveryCadence"
        },
        "error": {
          "default": "",
          "title": "Error",
          "type": "string"
        },
        "http_status": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Http Status"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "item_count": {
          "default": 0,
          "title": "Item Count",
          "type": "integer"
        },
        "method": {
          "$ref": "#/$defs/WebhookMethod"
        },
        "started_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Started At"
        },
        "state": {
          "$ref": "#/$defs/WatchActionDeliveryState"
        },
        "target_host": {
          "default": "",
          "title": "Target Host",
          "type": "string"
        },
        "watch_id": {
          "title": "Watch Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "watch_id",
        "action_id",
        "delivery",
        "method",
        "state"
      ],
      "title": "WatchActionDeliveryWire",
      "type": "object"
    },
    "WatchActionMutationResponse": {
      "description": "Single-action add/edit response (POST `/actions` + PUT `/actions/<id>`,\nreal and `?dry_run=true`). `dry_run` is True for a validation-only preview;\n`action` is the typed action node (a discriminated union). On a dry-run the\naction isn't persisted, so `action.id` is \"\" (WatchActionWire can't be\nsubclassed to null the id, so the response nests the action rather than\nflattening it as the pre-union shape did).",
      "properties": {
        "action": {
          "discriminator": {
            "mapping": {
              "extract": "#/$defs/ExtractActionWire",
              "log": "#/$defs/LogActionWire",
              "semantic_filter": "#/$defs/SemanticFilterActionWire",
              "webhook": "#/$defs/WebhookActionWire"
            },
            "propertyName": "kind"
          },
          "oneOf": [
            {
              "$ref": "#/$defs/SemanticFilterActionWire"
            },
            {
              "$ref": "#/$defs/ExtractActionWire"
            },
            {
              "$ref": "#/$defs/LogActionWire"
            },
            {
              "$ref": "#/$defs/WebhookActionWire"
            }
          ],
          "title": "Action"
        },
        "dry_run": {
          "title": "Dry Run",
          "type": "boolean"
        }
      },
      "required": [
        "dry_run",
        "action"
      ],
      "title": "WatchActionMutationResponse",
      "type": "object"
    },
    "WatchActionRunListResponse": {
      "description": "`GET /v1/actions/<action_id>/activity` envelope. Cursor-paginated by ULID\npk, newest-first. `?after=<id>` for the next page; `next_cursor` null\nwhen the page wasn't full. Filter by `?state=` (a WatchActionRunState\nvalue). `summary` (the full per-state breakdown) is present on the\nfirst page only (omitted when paging with `?after=`).",
      "properties": {
        "action": {
          "anyOf": [
            {
              "discriminator": {
                "mapping": {
                  "extract": "#/$defs/ExtractActionWire",
                  "log": "#/$defs/LogActionWire",
                  "semantic_filter": "#/$defs/SemanticFilterActionWire",
                  "webhook": "#/$defs/WebhookActionWire"
                },
                "propertyName": "kind"
              },
              "oneOf": [
                {
                  "$ref": "#/$defs/SemanticFilterActionWire"
                },
                {
                  "$ref": "#/$defs/ExtractActionWire"
                },
                {
                  "$ref": "#/$defs/LogActionWire"
                },
                {
                  "$ref": "#/$defs/WebhookActionWire"
                }
              ]
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Action"
        },
        "feed_items": {
          "additionalProperties": {
            "$ref": "#/$defs/RunFeedItem"
          },
          "title": "Feed Items",
          "type": "object"
        },
        "feeds": {
          "additionalProperties": {
            "$ref": "#/$defs/RunFeed"
          },
          "title": "Feeds",
          "type": "object"
        },
        "items": {
          "items": {
            "discriminator": {
              "mapping": {
                "extract": "#/$defs/ExtractRunWire",
                "log": "#/$defs/LogRunWire",
                "semantic_filter": "#/$defs/SemanticFilterRunWire",
                "webhook": "#/$defs/WebhookRunWire"
              },
              "propertyName": "kind"
            },
            "oneOf": [
              {
                "$ref": "#/$defs/SemanticFilterRunWire"
              },
              {
                "$ref": "#/$defs/ExtractRunWire"
              },
              {
                "$ref": "#/$defs/LogRunWire"
              },
              {
                "$ref": "#/$defs/WebhookRunWire"
              }
            ]
          },
          "title": "Items",
          "type": "array"
        },
        "next_cursor": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Next Cursor"
        },
        "summary": {
          "anyOf": [
            {
              "$ref": "#/$defs/WatchActionRunSummary"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        }
      },
      "title": "WatchActionRunListResponse",
      "type": "object"
    },
    "WatchActionRunState": {
      "description": "Lifecycle of one WatchActionRun (one action executing one item).\n\nThe runner advances the chain to the next action IFF a run reaches\n`SUCCEEDED`. The control-flow fact lives in this column so the audit\nlog is self-documenting (the score etc. stay in `result`).\n\nTerminal states, and how the drain treats each:\n  - SUCCEEDED : ran, score met threshold -> advance the chain.\n  - GATED     : ran cleanly, score below threshold -> chain stops. Not\n                a failure ; the expected \"didn't pass the filter\" path.\n  - FAILED    : a TRANSIENT error (engine down, timeout, bad response).\n                Retryable -> the drain re-claims it until attempts hit\n                WATCH_RUN_MAX_ATTEMPTS, then it stays FAILED.\n  - ERRORED   : a PERMANENT backend defect (e.g. a feed item whose\n                stored data can't be rehydrated). Terminal, NEVER\n                retried ; distinct from FAILED so an audit query can\n                tell \"broken, look at it\" from \"transient, gave up\".\n  - SKIPPED   : a DELIBERATE non-run (operator paused the watch, a\n                policy chose not to run this action). Reserved for that\n                intent ; NOT used for defects (use ERRORED).\nNon-terminal: PENDING (queued) -> RUNNING (claimed by the drain).",
      "enum": [
        "pending",
        "running",
        "succeeded",
        "gated",
        "failed",
        "errored",
        "skipped"
      ],
      "title": "WatchActionRunState",
      "type": "string"
    },
    "WatchActionRunSummary": {
      "description": "Activity over an action's runs, so an operator sees \"what is this\naction doing?\" without scrolling the log.\n\n`evaluated` is the per-terminal-state breakdown of runs JUDGED within\n[since, until), windowed on completion (evaluation) time, not enqueue\ntime. `pending` / `running` / `retrying` are the CURRENT live backlog,\nNOT time-bound (those runs have no completion time yet), surfaced so the\nqueue stays visible. `window` is the requested preset ; `since` / `until`\nare the concrete bounds the server resolved it to.\n\nDISJOINT by `completed_at`: a FAILED run is counted in `evaluated[failed]`\niff it's terminal (attempts exhausted -> has a completion time) and in\n`retrying` iff it's not (still under the cap -> no completion time). So a\nconsumer can sum across buckets without double-counting.",
      "properties": {
        "evaluated": {
          "additionalProperties": {
            "type": "integer"
          },
          "propertyNames": {
            "$ref": "#/$defs/WatchActionRunState"
          },
          "title": "Evaluated",
          "type": "object"
        },
        "pending": {
          "default": 0,
          "title": "Pending",
          "type": "integer"
        },
        "retrying": {
          "default": 0,
          "title": "Retrying",
          "type": "integer"
        },
        "running": {
          "default": 0,
          "title": "Running",
          "type": "integer"
        },
        "since": {
          "format": "date-time",
          "title": "Since",
          "type": "string"
        },
        "until": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Until"
        },
        "window": {
          "$ref": "#/$defs/WatchActivityWindow"
        }
      },
      "required": [
        "window",
        "since"
      ],
      "title": "WatchActionRunSummary",
      "type": "object"
    },
    "WatchActionRunView": {
      "description": "`GET /v1/action-activity/<id>`: one run (\"activity entry\") in full, with\nthe joined item / feed / action so a reader sees WHAT it judged and under\nWHICH action without a second call. `run` is NESTED (not inherited the way\nWatchActionDeliveryView extends its wire) because the item / feed / action are\npeer joins onto the run, not extra columns on it. The list returns the same\njoin as keyed side tables; the detail inlines the one item + feed it needs.\n`feed_item` / `feed` are null when the item has been pruned by retention (the\nrun still renders by `run.feed_item_id`); `action` is null only if it was\nremoved.",
      "properties": {
        "action": {
          "anyOf": [
            {
              "discriminator": {
                "mapping": {
                  "extract": "#/$defs/ExtractActionWire",
                  "log": "#/$defs/LogActionWire",
                  "semantic_filter": "#/$defs/SemanticFilterActionWire",
                  "webhook": "#/$defs/WebhookActionWire"
                },
                "propertyName": "kind"
              },
              "oneOf": [
                {
                  "$ref": "#/$defs/SemanticFilterActionWire"
                },
                {
                  "$ref": "#/$defs/ExtractActionWire"
                },
                {
                  "$ref": "#/$defs/LogActionWire"
                },
                {
                  "$ref": "#/$defs/WebhookActionWire"
                }
              ]
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Action"
        },
        "feed": {
          "anyOf": [
            {
              "$ref": "#/$defs/RunFeed"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "feed_item": {
          "anyOf": [
            {
              "$ref": "#/$defs/RunFeedItem"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "run": {
          "discriminator": {
            "mapping": {
              "extract": "#/$defs/ExtractRunWire",
              "log": "#/$defs/LogRunWire",
              "semantic_filter": "#/$defs/SemanticFilterRunWire",
              "webhook": "#/$defs/WebhookRunWire"
            },
            "propertyName": "kind"
          },
          "oneOf": [
            {
              "$ref": "#/$defs/SemanticFilterRunWire"
            },
            {
              "$ref": "#/$defs/ExtractRunWire"
            },
            {
              "$ref": "#/$defs/LogRunWire"
            },
            {
              "$ref": "#/$defs/WebhookRunWire"
            }
          ],
          "title": "Run"
        }
      },
      "required": [
        "run"
      ],
      "title": "WatchActionRunView",
      "type": "object"
    },
    "WatchActivityWindow": {
      "description": "Bounded time windows for the action-activity summary, selected by a\nclient and resolved to concrete `(since, until)` bounds SERVER-side (one\nsource of truth, server clock). Applied by run EVALUATION time\n(`completed_at`). No unbounded 'all' value: a count is always over a\nfinite range. Default is `WEEK`.",
      "enum": [
        "24h",
        "yesterday",
        "7d",
        "30d"
      ],
      "title": "WatchActivityWindow",
      "type": "string"
    },
    "WatchInput": {
      "description": "The envelope the CLI constructs for a watch write (request side).\nCLI-owned, distinct from the server-emitted models. `feed_ids` is the\nsubscription set; `actions` is the initial path's ordered chain. The\nserver creates the Watch + its single WatchPath + WatchFeed rows\natomically. Extra keys ignored so an edit seed's read-only fields\ndrop on round-trip.",
      "properties": {
        "actions": {
          "items": {
            "discriminator": {
              "mapping": {
                "extract": "#/$defs/ExtractActionInput",
                "log": "#/$defs/LogActionInput",
                "semantic_filter": "#/$defs/SemanticFilterActionInput",
                "webhook": "#/$defs/WebhookActionInput"
              },
              "propertyName": "kind"
            },
            "oneOf": [
              {
                "$ref": "#/$defs/SemanticFilterActionInput"
              },
              {
                "$ref": "#/$defs/ExtractActionInput"
              },
              {
                "$ref": "#/$defs/LogActionInput"
              },
              {
                "$ref": "#/$defs/WebhookActionInput"
              }
            ]
          },
          "title": "Actions",
          "type": "array"
        },
        "feed_ids": {
          "items": {
            "type": "string"
          },
          "title": "Feed Ids",
          "type": "array"
        },
        "is_active": {
          "default": true,
          "title": "Is Active",
          "type": "boolean"
        },
        "name": {
          "title": "Name",
          "type": "string"
        }
      },
      "required": [
        "name"
      ],
      "title": "WatchInput",
      "type": "object"
    },
    "WatchListResponse": {
      "description": "`GET /v1/watches` -> `{\"items\": [...], \"next_cursor\": <id>|None}`.\n\nCursor-paginated by ULID pk, newest-first. Pass `?after=<id>` for the\nnext page; `next_cursor` is the id to send back, or null when the\npage wasn't full (no more rows).",
      "properties": {
        "items": {
          "items": {
            "$ref": "#/$defs/WatchWire"
          },
          "title": "Items",
          "type": "array"
        },
        "next_cursor": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Next Cursor"
        }
      },
      "title": "WatchListResponse",
      "type": "object"
    },
    "WatchMutationResponse": {
      "description": "Create / edit response (POST + PUT, real and `?dry_run=true`).\n`id` is absent on a create dry-run preview (server omits the pre-save\nplaceholder), hence `str | None`. `dry_run` is True for a\nvalidation-only preview. Carries the same `actions` enrichment as\nWatchView so the CLI's confirm-preview shows the resulting chain.",
      "properties": {
        "actions": {
          "items": {
            "discriminator": {
              "mapping": {
                "extract": "#/$defs/ExtractActionWire",
                "log": "#/$defs/LogActionWire",
                "semantic_filter": "#/$defs/SemanticFilterActionWire",
                "webhook": "#/$defs/WebhookActionWire"
              },
              "propertyName": "kind"
            },
            "oneOf": [
              {
                "$ref": "#/$defs/SemanticFilterActionWire"
              },
              {
                "$ref": "#/$defs/ExtractActionWire"
              },
              {
                "$ref": "#/$defs/LogActionWire"
              },
              {
                "$ref": "#/$defs/WebhookActionWire"
              }
            ]
          },
          "title": "Actions",
          "type": "array"
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "dry_run": {
          "title": "Dry Run",
          "type": "boolean"
        },
        "feed_ids": {
          "items": {
            "type": "string"
          },
          "title": "Feed Ids",
          "type": "array"
        },
        "id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Id"
        },
        "is_active": {
          "title": "Is Active",
          "type": "boolean"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "user_id": {
          "title": "User Id",
          "type": "string"
        }
      },
      "required": [
        "name",
        "is_active",
        "user_id",
        "dry_run"
      ],
      "title": "WatchMutationResponse",
      "type": "object"
    },
    "WatchView": {
      "description": "`GET /v1/watches/<id>`, the read view: the envelope plus the\nordered action chain of the watch's initial path. v1 has exactly one\npath, so `actions` is that path's actions by `rank` ; the path layer\nstays hidden on the wire until multi-path ships.",
      "properties": {
        "actions": {
          "items": {
            "discriminator": {
              "mapping": {
                "extract": "#/$defs/ExtractActionWire",
                "log": "#/$defs/LogActionWire",
                "semantic_filter": "#/$defs/SemanticFilterActionWire",
                "webhook": "#/$defs/WebhookActionWire"
              },
              "propertyName": "kind"
            },
            "oneOf": [
              {
                "$ref": "#/$defs/SemanticFilterActionWire"
              },
              {
                "$ref": "#/$defs/ExtractActionWire"
              },
              {
                "$ref": "#/$defs/LogActionWire"
              },
              {
                "$ref": "#/$defs/WebhookActionWire"
              }
            ]
          },
          "title": "Actions",
          "type": "array"
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "feed_ids": {
          "items": {
            "type": "string"
          },
          "title": "Feed Ids",
          "type": "array"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "is_active": {
          "title": "Is Active",
          "type": "boolean"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "user_id": {
          "title": "User Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "name",
        "is_active",
        "user_id"
      ],
      "title": "WatchView",
      "type": "object"
    },
    "WatchWire": {
      "description": "The envelope every `/v1/watches` response item carries. List-item\nshape and base for the detail / mutation responses.\n\n`feed_ids` is the watch's subscription set (the WatchFeed rows,\nminus the internal per-feed watermark which never crosses the wire).\nDatetimes are real `datetime` (None pre-save); JSON encoding is the\nrenderer's job. `user_id` is creator/audit only (account-scoped\nreads, not an ownership filter).",
      "properties": {
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "feed_ids": {
          "items": {
            "type": "string"
          },
          "title": "Feed Ids",
          "type": "array"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "is_active": {
          "title": "Is Active",
          "type": "boolean"
        },
        "name": {
          "title": "Name",
          "type": "string"
        },
        "user_id": {
          "title": "User Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "name",
        "is_active",
        "user_id"
      ],
      "title": "WatchWire",
      "type": "object"
    },
    "WebhookActionInput": {
      "properties": {
        "config": {
          "$ref": "#/$defs/WebhookConfig"
        },
        "id": {
          "default": "",
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "webhook",
          "default": "webhook",
          "title": "Kind",
          "type": "string"
        },
        "rank": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Rank"
        }
      },
      "required": [
        "config"
      ],
      "title": "WebhookActionInput",
      "type": "object"
    },
    "WebhookActionWire": {
      "properties": {
        "config": {
          "anyOf": [
            {
              "$ref": "#/$defs/WebhookConfig"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "id": {
          "default": "",
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "webhook",
          "default": "webhook",
          "title": "Kind",
          "type": "string"
        },
        "rank": {
          "title": "Rank",
          "type": "integer"
        },
        "summary": {
          "$ref": "#/$defs/WatchActionConfigSummary"
        }
      },
      "required": [
        "rank"
      ],
      "title": "WebhookActionWire",
      "type": "object"
    },
    "WebhookConfig": {
      "description": "Config for a WatchAction with kind == 'webhook'.\n\nSends the item to `url` via `method` (POST | PUT | PATCH) with `headers`\n(sent verbatim, so they carry any auth token). `include_fields` whitelists\nwhich item fields are sent (empty = all). `delivery` /\n`digest_interval_seconds` (from the base) pick instant vs batched delivery.\n\nSecret-bearing: `url` path/query and every header VALUE are masked by\n`redacted_dump` and carried forward by `merge_preserving` when the\noperator leaves them masked on an edit.",
      "properties": {
        "delivery": {
          "$ref": "#/$defs/DeliveryCadence",
          "default": "instant"
        },
        "digest_interval_seconds": {
          "default": 0,
          "minimum": 0,
          "title": "Digest Interval Seconds",
          "type": "integer"
        },
        "headers": {
          "additionalProperties": {
            "type": "string"
          },
          "title": "Headers",
          "type": "object"
        },
        "include_fields": {
          "items": {
            "type": "string"
          },
          "title": "Include Fields",
          "type": "array"
        },
        "method": {
          "$ref": "#/$defs/WebhookMethod",
          "default": "POST"
        },
        "url": {
          "title": "Url",
          "type": "string"
        }
      },
      "required": [
        "url"
      ],
      "title": "WebhookConfig",
      "type": "object"
    },
    "WebhookMethod": {
      "description": "HTTP verb a webhook action delivers with. Body-bearing verbs only:\nGET (no body) is deferred (it would map item fields to query params, a\nseparate shape). PUT / PATCH are idempotent, which pairs with the\ndelivery dedup.",
      "enum": [
        "POST",
        "PUT",
        "PATCH"
      ],
      "title": "WebhookMethod",
      "type": "string"
    },
    "WebhookResult": {
      "description": "Result a webhook run writes to WatchActionRun.result: the HTTP status\nof the call that SUCCEEDED (a 2xx ; a non-2xx raises and the run is\nFAILED instead).",
      "properties": {
        "http_status": {
          "title": "Http Status",
          "type": "integer"
        }
      },
      "required": [
        "http_status"
      ],
      "title": "WebhookResult",
      "type": "object"
    },
    "WebhookRunWire": {
      "properties": {
        "action_id": {
          "title": "Action Id",
          "type": "string"
        },
        "completed_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Completed At"
        },
        "created_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Created At"
        },
        "error": {
          "default": "",
          "title": "Error",
          "type": "string"
        },
        "feed_item_id": {
          "title": "Feed Item Id",
          "type": "string"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "kind": {
          "const": "webhook",
          "default": "webhook",
          "title": "Kind",
          "type": "string"
        },
        "result": {
          "anyOf": [
            {
              "$ref": "#/$defs/WebhookResult"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "scheduled_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Scheduled At"
        },
        "started_at": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Started At"
        },
        "state": {
          "$ref": "#/$defs/WatchActionRunState"
        },
        "watch_id": {
          "title": "Watch Id",
          "type": "string"
        }
      },
      "required": [
        "id",
        "watch_id",
        "action_id",
        "feed_item_id",
        "state"
      ],
      "title": "WebhookRunWire",
      "type": "object"
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "OpenMagpie contract",
  "x-roots": [
    {
      "$ref": "#/$defs/FeedView"
    },
    {
      "$ref": "#/$defs/FeedMutationResponse"
    },
    {
      "$ref": "#/$defs/FeedListResponse"
    },
    {
      "$ref": "#/$defs/FeedItemWire"
    },
    {
      "$ref": "#/$defs/FeedItemListResponse"
    },
    {
      "$ref": "#/$defs/FeedInput"
    },
    {
      "$ref": "#/$defs/CuratedFeedConfig"
    },
    {
      "$ref": "#/$defs/SourceInput"
    },
    {
      "$ref": "#/$defs/SourceWire"
    },
    {
      "$ref": "#/$defs/SourceSetPayload"
    },
    {
      "$ref": "#/$defs/SourceSetResult"
    },
    {
      "$ref": "#/$defs/WatchView"
    },
    {
      "$ref": "#/$defs/WatchMutationResponse"
    },
    {
      "$ref": "#/$defs/WatchListResponse"
    },
    {
      "$ref": "#/$defs/WatchInput"
    },
    {
      "$ref": "#/$defs/WatchActionMutationResponse"
    },
    {
      "$ref": "#/$defs/WatchActionRunView"
    },
    {
      "$ref": "#/$defs/WatchActionRunListResponse"
    },
    {
      "$ref": "#/$defs/WatchActionDeliveryView"
    },
    {
      "$ref": "#/$defs/WatchActionDeliveryListResponse"
    },
    {
      "$ref": "#/$defs/SemanticFilterConfig"
    },
    {
      "$ref": "#/$defs/SemanticFilterResult"
    },
    {
      "$ref": "#/$defs/ExtractConfig"
    },
    {
      "$ref": "#/$defs/ExtractResult"
    },
    {
      "$ref": "#/$defs/LogConfig"
    },
    {
      "$ref": "#/$defs/LogResult"
    },
    {
      "$ref": "#/$defs/WebhookConfig"
    },
    {
      "$ref": "#/$defs/WebhookResult"
    },
    {
      "$ref": "#/$defs/RedditSubredditSourceSpec"
    },
    {
      "$ref": "#/$defs/RssSourceSpec"
    },
    {
      "$ref": "#/$defs/HackerNewsFeedSourceSpec"
    },
    {
      "$ref": "#/$defs/HackerNewsCommentSourceSpec"
    },
    {
      "$ref": "#/$defs/EngineStatus"
    },
    {
      "$ref": "#/$defs/EngineListResponse"
    },
    {
      "$ref": "#/$defs/TelemetryState"
    },
    {
      "$ref": "#/$defs/AuthUser"
    }
  ]
}
