// AUTO-GENERATED by build.rs from schemas/*.schema.json
// DO NOT EDIT — changes will be overwritten on next build.
// To modify types, edit the JSON Schema files in schemas/ and rebuild.
/// Error types.
pub mod error {
/// Error from a `TryFrom` or `FromStr` implementation.
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
///The business function the platform assigned to an agent's owner — the frozen 14-value vocabulary of the Agent Ownership PRD (D9: adding a value is already a contract change plus a client release, so a closed enum here couples nothing new). Closed enum, like ChurnLayer: it is the value type of a policy rule's conditions[].value (policy-bundle.schema.json), where the client compares by enum equality and a value it does not know fails that ONE rule at deserialization (dropped as unrecognized_field, the bundle stays active) — never the whole document. It is deliberately NOT the type of client_config.agent_context.function, which stays an open string carrying this list as x-known-values: client_config is deserialized as one typed object outside the per-rule tolerance, so an enum there would fail the whole bundle (fail-static fleet-wide) on a single unrecognised value. That buys tolerance for an unrecognised WORD only — a non-string function still fails the whole document, because client_config is typed either way. 'unknown' is an ordinary member — a rule listing it matches only agents whose function is 'unknown'; it is never a wildcard.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The business function the platform assigned to an agent's owner — the frozen 14-value vocabulary of the Agent Ownership PRD (D9: adding a value is already a contract change plus a client release, so a closed enum here couples nothing new). Closed enum, like ChurnLayer: it is the value type of a policy rule's conditions[].value (policy-bundle.schema.json), where the client compares by enum equality and a value it does not know fails that ONE rule at deserialization (dropped as unrecognized_field, the bundle stays active) — never the whole document. It is deliberately NOT the type of client_config.agent_context.function, which stays an open string carrying this list as x-known-values: client_config is deserialized as one typed object outside the per-rule tolerance, so an enum there would fail the whole bundle (fail-static fleet-wide) on a single unrecognised value. That buys tolerance for an unrecognised WORD only — a non-string function still fails the whole document, because client_config is typed either way. 'unknown' is an ordinary member — a rule listing it matches only agents whose function is 'unknown'; it is never a wildcard.",
/// "type": "string",
/// "enum": [
/// "engineering",
/// "product",
/// "data",
/// "security",
/// "it_ops",
/// "sales",
/// "marketing",
/// "finance",
/// "legal",
/// "hr",
/// "support",
/// "research",
/// "other",
/// "unknown"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum AgentFunction {
#[serde(rename = "engineering")]
Engineering,
#[serde(rename = "product")]
Product,
#[serde(rename = "data")]
Data,
#[serde(rename = "security")]
Security,
#[serde(rename = "it_ops")]
ItOps,
#[serde(rename = "sales")]
Sales,
#[serde(rename = "marketing")]
Marketing,
#[serde(rename = "finance")]
Finance,
#[serde(rename = "legal")]
Legal,
#[serde(rename = "hr")]
Hr,
#[serde(rename = "support")]
Support,
#[serde(rename = "research")]
Research,
#[serde(rename = "other")]
Other,
#[serde(rename = "unknown")]
Unknown,
}
impl ::std::fmt::Display for AgentFunction {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Engineering => f.write_str("engineering"),
Self::Product => f.write_str("product"),
Self::Data => f.write_str("data"),
Self::Security => f.write_str("security"),
Self::ItOps => f.write_str("it_ops"),
Self::Sales => f.write_str("sales"),
Self::Marketing => f.write_str("marketing"),
Self::Finance => f.write_str("finance"),
Self::Legal => f.write_str("legal"),
Self::Hr => f.write_str("hr"),
Self::Support => f.write_str("support"),
Self::Research => f.write_str("research"),
Self::Other => f.write_str("other"),
Self::Unknown => f.write_str("unknown"),
}
}
}
impl ::std::str::FromStr for AgentFunction {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"engineering" => Ok(Self::Engineering),
"product" => Ok(Self::Product),
"data" => Ok(Self::Data),
"security" => Ok(Self::Security),
"it_ops" => Ok(Self::ItOps),
"sales" => Ok(Self::Sales),
"marketing" => Ok(Self::Marketing),
"finance" => Ok(Self::Finance),
"legal" => Ok(Self::Legal),
"hr" => Ok(Self::Hr),
"support" => Ok(Self::Support),
"research" => Ok(Self::Research),
"other" => Ok(Self::Other),
"unknown" => Ok(Self::Unknown),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for AgentFunction {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for AgentFunction {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for AgentFunction {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Response from GET /api/v1/users/me for auth status validation (D-19) and PostHog identity stitching (telemetry Phase C). user_db_id, when present, is used as the persistent distinct_id and triggers a one-time $create_alias merging prior agent_id events into the platform person. Mirrors the `id` field on the platform side; both carry the stable better-auth user TEXT primary key.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Response from GET /api/v1/users/me for auth status validation (D-19) and PostHog identity stitching (telemetry Phase C). user_db_id, when present, is used as the persistent distinct_id and triggers a one-time $create_alias merging prior agent_id events into the platform person. Mirrors the `id` field on the platform side; both carry the stable better-auth user TEXT primary key.",
/// "examples": [
/// {
/// "email": "alice@example.com",
/// "id": "usr_019d8af1-f8da-73b3-92eb-79a99e59b10b",
/// "organization_id": "a1b2c3d4-e5f6-4000-a000-000000000001",
/// "organization_name": "Acme Corp",
/// "user_db_id": "usr_019d8af1-f8da-73b3-92eb-79a99e59b10b"
/// }
/// ],
/// "type": "object",
/// "properties": {
/// "email": {
/// "description": "Email of the authenticated user.",
/// "type": "string"
/// },
/// "id": {
/// "description": "Stable database identifier for the authenticated user (better-auth user.id). Mirror of user_db_id — either field may be read; prefer user_db_id for telemetry alias semantics.",
/// "type": "string"
/// },
/// "organization_id": {
/// "description": "Organization id for the user's active organization.",
/// "type": "string"
/// },
/// "organization_name": {
/// "description": "Human-readable display name of the user's active organization. Surfaced by the client on re-runs of `openlatch init` so the user can confirm which org their cached credential belongs to. Optional — when absent, the client displays `Authenticated` without the parenthetical org suffix.",
/// "type": "string"
/// },
/// "user_db_id": {
/// "description": "Stable database identifier for the authenticated user. Used as the PostHog distinct_id post-auth and as the alias target for $create_alias. Optional in this client schema for backwards compatibility — older platforms may not return it; client falls back to agent_id and skips the alias when absent.",
/// "type": "string"
/// }
/// },
/// "additionalProperties": true,
/// "x-postgresql-skip": true
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct AuthMeResponse {
///Email of the authenticated user.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub email: ::std::option::Option<::std::string::String>,
///Stable database identifier for the authenticated user (better-auth user.id). Mirror of user_db_id — either field may be read; prefer user_db_id for telemetry alias semantics.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub id: ::std::option::Option<::std::string::String>,
///Organization id for the user's active organization.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub organization_id: ::std::option::Option<::std::string::String>,
///Human-readable display name of the user's active organization. Surfaced by the client on re-runs of `openlatch init` so the user can confirm which org their cached credential belongs to. Optional — when absent, the client displays `Authenticated` without the parenthetical org suffix.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub organization_name: ::std::option::Option<::std::string::String>,
///Stable database identifier for the authenticated user. Used as the PostHog distinct_id post-auth and as the alias target for $create_alias. Optional in this client schema for backwards compatibility — older platforms may not return it; client falls back to agent_id and skips the alias when absent.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub user_db_id: ::std::option::Option<::std::string::String>,
}
impl ::std::default::Default for AuthMeResponse {
fn default() -> Self {
Self {
email: Default::default(),
id: Default::default(),
organization_id: Default::default(),
organization_name: Default::default(),
user_db_id: Default::default(),
}
}
}
///Which prompt-cache layer a kind=request policy rule targets — the frozen churn_layer enum owned by the Model Boundary Enforcement Layer. 'tools' invalidates everything downstream on a change; 'messages' the least. Closed enum, like Verdict: the values are a contract between the platform's rule author and the client's boundary listener, so the client branches exhaustively on them. A hand-written boundary-internal twin exists at src/boundary/churn.rs; this $def is the wire-side type and the single owner of the values.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Which prompt-cache layer a kind=request policy rule targets — the frozen churn_layer enum owned by the Model Boundary Enforcement Layer. 'tools' invalidates everything downstream on a change; 'messages' the least. Closed enum, like Verdict: the values are a contract between the platform's rule author and the client's boundary listener, so the client branches exhaustively on them. A hand-written boundary-internal twin exists at src/boundary/churn.rs; this $def is the wire-side type and the single owner of the values.",
/// "type": "string",
/// "enum": [
/// "tools",
/// "system",
/// "messages"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum ChurnLayer {
#[serde(rename = "tools")]
Tools,
#[serde(rename = "system")]
System,
#[serde(rename = "messages")]
Messages,
}
impl ::std::fmt::Display for ChurnLayer {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Tools => f.write_str("tools"),
Self::System => f.write_str("system"),
Self::Messages => f.write_str("messages"),
}
}
}
impl ::std::str::FromStr for ChurnLayer {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"tools" => Ok(Self::Tools),
"system" => Ok(Self::System),
"messages" => Ok(Self::Messages),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ChurnLayer {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ChurnLayer {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ChurnLayer {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///HTTP request body for POST /api/v1/events/ingest sent by openlatch-client. CloudEvents v1.0.2 batch mode — a bare JSON array of EventEnvelope objects, sent with Content-Type: application/cloudevents-batch+json. Client-wide metadata (schema_version, agent_id) is carried on each CloudEvent via extension attributes rather than a wrapper object.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "HTTP request body for POST /api/v1/events/ingest sent by openlatch-client. CloudEvents v1.0.2 batch mode — a bare JSON array of EventEnvelope objects, sent with Content-Type: application/cloudevents-batch+json. Client-wide metadata (schema_version, agent_id) is carried on each CloudEvent via extension attributes rather than a wrapper object.",
/// "examples": [
/// [
/// {
/// "arch": "x86_64",
/// "clientversion": "0.2.0",
/// "data": {
/// "tool_input": {
/// "command": "ls -la"
/// },
/// "tool_name": "Bash"
/// },
/// "datacontenttype": "application/json",
/// "id": "evt_019d8af1-f8da-73b3-92eb-79a99e59b10b",
/// "os": "linux",
/// "source": "claude-code",
/// "specversion": "1.0",
/// "subject": "sess_abc123",
/// "time": "2026-04-16T12:00:00Z",
/// "type": "pre_tool_use"
/// }
/// ]
/// ],
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/EventEnvelope"
/// },
/// "maxItems": 100,
/// "minItems": 1,
/// "x-postgresql-skip": true
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(transparent)]
pub struct CloudIngestionRequest(pub ::std::vec::Vec<EventEnvelope>);
impl ::std::ops::Deref for CloudIngestionRequest {
type Target = ::std::vec::Vec<EventEnvelope>;
fn deref(&self) -> &::std::vec::Vec<EventEnvelope> {
&self.0
}
}
impl ::std::convert::From<CloudIngestionRequest> for ::std::vec::Vec<EventEnvelope> {
fn from(value: CloudIngestionRequest) -> Self {
value.0
}
}
impl ::std::convert::From<::std::vec::Vec<EventEnvelope>> for CloudIngestionRequest {
fn from(value: ::std::vec::Vec<EventEnvelope>) -> Self {
Self(value)
}
}
///HTTP response body for POST /api/v1/events/ingest returned to openlatch-client. The client uses status to determine whether to retry, and event_id to correlate verdicts.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "HTTP response body for POST /api/v1/events/ingest returned to openlatch-client. The client uses status to determine whether to retry, and event_id to correlate verdicts.",
/// "examples": [
/// {
/// "event_id": "019d8af1-f8da-73b3-92eb-79a99e59b10b",
/// "status": "accepted"
/// },
/// {
/// "error": "Envelope failed schema validation: missing required attribute 'specversion'",
/// "status": "rejected"
/// }
/// ],
/// "type": "object",
/// "required": [
/// "status"
/// ],
/// "properties": {
/// "error": {
/// "description": "Human-readable error description. Present when status is rejected.",
/// "type": "string"
/// },
/// "event_id": {
/// "description": "Server-assigned UUIDv7 event_id for the stored event. Present when status is accepted.",
/// "type": "string"
/// },
/// "status": {
/// "description": "Ingestion outcome — accepted means persisted (or duplicate), rejected means permanently invalid.",
/// "type": "string",
/// "enum": [
/// "accepted",
/// "rejected"
/// ]
/// }
/// },
/// "additionalProperties": false,
/// "x-postgresql-skip": true
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct CloudIngestionResponse {
///Human-readable error description. Present when status is rejected.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub error: ::std::option::Option<::std::string::String>,
///Server-assigned UUIDv7 event_id for the stored event. Present when status is accepted.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub event_id: ::std::option::Option<::std::string::String>,
///Ingestion outcome — accepted means persisted (or duplicate), rejected means permanently invalid.
pub status: CloudIngestionResponseStatus,
}
///Ingestion outcome — accepted means persisted (or duplicate), rejected means permanently invalid.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Ingestion outcome — accepted means persisted (or duplicate), rejected means permanently invalid.",
/// "type": "string",
/// "enum": [
/// "accepted",
/// "rejected"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum CloudIngestionResponseStatus {
#[serde(rename = "accepted")]
Accepted,
#[serde(rename = "rejected")]
Rejected,
}
impl ::std::fmt::Display for CloudIngestionResponseStatus {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Accepted => f.write_str("accepted"),
Self::Rejected => f.write_str("rejected"),
}
}
}
impl ::std::str::FromStr for CloudIngestionResponseStatus {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"accepted" => Ok(Self::Accepted),
"rejected" => Ok(Self::Rejected),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for CloudIngestionResponseStatus {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for CloudIngestionResponseStatus {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for CloudIngestionResponseStatus {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///CloudEvents v1.0.2 structured-mode envelope for agent hook events. Content-Type is application/cloudevents+json (single) or application/cloudevents-batch+json (batch). The 'data' field contains the raw agent payload untouched; all OpenLatch metadata lives in CloudEvents extension attributes. Extension attribute names MUST match ^[a-z0-9]+$ per the CloudEvents spec. verdict and latency_ms are NOT on the wire — they are produced by the daemon after processing and attached to stored events as the olverdict and ollatencyms extension attributes.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "CloudEvents v1.0.2 structured-mode envelope for agent hook events. Content-Type is application/cloudevents+json (single) or application/cloudevents-batch+json (batch). The 'data' field contains the raw agent payload untouched; all OpenLatch metadata lives in CloudEvents extension attributes. Extension attribute names MUST match ^[a-z0-9]+$ per the CloudEvents spec. verdict and latency_ms are NOT on the wire — they are produced by the daemon after processing and attached to stored events as the olverdict and ollatencyms extension attributes.",
/// "examples": [
/// {
/// "agentversion": "1.2.0",
/// "arch": "x86_64",
/// "clientversion": "0.2.0",
/// "data": {
/// "tool_input": {
/// "command": "ls -la"
/// },
/// "tool_name": "Bash"
/// },
/// "datacontenttype": "application/json",
/// "id": "evt_019d8af1-f8da-73b3-92eb-79a99e59b10b",
/// "localipv4": "192.168.1.42",
/// "os": "linux",
/// "publicipv4": "203.0.113.7",
/// "source": "claude-code",
/// "specversion": "1.0",
/// "subject": "sess_abc123",
/// "time": "2026-04-16T12:00:00Z",
/// "type": "pre_tool_use"
/// }
/// ],
/// "type": "object",
/// "required": [
/// "id",
/// "source",
/// "specversion",
/// "time",
/// "type"
/// ],
/// "properties": {
/// "agentid": {
/// "description": "OpenLatch extension. Stamped by the daemon on outbound events — identifies the AI agent install (one openlatch-client installation) that emitted this event (agt_<uuid>).",
/// "type": "string"
/// },
/// "agentversion": {
/// "description": "OpenLatch extension (was 'agent_version'). Agent software version, if reported by the hook.",
/// "type": "string"
/// },
/// "arch": {
/// "description": "OpenLatch extension. CPU architecture ('x86_64', 'aarch64').",
/// "type": "string"
/// },
/// "clientversion": {
/// "description": "OpenLatch extension (was 'client_version'). Semver of the openlatch-client that emitted the envelope.",
/// "type": "string"
/// },
/// "data": {
/// "description": "Raw agent payload, forwarded verbatim. Shape is agent-specific (e.g. Claude Code PreToolUse emits { tool_name, tool_input, … }; Cursor beforeShellExecution emits { command, … }). OpenLatch does NOT normalise this field. When `type` is `ai.openlatch.config.*` and `configkind` is `mcp`, the payload may additionally include an optional `scope` key — one of `enterprise` / `personal` / `project` / `local` — declaring the precedence tier of the source path; the cloud routing engine reads it to resolve same-named MCP servers across multiple filesystem locations."
/// },
/// "datacontenttype": {
/// "description": "Media type of the 'data' field. CloudEvents core optional attribute. Fixed to application/json for all OpenLatch events.",
/// "type": "string",
/// "const": "application/json"
/// },
/// "gitemail": {
/// "description": "OpenLatch extension. git config user.email for the session's working directory. Absent outside a repository.",
/// "type": "string"
/// },
/// "id": {
/// "description": "Unique event identifier — UUIDv7 with 'evt_' prefix. CloudEvents core attribute.",
/// "type": "string"
/// },
/// "localipv4": {
/// "description": "OpenLatch extension (was 'local_ipv4'). Machine's local IPv4 address. Detected once at daemon startup, cached for the process lifetime. Omitted when no non-loopback interface is available.",
/// "type": "string",
/// "format": "ipv4"
/// },
/// "localipv6": {
/// "description": "OpenLatch extension (was 'local_ipv6'). Machine's local IPv6 address.",
/// "type": "string",
/// "format": "ipv6"
/// },
/// "os": {
/// "description": "OpenLatch extension. Operating system ('linux', 'macos', 'windows'). CloudEvents extension — lowercase alphanumeric attribute name.",
/// "type": "string"
/// },
/// "osuser": {
/// "description": "OpenLatch extension. Logged-in OS user on the host. Absent when capture is disabled or no interactive user exists.",
/// "type": "string"
/// },
/// "provideracct": {
/// "description": "OpenLatch extension. AI-provider account identifier, or the literal shared-key when the credential carries no human. Never a token or key.",
/// "type": "string"
/// },
/// "publicipv4": {
/// "description": "OpenLatch extension (was 'public_ipv4'). Machine's public IPv4 address.",
/// "type": "string",
/// "format": "ipv4"
/// },
/// "publicipv6": {
/// "description": "OpenLatch extension (was 'public_ipv6'). Machine's public IPv6 address.",
/// "type": "string",
/// "format": "ipv6"
/// },
/// "source": {
/// "description": "Agent platform identifier (was 'agent_platform'). CloudEvents core attribute. Bare string per OpenLatch convention; any string is valid. See x-known-values in enums.schema.json#/$defs/AgentType for the canonical set.",
/// "$ref": "#/$defs/AgentType"
/// },
/// "specversion": {
/// "description": "CloudEvents spec version. MUST be '1.0' for CloudEvents v1.0.2.",
/// "type": "string",
/// "const": "1.0"
/// },
/// "subject": {
/// "description": "CloudEvents 'subject' attribute. OpenLatch uses this for the agent session identifier (was 'session_id'). Consumers can group events by subject for per-session analytics.",
/// "type": "string"
/// },
/// "time": {
/// "description": "Event creation timestamp (was 'timestamp'). RFC 3339 UTC with Z suffix. CloudEvents core attribute.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "type": {
/// "description": "Hook event lifecycle name (was 'event_type'). CloudEvents core attribute. Any string is valid. See x-known-values in enums.schema.json#/$defs/HookEventType for the canonical set.",
/// "$ref": "#/$defs/HookEventType"
/// }
/// },
/// "additionalProperties": true,
/// "x-postgresql-skip": true
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct EventEnvelope {
///OpenLatch extension. Stamped by the daemon on outbound events — identifies the AI agent install (one openlatch-client installation) that emitted this event (agt_<uuid>).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub agentid: ::std::option::Option<::std::string::String>,
///OpenLatch extension (was 'agent_version'). Agent software version, if reported by the hook.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub agentversion: ::std::option::Option<::std::string::String>,
///OpenLatch extension. CPU architecture ('x86_64', 'aarch64').
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub arch: ::std::option::Option<::std::string::String>,
///OpenLatch extension (was 'client_version'). Semver of the openlatch-client that emitted the envelope.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub clientversion: ::std::option::Option<::std::string::String>,
///Raw agent payload, forwarded verbatim. Shape is agent-specific (e.g. Claude Code PreToolUse emits { tool_name, tool_input, … }; Cursor beforeShellExecution emits { command, … }). OpenLatch does NOT normalise this field. When `type` is `ai.openlatch.config.*` and `configkind` is `mcp`, the payload may additionally include an optional `scope` key — one of `enterprise` / `personal` / `project` / `local` — declaring the precedence tier of the source path; the cloud routing engine reads it to resolve same-named MCP servers across multiple filesystem locations.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub data: ::std::option::Option<::serde_json::Value>,
///Media type of the 'data' field. CloudEvents core optional attribute. Fixed to application/json for all OpenLatch events.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub datacontenttype: ::std::option::Option<::std::string::String>,
///OpenLatch extension. git config user.email for the session's working directory. Absent outside a repository.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub gitemail: ::std::option::Option<::std::string::String>,
///Unique event identifier — UUIDv7 with 'evt_' prefix. CloudEvents core attribute.
pub id: ::std::string::String,
///OpenLatch extension (was 'local_ipv4'). Machine's local IPv4 address. Detected once at daemon startup, cached for the process lifetime. Omitted when no non-loopback interface is available.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub localipv4: ::std::option::Option<::std::net::Ipv4Addr>,
///OpenLatch extension (was 'local_ipv6'). Machine's local IPv6 address.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub localipv6: ::std::option::Option<::std::net::Ipv6Addr>,
///OpenLatch extension. Operating system ('linux', 'macos', 'windows'). CloudEvents extension — lowercase alphanumeric attribute name.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub os: ::std::option::Option<::std::string::String>,
///OpenLatch extension. Logged-in OS user on the host. Absent when capture is disabled or no interactive user exists.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub osuser: ::std::option::Option<::std::string::String>,
///OpenLatch extension. AI-provider account identifier, or the literal shared-key when the credential carries no human. Never a token or key.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub provideracct: ::std::option::Option<::std::string::String>,
///OpenLatch extension (was 'public_ipv4'). Machine's public IPv4 address.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub publicipv4: ::std::option::Option<::std::net::Ipv4Addr>,
///OpenLatch extension (was 'public_ipv6'). Machine's public IPv6 address.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub publicipv6: ::std::option::Option<::std::net::Ipv6Addr>,
///Agent platform identifier (was 'agent_platform'). CloudEvents core attribute. Bare string per OpenLatch convention; any string is valid. See x-known-values in enums.schema.json#/$defs/AgentType for the canonical set.
pub source: crate::core::envelope::known_types::AgentType,
///CloudEvents spec version. MUST be '1.0' for CloudEvents v1.0.2.
pub specversion: ::std::string::String,
///CloudEvents 'subject' attribute. OpenLatch uses this for the agent session identifier (was 'session_id'). Consumers can group events by subject for per-session analytics.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub subject: ::std::option::Option<::std::string::String>,
///Event creation timestamp (was 'timestamp'). RFC 3339 UTC with Z suffix. CloudEvents core attribute.
pub time: ::chrono::DateTime<::chrono::offset::Utc>,
///Hook event lifecycle name (was 'event_type'). CloudEvents core attribute. Any string is valid. See x-known-values in enums.schema.json#/$defs/HookEventType for the canonical set.
#[serde(rename = "type")]
pub type_: crate::core::envelope::known_types::HookEventType,
}
///The policy bundle served by GET /api/v1/policy/bundle and held resident by the daemon. Canonicalized by the platform with RFC 8785 (JCS); the ETag is 'sha256:<hex>' over exactly those bytes and the client verifies the digest against the raw response body it received — it MUST NOT re-serialize before hashing. The bundle carries strings, booleans, integers and null only: no floats anywhere, which eliminates the RFC 8785 ES6 Number::toString divergence between Python and Rust by construction. A resident bundle keeps enforcing offline forever; there is no expiry.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The policy bundle served by GET /api/v1/policy/bundle and held resident by the daemon. Canonicalized by the platform with RFC 8785 (JCS); the ETag is 'sha256:<hex>' over exactly those bytes and the client verifies the digest against the raw response body it received — it MUST NOT re-serialize before hashing. The bundle carries strings, booleans, integers and null only: no floats anywhere, which eliminates the RFC 8785 ES6 Number::toString divergence between Python and Rust by construction. A resident bundle keeps enforcing offline forever; there is no expiry.",
/// "examples": [
/// {
/// "built_at": "2026-07-21T09:00:00Z",
/// "enforcement_enabled": true,
/// "organization_id": "0192f8a1-4c3b-7e2a-9f10-5d8c3b1a7e42",
/// "revision": 42,
/// "rules": [
/// {
/// "action": "deny",
/// "kind": "command",
/// "match_pattern": "*rm -rf /*",
/// "mode": "enforce",
/// "reason": "Recursive delete of a root path",
/// "rule_id": "OL-CMD-001",
/// "severity": "critical"
/// }
/// ],
/// "schema_version": 1,
/// "signature": null
/// },
/// {
/// "built_at": "2026-07-20T11:30:00Z",
/// "enforcement_enabled": true,
/// "organization_id": "0192f8a1-4c3b-7e2a-9f10-5d8c3b1a7e42",
/// "revision": 1,
/// "rules": [],
/// "schema_version": 1,
/// "signature": null
/// },
/// {
/// "built_at": "2026-07-24T14:05:00Z",
/// "enforcement_enabled": true,
/// "organization_id": "0192f8a1-4c3b-7e2a-9f10-5d8c3b1a7e42",
/// "revision": 77,
/// "rules": [
/// {
/// "action": "deny",
/// "kind": "command",
/// "match_pattern": "*rm -rf /*",
/// "mode": "enforce",
/// "reason": "Recursive delete of a root path",
/// "rule_id": "OL-CMD-001",
/// "severity": "critical"
/// },
/// {
/// "action": "prefix_reorder",
/// "kind": "request",
/// "mode": "observe",
/// "reason": "Stabilize the cache prefix so the tool block stops churning",
/// "rule_id": "OL-REQ-001",
/// "rule_version": 3,
/// "select": {
/// "exclude_layers": [
/// "tools"
/// ],
/// "model_in": [
/// "claude-opus-5"
/// ]
/// },
/// "severity": "low"
/// },
/// {
/// "action": "prefix_reorder",
/// "kind": "request",
/// "mode": "observe",
/// "params": {
/// "mechanism": "insert_breakpoints"
/// },
/// "reason": "Repeated context is served at full input price (only 22% cache-read); caching the stable prefix recovers most of it",
/// "rule_id": "OL-ECO-CACHE-3f9c1a2b",
/// "rule_version": 1,
/// "severity": "medium"
/// },
/// {
/// "action": "history_trim",
/// "kind": "request",
/// "mode": "observe",
/// "params": {
/// "keep_messages": 20
/// },
/// "reason": "Long sessions carry more history than the task needs",
/// "rule_id": "OL-REQ-002",
/// "rule_version": 1,
/// "select": {
/// "min_messages": 40
/// },
/// "severity": "low"
/// }
/// ],
/// "schema_version": 1,
/// "signature": null
/// },
/// {
/// "built_at": "2026-08-16T08:00:00Z",
/// "enforcement_enabled": true,
/// "organization_id": "0192f8a1-4c3b-7e2a-9f10-5d8c3b1a7e42",
/// "revision": 79,
/// "rules": [
/// {
/// "action": "deny",
/// "kind": "command",
/// "match_pattern": "*rm -rf /*",
/// "mode": "enforce",
/// "reason": "Recursive delete of a root path",
/// "rule_id": "OL-CMD-001",
/// "severity": "critical"
/// },
/// {
/// "action": "deny",
/// "conditions": [
/// {
/// "field": "agent.function",
/// "op": "in",
/// "value": [
/// "marketing",
/// "sales"
/// ]
/// }
/// ],
/// "kind": "command",
/// "match_pattern": "*psql*",
/// "mode": "enforce",
/// "reason": "Direct database access is not part of a marketing or sales workflow",
/// "rule_id": "OL-CMD-002",
/// "severity": "high"
/// }
/// ],
/// "schema_version": 1,
/// "signature": null
/// },
/// {
/// "built_at": "2026-08-16T08:00:00Z",
/// "client_config": {
/// "agent_context": {
/// "function": "unknown"
/// },
/// "capture_identity_signals": true
/// },
/// "enforcement_enabled": true,
/// "organization_id": "0192f8a1-4c3b-7e2a-9f10-5d8c3b1a7e42",
/// "revision": 80,
/// "rules": [
/// {
/// "action": "deny",
/// "conditions": [
/// {
/// "field": "agent.function",
/// "op": "in",
/// "value": [
/// "unknown"
/// ]
/// }
/// ],
/// "kind": "command",
/// "match_pattern": "*curl*",
/// "mode": "observe",
/// "reason": "Outbound transfers from an agent nobody has claimed are reviewed before they are allowed",
/// "rule_id": "OL-CMD-003",
/// "severity": "medium"
/// }
/// ],
/// "schema_version": 1,
/// "signature": null
/// }
/// ],
/// "type": "object",
/// "required": [
/// "built_at",
/// "enforcement_enabled",
/// "organization_id",
/// "revision",
/// "rules",
/// "schema_version",
/// "signature"
/// ],
/// "properties": {
/// "built_at": {
/// "description": "RFC 3339 UTC timestamp with a Z suffix (microsecond precision allowed) recording when the rule content last actually changed — not when the bundle was served. Drives olpolicybundleage (now minus built_at, clamped at 0), which measures policy freshness. It does NOT drive the staleness warning, which is measured from the last successful fetch. Deliberately a plain string rather than format: date-time so the generated Rust type stays a String; the poller parses it once into a SystemTime.",
/// "type": "string"
/// },
/// "client_config": {
/// "description": "Client-bound configuration delivered on the bundle — the only client-bound configuration channel. Optional; a client that never receives it captures nothing (fail closed). The platform MUST NOT emit this field to a fleet that still runs clients predating it: the bundle root is additionalProperties:false, so such a client rejects the whole document (OL-1212) and fails static on its previous rules.",
/// "type": "object",
/// "properties": {
/// "agent_context": {
/// "description": "This install's own agent context, composed by the platform at serve time from the X-OpenLatch-Agent-Id header the poller sends — one object about THIS agent, never a fleet roster. Optional: a client that never receives it holds no context, and every rule carrying conditions matches nothing (absent context is not 'unknown'). Tolerant like its parent, so a newer platform can add keys without failing older clients.",
/// "type": "object",
/// "properties": {
/// "function": {
/// "description": "The business function assigned to this agent's owner. Deliberately an OPEN string carrying the AgentFunction vocabulary as x-known-values rather than a $ref to that enum — the same reasoning as params.mechanism: client_config is deserialized as one typed object outside the per-rule tolerance, so an enum here would fail the WHOLE bundle (fleet-wide fail-static) on a single out-of-vocabulary value. The client parses it into AgentFunction at load; a STRING value it cannot parse reads as no context, so scoped rules match nothing rather than the bundle being rejected. The tolerance is over the vocabulary, not the shape: a non-string value here (or a non-object agent_context) is a malformed client_config and does fail the whole document, exactly as a malformed capture_identity_signals does. 'unknown' is a real platform-assigned value, distinct from the field being absent.",
/// "type": "string",
/// "x-known-values": [
/// "engineering",
/// "product",
/// "data",
/// "security",
/// "it_ops",
/// "sales",
/// "marketing",
/// "finance",
/// "legal",
/// "hr",
/// "support",
/// "research",
/// "other",
/// "unknown"
/// ]
/// }
/// },
/// "additionalProperties": true
/// },
/// "capture_identity_signals": {
/// "description": "The client's copy of the org setting identity.capture_signals. Identity signals are captured and stamped only when this is explicitly true.",
/// "type": "boolean"
/// }
/// },
/// "additionalProperties": true
/// },
/// "enforcement_enabled": {
/// "description": "The organization-wide kill switch. When false, every rule behaves as observe regardless of its own mode — matches produce a shadow verdict and nothing is blocked. Stored outside the built artifact on the platform so it survives every rebuild.",
/// "type": "boolean"
/// },
/// "organization_id": {
/// "description": "Canonical UUID form of the owning organization, matching the platform's organization_id column — not the API-key prefix. The client learns its own org from GET /api/v1/users/me, caches it in bundle.meta.json, and MUST reject a bundle whose value differs (OL-1211). Never evaluate another org's rules.",
/// "type": "string"
/// },
/// "revision": {
/// "description": "Monotonic per organization, incrementing only on real content change — a rebuild over unchanged rules is a no-op that leaves this untouched. Serialized as a JSON number and treated as i64 in Rust. Recorded on every verdict as olpolicybundlerev: the replay key.",
/// "type": "integer"
/// },
/// "rules": {
/// "description": "The active rule set, sorted by rule_id so the serialization is deterministic. May be empty, which means 'allow everything' and is distinguishable in telemetry from 'no bundle' because olpolicybundlerev is still stamped. Rules disabled by the author are omitted at build time — every rule present here is active.",
/// "type": "array",
/// "items": {
/// "title": "PolicyRule",
/// "description": "One authored rule, on either plane. A kind=command rule is evaluated against the normalized command string extracted from a pre_tool_use envelope. A kind=request rule targets an outbound model request at the boundary and is held, not evaluated, in v1. The allOf below is the per-kind/per-action gate: the platform enforces it at authoring time (422) and the client validates every rule against it individually at bundle load, skipping the ones that fail — one bad rule must never cost the fleet its denies.",
/// "type": "object",
/// "required": [
/// "action",
/// "kind",
/// "mode",
/// "reason",
/// "rule_id",
/// "severity"
/// ],
/// "properties": {
/// "action": {
/// "description": "What a match does. Authorable actions are 'deny' (the only one on kind=command) and 'prefix_reorder', 'history_trim', 'prompt_edit' (kind=request). Which action is legal for which kind is owned by the gate below, not by this list. An open string for the same forward-compatibility reason as kind.",
/// "type": "string"
/// },
/// "conditions": {
/// "description": "Which agents the rule applies to, decided locally by the client against the client_config.agent_context THIS install received — the network is not on the evaluation path. Absent (or empty) means unconditional: the rule applies on every agent, exactly as before this field existed. When present, EVERY condition must hold (AND semantics), and a rule whose context never arrived matches nothing — absent context is not 'unknown'. v1 is the command plane only: forbidden on kind=request by the gate below, and the only vocabulary is field 'agent.function' with op 'in'. The field/op enums are the general shape, so widening either later is additive; a client that predates a widening drops that one rule at load (unrecognized_field) and keeps the bundle. Values are the closed AgentFunction enum, so a value the client does not know fails that one rule at deserialization, never the whole document. 'unknown' is an ordinary value, not a wildcard.",
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "field",
/// "op",
/// "value"
/// ],
/// "properties": {
/// "field": {
/// "description": "The agent-context attribute the condition reads. v1 knows exactly one: 'agent.function', read from client_config.agent_context.function.",
/// "type": "string",
/// "enum": [
/// "agent.function"
/// ]
/// },
/// "op": {
/// "description": "The comparison. v1 knows exactly one: 'in' — the attribute's value is a member of `value`. Several conditions on the same field AND together, so two 'in' sets intersect.",
/// "type": "string",
/// "enum": [
/// "in"
/// ]
/// },
/// "value": {
/// "description": "The set the attribute must belong to. At least one member — an empty set would match nothing while looking like a scoped rule, and the platform rejects it at authoring (422). Compared by enum equality on the client.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/AgentFunction"
/// },
/// "minItems": 1
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "kind": {
/// "description": "Which plane the rule acts on. Two kinds are authorable: 'command' (a shell command intercepted at the pre_tool_use hook) and 'request' (an outbound model request intercepted at the boundary). Deliberately an open string rather than a closed enum: the client MUST skip a rule whose kind it does not recognize and keep the rest of the bundle active, so that a v1 client tolerates a v1.1 bundle. A closed enum would fail deserialization of the whole document instead.",
/// "type": "string"
/// },
/// "match_pattern": {
/// "description": "Required for kind=command and forbidden for kind=request — see the gate below; it is optional in the base only so that a request rule does not fail deserialization on a client that predates the gate. Fully-anchored, case-sensitive glob over the entire normalized command string. Exactly two metacharacters: '*' (any sequence including empty, crossing '/' and every other character — this is not path-aware globbing) and '?' (exactly one character). No character classes, no escapes, no alternation, no regex. Anchoring surprises authors: 'rm -rf /*' does not match 'sudo rm -rf /tmp'; write '*rm -rf*' to catch a command anywhere in the string. The platform normalizes and validates this at authoring time and rejects empty, over-1024-character, control-character-bearing, and bare-'*' patterns with 422.",
/// "type": "string"
/// },
/// "mode": {
/// "description": "Staged rollout control. 'observe' records a shadow verdict and allows the action; 'enforce' denies it. Most restrictive wins across matching rules. Overridden to observe for every rule when enforcement_enabled is false.",
/// "type": "string",
/// "enum": [
/// "observe",
/// "enforce"
/// ]
/// },
/// "params": {
/// "description": "How a kind=request rule transforms the request. Forbidden on kind=command. Exactly which keys are required and which are rejected is per-action, in the gate below.",
/// "type": "object",
/// "properties": {
/// "keep_messages": {
/// "description": "How many of the most recent messages history_trim retains. Required by history_trim, rejected everywhere else.",
/// "type": "integer"
/// },
/// "marker": {
/// "description": "The literal marker prompt_edit rewrites around. Deliberately unconstrained here: a length or pattern keyword would make the generated Rust type a constrained newtype whose deserializer fails the WHOLE bundle on one over-long value. Length is bounded platform-side at authoring.",
/// "type": "string"
/// },
/// "max_system_tokens": {
/// "description": "Token ceiling prompt_edit trims the system block to. Alternative to marker — exactly one of the two is required.",
/// "type": "integer"
/// },
/// "mechanism": {
/// "description": "Which caching intervention a prefix_reorder rule means. The detector that authors these rules fires on two different causes needing two different interventions, and the action alone cannot tell them apart: 'insert_breakpoints' — nothing is being cached at all, so cache_control breakpoints must be INJECTED; 'reorder_blocks' — breakpoints exist but a volatile block sits early in the prefix and must be MOVED after the stable ones. Read by prefix_reorder only. Deliberately an OPEN string rather than an enum, for the same reason as kind and action: an out-of-vocabulary value must fail one rule, not deserialization of the whole bundle. An enum here would be a known field with an unknown value, which the unknown-FIELD tolerance does not cover. Absent means the rule names no mechanism — a client that cannot infer one skips it rather than guessing.",
/// "examples": [
/// "insert_breakpoints",
/// "reorder_blocks"
/// ],
/// "type": "string"
/// }
/// },
/// "additionalProperties": false
/// },
/// "reason": {
/// "description": "Plain-language explanation shown to the developer verbatim when the rule blocks an action. A deny the developer cannot understand is a broken feature.",
/// "type": "string"
/// },
/// "rule_id": {
/// "description": "Stable public identifier, e.g. 'OL-CMD-001'. Unique within the organization. Reported on the verdict as olpolicyruleid, and the tiebreak key: the lexicographically first rule within the deciding set is the one reported.",
/// "type": "string"
/// },
/// "rule_version": {
/// "description": "Required for kind=request, absent on kind=command — the middle term of the D-04 replay tuple, so a would-have report can be tied to the exact rule text that produced it. Platform-managed: set on create and incremented on every edit, never authored by hand.",
/// "type": "integer"
/// },
/// "select": {
/// "description": "Which requests a kind=request rule applies to; an absent select means every request the boundary sees. Forbidden on kind=command. Each key is narrowed further per action by the gate below — a key that an action does not read is rejected rather than silently ignored.",
/// "type": "object",
/// "properties": {
/// "exclude_layers": {
/// "description": "Cache layers this rule must leave untouched. Read by prefix_reorder only.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/ChurnLayer"
/// }
/// },
/// "min_messages": {
/// "description": "Apply only once the in-context history has at least this many messages. Read by history_trim only.",
/// "type": "integer"
/// },
/// "model_in": {
/// "description": "Apply only to requests whose model identifier is in this list. Matched verbatim against the model string on the wire; no globbing.",
/// "type": "array",
/// "items": {
/// "type": "string"
/// }
/// }
/// },
/// "additionalProperties": false
/// },
/// "severity": {
/// "description": "Author-assigned severity, surfaced on the verdict returned to the agent hook.",
/// "type": "string",
/// "enum": [
/// "low",
/// "medium",
/// "high",
/// "critical"
/// ]
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "schema_version": {
/// "description": "Bundle format version. Always 1 in v1. The client MUST refuse a bundle whose schema_version it does not know and keep the previous bundle (OL-1212). Forward compatibility is the platform's responsibility, not the client's.",
/// "type": "integer"
/// },
/// "signature": {
/// "description": "Reserved for a detached Ed25519 signature over the canonical bytes. Required but always null in v1, and the client MUST accept null. A client that cannot verify a non-null signature MUST reject the bundle rather than ignore the field, so that enabling signing later cannot be silently downgraded by an old client.",
/// "default": null,
/// "type": [
/// "string",
/// "null"
/// ]
/// }
/// },
/// "additionalProperties": false,
/// "x-postgresql-skip": true
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct PolicyBundle {
///RFC 3339 UTC timestamp with a Z suffix (microsecond precision allowed) recording when the rule content last actually changed — not when the bundle was served. Drives olpolicybundleage (now minus built_at, clamped at 0), which measures policy freshness. It does NOT drive the staleness warning, which is measured from the last successful fetch. Deliberately a plain string rather than format: date-time so the generated Rust type stays a String; the poller parses it once into a SystemTime.
pub built_at: ::std::string::String,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub client_config: ::std::option::Option<PolicyBundleClientConfig>,
///The organization-wide kill switch. When false, every rule behaves as observe regardless of its own mode — matches produce a shadow verdict and nothing is blocked. Stored outside the built artifact on the platform so it survives every rebuild.
pub enforcement_enabled: bool,
///Canonical UUID form of the owning organization, matching the platform's organization_id column — not the API-key prefix. The client learns its own org from GET /api/v1/users/me, caches it in bundle.meta.json, and MUST reject a bundle whose value differs (OL-1211). Never evaluate another org's rules.
pub organization_id: ::std::string::String,
///Monotonic per organization, incrementing only on real content change — a rebuild over unchanged rules is a no-op that leaves this untouched. Serialized as a JSON number and treated as i64 in Rust. Recorded on every verdict as olpolicybundlerev: the replay key.
pub revision: i64,
///The active rule set, sorted by rule_id so the serialization is deterministic. May be empty, which means 'allow everything' and is distinguishable in telemetry from 'no bundle' because olpolicybundlerev is still stamped. Rules disabled by the author are omitted at build time — every rule present here is active.
pub rules: ::std::vec::Vec<PolicyRule>,
///Bundle format version. Always 1 in v1. The client MUST refuse a bundle whose schema_version it does not know and keep the previous bundle (OL-1212). Forward compatibility is the platform's responsibility, not the client's.
pub schema_version: i64,
///Reserved for a detached Ed25519 signature over the canonical bytes. Required but always null in v1, and the client MUST accept null. A client that cannot verify a non-null signature MUST reject the bundle rather than ignore the field, so that enabling signing later cannot be silently downgraded by an old client.
pub signature: ::std::option::Option<::std::string::String>,
}
///Client-bound configuration delivered on the bundle — the only client-bound configuration channel. Optional; a client that never receives it captures nothing (fail closed). The platform MUST NOT emit this field to a fleet that still runs clients predating it: the bundle root is additionalProperties:false, so such a client rejects the whole document (OL-1212) and fails static on its previous rules.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Client-bound configuration delivered on the bundle — the only client-bound configuration channel. Optional; a client that never receives it captures nothing (fail closed). The platform MUST NOT emit this field to a fleet that still runs clients predating it: the bundle root is additionalProperties:false, so such a client rejects the whole document (OL-1212) and fails static on its previous rules.",
/// "type": "object",
/// "properties": {
/// "agent_context": {
/// "description": "This install's own agent context, composed by the platform at serve time from the X-OpenLatch-Agent-Id header the poller sends — one object about THIS agent, never a fleet roster. Optional: a client that never receives it holds no context, and every rule carrying conditions matches nothing (absent context is not 'unknown'). Tolerant like its parent, so a newer platform can add keys without failing older clients.",
/// "type": "object",
/// "properties": {
/// "function": {
/// "description": "The business function assigned to this agent's owner. Deliberately an OPEN string carrying the AgentFunction vocabulary as x-known-values rather than a $ref to that enum — the same reasoning as params.mechanism: client_config is deserialized as one typed object outside the per-rule tolerance, so an enum here would fail the WHOLE bundle (fleet-wide fail-static) on a single out-of-vocabulary value. The client parses it into AgentFunction at load; a STRING value it cannot parse reads as no context, so scoped rules match nothing rather than the bundle being rejected. The tolerance is over the vocabulary, not the shape: a non-string value here (or a non-object agent_context) is a malformed client_config and does fail the whole document, exactly as a malformed capture_identity_signals does. 'unknown' is a real platform-assigned value, distinct from the field being absent.",
/// "type": "string",
/// "x-known-values": [
/// "engineering",
/// "product",
/// "data",
/// "security",
/// "it_ops",
/// "sales",
/// "marketing",
/// "finance",
/// "legal",
/// "hr",
/// "support",
/// "research",
/// "other",
/// "unknown"
/// ]
/// }
/// },
/// "additionalProperties": true
/// },
/// "capture_identity_signals": {
/// "description": "The client's copy of the org setting identity.capture_signals. Identity signals are captured and stamped only when this is explicitly true.",
/// "type": "boolean"
/// }
/// },
/// "additionalProperties": true
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct PolicyBundleClientConfig {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub agent_context: ::std::option::Option<PolicyBundleClientConfigAgentContext>,
///The client's copy of the org setting identity.capture_signals. Identity signals are captured and stamped only when this is explicitly true.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub capture_identity_signals: ::std::option::Option<bool>,
}
impl ::std::default::Default for PolicyBundleClientConfig {
fn default() -> Self {
Self {
agent_context: Default::default(),
capture_identity_signals: Default::default(),
}
}
}
///This install's own agent context, composed by the platform at serve time from the X-OpenLatch-Agent-Id header the poller sends — one object about THIS agent, never a fleet roster. Optional: a client that never receives it holds no context, and every rule carrying conditions matches nothing (absent context is not 'unknown'). Tolerant like its parent, so a newer platform can add keys without failing older clients.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "This install's own agent context, composed by the platform at serve time from the X-OpenLatch-Agent-Id header the poller sends — one object about THIS agent, never a fleet roster. Optional: a client that never receives it holds no context, and every rule carrying conditions matches nothing (absent context is not 'unknown'). Tolerant like its parent, so a newer platform can add keys without failing older clients.",
/// "type": "object",
/// "properties": {
/// "function": {
/// "description": "The business function assigned to this agent's owner. Deliberately an OPEN string carrying the AgentFunction vocabulary as x-known-values rather than a $ref to that enum — the same reasoning as params.mechanism: client_config is deserialized as one typed object outside the per-rule tolerance, so an enum here would fail the WHOLE bundle (fleet-wide fail-static) on a single out-of-vocabulary value. The client parses it into AgentFunction at load; a STRING value it cannot parse reads as no context, so scoped rules match nothing rather than the bundle being rejected. The tolerance is over the vocabulary, not the shape: a non-string value here (or a non-object agent_context) is a malformed client_config and does fail the whole document, exactly as a malformed capture_identity_signals does. 'unknown' is a real platform-assigned value, distinct from the field being absent.",
/// "type": "string",
/// "x-known-values": [
/// "engineering",
/// "product",
/// "data",
/// "security",
/// "it_ops",
/// "sales",
/// "marketing",
/// "finance",
/// "legal",
/// "hr",
/// "support",
/// "research",
/// "other",
/// "unknown"
/// ]
/// }
/// },
/// "additionalProperties": true
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct PolicyBundleClientConfigAgentContext {
///The business function assigned to this agent's owner. Deliberately an OPEN string carrying the AgentFunction vocabulary as x-known-values rather than a $ref to that enum — the same reasoning as params.mechanism: client_config is deserialized as one typed object outside the per-rule tolerance, so an enum here would fail the WHOLE bundle (fleet-wide fail-static) on a single out-of-vocabulary value. The client parses it into AgentFunction at load; a STRING value it cannot parse reads as no context, so scoped rules match nothing rather than the bundle being rejected. The tolerance is over the vocabulary, not the shape: a non-string value here (or a non-object agent_context) is a malformed client_config and does fail the whole document, exactly as a malformed capture_identity_signals does. 'unknown' is a real platform-assigned value, distinct from the field being absent.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub function: ::std::option::Option<::std::string::String>,
}
impl ::std::default::Default for PolicyBundleClientConfigAgentContext {
fn default() -> Self {
Self {
function: Default::default(),
}
}
}
///One authored rule, on either plane. A kind=command rule is evaluated against the normalized command string extracted from a pre_tool_use envelope. A kind=request rule targets an outbound model request at the boundary and is held, not evaluated, in v1. The allOf below is the per-kind/per-action gate: the platform enforces it at authoring time (422) and the client validates every rule against it individually at bundle load, skipping the ones that fail — one bad rule must never cost the fleet its denies.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "PolicyRule",
/// "description": "One authored rule, on either plane. A kind=command rule is evaluated against the normalized command string extracted from a pre_tool_use envelope. A kind=request rule targets an outbound model request at the boundary and is held, not evaluated, in v1. The allOf below is the per-kind/per-action gate: the platform enforces it at authoring time (422) and the client validates every rule against it individually at bundle load, skipping the ones that fail — one bad rule must never cost the fleet its denies.",
/// "type": "object",
/// "required": [
/// "action",
/// "kind",
/// "mode",
/// "reason",
/// "rule_id",
/// "severity"
/// ],
/// "properties": {
/// "action": {
/// "description": "What a match does. Authorable actions are 'deny' (the only one on kind=command) and 'prefix_reorder', 'history_trim', 'prompt_edit' (kind=request). Which action is legal for which kind is owned by the gate below, not by this list. An open string for the same forward-compatibility reason as kind.",
/// "type": "string"
/// },
/// "conditions": {
/// "description": "Which agents the rule applies to, decided locally by the client against the client_config.agent_context THIS install received — the network is not on the evaluation path. Absent (or empty) means unconditional: the rule applies on every agent, exactly as before this field existed. When present, EVERY condition must hold (AND semantics), and a rule whose context never arrived matches nothing — absent context is not 'unknown'. v1 is the command plane only: forbidden on kind=request by the gate below, and the only vocabulary is field 'agent.function' with op 'in'. The field/op enums are the general shape, so widening either later is additive; a client that predates a widening drops that one rule at load (unrecognized_field) and keeps the bundle. Values are the closed AgentFunction enum, so a value the client does not know fails that one rule at deserialization, never the whole document. 'unknown' is an ordinary value, not a wildcard.",
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "field",
/// "op",
/// "value"
/// ],
/// "properties": {
/// "field": {
/// "description": "The agent-context attribute the condition reads. v1 knows exactly one: 'agent.function', read from client_config.agent_context.function.",
/// "type": "string",
/// "enum": [
/// "agent.function"
/// ]
/// },
/// "op": {
/// "description": "The comparison. v1 knows exactly one: 'in' — the attribute's value is a member of `value`. Several conditions on the same field AND together, so two 'in' sets intersect.",
/// "type": "string",
/// "enum": [
/// "in"
/// ]
/// },
/// "value": {
/// "description": "The set the attribute must belong to. At least one member — an empty set would match nothing while looking like a scoped rule, and the platform rejects it at authoring (422). Compared by enum equality on the client.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/AgentFunction"
/// },
/// "minItems": 1
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "kind": {
/// "description": "Which plane the rule acts on. Two kinds are authorable: 'command' (a shell command intercepted at the pre_tool_use hook) and 'request' (an outbound model request intercepted at the boundary). Deliberately an open string rather than a closed enum: the client MUST skip a rule whose kind it does not recognize and keep the rest of the bundle active, so that a v1 client tolerates a v1.1 bundle. A closed enum would fail deserialization of the whole document instead.",
/// "type": "string"
/// },
/// "match_pattern": {
/// "description": "Required for kind=command and forbidden for kind=request — see the gate below; it is optional in the base only so that a request rule does not fail deserialization on a client that predates the gate. Fully-anchored, case-sensitive glob over the entire normalized command string. Exactly two metacharacters: '*' (any sequence including empty, crossing '/' and every other character — this is not path-aware globbing) and '?' (exactly one character). No character classes, no escapes, no alternation, no regex. Anchoring surprises authors: 'rm -rf /*' does not match 'sudo rm -rf /tmp'; write '*rm -rf*' to catch a command anywhere in the string. The platform normalizes and validates this at authoring time and rejects empty, over-1024-character, control-character-bearing, and bare-'*' patterns with 422.",
/// "type": "string"
/// },
/// "mode": {
/// "description": "Staged rollout control. 'observe' records a shadow verdict and allows the action; 'enforce' denies it. Most restrictive wins across matching rules. Overridden to observe for every rule when enforcement_enabled is false.",
/// "type": "string",
/// "enum": [
/// "observe",
/// "enforce"
/// ]
/// },
/// "params": {
/// "description": "How a kind=request rule transforms the request. Forbidden on kind=command. Exactly which keys are required and which are rejected is per-action, in the gate below.",
/// "type": "object",
/// "properties": {
/// "keep_messages": {
/// "description": "How many of the most recent messages history_trim retains. Required by history_trim, rejected everywhere else.",
/// "type": "integer"
/// },
/// "marker": {
/// "description": "The literal marker prompt_edit rewrites around. Deliberately unconstrained here: a length or pattern keyword would make the generated Rust type a constrained newtype whose deserializer fails the WHOLE bundle on one over-long value. Length is bounded platform-side at authoring.",
/// "type": "string"
/// },
/// "max_system_tokens": {
/// "description": "Token ceiling prompt_edit trims the system block to. Alternative to marker — exactly one of the two is required.",
/// "type": "integer"
/// },
/// "mechanism": {
/// "description": "Which caching intervention a prefix_reorder rule means. The detector that authors these rules fires on two different causes needing two different interventions, and the action alone cannot tell them apart: 'insert_breakpoints' — nothing is being cached at all, so cache_control breakpoints must be INJECTED; 'reorder_blocks' — breakpoints exist but a volatile block sits early in the prefix and must be MOVED after the stable ones. Read by prefix_reorder only. Deliberately an OPEN string rather than an enum, for the same reason as kind and action: an out-of-vocabulary value must fail one rule, not deserialization of the whole bundle. An enum here would be a known field with an unknown value, which the unknown-FIELD tolerance does not cover. Absent means the rule names no mechanism — a client that cannot infer one skips it rather than guessing.",
/// "examples": [
/// "insert_breakpoints",
/// "reorder_blocks"
/// ],
/// "type": "string"
/// }
/// },
/// "additionalProperties": false
/// },
/// "reason": {
/// "description": "Plain-language explanation shown to the developer verbatim when the rule blocks an action. A deny the developer cannot understand is a broken feature.",
/// "type": "string"
/// },
/// "rule_id": {
/// "description": "Stable public identifier, e.g. 'OL-CMD-001'. Unique within the organization. Reported on the verdict as olpolicyruleid, and the tiebreak key: the lexicographically first rule within the deciding set is the one reported.",
/// "type": "string"
/// },
/// "rule_version": {
/// "description": "Required for kind=request, absent on kind=command — the middle term of the D-04 replay tuple, so a would-have report can be tied to the exact rule text that produced it. Platform-managed: set on create and incremented on every edit, never authored by hand.",
/// "type": "integer"
/// },
/// "select": {
/// "description": "Which requests a kind=request rule applies to; an absent select means every request the boundary sees. Forbidden on kind=command. Each key is narrowed further per action by the gate below — a key that an action does not read is rejected rather than silently ignored.",
/// "type": "object",
/// "properties": {
/// "exclude_layers": {
/// "description": "Cache layers this rule must leave untouched. Read by prefix_reorder only.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/ChurnLayer"
/// }
/// },
/// "min_messages": {
/// "description": "Apply only once the in-context history has at least this many messages. Read by history_trim only.",
/// "type": "integer"
/// },
/// "model_in": {
/// "description": "Apply only to requests whose model identifier is in this list. Matched verbatim against the model string on the wire; no globbing.",
/// "type": "array",
/// "items": {
/// "type": "string"
/// }
/// }
/// },
/// "additionalProperties": false
/// },
/// "severity": {
/// "description": "Author-assigned severity, surfaced on the verdict returned to the agent hook.",
/// "type": "string",
/// "enum": [
/// "low",
/// "medium",
/// "high",
/// "critical"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct PolicyRule {
///What a match does. Authorable actions are 'deny' (the only one on kind=command) and 'prefix_reorder', 'history_trim', 'prompt_edit' (kind=request). Which action is legal for which kind is owned by the gate below, not by this list. An open string for the same forward-compatibility reason as kind.
pub action: ::std::string::String,
///Which agents the rule applies to, decided locally by the client against the client_config.agent_context THIS install received — the network is not on the evaluation path. Absent (or empty) means unconditional: the rule applies on every agent, exactly as before this field existed. When present, EVERY condition must hold (AND semantics), and a rule whose context never arrived matches nothing — absent context is not 'unknown'. v1 is the command plane only: forbidden on kind=request by the gate below, and the only vocabulary is field 'agent.function' with op 'in'. The field/op enums are the general shape, so widening either later is additive; a client that predates a widening drops that one rule at load (unrecognized_field) and keeps the bundle. Values are the closed AgentFunction enum, so a value the client does not know fails that one rule at deserialization, never the whole document. 'unknown' is an ordinary value, not a wildcard.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub conditions: ::std::vec::Vec<PolicyRuleConditionsItem>,
///Which plane the rule acts on. Two kinds are authorable: 'command' (a shell command intercepted at the pre_tool_use hook) and 'request' (an outbound model request intercepted at the boundary). Deliberately an open string rather than a closed enum: the client MUST skip a rule whose kind it does not recognize and keep the rest of the bundle active, so that a v1 client tolerates a v1.1 bundle. A closed enum would fail deserialization of the whole document instead.
pub kind: ::std::string::String,
///Required for kind=command and forbidden for kind=request — see the gate below; it is optional in the base only so that a request rule does not fail deserialization on a client that predates the gate. Fully-anchored, case-sensitive glob over the entire normalized command string. Exactly two metacharacters: '*' (any sequence including empty, crossing '/' and every other character — this is not path-aware globbing) and '?' (exactly one character). No character classes, no escapes, no alternation, no regex. Anchoring surprises authors: 'rm -rf /*' does not match 'sudo rm -rf /tmp'; write '*rm -rf*' to catch a command anywhere in the string. The platform normalizes and validates this at authoring time and rejects empty, over-1024-character, control-character-bearing, and bare-'*' patterns with 422.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub match_pattern: ::std::option::Option<::std::string::String>,
///Staged rollout control. 'observe' records a shadow verdict and allows the action; 'enforce' denies it. Most restrictive wins across matching rules. Overridden to observe for every rule when enforcement_enabled is false.
pub mode: PolicyRuleMode,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub params: ::std::option::Option<PolicyRuleParams>,
///Plain-language explanation shown to the developer verbatim when the rule blocks an action. A deny the developer cannot understand is a broken feature.
pub reason: ::std::string::String,
///Stable public identifier, e.g. 'OL-CMD-001'. Unique within the organization. Reported on the verdict as olpolicyruleid, and the tiebreak key: the lexicographically first rule within the deciding set is the one reported.
pub rule_id: ::std::string::String,
///Required for kind=request, absent on kind=command — the middle term of the D-04 replay tuple, so a would-have report can be tied to the exact rule text that produced it. Platform-managed: set on create and incremented on every edit, never authored by hand.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub rule_version: ::std::option::Option<i64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub select: ::std::option::Option<PolicyRuleSelect>,
///Author-assigned severity, surfaced on the verdict returned to the agent hook.
pub severity: PolicyRuleSeverity,
}
///`PolicyRuleConditionsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "field",
/// "op",
/// "value"
/// ],
/// "properties": {
/// "field": {
/// "description": "The agent-context attribute the condition reads. v1 knows exactly one: 'agent.function', read from client_config.agent_context.function.",
/// "type": "string",
/// "enum": [
/// "agent.function"
/// ]
/// },
/// "op": {
/// "description": "The comparison. v1 knows exactly one: 'in' — the attribute's value is a member of `value`. Several conditions on the same field AND together, so two 'in' sets intersect.",
/// "type": "string",
/// "enum": [
/// "in"
/// ]
/// },
/// "value": {
/// "description": "The set the attribute must belong to. At least one member — an empty set would match nothing while looking like a scoped rule, and the platform rejects it at authoring (422). Compared by enum equality on the client.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/AgentFunction"
/// },
/// "minItems": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct PolicyRuleConditionsItem {
///The agent-context attribute the condition reads. v1 knows exactly one: 'agent.function', read from client_config.agent_context.function.
pub field: PolicyRuleConditionsItemField,
///The comparison. v1 knows exactly one: 'in' — the attribute's value is a member of `value`. Several conditions on the same field AND together, so two 'in' sets intersect.
pub op: PolicyRuleConditionsItemOp,
///The set the attribute must belong to. At least one member — an empty set would match nothing while looking like a scoped rule, and the platform rejects it at authoring (422). Compared by enum equality on the client.
pub value: ::std::vec::Vec<AgentFunction>,
}
///The agent-context attribute the condition reads. v1 knows exactly one: 'agent.function', read from client_config.agent_context.function.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The agent-context attribute the condition reads. v1 knows exactly one: 'agent.function', read from client_config.agent_context.function.",
/// "type": "string",
/// "enum": [
/// "agent.function"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum PolicyRuleConditionsItemField {
#[serde(rename = "agent.function")]
AgentFunction,
}
impl ::std::fmt::Display for PolicyRuleConditionsItemField {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::AgentFunction => f.write_str("agent.function"),
}
}
}
impl ::std::str::FromStr for PolicyRuleConditionsItemField {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"agent.function" => Ok(Self::AgentFunction),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PolicyRuleConditionsItemField {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PolicyRuleConditionsItemField {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PolicyRuleConditionsItemField {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///The comparison. v1 knows exactly one: 'in' — the attribute's value is a member of `value`. Several conditions on the same field AND together, so two 'in' sets intersect.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The comparison. v1 knows exactly one: 'in' — the attribute's value is a member of `value`. Several conditions on the same field AND together, so two 'in' sets intersect.",
/// "type": "string",
/// "enum": [
/// "in"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum PolicyRuleConditionsItemOp {
#[serde(rename = "in")]
In,
}
impl ::std::fmt::Display for PolicyRuleConditionsItemOp {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::In => f.write_str("in"),
}
}
}
impl ::std::str::FromStr for PolicyRuleConditionsItemOp {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"in" => Ok(Self::In),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PolicyRuleConditionsItemOp {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PolicyRuleConditionsItemOp {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PolicyRuleConditionsItemOp {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Staged rollout control. 'observe' records a shadow verdict and allows the action; 'enforce' denies it. Most restrictive wins across matching rules. Overridden to observe for every rule when enforcement_enabled is false.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Staged rollout control. 'observe' records a shadow verdict and allows the action; 'enforce' denies it. Most restrictive wins across matching rules. Overridden to observe for every rule when enforcement_enabled is false.",
/// "type": "string",
/// "enum": [
/// "observe",
/// "enforce"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum PolicyRuleMode {
#[serde(rename = "observe")]
Observe,
#[serde(rename = "enforce")]
Enforce,
}
impl ::std::fmt::Display for PolicyRuleMode {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Observe => f.write_str("observe"),
Self::Enforce => f.write_str("enforce"),
}
}
}
impl ::std::str::FromStr for PolicyRuleMode {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"observe" => Ok(Self::Observe),
"enforce" => Ok(Self::Enforce),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PolicyRuleMode {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PolicyRuleMode {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PolicyRuleMode {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///How a kind=request rule transforms the request. Forbidden on kind=command. Exactly which keys are required and which are rejected is per-action, in the gate below.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "How a kind=request rule transforms the request. Forbidden on kind=command. Exactly which keys are required and which are rejected is per-action, in the gate below.",
/// "type": "object",
/// "properties": {
/// "keep_messages": {
/// "description": "How many of the most recent messages history_trim retains. Required by history_trim, rejected everywhere else.",
/// "type": "integer"
/// },
/// "marker": {
/// "description": "The literal marker prompt_edit rewrites around. Deliberately unconstrained here: a length or pattern keyword would make the generated Rust type a constrained newtype whose deserializer fails the WHOLE bundle on one over-long value. Length is bounded platform-side at authoring.",
/// "type": "string"
/// },
/// "max_system_tokens": {
/// "description": "Token ceiling prompt_edit trims the system block to. Alternative to marker — exactly one of the two is required.",
/// "type": "integer"
/// },
/// "mechanism": {
/// "description": "Which caching intervention a prefix_reorder rule means. The detector that authors these rules fires on two different causes needing two different interventions, and the action alone cannot tell them apart: 'insert_breakpoints' — nothing is being cached at all, so cache_control breakpoints must be INJECTED; 'reorder_blocks' — breakpoints exist but a volatile block sits early in the prefix and must be MOVED after the stable ones. Read by prefix_reorder only. Deliberately an OPEN string rather than an enum, for the same reason as kind and action: an out-of-vocabulary value must fail one rule, not deserialization of the whole bundle. An enum here would be a known field with an unknown value, which the unknown-FIELD tolerance does not cover. Absent means the rule names no mechanism — a client that cannot infer one skips it rather than guessing.",
/// "examples": [
/// "insert_breakpoints",
/// "reorder_blocks"
/// ],
/// "type": "string"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct PolicyRuleParams {
///How many of the most recent messages history_trim retains. Required by history_trim, rejected everywhere else.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub keep_messages: ::std::option::Option<i64>,
///The literal marker prompt_edit rewrites around. Deliberately unconstrained here: a length or pattern keyword would make the generated Rust type a constrained newtype whose deserializer fails the WHOLE bundle on one over-long value. Length is bounded platform-side at authoring.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub marker: ::std::option::Option<::std::string::String>,
///Token ceiling prompt_edit trims the system block to. Alternative to marker — exactly one of the two is required.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub max_system_tokens: ::std::option::Option<i64>,
///Which caching intervention a prefix_reorder rule means. The detector that authors these rules fires on two different causes needing two different interventions, and the action alone cannot tell them apart: 'insert_breakpoints' — nothing is being cached at all, so cache_control breakpoints must be INJECTED; 'reorder_blocks' — breakpoints exist but a volatile block sits early in the prefix and must be MOVED after the stable ones. Read by prefix_reorder only. Deliberately an OPEN string rather than an enum, for the same reason as kind and action: an out-of-vocabulary value must fail one rule, not deserialization of the whole bundle. An enum here would be a known field with an unknown value, which the unknown-FIELD tolerance does not cover. Absent means the rule names no mechanism — a client that cannot infer one skips it rather than guessing.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub mechanism: ::std::option::Option<::std::string::String>,
}
impl ::std::default::Default for PolicyRuleParams {
fn default() -> Self {
Self {
keep_messages: Default::default(),
marker: Default::default(),
max_system_tokens: Default::default(),
mechanism: Default::default(),
}
}
}
///Which requests a kind=request rule applies to; an absent select means every request the boundary sees. Forbidden on kind=command. Each key is narrowed further per action by the gate below — a key that an action does not read is rejected rather than silently ignored.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Which requests a kind=request rule applies to; an absent select means every request the boundary sees. Forbidden on kind=command. Each key is narrowed further per action by the gate below — a key that an action does not read is rejected rather than silently ignored.",
/// "type": "object",
/// "properties": {
/// "exclude_layers": {
/// "description": "Cache layers this rule must leave untouched. Read by prefix_reorder only.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/ChurnLayer"
/// }
/// },
/// "min_messages": {
/// "description": "Apply only once the in-context history has at least this many messages. Read by history_trim only.",
/// "type": "integer"
/// },
/// "model_in": {
/// "description": "Apply only to requests whose model identifier is in this list. Matched verbatim against the model string on the wire; no globbing.",
/// "type": "array",
/// "items": {
/// "type": "string"
/// }
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct PolicyRuleSelect {
///Cache layers this rule must leave untouched. Read by prefix_reorder only.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub exclude_layers: ::std::vec::Vec<ChurnLayer>,
///Apply only once the in-context history has at least this many messages. Read by history_trim only.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub min_messages: ::std::option::Option<i64>,
///Apply only to requests whose model identifier is in this list. Matched verbatim against the model string on the wire; no globbing.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub model_in: ::std::vec::Vec<::std::string::String>,
}
impl ::std::default::Default for PolicyRuleSelect {
fn default() -> Self {
Self {
exclude_layers: Default::default(),
min_messages: Default::default(),
model_in: Default::default(),
}
}
}
///Author-assigned severity, surfaced on the verdict returned to the agent hook.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Author-assigned severity, surfaced on the verdict returned to the agent hook.",
/// "type": "string",
/// "enum": [
/// "low",
/// "medium",
/// "high",
/// "critical"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum PolicyRuleSeverity {
#[serde(rename = "low")]
Low,
#[serde(rename = "medium")]
Medium,
#[serde(rename = "high")]
High,
#[serde(rename = "critical")]
Critical,
}
impl ::std::fmt::Display for PolicyRuleSeverity {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Low => f.write_str("low"),
Self::Medium => f.write_str("medium"),
Self::High => f.write_str("high"),
Self::Critical => f.write_str("critical"),
}
}
}
impl ::std::str::FromStr for PolicyRuleSeverity {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"low" => Ok(Self::Low),
"medium" => Ok(Self::Medium),
"high" => Ok(Self::High),
"critical" => Ok(Self::Critical),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PolicyRuleSeverity {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PolicyRuleSeverity {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PolicyRuleSeverity {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Verdict returned to the agent hook. Closed enum — client must handle all three variants exhaustively. allow = proceed normally, approve = user-confirmed allow, deny = blocked.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Verdict returned to the agent hook. Closed enum — client must handle all three variants exhaustively. allow = proceed normally, approve = user-confirmed allow, deny = blocked.",
/// "type": "string",
/// "enum": [
/// "allow",
/// "approve",
/// "deny"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum Verdict {
#[serde(rename = "allow")]
Allow,
#[serde(rename = "approve")]
Approve,
#[serde(rename = "deny")]
Deny,
}
impl ::std::fmt::Display for Verdict {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Allow => f.write_str("allow"),
Self::Approve => f.write_str("approve"),
Self::Deny => f.write_str("deny"),
}
}
}
impl ::std::str::FromStr for Verdict {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"allow" => Ok(Self::Allow),
"approve" => Ok(Self::Approve),
"deny" => Ok(Self::Deny),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for Verdict {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for Verdict {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for Verdict {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Verdict response returned to the agent hook after processing. The client passes this through to the agent hook without interpreting the verdict itself. Mirrors the cloud response schema. Schema version '1.1' adds the optional 'context' object (headline / body / evidence[]) carrying user-facing copy that the agent renders in its end-user notification (D-16 of OpenRouter of Security).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Verdict response returned to the agent hook after processing. The client passes this through to the agent hook without interpreting the verdict itself. Mirrors the cloud response schema. Schema version '1.1' adds the optional 'context' object (headline / body / evidence[]) carrying user-facing copy that the agent renders in its end-user notification (D-16 of OpenRouter of Security).",
/// "examples": [
/// {
/// "event_id": "019d8af1-f8da-73b3-92eb-79a99e59b10b",
/// "latency_ms": 42,
/// "schema_version": "1.0",
/// "verdict": "allow"
/// },
/// {
/// "context": {
/// "body": "This action would share a credential (API key, token, password). Remove the credential and use a secrets manager.",
/// "evidence": [
/// {
/// "label": "credential_kind",
/// "value_redacted": "github_pat (ghp_****)"
/// }
/// ],
/// "headline": "Credential detected"
/// },
/// "details_url": "https://app.openlatch.ai/events/019d8af1-f8da-73b3-92eb-000000000002",
/// "event_id": "019d8af1-f8da-73b3-92eb-000000000002",
/// "latency_ms": 88,
/// "offline": false,
/// "reason": "Credential detected in tool output",
/// "rule_id": "rule_cred_001",
/// "schema_version": "1.1",
/// "severity": "critical",
/// "threat_category": "credential_detection",
/// "verdict": "deny"
/// },
/// {
/// "context": {
/// "body": "Your security detection tools are currently unreachable. This action was allowed to avoid blocking work. Check the platform routing page for status.",
/// "evidence": [],
/// "headline": "Security tools unreachable"
/// },
/// "event_id": "019d8af1-f8da-73b3-92eb-000000000003",
/// "latency_ms": 201,
/// "offline": true,
/// "schema_version": "1.1",
/// "verdict": "allow"
/// }
/// ],
/// "type": "object",
/// "required": [
/// "event_id",
/// "latency_ms",
/// "schema_version",
/// "verdict"
/// ],
/// "properties": {
/// "context": {
/// "description": "Schema 1.1+. Optional user-facing copy for rendering an end-user-visible notification when the verdict is rendered to the human. Clients render context.headline + context.body + context.evidence directly. 'remediation' is intentionally NOT on the wire (D-16) — it is stored on the platform and accessible via details_url. Older clients (1.0) ignore this field. Nullability is expressed via anyOf (object | null) instead of `\"type\": [\"object\", \"null\"]` because typify (the Rust codegen) does not yet support the JSON Schema 2020-12 type-array form for inline object shapes.",
/// "default": null,
/// "anyOf": [
/// {
/// "type": "null"
/// },
/// {
/// "type": "object",
/// "required": [
/// "body",
/// "headline"
/// ],
/// "properties": {
/// "body": {
/// "description": "One-paragraph explanation shown below the headline.",
/// "type": "string",
/// "maxLength": 500,
/// "minLength": 1
/// },
/// "evidence": {
/// "default": [],
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "label"
/// ],
/// "properties": {
/// "label": {
/// "description": "Short tag, e.g. 'credit_card', 'host', 'tool_name'.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "value_redacted": {
/// "description": "Redacted display string. Provider redacts before submission; platform re-runs SENSITIVE_FIELD_PATTERNS defensively.",
/// "type": "string",
/// "maxLength": 200
/// }
/// },
/// "additionalProperties": false
/// },
/// "maxItems": 16
/// },
/// "headline": {
/// "description": "One-line summary shown as the toast / notification title.",
/// "type": "string",
/// "maxLength": 120,
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
/// }
/// ]
/// },
/// "details_url": {
/// "description": "URL to the OpenLatch dashboard with detailed event analysis. Omitted when not available.",
/// "type": "string"
/// },
/// "event_id": {
/// "description": "Server-assigned or client-generated ID of the event this verdict responds to.",
/// "type": "string"
/// },
/// "latency_ms": {
/// "description": "Total end-to-end processing latency in milliseconds, including cloud round-trip when applicable.",
/// "type": "number",
/// "minimum": 0.0
/// },
/// "offline": {
/// "description": "Schema 1.1+. True when all configured security tools were unreachable. The verdict is fail-open ('allow') and 'context.headline' will read 'Security tools unreachable'. Older clients ignore this field.",
/// "default": false,
/// "type": "boolean"
/// },
/// "reason": {
/// "description": "Human-readable explanation for the verdict. Omitted for allow verdicts.",
/// "type": "string"
/// },
/// "rule_id": {
/// "description": "Identifier of the detection rule that triggered this verdict. Omitted when no rule matched.",
/// "type": "string"
/// },
/// "schema_version": {
/// "description": "Schema version for forward compatibility. '1.0' = legacy. '1.1' = adds optional 'context' + 'offline' fields (D-16 of OpenRouter of Security). Older clients ignore unknown fields and remain compatible.",
/// "type": "string"
/// },
/// "severity": {
/// "description": "Threat severity level (e.g., 'critical', 'high', 'medium', 'low'). Omitted when no threat was detected.",
/// "type": "string"
/// },
/// "threat_category": {
/// "description": "Category of detected threat (e.g., 'credential_exfiltration', 'command_injection'). Omitted when no threat detected.",
/// "type": "string"
/// },
/// "verdict": {
/// "description": "The verdict: allow = proceed, approve = user-confirmed allow, deny = blocked.",
/// "$ref": "#/$defs/Verdict"
/// }
/// },
/// "additionalProperties": false,
/// "x-postgresql-skip": true
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct VerdictResponse {
///Schema 1.1+. Optional user-facing copy for rendering an end-user-visible notification when the verdict is rendered to the human. Clients render context.headline + context.body + context.evidence directly. 'remediation' is intentionally NOT on the wire (D-16) — it is stored on the platform and accessible via details_url. Older clients (1.0) ignore this field. Nullability is expressed via anyOf (object | null) instead of `"type": ["object", "null"]` because typify (the Rust codegen) does not yet support the JSON Schema 2020-12 type-array form for inline object shapes.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub context: ::std::option::Option<VerdictResponseContext>,
///URL to the OpenLatch dashboard with detailed event analysis. Omitted when not available.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub details_url: ::std::option::Option<::std::string::String>,
///Server-assigned or client-generated ID of the event this verdict responds to.
pub event_id: ::std::string::String,
///Total end-to-end processing latency in milliseconds, including cloud round-trip when applicable.
pub latency_ms: f64,
///Schema 1.1+. True when all configured security tools were unreachable. The verdict is fail-open ('allow') and 'context.headline' will read 'Security tools unreachable'. Older clients ignore this field.
#[serde(default)]
pub offline: bool,
///Human-readable explanation for the verdict. Omitted for allow verdicts.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub reason: ::std::option::Option<::std::string::String>,
///Identifier of the detection rule that triggered this verdict. Omitted when no rule matched.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub rule_id: ::std::option::Option<::std::string::String>,
///Schema version for forward compatibility. '1.0' = legacy. '1.1' = adds optional 'context' + 'offline' fields (D-16 of OpenRouter of Security). Older clients ignore unknown fields and remain compatible.
pub schema_version: ::std::string::String,
///Threat severity level (e.g., 'critical', 'high', 'medium', 'low'). Omitted when no threat was detected.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub severity: ::std::option::Option<::std::string::String>,
///Category of detected threat (e.g., 'credential_exfiltration', 'command_injection'). Omitted when no threat detected.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub threat_category: ::std::option::Option<::std::string::String>,
///The verdict: allow = proceed, approve = user-confirmed allow, deny = blocked.
pub verdict: Verdict,
}
///`VerdictResponseContext`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "body",
/// "headline"
/// ],
/// "properties": {
/// "body": {
/// "description": "One-paragraph explanation shown below the headline.",
/// "type": "string",
/// "maxLength": 500,
/// "minLength": 1
/// },
/// "evidence": {
/// "default": [],
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "label"
/// ],
/// "properties": {
/// "label": {
/// "description": "Short tag, e.g. 'credit_card', 'host', 'tool_name'.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "value_redacted": {
/// "description": "Redacted display string. Provider redacts before submission; platform re-runs SENSITIVE_FIELD_PATTERNS defensively.",
/// "type": "string",
/// "maxLength": 200
/// }
/// },
/// "additionalProperties": false
/// },
/// "maxItems": 16
/// },
/// "headline": {
/// "description": "One-line summary shown as the toast / notification title.",
/// "type": "string",
/// "maxLength": 120,
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct VerdictResponseContext {
///One-paragraph explanation shown below the headline.
pub body: VerdictResponseContextBody,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub evidence: ::std::vec::Vec<VerdictResponseContextEvidenceItem>,
///One-line summary shown as the toast / notification title.
pub headline: VerdictResponseContextHeadline,
}
///One-paragraph explanation shown below the headline.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "One-paragraph explanation shown below the headline.",
/// "type": "string",
/// "maxLength": 500,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VerdictResponseContextBody(::std::string::String);
impl ::std::ops::Deref for VerdictResponseContextBody {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VerdictResponseContextBody> for ::std::string::String {
fn from(value: VerdictResponseContextBody) -> Self {
value.0
}
}
impl ::std::str::FromStr for VerdictResponseContextBody {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 500usize {
return Err("longer than 500 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for VerdictResponseContextBody {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for VerdictResponseContextBody {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for VerdictResponseContextBody {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for VerdictResponseContextBody {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`VerdictResponseContextEvidenceItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "label"
/// ],
/// "properties": {
/// "label": {
/// "description": "Short tag, e.g. 'credit_card', 'host', 'tool_name'.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "value_redacted": {
/// "description": "Redacted display string. Provider redacts before submission; platform re-runs SENSITIVE_FIELD_PATTERNS defensively.",
/// "type": "string",
/// "maxLength": 200
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct VerdictResponseContextEvidenceItem {
///Short tag, e.g. 'credit_card', 'host', 'tool_name'.
pub label: VerdictResponseContextEvidenceItemLabel,
///Redacted display string. Provider redacts before submission; platform re-runs SENSITIVE_FIELD_PATTERNS defensively.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub value_redacted: ::std::option::Option<VerdictResponseContextEvidenceItemValueRedacted>,
}
///Short tag, e.g. 'credit_card', 'host', 'tool_name'.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Short tag, e.g. 'credit_card', 'host', 'tool_name'.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VerdictResponseContextEvidenceItemLabel(::std::string::String);
impl ::std::ops::Deref for VerdictResponseContextEvidenceItemLabel {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VerdictResponseContextEvidenceItemLabel> for ::std::string::String {
fn from(value: VerdictResponseContextEvidenceItemLabel) -> Self {
value.0
}
}
impl ::std::str::FromStr for VerdictResponseContextEvidenceItemLabel {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for VerdictResponseContextEvidenceItemLabel {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for VerdictResponseContextEvidenceItemLabel {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for VerdictResponseContextEvidenceItemLabel {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for VerdictResponseContextEvidenceItemLabel {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Redacted display string. Provider redacts before submission; platform re-runs SENSITIVE_FIELD_PATTERNS defensively.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Redacted display string. Provider redacts before submission; platform re-runs SENSITIVE_FIELD_PATTERNS defensively.",
/// "type": "string",
/// "maxLength": 200
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VerdictResponseContextEvidenceItemValueRedacted(::std::string::String);
impl ::std::ops::Deref for VerdictResponseContextEvidenceItemValueRedacted {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VerdictResponseContextEvidenceItemValueRedacted>
for ::std::string::String
{
fn from(value: VerdictResponseContextEvidenceItemValueRedacted) -> Self {
value.0
}
}
impl ::std::str::FromStr for VerdictResponseContextEvidenceItemValueRedacted {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 200usize {
return Err("longer than 200 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for VerdictResponseContextEvidenceItemValueRedacted {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for VerdictResponseContextEvidenceItemValueRedacted
{
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for VerdictResponseContextEvidenceItemValueRedacted
{
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for VerdictResponseContextEvidenceItemValueRedacted {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///One-line summary shown as the toast / notification title.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "One-line summary shown as the toast / notification title.",
/// "type": "string",
/// "maxLength": 120,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VerdictResponseContextHeadline(::std::string::String);
impl ::std::ops::Deref for VerdictResponseContextHeadline {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VerdictResponseContextHeadline> for ::std::string::String {
fn from(value: VerdictResponseContextHeadline) -> Self {
value.0
}
}
impl ::std::str::FromStr for VerdictResponseContextHeadline {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 120usize {
return Err("longer than 120 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for VerdictResponseContextHeadline {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for VerdictResponseContextHeadline {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for VerdictResponseContextHeadline {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for VerdictResponseContextHeadline {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}