From 01f38519b0e28e9431ac17fb325b3283f3ea41ef Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Wed, 22 Jul 2026 01:12:26 -0400 Subject: [PATCH 01/15] feat(agents): introduce agent lifecycle domain and proto Land the agent lifecycle domain under the crates/ topology from ADR#0034: the domain crate in crates/agents/ and its generated proto in crates/platform/trogonai-proto/, so the agents family follows the accepted grouping rather than the pre-reorg flat layout. Signed-off-by: Yordis Prieto --- .../state/v1/provision_agent_state.proto | 23 + proto/trogonai/agents/agents/v1/agent.proto | 57 + .../agents/agents/v1/agent_provisioned.proto | 32 + proto/trogonai/agents/agents/v1/events.proto | 12 + .../agents/agents/v1/provision_agent.proto | 26 + rsworkspace/Cargo.lock | 10 + .../agents/trogonai-agents-domain/Cargo.toml | 18 + .../src/commands/domain/agent_charter.rs | 51 + .../commands/domain/agent_charter/tests.rs | 14 + .../src/commands/domain/agent_definition.rs | 58 + .../commands/domain/agent_definition/tests.rs | 22 + .../src/commands/domain/agent_id.rs | 70 + .../src/commands/domain/agent_id/tests.rs | 40 + .../src/commands/domain/agent_name.rs | 51 + .../src/commands/domain/agent_name/tests.rs | 23 + .../src/commands/domain/annotations.rs | 46 + .../src/commands/domain/annotations/tests.rs | 29 + .../src/commands/domain/content_digest.rs | 66 + .../commands/domain/content_digest/tests.rs | 31 + .../src/commands/domain/delegate_selectors.rs | 55 + .../domain/delegate_selectors/tests.rs | 30 + .../src/commands/domain/kubernetes_syntax.rs | 52 + .../src/commands/domain/labels.rs | 54 + .../src/commands/domain/labels/tests.rs | 46 + .../src/commands/domain/mod.rs | 33 + .../src/commands/domain/model_id.rs | 51 + .../src/commands/domain/model_id/tests.rs | 17 + .../src/commands/domain/model_parameters.rs | 30 + .../commands/domain/model_parameters/tests.rs | 12 + .../src/commands/domain/nonblank.rs | 17 + .../src/commands/domain/parent_ref.rs | 51 + .../src/commands/domain/parent_ref/tests.rs | 21 + .../src/commands/domain/principal.rs | 89 + .../src/commands/domain/principal/tests.rs | 62 + .../src/commands/domain/revision_number.rs | 34 + .../commands/domain/revision_number/tests.rs | 14 + .../src/commands/domain/runtime_id.rs | 51 + .../src/commands/domain/runtime_id/tests.rs | 17 + .../src/commands/domain/tool_selectors.rs | 55 + .../commands/domain/tool_selectors/tests.rs | 30 + .../src/commands/event_fold.rs | 34 + .../src/commands/event_fold/tests.rs | 30 + .../src/commands/mod.rs | 10 + .../src/commands/proto_wire.rs | 285 +++ .../src/commands/proto_wire/tests.rs | 277 +++ .../src/commands/provision_agent.rs | 99 + .../src/commands/provision_agent/tests.rs | 86 + .../src/commands/test_support.rs | 92 + .../agents/trogonai-agents-domain/src/lib.rs | 7 + .../crates/platform/trogonai-proto/Cargo.toml | 1 + .../trogonai-proto/src/agents/agents/codec.rs | 69 + .../src/agents/agents/codec/tests.rs | 92 + .../trogonai-proto/src/agents/agents/mod.rs | 22 + .../platform/trogonai-proto/src/agents/mod.rs | 5 + .../platform/trogonai-proto/src/gen/mod.rs | 72 + .../trogonai.agents.agents.state.v1.mod.rs | 38 + ...s.state.v1.provision_agent_state.__view.rs | 866 +++++++ ...s.agents.state.v1.provision_agent_state.rs | 380 +++ .../trogonai.agents.agents.v1.agent.__view.rs | 2097 +++++++++++++++++ .../gen/trogonai.agents.agents.v1.agent.rs | 942 ++++++++ ...ents.agents.v1.agent_provisioned.__view.rs | 773 ++++++ ...onai.agents.agents.v1.agent_provisioned.rs | 343 +++ ...rogonai.agents.agents.v1.events.__oneof.rs | 39 + ...trogonai.agents.agents.v1.events.__view.rs | 312 +++ ...ai.agents.agents.v1.events.__view_oneof.rs | 15 + .../gen/trogonai.agents.agents.v1.events.rs | 203 ++ .../src/gen/trogonai.agents.agents.v1.mod.rs | 94 + ...agents.agents.v1.provision_agent.__view.rs | 719 ++++++ ...ogonai.agents.agents.v1.provision_agent.rs | 320 +++ .../crates/platform/trogonai-proto/src/lib.rs | 20 +- .../platform/trogonai-proto/src/tests.rs | 38 +- 71 files changed, 9871 insertions(+), 9 deletions(-) create mode 100644 proto/trogonai/agents/agents/state/v1/provision_agent_state.proto create mode 100644 proto/trogonai/agents/agents/v1/agent.proto create mode 100644 proto/trogonai/agents/agents/v1/agent_provisioned.proto create mode 100644 proto/trogonai/agents/agents/v1/events.proto create mode 100644 proto/trogonai/agents/agents/v1/provision_agent.proto create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/Cargo.toml create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_charter.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_charter/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_definition.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_definition/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_id.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_id/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_name.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_name/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/annotations.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/annotations/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/content_digest.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/content_digest/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/delegate_selectors.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/delegate_selectors/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/kubernetes_syntax.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/labels.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/labels/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/mod.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_id.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_id/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_parameters.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_parameters/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/nonblank.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/parent_ref.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/parent_ref/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/principal.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/principal/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/revision_number.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/revision_number/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/runtime_id.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/runtime_id/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/tool_selectors.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/tool_selectors/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/event_fold.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/event_fold/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/mod.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/proto_wire.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/proto_wire/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/provision_agent.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/provision_agent/tests.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/commands/test_support.rs create mode 100644 rsworkspace/crates/agents/trogonai-agents-domain/src/lib.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/agents/agents/codec.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/agents/agents/codec/tests.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/agents/agents/mod.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/agents/mod.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.mod.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.provision_agent_state.__view.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.provision_agent_state.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.agent.__view.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.agent.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.agent_provisioned.__view.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.agent_provisioned.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.events.__oneof.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.events.__view.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.events.__view_oneof.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.events.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.mod.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.provision_agent.__view.rs create mode 100644 rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.provision_agent.rs diff --git a/proto/trogonai/agents/agents/state/v1/provision_agent_state.proto b/proto/trogonai/agents/agents/state/v1/provision_agent_state.proto new file mode 100644 index 0000000000..d928a64bfb --- /dev/null +++ b/proto/trogonai/agents/agents/state/v1/provision_agent_state.proto @@ -0,0 +1,23 @@ +edition = "2024"; + +package trogonai.agents.agents.state.v1; + +import "trogonai/agents/agents/v1/agent.proto"; + +message ProvisionAgentState { + // Absence means the agent has not been provisioned. + ProvisionedAgent provisioned = 1; +} + +// ProvisionedAgent contains only the persistent fields used to classify +// retries. Delivery metadata and the provisioning actor never enter state. +message ProvisionedAgent { + string agent_id = 1 [features.field_presence = LEGACY_REQUIRED]; + string name = 2 [features.field_presence = LEGACY_REQUIRED]; + string parent = 3 [features.field_presence = LEGACY_REQUIRED]; + string owner = 4 [features.field_presence = LEGACY_REQUIRED]; + repeated trogonai.agents.agents.v1.Label labels = 5; + map annotations = 6; + trogonai.agents.agents.v1.Charter charter = 7 [features.field_presence = LEGACY_REQUIRED]; + string content_digest = 8 [features.field_presence = LEGACY_REQUIRED]; +} diff --git a/proto/trogonai/agents/agents/v1/agent.proto b/proto/trogonai/agents/agents/v1/agent.proto new file mode 100644 index 0000000000..a7d692f02f --- /dev/null +++ b/proto/trogonai/agents/agents/v1/agent.proto @@ -0,0 +1,57 @@ +edition = "2024"; + +package trogonai.agents.agents.v1; + +// Charter is the minimal, versioned declaration of an agent (design Q16): +// identity, engine, and dependency declarations. Nothing economic, temporal, +// evaluative, or reputational lives here; those are stances on their own +// planes (policy, evaluation, scheduling), selector-bound and human-owned. +message Charter { + // Execution engine. Immutable for the agent's v1 lifetime; switching + // engines mints a new sibling agent rather than a revision (design Q24). + string runtime = 1 [features.field_presence = LEGACY_REQUIRED]; + // Default model; a per-session override does not mint a revision. + Model model = 2 [features.field_presence = LEGACY_REQUIRED]; + ToolDependencies tools = 3 [features.field_presence = LEGACY_REQUIRED]; + DelegateDependencies delegates = 4 [features.field_presence = LEGACY_REQUIRED]; +} + +// ModelParameter is one deterministic model-configuration entry. Entries must +// have unique keys and be ordered by key before they cross the wire boundary. +message ModelParameter { + string key = 1 [features.field_presence = LEGACY_REQUIRED]; + string value = 2 [features.field_presence = LEGACY_REQUIRED]; +} + +// Label is selectable metadata. Entries must have unique keys and be ordered +// by key so guest Wasm receives a deterministic sequence. +message Label { + string key = 1 [features.field_presence = LEGACY_REQUIRED]; + string value = 2 [features.field_presence = LEGACY_REQUIRED]; +} + +// Model is a default, not a constraint; sessions may override it without +// minting a revision. +message Model { + string id = 1 [features.field_presence = LEGACY_REQUIRED]; + repeated ModelParameter params = 2; +} + +// ToolDependencies are declarations, not grants. Grants are human-held, +// evaluated live at every tool call on the Grants stream (design Q8/Q16). +message ToolDependencies { + repeated string required = 1; + repeated string optional = 2; +} + +// DelegateDependencies are declarations, not permissions. +message DelegateDependencies { + repeated string required = 1; + repeated string optional = 2; +} + +// RevisionRef pins a specific revision by number and content digest. +message RevisionRef { + uint64 number = 1 [features.field_presence = LEGACY_REQUIRED]; + string content_digest = 2 [features.field_presence = LEGACY_REQUIRED]; +} diff --git a/proto/trogonai/agents/agents/v1/agent_provisioned.proto b/proto/trogonai/agents/agents/v1/agent_provisioned.proto new file mode 100644 index 0000000000..e0e114d06a --- /dev/null +++ b/proto/trogonai/agents/agents/v1/agent_provisioned.proto @@ -0,0 +1,32 @@ +edition = "2024"; + +package trogonai.agents.agents.v1; + +import "trogonai/agents/agents/v1/agent.proto"; + +// AgentProvisioned is the Agent registry stream's genesis event; implicitly revision 1. +// No limits, no rubrics, no grants live here (design Q15/Q16); those are +// stances on their own planes. +message AgentProvisioned { + string agent_id = 1 [features.field_presence = LEGACY_REQUIRED]; + string name = 2 [features.field_presence = LEGACY_REQUIRED]; + // Placement node ref (bare spelling per the spelling rule: unsuffixed + // `parent` means placement, kinship is always `parentId`). + string parent = 3 [features.field_presence = LEGACY_REQUIRED]; + // Opaque owner identity. The human-owner requirement is enforced before + // command dispatch, where principal kind is known. + string owner = 4 [features.field_presence = LEGACY_REQUIRED]; + // Selector-only keys (e.g. family); bounded, matchable syntax. + repeated Label labels = 5; + // Permanent opaque metadata recorded on the event and Agent projection. + map annotations = 6; + Charter charter = 7 [features.field_presence = LEGACY_REQUIRED]; + // Opaque identity that performed the provisioning operation. + string provisioned_by = 8 [features.field_presence = LEGACY_REQUIRED]; + // Implicit revision 1: the provisioned definition, addressed by number + // and content digest so downstream readers never need to special-case + // the genesis revision. + RevisionRef revision = 9 [features.field_presence = LEGACY_REQUIRED]; + // Delivery-scoped metadata; never part of the Agent projection. + map transient_annotations = 10; +} diff --git a/proto/trogonai/agents/agents/v1/events.proto b/proto/trogonai/agents/agents/v1/events.proto new file mode 100644 index 0000000000..de36eb5cd9 --- /dev/null +++ b/proto/trogonai/agents/agents/v1/events.proto @@ -0,0 +1,12 @@ +edition = "2024"; + +package trogonai.agents.agents.v1; + +import "trogonai/agents/agents/v1/agent_provisioned.proto"; + +// AgentEvent is the Agent registry stream's event union. +message AgentEvent { + oneof event { + AgentProvisioned agent_provisioned = 1; + } +} diff --git a/proto/trogonai/agents/agents/v1/provision_agent.proto b/proto/trogonai/agents/agents/v1/provision_agent.proto new file mode 100644 index 0000000000..f0067b846b --- /dev/null +++ b/proto/trogonai/agents/agents/v1/provision_agent.proto @@ -0,0 +1,26 @@ +edition = "2024"; + +package trogonai.agents.agents.v1; + +import "trogonai/agents/agents/v1/agent.proto"; + +// Wire command to provision a new agent. Retries within one agent_id stream +// are classified as identical or conflicting definitions. Name uniqueness at +// the requested parent is an upstream precondition outside this decider. +message ProvisionAgent { + string agent_id = 1 [features.field_presence = LEGACY_REQUIRED]; + string name = 2 [features.field_presence = LEGACY_REQUIRED]; + string parent = 3 [features.field_presence = LEGACY_REQUIRED]; + string owner = 4 [features.field_presence = LEGACY_REQUIRED]; + repeated Label labels = 5; + // Permanent opaque metadata for this write and the initial Agent projection. + map annotations = 6; + Charter charter = 7 [features.field_presence = LEGACY_REQUIRED]; + // Opaque identity requesting the provisioning operation. + string principal = 8 [features.field_presence = LEGACY_REQUIRED]; + // SHA-256 digest of the full initial revision bundle, including content that + // is stored outside this decider contract. + string content_digest = 9 [features.field_presence = LEGACY_REQUIRED]; + // Delivery-scoped metadata propagated to emitted records, never projected. + map transient_annotations = 10; +} diff --git a/rsworkspace/Cargo.lock b/rsworkspace/Cargo.lock index a83d9cf062..2a9b5892cb 100644 --- a/rsworkspace/Cargo.lock +++ b/rsworkspace/Cargo.lock @@ -6789,6 +6789,16 @@ dependencies = [ "trogon-std", ] +[[package]] +name = "trogonai-agents-domain" +version = "0.1.0" +dependencies = [ + "buffa", + "thiserror 2.0.18", + "trogon-decider", + "trogonai-proto", +] + [[package]] name = "trogonai-proto" version = "0.1.0" diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/Cargo.toml b/rsworkspace/crates/agents/trogonai-agents-domain/Cargo.toml new file mode 100644 index 0000000000..d2dccd5137 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "trogonai-agents-domain" +version = "0.1.0" +edition = "2024" +license = "Apache-2.0" +publish = false + +[lints] +workspace = true + +[dependencies] +buffa = { workspace = true } +thiserror = { workspace = true } +trogon-decider = { workspace = true } +trogonai-proto = { path = "../../platform/trogonai-proto", default-features = false, features = ["agents"] } + +[dev-dependencies] +trogon-decider = { workspace = true, features = ["test-support"] } diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_charter.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_charter.rs new file mode 100644 index 0000000000..262fec8e62 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_charter.rs @@ -0,0 +1,51 @@ +use super::{DelegateSelectors, ModelId, ModelParameters, RuntimeId, ToolSelectors}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AgentCharter { + runtime: RuntimeId, + model_id: ModelId, + model_params: ModelParameters, + tools: ToolSelectors, + delegates: DelegateSelectors, +} + +impl AgentCharter { + pub const fn new( + runtime: RuntimeId, + model_id: ModelId, + model_params: ModelParameters, + tools: ToolSelectors, + delegates: DelegateSelectors, + ) -> Self { + Self { + runtime, + model_id, + model_params, + tools, + delegates, + } + } + + pub const fn runtime(&self) -> &RuntimeId { + &self.runtime + } + + pub const fn model_id(&self) -> &ModelId { + &self.model_id + } + + pub const fn model_params(&self) -> &ModelParameters { + &self.model_params + } + + pub const fn tools(&self) -> &ToolSelectors { + &self.tools + } + + pub const fn delegates(&self) -> &DelegateSelectors { + &self.delegates + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_charter/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_charter/tests.rs new file mode 100644 index 0000000000..af85287420 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_charter/tests.rs @@ -0,0 +1,14 @@ +use super::*; + +#[test] +fn composes_independently_validated_charter_values() { + let charter = AgentCharter::new( + RuntimeId::parse("claude-code").unwrap(), + ModelId::parse("anthropic/opus").unwrap(), + ModelParameters::default(), + ToolSelectors::default(), + DelegateSelectors::default(), + ); + assert_eq!(charter.runtime().as_str(), "claude-code"); + assert_eq!(charter.model_id().as_str(), "anthropic/opus"); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_definition.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_definition.rs new file mode 100644 index 0000000000..7397eec422 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_definition.rs @@ -0,0 +1,58 @@ +use super::{AgentCharter, AgentName, Annotations, Labels, ParentRef, Principal}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AgentDefinition { + name: AgentName, + parent: ParentRef, + owner: Principal, + labels: Labels, + annotations: Annotations, + charter: AgentCharter, +} + +impl AgentDefinition { + pub fn new( + name: AgentName, + parent: ParentRef, + owner: Principal, + labels: Labels, + annotations: Annotations, + charter: AgentCharter, + ) -> Self { + Self { + name, + parent, + owner, + labels, + annotations, + charter, + } + } + + pub fn name(&self) -> &AgentName { + &self.name + } + + pub fn parent(&self) -> &ParentRef { + &self.parent + } + + pub fn owner(&self) -> &Principal { + &self.owner + } + + pub fn labels(&self) -> &Labels { + &self.labels + } + + pub fn annotations(&self) -> &Annotations { + &self.annotations + } + + pub fn charter(&self) -> &AgentCharter { + &self.charter + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_definition/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_definition/tests.rs new file mode 100644 index 0000000000..6564bb2250 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_definition/tests.rs @@ -0,0 +1,22 @@ +use super::*; +use crate::commands::domain::{DelegateSelectors, ModelId, ModelParameters, RuntimeId, ToolSelectors}; + +#[test] +fn composes_only_validated_domain_values() { + let definition = AgentDefinition::new( + AgentName::parse("reviewer").unwrap(), + ParentRef::parse("org/root").unwrap(), + Principal::parse("owner@tenant").unwrap(), + Labels::default(), + Annotations::default(), + AgentCharter::new( + RuntimeId::parse("runtime").unwrap(), + ModelId::parse("model").unwrap(), + ModelParameters::default(), + ToolSelectors::default(), + DelegateSelectors::default(), + ), + ); + assert_eq!(definition.name().as_str(), "reviewer"); + assert_eq!(definition.parent().as_str(), "org/root"); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_id.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_id.rs new file mode 100644 index 0000000000..8409c4ebda --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_id.rs @@ -0,0 +1,70 @@ +use std::fmt; +use std::str::FromStr; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AgentId(String); + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +pub enum AgentIdViolation { + #[error("must not be empty")] + Empty, + #[error("must not have leading or trailing whitespace")] + SurroundingWhitespace, +} + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[error("agent id '{raw}' is invalid: {violation}")] +pub struct AgentIdError { + raw: String, + violation: AgentIdViolation, +} + +impl AgentId { + pub fn parse(raw: &str) -> Result { + if raw.is_empty() { + return Err(AgentIdError::new(raw, AgentIdViolation::Empty)); + } + + if raw.trim() != raw { + return Err(AgentIdError::new(raw, AgentIdViolation::SurroundingWhitespace)); + } + + Ok(Self(raw.to_string())) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl AsRef for AgentId { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl FromStr for AgentId { + type Err = AgentIdError; + + fn from_str(s: &str) -> Result { + Self::parse(s) + } +} + +impl AgentIdError { + fn new(raw: &str, violation: AgentIdViolation) -> Self { + Self { + raw: raw.to_string(), + violation, + } + } +} + +impl fmt::Display for AgentId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_id/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_id/tests.rs new file mode 100644 index 0000000000..62c4e158ce --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_id/tests.rs @@ -0,0 +1,40 @@ +use super::*; + +#[test] +fn rejects_empty() { + let error = AgentId::parse("").unwrap_err(); + assert_eq!(error.violation, AgentIdViolation::Empty); +} + +#[test] +fn rejects_surrounding_whitespace() { + for raw in [" agent-1", "agent-1 ", "\tagent-1", "agent-1\n"] { + let error = AgentId::parse(raw).unwrap_err(); + assert_eq!(error.violation, AgentIdViolation::SurroundingWhitespace, "{raw:?}"); + } +} + +#[test] +fn accepts_valid_ids() { + for raw in ["agent-1", "operator.agent42", "tenant_acme/agent"] { + assert_eq!(AgentId::parse(raw).unwrap().as_str(), raw, "{raw:?}"); + } +} + +#[test] +fn supports_standard_string_conversions_and_display() { + let id = AgentId::parse("agent-1").unwrap(); + + assert_eq!(id.as_ref(), "agent-1"); + assert_eq!("agent-1".parse::().unwrap().as_str(), "agent-1"); + assert_eq!(id.to_string(), "agent-1"); + assert_eq!(AgentIdViolation::Empty.to_string(), "must not be empty"); + assert_eq!( + AgentIdViolation::SurroundingWhitespace.to_string(), + "must not have leading or trailing whitespace" + ); + assert_eq!( + AgentId::parse("").unwrap_err().to_string(), + "agent id '' is invalid: must not be empty" + ); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_name.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_name.rs new file mode 100644 index 0000000000..51ae26e84a --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_name.rs @@ -0,0 +1,51 @@ +use std::fmt; +use std::str::FromStr; + +use super::nonblank::{NonBlankViolation, validate_nonblank}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct AgentName(String); + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +#[error("agent name '{raw}' is invalid: {violation}")] +pub struct AgentNameError { + raw: String, + violation: NonBlankViolation, +} + +impl AgentName { + pub fn parse(raw: &str) -> Result { + validate_nonblank(raw).map_err(|violation| AgentNameError { + raw: raw.to_string(), + violation, + })?; + Ok(Self(raw.to_string())) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl AsRef for AgentName { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl FromStr for AgentName { + type Err = AgentNameError; + + fn from_str(value: &str) -> Result { + Self::parse(value) + } +} + +impl fmt::Display for AgentName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_name/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_name/tests.rs new file mode 100644 index 0000000000..cae787156e --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/agent_name/tests.rs @@ -0,0 +1,23 @@ +use super::*; + +#[test] +fn accepts_nonblank_trimmed_name() { + let name = AgentName::parse("pr-reviewer").unwrap(); + assert_eq!(name.as_str(), "pr-reviewer"); +} + +#[test] +fn rejects_empty_and_surrounding_whitespace() { + assert!(AgentName::parse("").is_err()); + assert!(AgentName::parse(" pr-reviewer").is_err()); + assert!(AgentName::parse("pr-reviewer ").is_err()); +} + +#[test] +fn supports_standard_string_conversions_and_display() { + let value = AgentName::parse("pr-reviewer").unwrap(); + + assert_eq!(value.as_ref(), "pr-reviewer"); + assert_eq!("pr-reviewer".parse::().unwrap().as_str(), "pr-reviewer"); + assert_eq!(value.to_string(), "pr-reviewer"); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/annotations.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/annotations.rs new file mode 100644 index 0000000000..25fb38cc67 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/annotations.rs @@ -0,0 +1,46 @@ +use std::collections::BTreeMap; + +use super::kubernetes_syntax::valid_qualified_name; + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct Annotations(BTreeMap); + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +#[error("annotation key '{key}' does not use Kubernetes qualified-name syntax")] +pub struct AnnotationsError { + key: String, +} + +impl Annotations { + pub fn new(values: BTreeMap) -> Result { + for key in values.keys() { + if !valid_qualified_name(key) { + return Err(AnnotationsError { key: key.clone() }); + } + } + Ok(Self(values)) + } + + pub fn as_map(&self) -> &BTreeMap { + &self.0 + } + + pub fn get(&self, key: &str) -> Option<&str> { + self.0.get(key).map(String::as_str) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl TryFrom> for Annotations { + type Error = AnnotationsError; + + fn try_from(value: BTreeMap) -> Result { + Self::new(value) + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/annotations/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/annotations/tests.rs new file mode 100644 index 0000000000..5dc10a5879 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/annotations/tests.rs @@ -0,0 +1,29 @@ +use std::collections::BTreeMap; + +use super::*; + +#[test] +fn preserves_opaque_annotation_values_in_key_order() { + let annotations = Annotations::new(BTreeMap::from([ + ("z.example.com/note".to_string(), " spaces and unicode ✓ ".to_string()), + ("empty".to_string(), String::new()), + ])) + .unwrap(); + assert_eq!(annotations.get("z.example.com/note"), Some(" spaces and unicode ✓ ")); + assert_eq!(annotations.as_map().keys().next().map(String::as_str), Some("empty")); +} + +#[test] +fn rejects_invalid_annotation_key() { + assert!(Annotations::new(BTreeMap::from([("bad/key/again".to_string(), "x".to_string())])).is_err()); +} + +#[test] +fn reports_emptiness_and_supports_try_from() { + assert!(Annotations::new(BTreeMap::new()).unwrap().is_empty()); + let annotations: Annotations = BTreeMap::from([("app".to_string(), "reviewer".to_string())]) + .try_into() + .unwrap(); + assert!(!annotations.is_empty()); + assert_eq!(annotations.get("app"), Some("reviewer")); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/content_digest.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/content_digest.rs new file mode 100644 index 0000000000..f66a5b084a --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/content_digest.rs @@ -0,0 +1,66 @@ +use std::fmt; +use std::str::FromStr; + +const PREFIX: &str = "sha256:"; +const HEX_LENGTH: usize = 64; +const ENCODED_LENGTH: usize = PREFIX.len() + HEX_LENGTH; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ContentDigest(String); + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum ContentDigestError { + #[error("content digest must start with 'sha256:'")] + InvalidPrefix, + #[error("content digest must contain exactly 64 lowercase hexadecimal digits, got {actual}")] + InvalidLength { actual: usize }, + #[error("content digest contains invalid hexadecimal character '{character}' at index {index}")] + InvalidHex { index: usize, character: char }, +} + +impl ContentDigest { + pub fn parse(raw: &str) -> Result { + let Some(hex) = raw.strip_prefix(PREFIX) else { + return Err(ContentDigestError::InvalidPrefix); + }; + if raw.len() != ENCODED_LENGTH { + return Err(ContentDigestError::InvalidLength { + actual: hex.chars().count(), + }); + } + if let Some((index, character)) = hex + .char_indices() + .find(|(_, character)| !matches!(character, '0'..='9' | 'a'..='f')) + { + return Err(ContentDigestError::InvalidHex { index, character }); + } + Ok(Self(raw.to_string())) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl AsRef for ContentDigest { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl FromStr for ContentDigest { + type Err = ContentDigestError; + + fn from_str(value: &str) -> Result { + Self::parse(value) + } +} + +impl fmt::Display for ContentDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/content_digest/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/content_digest/tests.rs new file mode 100644 index 0000000000..62af4e5455 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/content_digest/tests.rs @@ -0,0 +1,31 @@ +use super::*; + +const VALID: &str = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + +#[test] +fn accepts_canonical_sha256_digest() { + assert_eq!(ContentDigest::parse(VALID).unwrap().as_str(), VALID); +} + +#[test] +fn rejects_missing_prefix_wrong_length_and_noncanonical_hex() { + assert_eq!(ContentDigest::parse(""), Err(ContentDigestError::InvalidPrefix)); + assert_eq!( + ContentDigest::parse("sha256:abc"), + Err(ContentDigestError::InvalidLength { actual: 3 }) + ); + let uppercase = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdeF"; + assert!(matches!( + ContentDigest::parse(uppercase), + Err(ContentDigestError::InvalidHex { character: 'F', .. }) + )); +} + +#[test] +fn supports_standard_string_conversions_and_display() { + let digest = ContentDigest::parse(VALID).unwrap(); + + assert_eq!(digest.as_ref(), VALID); + assert_eq!(VALID.parse::().unwrap(), digest); + assert_eq!(digest.to_string(), VALID); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/delegate_selectors.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/delegate_selectors.rs new file mode 100644 index 0000000000..81bbd074e8 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/delegate_selectors.rs @@ -0,0 +1,55 @@ +use std::collections::BTreeSet; + +use super::nonblank::validate_nonblank; + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct DelegateSelectors { + required: BTreeSet, + optional: BTreeSet, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum DelegateSelectorsError { + #[error("required delegate selector '{selector}' must be nonblank and trimmed")] + InvalidRequired { selector: String }, + #[error("optional delegate selector '{selector}' must be nonblank and trimmed")] + InvalidOptional { selector: String }, + #[error("delegate selector '{selector}' cannot be both required and optional")] + Overlap { selector: String }, +} + +impl DelegateSelectors { + pub fn new(required: BTreeSet, optional: BTreeSet) -> Result { + for selector in &required { + if validate_nonblank(selector).is_err() { + return Err(DelegateSelectorsError::InvalidRequired { + selector: selector.clone(), + }); + } + } + for selector in &optional { + if validate_nonblank(selector).is_err() { + return Err(DelegateSelectorsError::InvalidOptional { + selector: selector.clone(), + }); + } + } + if let Some(selector) = required.intersection(&optional).next() { + return Err(DelegateSelectorsError::Overlap { + selector: selector.clone(), + }); + } + Ok(Self { required, optional }) + } + + pub fn required(&self) -> &BTreeSet { + &self.required + } + + pub fn optional(&self) -> &BTreeSet { + &self.optional + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/delegate_selectors/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/delegate_selectors/tests.rs new file mode 100644 index 0000000000..e1c4d1add4 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/delegate_selectors/tests.rs @@ -0,0 +1,30 @@ +use super::*; + +#[test] +fn validates_ordered_disjoint_delegate_selectors() { + let selectors = DelegateSelectors::new( + BTreeSet::from(["family=reviewer".to_string()]), + BTreeSet::from(["family=researcher".to_string()]), + ) + .unwrap(); + assert!(selectors.required().contains("family=reviewer")); + assert!(matches!( + DelegateSelectors::new( + BTreeSet::from(["family=reviewer".to_string()]), + BTreeSet::from(["family=reviewer".to_string()]) + ), + Err(DelegateSelectorsError::Overlap { .. }) + )); +} + +#[test] +fn rejects_blank_required_and_optional_selectors() { + assert!(matches!( + DelegateSelectors::new(BTreeSet::from([" family=reviewer".to_string()]), BTreeSet::new()), + Err(DelegateSelectorsError::InvalidRequired { .. }) + )); + assert!(matches!( + DelegateSelectors::new(BTreeSet::new(), BTreeSet::from([" family=researcher".to_string()])), + Err(DelegateSelectorsError::InvalidOptional { .. }) + )); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/kubernetes_syntax.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/kubernetes_syntax.rs new file mode 100644 index 0000000000..05ae90c6c2 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/kubernetes_syntax.rs @@ -0,0 +1,52 @@ +pub(crate) fn valid_qualified_name(value: &str) -> bool { + let mut parts = value.split('/'); + let first = parts.next().unwrap_or_default(); + let second = parts.next(); + if parts.next().is_some() { + return false; + } + match second { + Some(name) => valid_dns_subdomain(first) && valid_name_part(name), + None => valid_name_part(first), + } +} + +pub(crate) fn valid_label_value(value: &str) -> bool { + value.is_empty() || valid_name_part(value) +} + +fn valid_name_part(value: &str) -> bool { + if value.is_empty() || value.len() > 63 || !value.is_ascii() { + return false; + } + let bytes = value.as_bytes(); + is_ascii_alphanumeric(bytes[0]) + && is_ascii_alphanumeric(bytes[bytes.len() - 1]) + && bytes + .iter() + .all(|byte| is_ascii_alphanumeric(*byte) || matches!(byte, b'-' | b'_' | b'.')) +} + +fn valid_dns_subdomain(value: &str) -> bool { + !value.is_empty() && value.len() <= 253 && value.split('.').all(valid_dns_label) +} + +fn valid_dns_label(value: &str) -> bool { + if value.is_empty() || value.len() > 63 || !value.is_ascii() { + return false; + } + let bytes = value.as_bytes(); + is_ascii_lower_alphanumeric(bytes[0]) + && is_ascii_lower_alphanumeric(bytes[bytes.len() - 1]) + && bytes + .iter() + .all(|byte| is_ascii_lower_alphanumeric(*byte) || *byte == b'-') +} + +const fn is_ascii_alphanumeric(byte: u8) -> bool { + byte.is_ascii_alphanumeric() +} + +const fn is_ascii_lower_alphanumeric(byte: u8) -> bool { + byte.is_ascii_lowercase() || byte.is_ascii_digit() +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/labels.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/labels.rs new file mode 100644 index 0000000000..7bebce321f --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/labels.rs @@ -0,0 +1,54 @@ +use std::collections::BTreeMap; + +use super::kubernetes_syntax::{valid_label_value, valid_qualified_name}; + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct Labels(BTreeMap); + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum LabelsError { + #[error("label key '{key}' does not use Kubernetes qualified-name syntax")] + InvalidKey { key: String }, + #[error("label '{key}' has value '{value}' outside Kubernetes label-value syntax")] + InvalidValue { key: String, value: String }, +} + +impl Labels { + pub fn new(values: BTreeMap) -> Result { + for (key, value) in &values { + if !valid_qualified_name(key) { + return Err(LabelsError::InvalidKey { key: key.clone() }); + } + if !valid_label_value(value) { + return Err(LabelsError::InvalidValue { + key: key.clone(), + value: value.clone(), + }); + } + } + Ok(Self(values)) + } + + pub fn as_map(&self) -> &BTreeMap { + &self.0 + } + + pub fn get(&self, key: &str) -> Option<&str> { + self.0.get(key).map(String::as_str) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl TryFrom> for Labels { + type Error = LabelsError; + + fn try_from(value: BTreeMap) -> Result { + Self::new(value) + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/labels/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/labels/tests.rs new file mode 100644 index 0000000000..a4a05a1db0 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/labels/tests.rs @@ -0,0 +1,46 @@ +use std::collections::BTreeMap; + +use super::*; + +#[test] +fn accepts_and_orders_kubernetes_labels() { + let labels = Labels::new(BTreeMap::from([ + ("team.example.com/family".to_string(), "reviewer.v1".to_string()), + ("app".to_string(), String::new()), + ])) + .unwrap(); + assert_eq!( + labels.as_map().keys().map(String::as_str).collect::>(), + vec!["app", "team.example.com/family"] + ); +} + +#[test] +fn rejects_invalid_key_prefix_and_value_boundaries() { + assert!(matches!( + Labels::new(BTreeMap::from([("UPPER.example/key".to_string(), "ok".to_string())])), + Err(LabelsError::InvalidKey { .. }) + )); + assert!(matches!( + Labels::new(BTreeMap::from([("family".to_string(), "-bad".to_string())])), + Err(LabelsError::InvalidValue { .. }) + )); + assert!(matches!( + Labels::new(BTreeMap::from([("family".to_string(), "a".repeat(64))])), + Err(LabelsError::InvalidValue { .. }) + )); + assert!(matches!( + Labels::new(BTreeMap::from([("a..b/key".to_string(), "ok".to_string())])), + Err(LabelsError::InvalidKey { .. }) + )); +} + +#[test] +fn reports_emptiness_and_supports_try_from() { + assert!(Labels::new(BTreeMap::new()).unwrap().is_empty()); + let labels: Labels = BTreeMap::from([("app".to_string(), "reviewer".to_string())]) + .try_into() + .unwrap(); + assert!(!labels.is_empty()); + assert_eq!(labels.get("app"), Some("reviewer")); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/mod.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/mod.rs new file mode 100644 index 0000000000..83abf8bedc --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/mod.rs @@ -0,0 +1,33 @@ +mod agent_charter; +mod agent_definition; +mod agent_id; +mod agent_name; +mod annotations; +mod content_digest; +mod delegate_selectors; +mod kubernetes_syntax; +mod labels; +mod model_id; +mod model_parameters; +mod nonblank; +mod parent_ref; +mod principal; +mod revision_number; +mod runtime_id; +mod tool_selectors; + +pub use agent_charter::AgentCharter; +pub use agent_definition::AgentDefinition; +pub use agent_id::{AgentId, AgentIdError, AgentIdViolation}; +pub use agent_name::{AgentName, AgentNameError}; +pub use annotations::{Annotations, AnnotationsError}; +pub use content_digest::{ContentDigest, ContentDigestError}; +pub use delegate_selectors::{DelegateSelectors, DelegateSelectorsError}; +pub use labels::{Labels, LabelsError}; +pub use model_id::{ModelId, ModelIdError}; +pub use model_parameters::{ModelParameters, ModelParametersError}; +pub use parent_ref::{ParentRef, ParentRefError}; +pub use principal::{Principal, PrincipalError, PrincipalViolation}; +pub use revision_number::{RevisionNumber, RevisionNumberError}; +pub use runtime_id::{RuntimeId, RuntimeIdError}; +pub use tool_selectors::{ToolSelectors, ToolSelectorsError}; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_id.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_id.rs new file mode 100644 index 0000000000..cda680c528 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_id.rs @@ -0,0 +1,51 @@ +use std::fmt; +use std::str::FromStr; + +use super::nonblank::{NonBlankViolation, validate_nonblank}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ModelId(String); + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +#[error("model id '{raw}' is invalid: {violation}")] +pub struct ModelIdError { + raw: String, + violation: NonBlankViolation, +} + +impl ModelId { + pub fn parse(raw: &str) -> Result { + validate_nonblank(raw).map_err(|violation| ModelIdError { + raw: raw.to_string(), + violation, + })?; + Ok(Self(raw.to_string())) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl AsRef for ModelId { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl FromStr for ModelId { + type Err = ModelIdError; + + fn from_str(value: &str) -> Result { + Self::parse(value) + } +} + +impl fmt::Display for ModelId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_id/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_id/tests.rs new file mode 100644 index 0000000000..59f2cc917e --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_id/tests.rs @@ -0,0 +1,17 @@ +use super::*; + +#[test] +fn validates_model_id() { + assert_eq!(ModelId::parse("anthropic/opus").unwrap().as_str(), "anthropic/opus"); + assert!(ModelId::parse("").is_err()); + assert!(ModelId::parse("anthropic/opus ").is_err()); +} + +#[test] +fn supports_standard_string_conversions_and_display() { + let value = ModelId::parse("anthropic/opus").unwrap(); + + assert_eq!(value.as_ref(), "anthropic/opus"); + assert_eq!("anthropic/opus".parse::().unwrap().as_str(), "anthropic/opus"); + assert_eq!(value.to_string(), "anthropic/opus"); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_parameters.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_parameters.rs new file mode 100644 index 0000000000..bd6d5f7900 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_parameters.rs @@ -0,0 +1,30 @@ +use std::collections::BTreeMap; + +use super::nonblank::validate_nonblank; + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct ModelParameters(BTreeMap); + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +#[error("model parameter key '{key}' must be nonblank and trimmed")] +pub struct ModelParametersError { + key: String, +} + +impl ModelParameters { + pub fn new(values: BTreeMap) -> Result { + for key in values.keys() { + if validate_nonblank(key).is_err() { + return Err(ModelParametersError { key: key.clone() }); + } + } + Ok(Self(values)) + } + + pub fn as_map(&self) -> &BTreeMap { + &self.0 + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_parameters/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_parameters/tests.rs new file mode 100644 index 0000000000..500b001c7c --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/model_parameters/tests.rs @@ -0,0 +1,12 @@ +use super::*; + +#[test] +fn validates_ordered_parameters_but_keeps_values_opaque() { + let params = ModelParameters::new(BTreeMap::from([ + ("temperature".to_string(), "0.2".to_string()), + ("note".to_string(), " opaque value ".to_string()), + ])) + .unwrap(); + assert_eq!(params.as_map().keys().next().map(String::as_str), Some("note")); + assert!(ModelParameters::new(BTreeMap::from([(String::new(), "x".to_string())])).is_err()); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/nonblank.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/nonblank.rs new file mode 100644 index 0000000000..716ececa4b --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/nonblank.rs @@ -0,0 +1,17 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] +pub(crate) enum NonBlankViolation { + #[error("must not be empty")] + Empty, + #[error("must not have leading or trailing whitespace")] + SurroundingWhitespace, +} + +pub(crate) fn validate_nonblank(raw: &str) -> Result<(), NonBlankViolation> { + if raw.is_empty() { + return Err(NonBlankViolation::Empty); + } + if raw.trim() != raw { + return Err(NonBlankViolation::SurroundingWhitespace); + } + Ok(()) +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/parent_ref.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/parent_ref.rs new file mode 100644 index 0000000000..ac57829051 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/parent_ref.rs @@ -0,0 +1,51 @@ +use std::fmt; +use std::str::FromStr; + +use super::nonblank::{NonBlankViolation, validate_nonblank}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ParentRef(String); + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +#[error("parent reference '{raw}' is invalid: {violation}")] +pub struct ParentRefError { + raw: String, + violation: NonBlankViolation, +} + +impl ParentRef { + pub fn parse(raw: &str) -> Result { + validate_nonblank(raw).map_err(|violation| ParentRefError { + raw: raw.to_string(), + violation, + })?; + Ok(Self(raw.to_string())) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl AsRef for ParentRef { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl FromStr for ParentRef { + type Err = ParentRefError; + + fn from_str(value: &str) -> Result { + Self::parse(value) + } +} + +impl fmt::Display for ParentRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/parent_ref/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/parent_ref/tests.rs new file mode 100644 index 0000000000..f85f03b56f --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/parent_ref/tests.rs @@ -0,0 +1,21 @@ +use super::*; + +#[test] +fn accepts_nonblank_trimmed_parent_ref() { + assert_eq!(ParentRef::parse("org/root").unwrap().as_str(), "org/root"); +} + +#[test] +fn rejects_blank_or_untrimmed_parent_ref() { + assert!(ParentRef::parse("").is_err()); + assert!(ParentRef::parse(" org/root").is_err()); +} + +#[test] +fn supports_standard_string_conversions_and_display() { + let value = ParentRef::parse("org/root").unwrap(); + + assert_eq!(value.as_ref(), "org/root"); + assert_eq!("org/root".parse::().unwrap().as_str(), "org/root"); + assert_eq!(value.to_string(), "org/root"); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/principal.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/principal.rs new file mode 100644 index 0000000000..17f997569b --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/principal.rs @@ -0,0 +1,89 @@ +use std::fmt; +use std::str::FromStr; + +const MAX_LENGTH: usize = 256; + +/// An opaque principal identifier carried by provisioning actor and owner fields. +/// The domain never distinguishes human from machine principals: that +/// distinction is authentication context (aauth, ADR-0017), an app-level +/// concern enforced at command dispatch, not inside `decide`. `Principal` +/// carries no kind tag and compares by value only. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Principal(String); + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +pub enum PrincipalViolation { + #[error("must not be empty")] + Empty, + #[error("must not have leading or trailing whitespace")] + SurroundingWhitespace, + #[error("must not exceed {max} characters, got {actual}")] + TooLong { max: usize, actual: usize }, +} + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[error("principal '{raw}' is invalid: {violation}")] +pub struct PrincipalError { + raw: String, + violation: PrincipalViolation, +} + +impl Principal { + pub fn parse(raw: &str) -> Result { + if raw.is_empty() { + return Err(PrincipalError::new(raw, PrincipalViolation::Empty)); + } + + if raw.trim() != raw { + return Err(PrincipalError::new(raw, PrincipalViolation::SurroundingWhitespace)); + } + + if raw.len() > MAX_LENGTH { + return Err(PrincipalError::new( + raw, + PrincipalViolation::TooLong { + max: MAX_LENGTH, + actual: raw.len(), + }, + )); + } + + Ok(Self(raw.to_string())) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl AsRef for Principal { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl FromStr for Principal { + type Err = PrincipalError; + + fn from_str(s: &str) -> Result { + Self::parse(s) + } +} + +impl PrincipalError { + fn new(raw: &str, violation: PrincipalViolation) -> Self { + Self { + raw: raw.to_string(), + violation, + } + } +} + +impl fmt::Display for Principal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/principal/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/principal/tests.rs new file mode 100644 index 0000000000..03940829e7 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/principal/tests.rs @@ -0,0 +1,62 @@ +use super::*; + +#[test] +fn rejects_empty() { + let error = Principal::parse("").unwrap_err(); + assert_eq!(error.violation, PrincipalViolation::Empty); +} + +#[test] +fn rejects_surrounding_whitespace() { + for raw in [ + " operator@tenant_acme", + "operator@tenant_acme ", + "\toperator@tenant_acme", + "operator@tenant_acme\n", + ] { + let error = Principal::parse(raw).unwrap_err(); + assert_eq!(error.violation, PrincipalViolation::SurroundingWhitespace, "{raw:?}"); + } +} + +#[test] +fn rejects_over_long_principals() { + let raw = "a".repeat(257); + let error = Principal::parse(&raw).unwrap_err(); + assert_eq!(error.violation, PrincipalViolation::TooLong { max: 256, actual: 257 }); +} + +#[test] +fn accepts_a_human_principal() { + let principal = Principal::parse("operator@tenant_acme").unwrap(); + assert_eq!(principal.as_str(), "operator@tenant_acme"); +} + +#[test] +fn accepts_a_machine_principal() { + let principal = Principal::parse("verifier_run_5521@platform").unwrap(); + assert_eq!(principal.as_str(), "verifier_run_5521@platform"); +} + +#[test] +fn display_round_trips_through_parse() { + for raw in ["operator@tenant_acme", "service@platform"] { + let principal = Principal::parse(raw).unwrap(); + let rendered = format!("{principal}"); + assert_eq!(rendered.parse::().unwrap(), principal); + } +} + +#[test] +fn supports_standard_string_conversions_and_display() { + let principal = Principal::parse("operator@tenant_acme").unwrap(); + + assert_eq!("operator@tenant_acme".parse::().unwrap(), principal); + assert_eq!(principal.as_ref(), "operator@tenant_acme"); + assert_eq!(principal.to_string(), "operator@tenant_acme"); + assert_eq!(PrincipalViolation::Empty.to_string(), "must not be empty"); + assert_eq!( + Principal::parse("").unwrap_err().to_string(), + "principal '' is invalid: must not be empty" + ); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/revision_number.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/revision_number.rs new file mode 100644 index 0000000000..66a345be85 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/revision_number.rs @@ -0,0 +1,34 @@ +use std::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RevisionNumber(u64); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] +pub enum RevisionNumberError { + #[error("revision number must be at least 1")] + Zero, +} + +impl RevisionNumber { + pub const GENESIS: Self = Self(1); + + pub fn new(value: u64) -> Result { + if value == 0 { + return Err(RevisionNumberError::Zero); + } + Ok(Self(value)) + } + + pub const fn get(self) -> u64 { + self.0 + } +} + +impl fmt::Display for RevisionNumber { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/revision_number/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/revision_number/tests.rs new file mode 100644 index 0000000000..cbf4113765 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/revision_number/tests.rs @@ -0,0 +1,14 @@ +use super::*; + +#[test] +fn rejects_zero() { + assert_eq!(RevisionNumber::new(0), Err(RevisionNumberError::Zero)); +} + +#[test] +fn orders_and_displays_by_value() { + let one = RevisionNumber::GENESIS; + let two = RevisionNumber::new(2).unwrap(); + assert!(one < two); + assert_eq!(two.to_string(), "2"); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/runtime_id.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/runtime_id.rs new file mode 100644 index 0000000000..69a901d3b3 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/runtime_id.rs @@ -0,0 +1,51 @@ +use std::fmt; +use std::str::FromStr; + +use super::nonblank::{NonBlankViolation, validate_nonblank}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RuntimeId(String); + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +#[error("runtime id '{raw}' is invalid: {violation}")] +pub struct RuntimeIdError { + raw: String, + violation: NonBlankViolation, +} + +impl RuntimeId { + pub fn parse(raw: &str) -> Result { + validate_nonblank(raw).map_err(|violation| RuntimeIdError { + raw: raw.to_string(), + violation, + })?; + Ok(Self(raw.to_string())) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl AsRef for RuntimeId { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl FromStr for RuntimeId { + type Err = RuntimeIdError; + + fn from_str(value: &str) -> Result { + Self::parse(value) + } +} + +impl fmt::Display for RuntimeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/runtime_id/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/runtime_id/tests.rs new file mode 100644 index 0000000000..2bb1ce4d02 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/runtime_id/tests.rs @@ -0,0 +1,17 @@ +use super::*; + +#[test] +fn validates_runtime_id() { + assert_eq!(RuntimeId::parse("claude-code").unwrap().as_str(), "claude-code"); + assert!(RuntimeId::parse("").is_err()); + assert!(RuntimeId::parse(" claude-code").is_err()); +} + +#[test] +fn supports_standard_string_conversions_and_display() { + let value = RuntimeId::parse("claude-code").unwrap(); + + assert_eq!(value.as_ref(), "claude-code"); + assert_eq!("claude-code".parse::().unwrap().as_str(), "claude-code"); + assert_eq!(value.to_string(), "claude-code"); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/tool_selectors.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/tool_selectors.rs new file mode 100644 index 0000000000..61174a1dee --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/tool_selectors.rs @@ -0,0 +1,55 @@ +use std::collections::BTreeSet; + +use super::nonblank::validate_nonblank; + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct ToolSelectors { + required: BTreeSet, + optional: BTreeSet, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum ToolSelectorsError { + #[error("required tool selector '{selector}' must be nonblank and trimmed")] + InvalidRequired { selector: String }, + #[error("optional tool selector '{selector}' must be nonblank and trimmed")] + InvalidOptional { selector: String }, + #[error("tool selector '{selector}' cannot be both required and optional")] + Overlap { selector: String }, +} + +impl ToolSelectors { + pub fn new(required: BTreeSet, optional: BTreeSet) -> Result { + for selector in &required { + if validate_nonblank(selector).is_err() { + return Err(ToolSelectorsError::InvalidRequired { + selector: selector.clone(), + }); + } + } + for selector in &optional { + if validate_nonblank(selector).is_err() { + return Err(ToolSelectorsError::InvalidOptional { + selector: selector.clone(), + }); + } + } + if let Some(selector) = required.intersection(&optional).next() { + return Err(ToolSelectorsError::Overlap { + selector: selector.clone(), + }); + } + Ok(Self { required, optional }) + } + + pub fn required(&self) -> &BTreeSet { + &self.required + } + + pub fn optional(&self) -> &BTreeSet { + &self.optional + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/tool_selectors/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/tool_selectors/tests.rs new file mode 100644 index 0000000000..9760694bb6 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/domain/tool_selectors/tests.rs @@ -0,0 +1,30 @@ +use super::*; + +#[test] +fn validates_ordered_disjoint_tool_selectors() { + let selectors = ToolSelectors::new( + BTreeSet::from(["bash".to_string()]), + BTreeSet::from(["web_search".to_string()]), + ) + .unwrap(); + assert!(selectors.required().contains("bash")); + assert!(matches!( + ToolSelectors::new( + BTreeSet::from(["bash".to_string()]), + BTreeSet::from(["bash".to_string()]) + ), + Err(ToolSelectorsError::Overlap { .. }) + )); +} + +#[test] +fn rejects_blank_required_and_optional_selectors() { + assert!(matches!( + ToolSelectors::new(BTreeSet::from([" bash".to_string()]), BTreeSet::new()), + Err(ToolSelectorsError::InvalidRequired { .. }) + )); + assert!(matches!( + ToolSelectors::new(BTreeSet::new(), BTreeSet::from([" web_search".to_string()])), + Err(ToolSelectorsError::InvalidOptional { .. }) + )); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/event_fold.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/event_fold.rs new file mode 100644 index 0000000000..0804098468 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/event_fold.rs @@ -0,0 +1,34 @@ +use trogonai_proto::agents::agents::{AgentEventCase, v1}; + +use super::domain::AgentId; + +#[derive(Debug, PartialEq, thiserror::Error)] +pub enum AgentEventFoldError { + #[error("agent event has no payload")] + MissingEvent, + #[error("event for agent '{actual}' was found in agent '{expected}' state")] + AgentIdMismatch { expected: AgentId, actual: AgentId }, + #[error("AgentProvisioned occurred more than once")] + DuplicateProvision, + #[error("invalid event payload: {0}")] + InvalidPayload(#[from] super::CommandWireError), +} + +pub fn provisioned_payload(event: &v1::AgentEvent) -> Result<&v1::AgentProvisioned, AgentEventFoldError> { + let AgentEventCase::AgentProvisioned(provisioned) = + event.event.as_ref().ok_or(AgentEventFoldError::MissingEvent)?; + Ok(provisioned) +} + +pub fn ensure_command_identity(expected: &AgentId, actual: &AgentId) -> Result<(), AgentEventFoldError> { + if expected != actual { + return Err(AgentEventFoldError::AgentIdMismatch { + expected: expected.clone(), + actual: actual.clone(), + }); + } + Ok(()) +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/event_fold/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/event_fold/tests.rs new file mode 100644 index 0000000000..596762a848 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/event_fold/tests.rs @@ -0,0 +1,30 @@ +use super::*; + +#[test] +fn rejects_an_event_without_a_payload() { + assert_eq!( + provisioned_payload(&v1::AgentEvent::default()), + Err(AgentEventFoldError::MissingEvent) + ); +} + +#[test] +fn extracts_the_provisioned_payload() { + let event = v1::AgentEvent { + event: Some(v1::AgentProvisioned::default().into()), + }; + + assert!(provisioned_payload(&event).is_ok()); +} + +#[test] +fn rejects_a_mismatched_command_identity() { + let expected = AgentId::parse("agent-1").unwrap(); + let actual = AgentId::parse("agent-2").unwrap(); + + assert!(matches!( + ensure_command_identity(&expected, &actual), + Err(AgentEventFoldError::AgentIdMismatch { expected, actual }) + if expected.as_str() == "agent-1" && actual.as_str() == "agent-2" + )); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/mod.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/mod.rs new file mode 100644 index 0000000000..33ffd0f4b4 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/mod.rs @@ -0,0 +1,10 @@ +pub mod domain; +mod event_fold; +mod proto_wire; +mod provision_agent; +#[cfg(test)] +mod test_support; + +pub use event_fold::AgentEventFoldError; +pub use proto_wire::CommandWireError; +pub use provision_agent::{ProvisionAgent, ProvisionAgentError}; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/proto_wire.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/proto_wire.rs new file mode 100644 index 0000000000..9bebb31365 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/proto_wire.rs @@ -0,0 +1,285 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use buffa::{MessageField, map_codec::Map as ProtoMap}; +use trogonai_proto::agents::agents::{state_v1, v1}; + +use super::ProvisionAgent; +use super::domain::{ + AgentCharter, AgentDefinition, AgentId, AgentIdError, AgentName, AgentNameError, Annotations, AnnotationsError, + ContentDigest, ContentDigestError, DelegateSelectors, DelegateSelectorsError, Labels, LabelsError, ModelId, + ModelIdError, ModelParameters, ModelParametersError, ParentRef, ParentRefError, Principal, PrincipalError, + RevisionNumber, RevisionNumberError, RuntimeId, RuntimeIdError, ToolSelectors, ToolSelectorsError, +}; + +#[derive(Debug, PartialEq, thiserror::Error)] +pub enum CommandWireError { + #[error("missing required field: {0}")] + MissingField(&'static str), + #[error("duplicate value '{value}' in field '{field}'")] + DuplicateValue { field: &'static str, value: String }, + #[error("initial revision must be 1, got {actual}")] + InvalidInitialRevision { actual: RevisionNumber }, + #[error("invalid agent id: {0}")] + InvalidAgentId(#[from] AgentIdError), + #[error("invalid agent name: {0}")] + InvalidAgentName(#[from] AgentNameError), + #[error("invalid parent reference: {0}")] + InvalidParent(#[from] ParentRefError), + #[error("invalid principal: {0}")] + InvalidPrincipal(#[from] PrincipalError), + #[error("invalid labels: {0}")] + InvalidLabels(#[from] LabelsError), + #[error("invalid annotations: {0}")] + InvalidAnnotations(#[from] AnnotationsError), + #[error("invalid runtime id: {0}")] + InvalidRuntime(#[from] RuntimeIdError), + #[error("invalid model id: {0}")] + InvalidModel(#[from] ModelIdError), + #[error("invalid model parameters: {0}")] + InvalidModelParameters(#[from] ModelParametersError), + #[error("invalid tool selectors: {0}")] + InvalidToolSelectors(#[from] ToolSelectorsError), + #[error("invalid delegate selectors: {0}")] + InvalidDelegateSelectors(#[from] DelegateSelectorsError), + #[error("invalid content digest: {0}")] + InvalidContentDigest(#[from] ContentDigestError), + #[error("invalid revision number: {0}")] + InvalidRevisionNumber(#[from] RevisionNumberError), +} + +#[derive(Debug, PartialEq, Eq)] +pub(super) struct ProvisionedAgentIdentity { + pub agent_id: AgentId, + pub definition: AgentDefinition, + pub content_digest: ContentDigest, +} + +impl TryFrom for ProvisionAgent { + type Error = CommandWireError; + + fn try_from(value: v1::ProvisionAgent) -> Result { + let charter = value + .charter + .as_option() + .ok_or(CommandWireError::MissingField("charter"))?; + Ok(Self { + agent_id: AgentId::parse(&value.agent_id)?, + definition: definition_from_wire(AgentDefinitionWire { + name: &value.name, + parent: &value.parent, + owner: &value.owner, + labels: &value.labels, + annotations: &value.annotations, + charter, + })?, + principal: Principal::parse(&value.principal)?, + content_digest: ContentDigest::parse(&value.content_digest)?, + transient_annotations: annotations_from_wire(&value.transient_annotations)?, + }) + } +} + +pub(super) fn provisioned_agent_from_event( + value: &v1::AgentProvisioned, +) -> Result { + let charter = value + .charter + .as_option() + .ok_or(CommandWireError::MissingField("charter"))?; + let revision = value + .revision + .as_option() + .ok_or(CommandWireError::MissingField("revision"))?; + let revision_number = RevisionNumber::new(revision.number)?; + if revision_number != RevisionNumber::GENESIS { + return Err(CommandWireError::InvalidInitialRevision { + actual: revision_number, + }); + } + Principal::parse(&value.provisioned_by)?; + annotations_from_wire(&value.transient_annotations)?; + Ok(ProvisionedAgentIdentity { + agent_id: AgentId::parse(&value.agent_id)?, + definition: definition_from_wire(AgentDefinitionWire { + name: &value.name, + parent: &value.parent, + owner: &value.owner, + labels: &value.labels, + annotations: &value.annotations, + charter, + })?, + content_digest: ContentDigest::parse(&revision.content_digest)?, + }) +} + +pub(super) fn provisioned_agent_from_state( + value: &state_v1::ProvisionedAgent, +) -> Result { + let charter = value + .charter + .as_option() + .ok_or(CommandWireError::MissingField("charter"))?; + Ok(ProvisionedAgentIdentity { + agent_id: AgentId::parse(&value.agent_id)?, + definition: definition_from_wire(AgentDefinitionWire { + name: &value.name, + parent: &value.parent, + owner: &value.owner, + labels: &value.labels, + annotations: &value.annotations, + charter, + })?, + content_digest: ContentDigest::parse(&value.content_digest)?, + }) +} + +pub(super) fn provisioned_agent_to_state(value: &ProvisionedAgentIdentity) -> state_v1::ProvisionedAgent { + let definition = &value.definition; + state_v1::ProvisionedAgent { + agent_id: value.agent_id.as_str().to_string(), + name: definition.name().as_str().to_string(), + parent: definition.parent().as_str().to_string(), + owner: definition.owner().as_str().to_string(), + labels: labels_to_wire(definition.labels()), + annotations: annotations_to_wire(definition.annotations()), + charter: MessageField::some(charter_to_wire(definition.charter())), + content_digest: value.content_digest.as_str().to_string(), + } +} + +struct AgentDefinitionWire<'a> { + name: &'a str, + parent: &'a str, + owner: &'a str, + labels: &'a [v1::Label], + annotations: &'a ProtoMap, + charter: &'a v1::Charter, +} + +fn definition_from_wire(value: AgentDefinitionWire<'_>) -> Result { + Ok(AgentDefinition::new( + AgentName::parse(value.name)?, + ParentRef::parse(value.parent)?, + Principal::parse(value.owner)?, + Labels::new(string_entries( + value.labels.iter().map(|entry| (&entry.key, &entry.value)), + "labels", + )?)?, + annotations_from_wire(value.annotations)?, + charter_from_wire(value.charter)?, + )) +} + +fn annotations_from_wire(values: &ProtoMap) -> Result { + Ok(Annotations::new( + values.iter().map(|(key, value)| (key.clone(), value.clone())).collect(), + )?) +} + +fn charter_from_wire(value: &v1::Charter) -> Result { + let model = value + .model + .as_option() + .ok_or(CommandWireError::MissingField("charter.model"))?; + let tools = value + .tools + .as_option() + .ok_or(CommandWireError::MissingField("charter.tools"))?; + let delegates = value + .delegates + .as_option() + .ok_or(CommandWireError::MissingField("charter.delegates"))?; + Ok(AgentCharter::new( + RuntimeId::parse(&value.runtime)?, + ModelId::parse(&model.id)?, + ModelParameters::new(string_entries( + model.params.iter().map(|entry| (&entry.key, &entry.value)), + "charter.model.params", + )?)?, + ToolSelectors::new( + unique_strings(&tools.required, "charter.tools.required")?, + unique_strings(&tools.optional, "charter.tools.optional")?, + )?, + DelegateSelectors::new( + unique_strings(&delegates.required, "charter.delegates.required")?, + unique_strings(&delegates.optional, "charter.delegates.optional")?, + )?, + )) +} + +fn string_entries<'a>( + values: impl IntoIterator, + field: &'static str, +) -> Result, CommandWireError> { + let mut result = BTreeMap::new(); + for (key, value) in values { + if result.insert(key.clone(), value.clone()).is_some() { + return Err(CommandWireError::DuplicateValue { + field, + value: key.clone(), + }); + } + } + Ok(result) +} + +fn unique_strings(values: &[String], field: &'static str) -> Result, CommandWireError> { + let mut result = BTreeSet::new(); + for value in values { + if !result.insert(value.clone()) { + return Err(CommandWireError::DuplicateValue { + field, + value: value.clone(), + }); + } + } + Ok(result) +} + +pub(super) fn charter_to_wire(value: &AgentCharter) -> v1::Charter { + v1::Charter { + runtime: value.runtime().as_str().to_string(), + model: buffa::MessageField::some(v1::Model { + id: value.model_id().as_str().to_string(), + params: value + .model_params() + .as_map() + .iter() + .map(|(key, value)| v1::ModelParameter { + key: key.clone(), + value: value.clone(), + }) + .collect(), + }), + tools: buffa::MessageField::some(v1::ToolDependencies { + required: value.tools().required().iter().cloned().collect(), + optional: value.tools().optional().iter().cloned().collect(), + }), + delegates: buffa::MessageField::some(v1::DelegateDependencies { + required: value.delegates().required().iter().cloned().collect(), + optional: value.delegates().optional().iter().cloned().collect(), + }), + } +} + +pub(super) fn labels_to_wire(value: &Labels) -> Vec { + value + .as_map() + .iter() + .map(|(key, value)| v1::Label { + key: key.clone(), + value: value.clone(), + }) + .collect() +} + +pub(super) fn annotations_to_wire(value: &Annotations) -> ProtoMap { + value + .as_map() + .iter() + .map(|(key, value)| (key.clone(), value.clone())) + .collect() +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/proto_wire/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/proto_wire/tests.rs new file mode 100644 index 0000000000..d0fb2819dd --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/proto_wire/tests.rs @@ -0,0 +1,277 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use buffa::MessageField; + +use super::*; +use crate::commands::domain::{DelegateSelectorsError, ToolSelectorsError}; +use crate::commands::test_support::digest; + +fn charter() -> v1::Charter { + v1::Charter { + runtime: "claude-code".to_string(), + model: MessageField::some(v1::Model { + id: "anthropic/claude-opus".to_string(), + params: vec![v1::ModelParameter { + key: "speed".to_string(), + value: "standard".to_string(), + }], + }), + tools: MessageField::some(v1::ToolDependencies { + required: vec!["read".to_string()], + optional: vec!["search".to_string()], + }), + delegates: MessageField::some(v1::DelegateDependencies { + required: Vec::new(), + optional: vec!["family=researcher".to_string()], + }), + } +} + +fn provision_proto() -> v1::ProvisionAgent { + v1::ProvisionAgent { + agent_id: "agent-1".to_string(), + name: "reviewer".to_string(), + parent: "folder-backend".to_string(), + owner: "owner@tenant".to_string(), + labels: vec![v1::Label { + key: "family".to_string(), + value: "reviewer".to_string(), + }], + annotations: [("vertical".to_string(), "software".to_string())].into_iter().collect(), + charter: MessageField::some(charter()), + principal: "operator@tenant".to_string(), + content_digest: digest('a').as_str().to_string(), + transient_annotations: [("request".to_string(), "first".to_string())].into_iter().collect(), + } +} + +fn provisioned_proto() -> v1::AgentProvisioned { + v1::AgentProvisioned { + agent_id: "agent-1".to_string(), + name: "reviewer".to_string(), + parent: "folder-backend".to_string(), + owner: "owner@tenant".to_string(), + labels: vec![v1::Label { + key: "family".to_string(), + value: "reviewer".to_string(), + }], + annotations: [("vertical".to_string(), "software".to_string())].into_iter().collect(), + charter: MessageField::some(charter()), + provisioned_by: "operator@tenant".to_string(), + revision: MessageField::some(v1::RevisionRef { + number: 1, + content_digest: digest('a').as_str().to_string(), + }), + transient_annotations: [("request".to_string(), "first".to_string())].into_iter().collect(), + } +} + +#[test] +fn converts_a_complete_provision_command_once() { + let command = ProvisionAgent::try_from(provision_proto()).unwrap(); + + assert_eq!(command.agent_id.as_str(), "agent-1"); + assert_eq!(command.definition.owner().as_str(), "owner@tenant"); + assert_eq!(command.definition.labels().get("family"), Some("reviewer")); + assert_eq!(command.definition.charter().runtime().as_str(), "claude-code"); + assert_eq!(command.content_digest, digest('a')); + assert_eq!(command.transient_annotations.get("request"), Some("first")); +} + +#[test] +fn rejects_a_non_sha256_provision_digest() { + let mut proto = provision_proto(); + proto.content_digest = "digest-1".to_string(); + + assert!(matches!( + ProvisionAgent::try_from(proto), + Err(CommandWireError::InvalidContentDigest(_)) + )); +} + +#[test] +fn rejects_duplicate_label_keys() { + let mut proto = provision_proto(); + proto.labels.push(proto.labels[0].clone()); + + assert!(matches!( + ProvisionAgent::try_from(proto), + Err(CommandWireError::DuplicateValue { field: "labels", .. }) + )); +} + +#[test] +fn rejects_invalid_annotation_keys() { + let mut proto = provision_proto(); + proto.annotations.insert("bad key".to_string(), "value".to_string()); + + assert!(matches!( + ProvisionAgent::try_from(proto), + Err(CommandWireError::InvalidAnnotations(_)) + )); +} + +#[test] +fn rejects_invalid_transient_annotation_keys() { + let mut proto = provision_proto(); + proto + .transient_annotations + .insert("bad key".to_string(), "value".to_string()); + + assert!(matches!( + ProvisionAgent::try_from(proto), + Err(CommandWireError::InvalidAnnotations(_)) + )); +} + +#[test] +fn rejects_a_missing_charter() { + let mut proto = provision_proto(); + proto.charter = MessageField::none(); + + assert_eq!( + ProvisionAgent::try_from(proto), + Err(CommandWireError::MissingField("charter")) + ); +} + +#[test] +fn rejects_duplicate_model_parameter_keys() { + let mut proto = provision_proto(); + proto + .charter + .as_option_mut() + .unwrap() + .model + .as_option_mut() + .unwrap() + .params + .push(v1::ModelParameter { + key: "speed".to_string(), + value: "turbo".to_string(), + }); + + assert!(matches!( + ProvisionAgent::try_from(proto), + Err(CommandWireError::DuplicateValue { + field: "charter.model.params", + .. + }) + )); +} + +#[test] +fn rejects_duplicate_required_tool_selectors() { + let mut proto = provision_proto(); + let tools = proto.charter.as_option_mut().unwrap().tools.as_option_mut().unwrap(); + tools.required.push(tools.required[0].clone()); + + assert!(matches!( + ProvisionAgent::try_from(proto), + Err(CommandWireError::DuplicateValue { + field: "charter.tools.required", + .. + }) + )); +} + +#[test] +fn rejects_duplicate_optional_delegate_selectors() { + let mut proto = provision_proto(); + let delegates = proto + .charter + .as_option_mut() + .unwrap() + .delegates + .as_option_mut() + .unwrap(); + delegates.optional.push(delegates.optional[0].clone()); + + assert!(matches!( + ProvisionAgent::try_from(proto), + Err(CommandWireError::DuplicateValue { + field: "charter.delegates.optional", + .. + }) + )); +} + +#[test] +fn rejects_a_tool_selector_in_both_sets() { + let mut proto = provision_proto(); + let tools = proto.charter.as_option_mut().unwrap().tools.as_option_mut().unwrap(); + tools.required = vec!["shared".to_string()]; + tools.optional = vec!["shared".to_string()]; + + assert!(matches!( + ProvisionAgent::try_from(proto), + Err(CommandWireError::InvalidToolSelectors( + ToolSelectorsError::Overlap { .. } + )) + )); +} + +#[test] +fn rejects_a_delegate_selector_in_both_sets() { + let mut proto = provision_proto(); + let delegates = proto + .charter + .as_option_mut() + .unwrap() + .delegates + .as_option_mut() + .unwrap(); + delegates.required = vec!["shared".to_string()]; + delegates.optional = vec!["shared".to_string()]; + + assert!(matches!( + ProvisionAgent::try_from(proto), + Err(CommandWireError::InvalidDelegateSelectors( + DelegateSelectorsError::Overlap { .. } + )) + )); +} + +#[test] +fn rejects_a_non_genesis_provisioned_revision() { + let mut proto = provisioned_proto(); + proto.revision.as_option_mut().unwrap().number = 2; + + assert!(matches!( + provisioned_agent_from_event(&proto), + Err(CommandWireError::InvalidInitialRevision { actual }) + if actual == RevisionNumber::new(2).unwrap() + )); +} + +#[test] +fn round_trips_only_persistent_retry_identity_through_state() { + let identity = provisioned_agent_from_event(&provisioned_proto()).unwrap(); + let state = provisioned_agent_to_state(&identity); + + assert_eq!(state.agent_id, "agent-1"); + assert_eq!(state.annotations.get("vertical").map(String::as_str), Some("software")); + assert_eq!(state.content_digest, digest('a').as_str()); + assert_eq!(provisioned_agent_from_state(&state).unwrap(), identity); +} + +#[test] +fn converts_model_parameters_to_wire_entries() { + let charter = AgentCharter::new( + RuntimeId::parse("claude-code").unwrap(), + ModelId::parse("anthropic/claude-opus").unwrap(), + ModelParameters::new(BTreeMap::from([("speed".to_string(), "standard".to_string())])).unwrap(), + ToolSelectors::new(BTreeSet::from(["read".to_string()]), BTreeSet::new()).unwrap(), + DelegateSelectors::default(), + ); + + let wire = charter_to_wire(&charter); + + assert_eq!( + wire.model.params, + vec![v1::ModelParameter { + key: "speed".to_string(), + value: "standard".to_string(), + }] + ); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/provision_agent.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/provision_agent.rs new file mode 100644 index 0000000000..d5db187eab --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/provision_agent.rs @@ -0,0 +1,99 @@ +use buffa::MessageField; +use trogon_decider::{Decider, Decision}; +use trogonai_proto::agents::agents::{state_v1, v1}; + +use super::CommandWireError; +use super::domain::{AgentDefinition, AgentId, Annotations, ContentDigest, Principal, RevisionNumber}; +use super::event_fold::{AgentEventFoldError, ensure_command_identity, provisioned_payload}; +use super::proto_wire::{ + annotations_to_wire, charter_to_wire, labels_to_wire, provisioned_agent_from_event, provisioned_agent_from_state, + provisioned_agent_to_state, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProvisionAgent { + pub agent_id: AgentId, + pub definition: AgentDefinition, + pub principal: Principal, + pub content_digest: ContentDigest, + pub transient_annotations: Annotations, +} + +#[derive(Debug, PartialEq, thiserror::Error)] +pub enum ProvisionAgentError { + #[error("agent '{agent_id}' is already provisioned with the same definition")] + AlreadyProvisionedIdentical { agent_id: AgentId }, + #[error("agent '{agent_id}' is already provisioned with a different definition")] + AlreadyProvisionedConflict { agent_id: AgentId }, + #[error("provision state is invalid: {0}")] + InvalidState(#[source] AgentEventFoldError), + #[error("provision state payload is invalid: {0}")] + InvalidStatePayload(#[source] CommandWireError), +} + +impl Decider for ProvisionAgent { + type StreamId = str; + type State = state_v1::ProvisionAgentState; + type Event = v1::AgentEvent; + type DecideError = ProvisionAgentError; + type EvolveError = AgentEventFoldError; + + fn stream_id(&self) -> &Self::StreamId { + self.agent_id.as_str() + } + + fn initial_state() -> Self::State { + state_v1::ProvisionAgentState::default() + } + + fn evolve(mut state: Self::State, event: &Self::Event) -> Result { + let provisioned = provisioned_payload(event)?; + if state.provisioned.as_option().is_some() { + return Err(AgentEventFoldError::DuplicateProvision); + } + let identity = provisioned_agent_from_event(provisioned)?; + state.provisioned = MessageField::some(provisioned_agent_to_state(&identity)); + Ok(state) + } + + fn decide(state: &Self::State, command: &Self) -> Result, Self::DecideError> { + let Some(existing) = state.provisioned.as_option() else { + let definition = &command.definition; + return Ok(Decision::event(v1::AgentEvent { + event: Some( + v1::AgentProvisioned { + agent_id: command.agent_id.as_str().to_string(), + name: definition.name().as_str().to_string(), + parent: definition.parent().as_str().to_string(), + owner: definition.owner().as_str().to_string(), + labels: labels_to_wire(definition.labels()), + annotations: annotations_to_wire(definition.annotations()), + charter: MessageField::some(charter_to_wire(definition.charter())), + provisioned_by: command.principal.as_str().to_string(), + revision: MessageField::some(v1::RevisionRef { + number: RevisionNumber::GENESIS.get(), + content_digest: command.content_digest.as_str().to_string(), + }), + transient_annotations: annotations_to_wire(&command.transient_annotations), + } + .into(), + ), + })); + }; + + let provisioned = provisioned_agent_from_state(existing).map_err(ProvisionAgentError::InvalidStatePayload)?; + ensure_command_identity(&provisioned.agent_id, &command.agent_id).map_err(ProvisionAgentError::InvalidState)?; + if provisioned.definition == command.definition && provisioned.content_digest == command.content_digest { + Err(ProvisionAgentError::AlreadyProvisionedIdentical { + agent_id: command.agent_id.clone(), + }) + } else { + Err(ProvisionAgentError::AlreadyProvisionedConflict { + agent_id: command.agent_id.clone(), + }) + } + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/provision_agent/tests.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/provision_agent/tests.rs new file mode 100644 index 0000000000..cf691f4024 --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/provision_agent/tests.rs @@ -0,0 +1,86 @@ +use std::collections::BTreeMap; + +use trogon_decider::{Decider, testing::TestCase}; + +use super::*; +use crate::commands::domain::{AgentDefinition, AgentName, Annotations}; +use crate::commands::test_support::*; + +#[test] +fn provisions_genesis_revision_from_validated_definition() { + TestCase::::new() + .given_no_history() + .when(provision_command()) + .then([provisioned_event()]); +} + +#[test] +fn classifies_an_identical_retry_by_agent_id() { + TestCase::::new() + .given([provisioned_event()]) + .when(provision_command()) + .then_error(ProvisionAgentError::AlreadyProvisionedIdentical { agent_id: agent_id() }); +} + +#[test] +fn excludes_transient_annotations_from_retry_identity() { + let mut command = provision_command(); + command.transient_annotations = + Annotations::new(BTreeMap::from([("request".to_string(), "retry".to_string())])).unwrap(); + + TestCase::::new() + .given([provisioned_event()]) + .when(command) + .then_error(ProvisionAgentError::AlreadyProvisionedIdentical { agent_id: agent_id() }); +} + +#[test] +fn stores_only_retry_identity_in_decider_state() { + let result = TestCase::::new() + .given_no_history() + .when(provision_command()) + .then([provisioned_event()]); + let provisioned = result.resulting_state().provisioned.as_option().unwrap(); + + assert_eq!(provisioned.agent_id, agent_id().as_str()); + assert_eq!(provisioned.name, "reviewer"); + assert_eq!( + provisioned.annotations.get("vertical").map(String::as_str), + Some("software") + ); + assert_eq!(provisioned.content_digest, digest('a').as_str()); +} + +#[test] +fn compares_the_initial_digest_on_retry() { + let mut command = provision_command(); + command.content_digest = digest('f'); + + TestCase::::new() + .given([provisioned_event()]) + .when(command) + .then_error(ProvisionAgentError::AlreadyProvisionedConflict { agent_id: agent_id() }); +} + +#[test] +fn compares_the_complete_definition_on_retry() { + let mut command = provision_command(); + command.definition = AgentDefinition::new( + AgentName::parse("different-name").unwrap(), + command.definition.parent().clone(), + command.definition.owner().clone(), + command.definition.labels().clone(), + command.definition.annotations().clone(), + command.definition.charter().clone(), + ); + + TestCase::::new() + .given([provisioned_event()]) + .when(command) + .then_error(ProvisionAgentError::AlreadyProvisionedConflict { agent_id: agent_id() }); +} + +#[test] +fn keeps_the_default_runtime_precondition_so_retries_replay_history() { + assert_eq!(ProvisionAgent::WRITE_PRECONDITION, None); +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/test_support.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/test_support.rs new file mode 100644 index 0000000000..28dc68c62c --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/commands/test_support.rs @@ -0,0 +1,92 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use buffa::MessageField; +use trogonai_proto::agents::agents::v1; + +use super::ProvisionAgent; +use super::domain::{ + AgentCharter, AgentDefinition, AgentId, AgentName, Annotations, ContentDigest, DelegateSelectors, Labels, ModelId, + ModelParameters, ParentRef, Principal, RuntimeId, ToolSelectors, +}; + +pub fn agent_id() -> AgentId { + AgentId::parse("agent-1").unwrap() +} + +fn operator() -> Principal { + Principal::parse("operator@tenant").unwrap() +} + +fn definition() -> AgentDefinition { + AgentDefinition::new( + AgentName::parse("reviewer").unwrap(), + ParentRef::parse("folder-backend").unwrap(), + Principal::parse("owner@tenant").unwrap(), + Labels::new(BTreeMap::from([("family".to_string(), "reviewer".to_string())])).unwrap(), + Annotations::new(BTreeMap::from([("vertical".to_string(), "software".to_string())])).unwrap(), + AgentCharter::new( + RuntimeId::parse("claude-code").unwrap(), + ModelId::parse("anthropic/claude-opus").unwrap(), + ModelParameters::default(), + ToolSelectors::new(BTreeSet::from(["read".to_string()]), BTreeSet::new()).unwrap(), + DelegateSelectors::default(), + ), + ) +} + +pub fn digest(character: char) -> ContentDigest { + ContentDigest::parse(&format!("sha256:{}", character.to_string().repeat(64))).unwrap() +} + +pub fn provision_command() -> ProvisionAgent { + ProvisionAgent { + agent_id: agent_id(), + definition: definition(), + principal: operator(), + content_digest: digest('a'), + transient_annotations: Annotations::new(BTreeMap::from([("request".to_string(), "first".to_string())])) + .unwrap(), + } +} + +pub fn provisioned_event() -> v1::AgentEvent { + let command = provision_command(); + let definition = command.definition; + v1::AgentEvent { + event: Some( + v1::AgentProvisioned { + agent_id: command.agent_id.as_str().to_string(), + name: definition.name().as_str().to_string(), + parent: definition.parent().as_str().to_string(), + owner: definition.owner().as_str().to_string(), + labels: vec![v1::Label { + key: "family".to_string(), + value: "reviewer".to_string(), + }], + annotations: [("vertical".to_string(), "software".to_string())].into_iter().collect(), + charter: MessageField::some(v1::Charter { + runtime: "claude-code".to_string(), + model: MessageField::some(v1::Model { + id: "anthropic/claude-opus".to_string(), + params: Vec::new(), + }), + tools: MessageField::some(v1::ToolDependencies { + required: vec!["read".to_string()], + optional: Vec::new(), + }), + delegates: MessageField::some(v1::DelegateDependencies { + required: Vec::new(), + optional: Vec::new(), + }), + }), + provisioned_by: operator().as_str().to_string(), + revision: MessageField::some(v1::RevisionRef { + number: 1, + content_digest: digest('a').as_str().to_string(), + }), + transient_annotations: [("request".to_string(), "first".to_string())].into_iter().collect(), + } + .into(), + ), + } +} diff --git a/rsworkspace/crates/agents/trogonai-agents-domain/src/lib.rs b/rsworkspace/crates/agents/trogonai-agents-domain/src/lib.rs new file mode 100644 index 0000000000..055d13e7ab --- /dev/null +++ b/rsworkspace/crates/agents/trogonai-agents-domain/src/lib.rs @@ -0,0 +1,7 @@ +//! Wasm-clean Agent provisioning domain. +#![cfg_attr(test, allow(clippy::expect_used, clippy::panic, clippy::unwrap_used))] + +mod commands; + +pub use commands::domain; +pub use commands::{AgentEventFoldError, CommandWireError, ProvisionAgent, ProvisionAgentError}; diff --git a/rsworkspace/crates/platform/trogonai-proto/Cargo.toml b/rsworkspace/crates/platform/trogonai-proto/Cargo.toml index 207e1cb9f4..f11459a3ca 100644 --- a/rsworkspace/crates/platform/trogonai-proto/Cargo.toml +++ b/rsworkspace/crates/platform/trogonai-proto/Cargo.toml @@ -14,6 +14,7 @@ chrono = ["dep:buffa-types", "dep:chrono"] schedules = ["dep:buffa", "dep:buffa-types", "dep:serde", "dep:trogon-decider", "chrono"] runtime-snapshot = ["schedules", "dep:trogon-decider-runtime"] runtime-host = ["schedules", "dep:trogon-decider-runtime"] +agents = ["dep:buffa", "dep:buffa-types", "dep:serde", "dep:trogon-decider"] [dependencies] thiserror = { workspace = true } diff --git a/rsworkspace/crates/platform/trogonai-proto/src/agents/agents/codec.rs b/rsworkspace/crates/platform/trogonai-proto/src/agents/agents/codec.rs new file mode 100644 index 0000000000..56fa89cffc --- /dev/null +++ b/rsworkspace/crates/platform/trogonai-proto/src/agents/agents/codec.rs @@ -0,0 +1,69 @@ +use buffa::Message as _; +use trogon_decider::{EventData, EventDecode, EventDecodeOutcome, EventEncode, EventPayloadError, EventType}; + +use super::{AgentEventCase, v1}; +use crate::codec::{decode_event_case, event_type}; + +#[cfg(feature = "runtime-host")] +use trogon_decider_runtime::EventIdentity; + +pub type AgentEventPayloadError = EventPayloadError; + +impl EventEncode for v1::AgentEvent { + type Error = AgentEventPayloadError; + + fn encode(&self) -> Result, Self::Error> { + self.event + .as_ref() + .map(encode_agent_event_case) + .ok_or(AgentEventPayloadError::MissingEvent) + } +} + +impl EventDecode for v1::AgentEvent { + type Error = AgentEventPayloadError; + + fn decode(event: EventData<'_>) -> Result, Self::Error> { + match decode_agent_event_case(event)? { + Some(event) => Ok(EventDecodeOutcome::Decoded(v1::AgentEvent { event: Some(event) })), + None => Ok(EventDecodeOutcome::Skipped), + } + } +} + +impl EventType for v1::AgentEvent { + type Error = AgentEventPayloadError; + + fn event_type(&self) -> Result<&'static str, Self::Error> { + self.event + .as_ref() + .map(agent_event_case_type) + .ok_or(AgentEventPayloadError::MissingEvent) + } +} + +#[cfg(feature = "runtime-host")] +impl EventIdentity for v1::AgentEvent {} + +fn encode_agent_event_case(event: &AgentEventCase) -> Vec { + match event { + AgentEventCase::AgentProvisioned(inner) => inner.encode_to_vec(), + } +} + +fn decode_agent_event_case(event: EventData<'_>) -> Result, AgentEventPayloadError> { + let Some(event) = decode_event_case::(&event) else { + return Ok(None); + }; + + event.map(Some).map_err(AgentEventPayloadError::Decode) +} + +fn agent_event_case_type(event: &AgentEventCase) -> &'static str { + match event { + AgentEventCase::AgentProvisioned(_) => event_type::(), + } +} + +#[cfg(test)] +mod tests; diff --git a/rsworkspace/crates/platform/trogonai-proto/src/agents/agents/codec/tests.rs b/rsworkspace/crates/platform/trogonai-proto/src/agents/agents/codec/tests.rs new file mode 100644 index 0000000000..6c5fb4bce3 --- /dev/null +++ b/rsworkspace/crates/platform/trogonai-proto/src/agents/agents/codec/tests.rs @@ -0,0 +1,92 @@ +use buffa::Message as _; +use trogon_decider::{EventData, EventDecode, EventDecodeOutcome, EventEncode, EventType}; + +use super::*; + +fn agent_provisioned() -> v1::AgentProvisioned { + v1::AgentProvisioned { + agent_id: "agent-1".to_string(), + annotations: [("example.com/source".to_string(), "import".to_string())] + .into_iter() + .collect(), + transient_annotations: [("example.com/trace".to_string(), "trace-1".to_string())] + .into_iter() + .collect(), + ..v1::AgentProvisioned::default() + } +} + +#[test] +fn event_encode_writes_inner_event_payload() { + let inner = agent_provisioned(); + let event = v1::AgentEvent { + event: Some(inner.clone().into()), + }; + + let encoded = EventEncode::encode(&event).unwrap(); + + assert_eq!(v1::AgentProvisioned::decode_from_slice(&encoded).unwrap(), inner); +} + +#[test] +fn event_encode_rejects_missing_event_case() { + let event = v1::AgentEvent { event: None }; + + assert!(matches!( + EventEncode::encode(&event), + Err(AgentEventPayloadError::MissingEvent) + )); +} + +#[test] +fn event_decode_dispatches_by_generated_full_name() { + let inner = agent_provisioned(); + let encoded = inner.encode_to_vec(); + + let decoded = ::decode(EventData::new( + ::FULL_NAME, + &encoded, + )) + .unwrap(); + + let decoded = decoded.into_decoded().unwrap(); + assert!(matches!(decoded.event, Some(AgentEventCase::AgentProvisioned(_)))); +} + +#[test] +fn event_decode_skips_unknown_event_type() { + assert!(matches!( + ::decode(EventData::new("trogonai.agents.agents.v1.Unknown", &[])), + Ok(EventDecodeOutcome::Skipped) + )); +} + +#[test] +fn event_decode_preserves_payload_decode_errors() { + assert!(matches!( + ::decode(EventData::new( + ::FULL_NAME, + b"\0" + )), + Err(AgentEventPayloadError::Decode(_)) + )); +} + +#[test] +fn event_type_returns_inner_event_full_name() { + let event = v1::AgentEvent { + event: Some(agent_provisioned().into()), + }; + + assert_eq!( + event.event_type().unwrap(), + ::FULL_NAME + ); +} + +#[test] +fn event_type_rejects_missing_event_case() { + let event = v1::AgentEvent { event: None }; + + assert!(matches!(event.event_type(), Err(AgentEventPayloadError::MissingEvent))); +} diff --git a/rsworkspace/crates/platform/trogonai-proto/src/agents/agents/mod.rs b/rsworkspace/crates/platform/trogonai-proto/src/agents/agents/mod.rs new file mode 100644 index 0000000000..2f789eb0b0 --- /dev/null +++ b/rsworkspace/crates/platform/trogonai-proto/src/agents/agents/mod.rs @@ -0,0 +1,22 @@ +mod codec; + +// Thin wrapper that re-exports the generated proto package, emitted as an +// inline module tree that mirrors the codegen layout. +#[cfg_attr(dylint_lib = "trogon_lints", allow(inline_module_block))] +pub mod state_v1 { + pub use crate::r#gen::trogonai::agents::agents::state::v1::*; +} + +#[cfg_attr(dylint_lib = "trogon_lints", allow(inline_module_block))] +pub mod v1 { + pub use crate::r#gen::trogonai::agents::agents::v1::*; +} + +pub use codec::AgentEventPayloadError; +pub use v1::__buffa::oneof::agent_event::Event as AgentEventCase; + +/// Stable type URLs for agent command envelopes. +pub const PROVISION_AGENT_TYPE_URL: &str = v1::ProvisionAgent::TYPE_URL; + +/// Schema-version tag for a [`state_v1::ProvisionAgentState`] snapshot. +pub const PROVISION_AGENT_STATE_SCHEMA_VERSION: &str = ::FULL_NAME; diff --git a/rsworkspace/crates/platform/trogonai-proto/src/agents/mod.rs b/rsworkspace/crates/platform/trogonai-proto/src/agents/mod.rs new file mode 100644 index 0000000000..8006a39aae --- /dev/null +++ b/rsworkspace/crates/platform/trogonai-proto/src/agents/mod.rs @@ -0,0 +1,5 @@ +// The product area (`agents`) and the aggregate (`agents`) share a name here +// because the brief mandates `proto/trogonai/agents/agents/v1/` verbatim +// (the product area is the aggregate for this stream). +#[allow(clippy::module_inception)] +pub mod agents; diff --git a/rsworkspace/crates/platform/trogonai-proto/src/gen/mod.rs b/rsworkspace/crates/platform/trogonai-proto/src/gen/mod.rs index 8b7215616d..8a41388e4d 100644 --- a/rsworkspace/crates/platform/trogonai-proto/src/gen/mod.rs +++ b/rsworkspace/crates/platform/trogonai-proto/src/gen/mod.rs @@ -122,6 +122,78 @@ pub mod trogonai { clippy::doc_lazy_continuation, clippy::module_inception )] + pub mod agents { + use super::*; + #[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception + )] + pub mod agents { + use super::*; + #[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception + )] + pub mod state { + use super::*; + #[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception + )] + pub mod v1 { + use super::*; + include!("trogonai.agents.agents.state.v1.mod.rs"); + } + } + #[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception + )] + pub mod v1 { + use super::*; + include!("trogonai.agents.agents.v1.mod.rs"); + } + } + } + #[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception + )] pub mod scheduler { use super::*; #[allow( diff --git a/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.mod.rs b/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.mod.rs new file mode 100644 index 0000000000..5bd88a28ac --- /dev/null +++ b/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.mod.rs @@ -0,0 +1,38 @@ +// @generated by buffa-codegen. DO NOT EDIT. + +include!("trogonai.agents.agents.state.v1.provision_agent_state.rs"); +#[allow( + non_camel_case_types, + dead_code, + unused_imports, + unused_qualifications, + clippy::derivable_impls, + clippy::match_single_binding, + clippy::uninlined_format_args, + clippy::doc_lazy_continuation, + clippy::module_inception +)] +pub mod __buffa { + #[allow(unused_imports)] + use super::*; + pub mod view { + #[allow(unused_imports)] + use super::*; + include!("trogonai.agents.agents.state.v1.provision_agent_state.__view.rs"); + } + /// Register this package's `Any` type entries and extension entries. + pub fn register_types(reg: &mut ::buffa::type_registry::TypeRegistry) { + reg.register_json_any(super::__PROVISION_AGENT_STATE_JSON_ANY); + reg.register_json_any(super::__PROVISIONED_AGENT_JSON_ANY); + } +} +#[doc(inline)] +pub use self::__buffa::view::ProvisionAgentStateView; +#[doc(inline)] +pub use self::__buffa::view::ProvisionAgentStateOwnedView; +#[doc(inline)] +pub use self::__buffa::view::ProvisionedAgentView; +#[doc(inline)] +pub use self::__buffa::view::ProvisionedAgentOwnedView; +#[doc(inline)] +pub use self::__buffa::register_types; diff --git a/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.provision_agent_state.__view.rs b/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.provision_agent_state.__view.rs new file mode 100644 index 0000000000..81c9c5a413 --- /dev/null +++ b/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.provision_agent_state.__view.rs @@ -0,0 +1,866 @@ +// @generated by buffa-codegen. DO NOT EDIT. +// source: trogonai/agents/agents/state/v1/provision_agent_state.proto + +#[derive(Clone, Debug, Default)] +pub struct ProvisionAgentStateView<'a> { + /// Absence means the agent has not been provisioned. + /// + /// Field 1: `provisioned` + pub provisioned: ::buffa::MessageFieldView< + super::super::__buffa::view::ProvisionedAgentView<'a>, + >, +} +impl<'a> ::buffa::MessageView<'a> for ProvisionAgentStateView<'a> { + type Owned = super::super::ProvisionAgentState; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + let __limit = ::core::cell::Cell::new(::buffa::DEFAULT_UNKNOWN_FIELD_LIMIT); + ::decode_view_ctx( + buf, + ::buffa::DecodeContext::new(::buffa::RECURSION_LIMIT, &__limit), + ) + } + fn decode_view_with_ctx( + buf: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result { + ::decode_view_ctx(buf, ctx) + } + fn merge_view_field( + &mut self, + tag: ::buffa::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<&'a [u8], ::buffa::DecodeError> { + let _ = ctx; + #[allow(unused_variables)] + let view = self; + let mut cur = cur; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + let __sub_ctx = ctx.descend()?; + let sub = ::buffa::types::borrow_bytes(&mut cur)?; + match view.provisioned.as_mut() { + Some(existing) => { + ::buffa::MessageView::merge_into_view(existing, sub, __sub_ctx)? + } + None => { + view.provisioned = ::buffa::MessageFieldView::set( + ::decode_view_ctx( + sub, + __sub_ctx, + )?, + ); + } + } + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, ctx.depth())?; + } + } + ::core::result::Result::Ok(cur) + } + fn to_owned_message( + &self, + ) -> ::core::result::Result< + super::super::ProvisionAgentState, + ::buffa::DecodeError, + > { + self.to_owned_from_source(None) + } + #[allow(clippy::useless_conversion, clippy::needless_update)] + fn to_owned_from_source( + &self, + __buffa_src: ::core::option::Option<&::buffa::bytes::Bytes>, + ) -> ::core::result::Result< + super::super::ProvisionAgentState, + ::buffa::DecodeError, + > { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + let _ = __buffa_src; + ::core::result::Result::Ok(super::super::ProvisionAgentState { + provisioned: match self.provisioned.as_option() { + Some(v) => { + ::buffa::MessageField::< + super::super::ProvisionedAgent, + >::some(v.to_owned_from_source(__buffa_src)?) + } + None => ::buffa::MessageField::none(), + }, + ..::core::default::Default::default() + }) + } +} +impl<'a> ::buffa::ViewEncode<'a> for ProvisionAgentStateView<'a> { + #[allow(clippy::needless_borrow, clippy::let_and_return)] + fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + if self.provisioned.is_set() { + let __slot = __cache.reserve(); + let inner_size = self.provisioned.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + size + } + #[allow(clippy::needless_borrow)] + fn write_to( + &self, + __cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + if self.provisioned.is_set() { + ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + self.provisioned.write_to(__cache, buf); + } + } +} +/// Serializes this view as protobuf JSON. +/// +/// Implicit-presence fields with default values are omitted, `required` +/// fields are always emitted, explicit-presence (`optional`) fields are +/// emitted only when set, bytes fields are base64-encoded, and enum +/// values are their proto name strings. +/// +/// This impl uses `serialize_map(None)` because the number of emitted +/// fields depends on default-omission rules; serializers that require +/// known map lengths (e.g. `bincode`) will return a runtime error. +/// Use the owned message type for those formats. +impl<'__a> ::serde::Serialize for ProvisionAgentStateView<'__a> { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + use ::serde::ser::SerializeMap as _; + let mut __map = __s.serialize_map(::core::option::Option::None)?; + { + if let ::core::option::Option::Some(__v) = self.provisioned.as_option() { + __map.serialize_entry("provisioned", __v)?; + } + } + __map.end() + } +} +impl<'a> ::buffa::MessageName for ProvisionAgentStateView<'a> { + const PACKAGE: &'static str = "trogonai.agents.agents.state.v1"; + const NAME: &'static str = "ProvisionAgentState"; + const FULL_NAME: &'static str = "trogonai.agents.agents.state.v1.ProvisionAgentState"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.state.v1.ProvisionAgentState"; +} +::buffa::impl_default_view_instance!(ProvisionAgentStateView); +::buffa::impl_view_reborrow!(ProvisionAgentStateView); +/** Self-contained, `'static` owned view of a `ProvisionAgentState` message. + + Wraps [`::buffa::OwnedView`]`<`[`ProvisionAgentStateView`]`<'static>>`: the decoded view and the [`::buffa::bytes::Bytes`] buffer it borrows from travel together, so the handle is `'static` and `Send + Sync` — suitable for async handlers, spawned tasks, and anywhere a `'static` bound is required. + + Field accessors return borrows tied to `&self`. Use [`Self::view`] to get the full [`ProvisionAgentStateView`] when you need struct patterns, iteration helpers, or to pass the view to lifetime-parameterised code.*/ +#[derive(Clone, Debug)] +pub struct ProvisionAgentStateOwnedView( + ::buffa::OwnedView>, +); +impl ProvisionAgentStateOwnedView { + /// Decode an owned view from a [`::buffa::bytes::Bytes`] buffer. + /// + /// The view borrows directly from the buffer's data; the buffer is + /// retained inside the returned handle. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer contains invalid + /// protobuf data. + pub fn decode( + bytes: ::buffa::bytes::Bytes, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ProvisionAgentStateOwnedView(::buffa::OwnedView::decode(bytes)?), + ) + } + /// Decode with custom [`::buffa::DecodeOptions`] (recursion limit, + /// max message size). + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer is invalid or + /// exceeds the configured limits. + pub fn decode_with_options( + bytes: ::buffa::bytes::Bytes, + opts: &::buffa::DecodeOptions, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ProvisionAgentStateOwnedView( + ::buffa::OwnedView::decode_with_options(bytes, opts)?, + ), + ) + } + /// Build from an owned message via an encode → decode round-trip. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// somehow invalid (should not happen for well-formed messages). + pub fn from_owned( + msg: &super::super::ProvisionAgentState, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ProvisionAgentStateOwnedView(::buffa::OwnedView::from_owned(msg)?), + ) + } + /// Borrow the full [`ProvisionAgentStateView`] with its lifetime tied to `&self`. + #[must_use] + pub fn view(&self) -> &ProvisionAgentStateView<'_> { + self.0.reborrow() + } + /// Convert to the owned message type. + /// + /// # Errors + /// + /// Returns an error if re-materializing preserved unknown fields + /// fails (e.g. the unknown-field limit is exceeded). + pub fn to_owned_message( + &self, + ) -> ::core::result::Result< + super::super::ProvisionAgentState, + ::buffa::DecodeError, + > { + self.0.to_owned_message() + } + /// The underlying bytes buffer. + #[must_use] + pub fn bytes(&self) -> &::buffa::bytes::Bytes { + self.0.bytes() + } + /// Consume the handle, returning the underlying bytes buffer. + #[must_use] + pub fn into_bytes(self) -> ::buffa::bytes::Bytes { + self.0.into_bytes() + } + /// Absence means the agent has not been provisioned. + /// + /// Field 1: `provisioned` + #[must_use] + pub fn provisioned( + &self, + ) -> &::buffa::MessageFieldView< + super::super::__buffa::view::ProvisionedAgentView<'_>, + > { + &self.0.reborrow().provisioned + } +} +impl ::core::convert::From<::buffa::OwnedView>> +for ProvisionAgentStateOwnedView { + fn from(inner: ::buffa::OwnedView>) -> Self { + ProvisionAgentStateOwnedView(inner) + } +} +impl ::core::convert::From +for ::buffa::OwnedView> { + fn from(wrapper: ProvisionAgentStateOwnedView) -> Self { + wrapper.0 + } +} +impl ::core::convert::AsRef<::buffa::OwnedView>> +for ProvisionAgentStateOwnedView { + fn as_ref(&self) -> &::buffa::OwnedView> { + &self.0 + } +} +impl ::buffa::HasMessageView for super::super::ProvisionAgentState { + type View<'a> = ProvisionAgentStateView<'a>; + type ViewHandle = ProvisionAgentStateOwnedView; +} +impl ::serde::Serialize for ProvisionAgentStateOwnedView { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + ::serde::Serialize::serialize(&self.0, __s) + } +} +/// ProvisionedAgent contains only the persistent fields used to classify +/// retries. Delivery metadata and the provisioning actor never enter state. +#[derive(Clone, Debug, Default)] +pub struct ProvisionedAgentView<'a> { + /// Field 1: `agent_id` + pub agent_id: &'a str, + /// Field 2: `name` + pub name: &'a str, + /// Field 3: `parent` + pub parent: &'a str, + /// Field 4: `owner` + pub owner: &'a str, + /// Field 5: `labels` + pub labels: ::buffa::RepeatedView< + 'a, + super::super::super::super::v1::__buffa::view::LabelView<'a>, + >, + /// Field 6: `annotations` (map) + pub annotations: ::buffa::MapView<'a, &'a str, &'a str>, + /// Field 7: `charter` + pub charter: ::buffa::MessageFieldView< + super::super::super::super::v1::__buffa::view::CharterView<'a>, + >, + /// Field 8: `content_digest` + pub content_digest: &'a str, + #[doc(hidden)] + pub __buffa_required_seen_0: u64, +} +impl<'a> ProvisionedAgentView<'a> { + /**Whether required field `agent_id` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_agent_id(&self) -> bool { + self.__buffa_required_seen_0 & 1u64 != 0 + } + /**Whether required field `name` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_name(&self) -> bool { + self.__buffa_required_seen_0 & 2u64 != 0 + } + /**Whether required field `parent` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_parent(&self) -> bool { + self.__buffa_required_seen_0 & 4u64 != 0 + } + /**Whether required field `owner` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_owner(&self) -> bool { + self.__buffa_required_seen_0 & 8u64 != 0 + } + /**Whether required field `charter` is set. + +Mirrors `is_set()` on the field: `true` after decoding a message where the field was present on the wire, and `true` on a hand-built view whose field is populated. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_charter(&self) -> bool { + self.charter.is_set() + } + /**Whether required field `content_digest` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_content_digest(&self) -> bool { + self.__buffa_required_seen_0 & 16u64 != 0 + } +} +impl<'a> ::buffa::MessageView<'a> for ProvisionedAgentView<'a> { + type Owned = super::super::ProvisionedAgent; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + let __limit = ::core::cell::Cell::new(::buffa::DEFAULT_UNKNOWN_FIELD_LIMIT); + ::decode_view_ctx( + buf, + ::buffa::DecodeContext::new(::buffa::RECURSION_LIMIT, &__limit), + ) + } + fn decode_view_with_ctx( + buf: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result { + ::decode_view_ctx(buf, ctx) + } + fn merge_view_field( + &mut self, + tag: ::buffa::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<&'a [u8], ::buffa::DecodeError> { + let _ = ctx; + #[allow(unused_variables)] + let view = self; + let mut cur = cur; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.agent_id = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 1u64; + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.name = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 2u64; + } + 3u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.parent = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 4u64; + } + 4u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.owner = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 8u64; + } + 7u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + let __sub_ctx = ctx.descend()?; + let sub = ::buffa::types::borrow_bytes(&mut cur)?; + match view.charter.as_mut() { + Some(existing) => { + ::buffa::MessageView::merge_into_view(existing, sub, __sub_ctx)? + } + None => { + view.charter = ::buffa::MessageFieldView::set( + ::decode_view_ctx( + sub, + __sub_ctx, + )?, + ); + } + } + } + 8u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.content_digest = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 16u64; + } + 5u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + let __sub_ctx = ctx.descend()?; + let sub = ::buffa::types::borrow_bytes(&mut cur)?; + view.labels + .push( + ::decode_view_ctx( + sub, + __sub_ctx, + )?, + ); + } + 6u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + let entry_bytes = ::buffa::types::borrow_bytes(&mut cur)?; + let mut entry_cur: &'a [u8] = entry_bytes; + let mut key = ""; + let mut val = ""; + while !entry_cur.is_empty() { + let entry_tag = ::buffa::encoding::Tag::decode(&mut entry_cur)?; + match entry_tag.field_number() { + 1 => { + ::buffa::encoding::check_wire_type( + entry_tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + key = ::buffa::types::borrow_str(&mut entry_cur)?; + } + 2 => { + ::buffa::encoding::check_wire_type( + entry_tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + val = ::buffa::types::borrow_str(&mut entry_cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth( + entry_tag, + &mut entry_cur, + ctx.depth(), + )?; + } + } + } + view.annotations.push(key, val); + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, ctx.depth())?; + } + } + ::core::result::Result::Ok(cur) + } + fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.to_owned_from_source(None) + } + #[allow(clippy::useless_conversion, clippy::needless_update)] + fn to_owned_from_source( + &self, + __buffa_src: ::core::option::Option<&::buffa::bytes::Bytes>, + ) -> ::core::result::Result { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + let _ = __buffa_src; + ::core::result::Result::Ok(super::super::ProvisionedAgent { + agent_id: self.agent_id.to_string(), + name: self.name.to_string(), + parent: self.parent.to_string(), + owner: self.owner.to_string(), + labels: self + .labels + .iter() + .map(|v| v.to_owned_from_source(__buffa_src)) + .collect::<::core::result::Result<_, ::buffa::DecodeError>>()?, + annotations: self + .annotations + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(), + charter: match self.charter.as_option() { + Some(v) => { + ::buffa::MessageField::< + super::super::super::super::v1::Charter, + >::some(v.to_owned_from_source(__buffa_src)?) + } + None => ::buffa::MessageField::none(), + }, + content_digest: self.content_digest.to_string(), + ..::core::default::Default::default() + }) + } +} +impl<'a> ::buffa::ViewEncode<'a> for ProvisionedAgentView<'a> { + #[allow(clippy::needless_borrow, clippy::let_and_return)] + fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.agent_id) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.name) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.parent) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.owner) as u32; + for v in &self.labels { + let __slot = __cache.reserve(); + let inner_size = v.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + #[allow(clippy::for_kv_map)] + for (k, v) in &self.annotations { + let entry_size: u32 = 1u32 + ::buffa::types::string_encoded_len(k) as u32 + + 1u32 + ::buffa::types::string_encoded_len(v) as u32; + size + += 1u32 + ::buffa::encoding::varint_len(entry_size as u64) as u32 + + entry_size; + } + if self.charter.is_set() { + let __slot = __cache.reserve(); + let inner_size = self.charter.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + size += 1u32 + ::buffa::types::string_encoded_len(&self.content_digest) as u32; + size + } + #[allow(clippy::needless_borrow)] + fn write_to( + &self, + __cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + ::buffa::types::put_string_field(1u32, &self.agent_id, buf); + ::buffa::types::put_string_field(2u32, &self.name, buf); + ::buffa::types::put_string_field(3u32, &self.parent, buf); + ::buffa::types::put_string_field(4u32, &self.owner, buf); + for v in &self.labels { + ::buffa::types::put_len_delimited_header(5u32, __cache.consume_next(), buf); + v.write_to(__cache, buf); + } + for (k, v) in &self.annotations { + let entry_size: u32 = 1u32 + ::buffa::types::string_encoded_len(k) as u32 + + 1u32 + ::buffa::types::string_encoded_len(v) as u32; + ::buffa::encoding::Tag::new( + 6u32, + ::buffa::encoding::WireType::LengthDelimited, + ) + .encode(buf); + ::buffa::encoding::encode_varint(entry_size as u64, buf); + ::buffa::encoding::Tag::new( + 1u32, + ::buffa::encoding::WireType::LengthDelimited, + ) + .encode(buf); + ::buffa::types::encode_string(k, buf); + ::buffa::encoding::Tag::new( + 2u32, + ::buffa::encoding::WireType::LengthDelimited, + ) + .encode(buf); + ::buffa::types::encode_string(v, buf); + } + if self.charter.is_set() { + ::buffa::types::put_len_delimited_header(7u32, __cache.consume_next(), buf); + self.charter.write_to(__cache, buf); + } + ::buffa::types::put_string_field(8u32, &self.content_digest, buf); + } +} +/// Serializes this view as protobuf JSON. +/// +/// Implicit-presence fields with default values are omitted, `required` +/// fields are always emitted, explicit-presence (`optional`) fields are +/// emitted only when set, bytes fields are base64-encoded, and enum +/// values are their proto name strings. +/// +/// This impl uses `serialize_map(None)` because the number of emitted +/// fields depends on default-omission rules; serializers that require +/// known map lengths (e.g. `bincode`) will return a runtime error. +/// Use the owned message type for those formats. +impl<'__a> ::serde::Serialize for ProvisionedAgentView<'__a> { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + use ::serde::ser::SerializeMap as _; + let mut __map = __s.serialize_map(::core::option::Option::None)?; + { + __map.serialize_entry("agentId", self.agent_id)?; + } + { + __map.serialize_entry("name", self.name)?; + } + { + __map.serialize_entry("parent", self.parent)?; + } + { + __map.serialize_entry("owner", self.owner)?; + } + if !self.labels.is_empty() { + __map.serialize_entry("labels", &*self.labels)?; + } + if !self.annotations.is_empty() { + struct _WM<'__a, '__x>(&'__x ::buffa::MapView<'__x, &'__a str, &'__a str>); + impl<'__a> ::serde::Serialize for _WM<'__a, '_> { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + use ::serde::ser::SerializeMap as _; + let mut __m = __s + .serialize_map(::core::option::Option::Some(self.0.len()))?; + for (k, v) in self.0.iter_unique() { + __m.serialize_entry(k, v)?; + } + __m.end() + } + } + __map.serialize_entry("annotations", &_WM(&self.annotations))?; + } + { + if let ::core::option::Option::Some(__v) = self.charter.as_option() { + __map.serialize_entry("charter", __v)?; + } + } + { + __map.serialize_entry("contentDigest", self.content_digest)?; + } + __map.end() + } +} +impl<'a> ::buffa::MessageName for ProvisionedAgentView<'a> { + const PACKAGE: &'static str = "trogonai.agents.agents.state.v1"; + const NAME: &'static str = "ProvisionedAgent"; + const FULL_NAME: &'static str = "trogonai.agents.agents.state.v1.ProvisionedAgent"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.state.v1.ProvisionedAgent"; +} +::buffa::impl_default_view_instance!(ProvisionedAgentView); +::buffa::impl_view_reborrow!(ProvisionedAgentView); +/** Self-contained, `'static` owned view of a `ProvisionedAgent` message. + + Wraps [`::buffa::OwnedView`]`<`[`ProvisionedAgentView`]`<'static>>`: the decoded view and the [`::buffa::bytes::Bytes`] buffer it borrows from travel together, so the handle is `'static` and `Send + Sync` — suitable for async handlers, spawned tasks, and anywhere a `'static` bound is required. + + Field accessors return borrows tied to `&self`. Use [`Self::view`] to get the full [`ProvisionedAgentView`] when you need struct patterns, iteration helpers, or to pass the view to lifetime-parameterised code.*/ +#[derive(Clone, Debug)] +pub struct ProvisionedAgentOwnedView(::buffa::OwnedView>); +impl ProvisionedAgentOwnedView { + /// Decode an owned view from a [`::buffa::bytes::Bytes`] buffer. + /// + /// The view borrows directly from the buffer's data; the buffer is + /// retained inside the returned handle. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer contains invalid + /// protobuf data. + pub fn decode( + bytes: ::buffa::bytes::Bytes, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ProvisionedAgentOwnedView(::buffa::OwnedView::decode(bytes)?), + ) + } + /// Decode with custom [`::buffa::DecodeOptions`] (recursion limit, + /// max message size). + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer is invalid or + /// exceeds the configured limits. + pub fn decode_with_options( + bytes: ::buffa::bytes::Bytes, + opts: &::buffa::DecodeOptions, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ProvisionedAgentOwnedView( + ::buffa::OwnedView::decode_with_options(bytes, opts)?, + ), + ) + } + /// Build from an owned message via an encode → decode round-trip. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// somehow invalid (should not happen for well-formed messages). + pub fn from_owned( + msg: &super::super::ProvisionedAgent, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ProvisionedAgentOwnedView(::buffa::OwnedView::from_owned(msg)?), + ) + } + /// Borrow the full [`ProvisionedAgentView`] with its lifetime tied to `&self`. + #[must_use] + pub fn view(&self) -> &ProvisionedAgentView<'_> { + self.0.reborrow() + } + /// Convert to the owned message type. + /// + /// # Errors + /// + /// Returns an error if re-materializing preserved unknown fields + /// fails (e.g. the unknown-field limit is exceeded). + pub fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.0.to_owned_message() + } + /// The underlying bytes buffer. + #[must_use] + pub fn bytes(&self) -> &::buffa::bytes::Bytes { + self.0.bytes() + } + /// Consume the handle, returning the underlying bytes buffer. + #[must_use] + pub fn into_bytes(self) -> ::buffa::bytes::Bytes { + self.0.into_bytes() + } + /// Field 1: `agent_id` + #[must_use] + pub fn agent_id(&self) -> &'_ str { + self.0.reborrow().agent_id + } + /// Field 2: `name` + #[must_use] + pub fn name(&self) -> &'_ str { + self.0.reborrow().name + } + /// Field 3: `parent` + #[must_use] + pub fn parent(&self) -> &'_ str { + self.0.reborrow().parent + } + /// Field 4: `owner` + #[must_use] + pub fn owner(&self) -> &'_ str { + self.0.reborrow().owner + } + /// Field 5: `labels` + #[must_use] + pub fn labels( + &self, + ) -> &::buffa::RepeatedView< + '_, + super::super::super::super::v1::__buffa::view::LabelView<'_>, + > { + &self.0.reborrow().labels + } + /// Field 6: `annotations` (map) + #[must_use] + pub fn annotations(&self) -> &::buffa::MapView<'_, &'_ str, &'_ str> { + &self.0.reborrow().annotations + } + /// Field 7: `charter` + #[must_use] + pub fn charter( + &self, + ) -> &::buffa::MessageFieldView< + super::super::super::super::v1::__buffa::view::CharterView<'_>, + > { + &self.0.reborrow().charter + } + /// Field 8: `content_digest` + #[must_use] + pub fn content_digest(&self) -> &'_ str { + self.0.reborrow().content_digest + } +} +impl ::core::convert::From<::buffa::OwnedView>> +for ProvisionedAgentOwnedView { + fn from(inner: ::buffa::OwnedView>) -> Self { + ProvisionedAgentOwnedView(inner) + } +} +impl ::core::convert::From +for ::buffa::OwnedView> { + fn from(wrapper: ProvisionedAgentOwnedView) -> Self { + wrapper.0 + } +} +impl ::core::convert::AsRef<::buffa::OwnedView>> +for ProvisionedAgentOwnedView { + fn as_ref(&self) -> &::buffa::OwnedView> { + &self.0 + } +} +impl ::buffa::HasMessageView for super::super::ProvisionedAgent { + type View<'a> = ProvisionedAgentView<'a>; + type ViewHandle = ProvisionedAgentOwnedView; +} +impl ::serde::Serialize for ProvisionedAgentOwnedView { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + ::serde::Serialize::serialize(&self.0, __s) + } +} diff --git a/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.provision_agent_state.rs b/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.provision_agent_state.rs new file mode 100644 index 0000000000..07240ad464 --- /dev/null +++ b/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.state.v1.provision_agent_state.rs @@ -0,0 +1,380 @@ +// @generated by buffa-codegen. DO NOT EDIT. +// source: trogonai/agents/agents/state/v1/provision_agent_state.proto + +#[derive(Clone, PartialEq, Default)] +#[derive(::serde::Serialize, ::serde::Deserialize)] +#[serde(default)] +pub struct ProvisionAgentState { + /// Absence means the agent has not been provisioned. + /// + /// Field 1: `provisioned` + #[serde( + rename = "provisioned", + skip_serializing_if = "::buffa::json_helpers::skip_if::is_unset_message_field" + )] + pub provisioned: ::buffa::MessageField, +} +impl ::core::fmt::Debug for ProvisionAgentState { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_struct("ProvisionAgentState") + .field("provisioned", &self.provisioned) + .finish() + } +} +impl ProvisionAgentState { + /// Protobuf type URL for this message, for use with `Any::pack` and + /// `Any::unpack_if`. + /// + /// Format: `type.googleapis.com/` + pub const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.state.v1.ProvisionAgentState"; +} +::buffa::impl_default_instance!(ProvisionAgentState); +impl ::buffa::MessageName for ProvisionAgentState { + const PACKAGE: &'static str = "trogonai.agents.agents.state.v1"; + const NAME: &'static str = "ProvisionAgentState"; + const FULL_NAME: &'static str = "trogonai.agents.agents.state.v1.ProvisionAgentState"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.state.v1.ProvisionAgentState"; +} +impl ::buffa::Message for ProvisionAgentState { + /// Returns the total encoded size in bytes. + /// + /// The result is a `u32`; the protobuf specification requires all + /// messages to fit within 2 GiB (2,147,483,647 bytes), so a + /// compliant message will never overflow this type. + #[allow(clippy::let_and_return)] + fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + if self.provisioned.is_set() { + let __slot = __cache.reserve(); + let inner_size = self.provisioned.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + size + } + fn write_to( + &self, + __cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + if self.provisioned.is_set() { + ::buffa::types::put_len_delimited_header(1u32, __cache.consume_next(), buf); + self.provisioned.write_to(__cache, buf); + } + } + fn merge_field( + &mut self, + tag: ::buffa::encoding::Tag, + buf: &mut impl ::buffa::bytes::Buf, + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + #[allow(unused_imports)] + use ::buffa::bytes::Buf as _; + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::Message::merge_length_delimited( + self.provisioned.get_or_insert_default(), + buf, + ctx, + )?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, buf, ctx.depth())?; + } + } + ::core::result::Result::Ok(()) + } + fn clear(&mut self) { + self.provisioned = ::buffa::MessageField::none(); + } +} +impl ::buffa::json_helpers::ProtoElemJson for ProvisionAgentState { + fn serialize_proto_json( + v: &Self, + s: S, + ) -> ::core::result::Result { + ::serde::Serialize::serialize(v, s) + } + fn deserialize_proto_json<'de, D: ::serde::Deserializer<'de>>( + d: D, + ) -> ::core::result::Result { + ::deserialize(d) + } +} +#[doc(hidden)] +pub const __PROVISION_AGENT_STATE_JSON_ANY: ::buffa::type_registry::JsonAnyEntry = ::buffa::type_registry::JsonAnyEntry { + type_url: "type.googleapis.com/trogonai.agents.agents.state.v1.ProvisionAgentState", + to_json: ::buffa::type_registry::any_to_json::, + from_json: ::buffa::type_registry::any_from_json::, + is_wkt: false, +}; +/// ProvisionedAgent contains only the persistent fields used to classify +/// retries. Delivery metadata and the provisioning actor never enter state. +#[derive(Clone, PartialEq, Default)] +#[derive(::serde::Serialize, ::serde::Deserialize)] +#[serde(default)] +pub struct ProvisionedAgent { + /// Field 1: `agent_id` + #[serde( + rename = "agentId", + alias = "agent_id", + with = "::buffa::json_helpers::proto_string" + )] + pub agent_id: ::buffa::alloc::string::String, + /// Field 2: `name` + #[serde(rename = "name", with = "::buffa::json_helpers::proto_string")] + pub name: ::buffa::alloc::string::String, + /// Field 3: `parent` + #[serde(rename = "parent", with = "::buffa::json_helpers::proto_string")] + pub parent: ::buffa::alloc::string::String, + /// Field 4: `owner` + #[serde(rename = "owner", with = "::buffa::json_helpers::proto_string")] + pub owner: ::buffa::alloc::string::String, + /// Field 5: `labels` + #[serde( + rename = "labels", + skip_serializing_if = "::buffa::json_helpers::skip_if::is_empty_vec", + deserialize_with = "::buffa::json_helpers::null_as_default" + )] + pub labels: ::buffa::alloc::vec::Vec, + /// Field 6: `annotations` + #[serde( + rename = "annotations", + skip_serializing_if = "::buffa::__private::HashMap::is_empty", + deserialize_with = "::buffa::json_helpers::null_as_default" + )] + pub annotations: ::buffa::__private::HashMap< + ::buffa::alloc::string::String, + ::buffa::alloc::string::String, + >, + /// Field 7: `charter` + #[serde(rename = "charter")] + pub charter: ::buffa::MessageField, + /// Field 8: `content_digest` + #[serde( + rename = "contentDigest", + alias = "content_digest", + with = "::buffa::json_helpers::proto_string" + )] + pub content_digest: ::buffa::alloc::string::String, +} +impl ::core::fmt::Debug for ProvisionedAgent { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_struct("ProvisionedAgent") + .field("agent_id", &self.agent_id) + .field("name", &self.name) + .field("parent", &self.parent) + .field("owner", &self.owner) + .field("labels", &self.labels) + .field("annotations", &self.annotations) + .field("charter", &self.charter) + .field("content_digest", &self.content_digest) + .finish() + } +} +impl ProvisionedAgent { + /// Protobuf type URL for this message, for use with `Any::pack` and + /// `Any::unpack_if`. + /// + /// Format: `type.googleapis.com/` + pub const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.state.v1.ProvisionedAgent"; +} +::buffa::impl_default_instance!(ProvisionedAgent); +impl ::buffa::MessageName for ProvisionedAgent { + const PACKAGE: &'static str = "trogonai.agents.agents.state.v1"; + const NAME: &'static str = "ProvisionedAgent"; + const FULL_NAME: &'static str = "trogonai.agents.agents.state.v1.ProvisionedAgent"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.state.v1.ProvisionedAgent"; +} +impl ::buffa::Message for ProvisionedAgent { + /// Returns the total encoded size in bytes. + /// + /// The result is a `u32`; the protobuf specification requires all + /// messages to fit within 2 GiB (2,147,483,647 bytes), so a + /// compliant message will never overflow this type. + #[allow(clippy::let_and_return)] + fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.agent_id) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.name) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.parent) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.owner) as u32; + for v in &self.labels { + let __slot = __cache.reserve(); + let inner_size = v.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + size + += ::buffa::map_codec::field_len::< + ::buffa::map_codec::Str, + ::buffa::map_codec::Str, + _, + >(&self.annotations, 1u32); + if self.charter.is_set() { + let __slot = __cache.reserve(); + let inner_size = self.charter.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + size += 1u32 + ::buffa::types::string_encoded_len(&self.content_digest) as u32; + size + } + fn write_to( + &self, + __cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + ::buffa::types::put_string_field(1u32, &self.agent_id, buf); + ::buffa::types::put_string_field(2u32, &self.name, buf); + ::buffa::types::put_string_field(3u32, &self.parent, buf); + ::buffa::types::put_string_field(4u32, &self.owner, buf); + for v in &self.labels { + ::buffa::types::put_len_delimited_header(5u32, __cache.consume_next(), buf); + v.write_to(__cache, buf); + } + ::buffa::map_codec::write_field::< + ::buffa::map_codec::Str, + ::buffa::map_codec::Str, + _, + >(&self.annotations, 6u32, buf); + if self.charter.is_set() { + ::buffa::types::put_len_delimited_header(7u32, __cache.consume_next(), buf); + self.charter.write_to(__cache, buf); + } + ::buffa::types::put_string_field(8u32, &self.content_digest, buf); + } + fn merge_field( + &mut self, + tag: ::buffa::encoding::Tag, + buf: &mut impl ::buffa::bytes::Buf, + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + #[allow(unused_imports)] + use ::buffa::bytes::Buf as _; + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::types::merge_string(&mut self.agent_id, buf)?; + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::types::merge_string(&mut self.name, buf)?; + } + 3u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::types::merge_string(&mut self.parent, buf)?; + } + 4u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::types::merge_string(&mut self.owner, buf)?; + } + 5u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + let mut elem = ::core::default::Default::default(); + ::buffa::Message::merge_length_delimited(&mut elem, buf, ctx)?; + self.labels.push(elem); + } + 6u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::map_codec::merge_entry::< + ::buffa::map_codec::Str, + ::buffa::map_codec::Str, + _, + >(&mut self.annotations, buf, ctx)?; + } + 7u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::Message::merge_length_delimited( + self.charter.get_or_insert_default(), + buf, + ctx, + )?; + } + 8u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::types::merge_string(&mut self.content_digest, buf)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, buf, ctx.depth())?; + } + } + ::core::result::Result::Ok(()) + } + fn clear(&mut self) { + self.agent_id.clear(); + self.name.clear(); + self.parent.clear(); + self.owner.clear(); + self.labels.clear(); + self.annotations.clear(); + self.charter = ::buffa::MessageField::none(); + self.content_digest.clear(); + } +} +impl ::buffa::json_helpers::ProtoElemJson for ProvisionedAgent { + fn serialize_proto_json( + v: &Self, + s: S, + ) -> ::core::result::Result { + ::serde::Serialize::serialize(v, s) + } + fn deserialize_proto_json<'de, D: ::serde::Deserializer<'de>>( + d: D, + ) -> ::core::result::Result { + ::deserialize(d) + } +} +#[doc(hidden)] +pub const __PROVISIONED_AGENT_JSON_ANY: ::buffa::type_registry::JsonAnyEntry = ::buffa::type_registry::JsonAnyEntry { + type_url: "type.googleapis.com/trogonai.agents.agents.state.v1.ProvisionedAgent", + to_json: ::buffa::type_registry::any_to_json::, + from_json: ::buffa::type_registry::any_from_json::, + is_wkt: false, +}; diff --git a/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.agent.__view.rs b/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.agent.__view.rs new file mode 100644 index 0000000000..6350473c15 --- /dev/null +++ b/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.agent.__view.rs @@ -0,0 +1,2097 @@ +// @generated by buffa-codegen. DO NOT EDIT. +// source: trogonai/agents/agents/v1/agent.proto + +/// Charter is the minimal, versioned declaration of an agent (design Q16): +/// identity, engine, and dependency declarations. Nothing economic, temporal, +/// evaluative, or reputational lives here; those are stances on their own +/// planes (policy, evaluation, scheduling), selector-bound and human-owned. +#[derive(Clone, Debug, Default)] +pub struct CharterView<'a> { + /// Execution engine. Immutable for the agent's v1 lifetime; switching + /// engines mints a new sibling agent rather than a revision (design Q24). + /// + /// Field 1: `runtime` + pub runtime: &'a str, + /// Default model; a per-session override does not mint a revision. + /// + /// Field 2: `model` + pub model: ::buffa::MessageFieldView>, + /// Field 3: `tools` + pub tools: ::buffa::MessageFieldView< + super::super::__buffa::view::ToolDependenciesView<'a>, + >, + /// Field 4: `delegates` + pub delegates: ::buffa::MessageFieldView< + super::super::__buffa::view::DelegateDependenciesView<'a>, + >, + #[doc(hidden)] + pub __buffa_required_seen_0: u64, +} +impl<'a> CharterView<'a> { + /**Whether required field `runtime` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_runtime(&self) -> bool { + self.__buffa_required_seen_0 & 1u64 != 0 + } + /**Whether required field `model` is set. + +Mirrors `is_set()` on the field: `true` after decoding a message where the field was present on the wire, and `true` on a hand-built view whose field is populated. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_model(&self) -> bool { + self.model.is_set() + } + /**Whether required field `tools` is set. + +Mirrors `is_set()` on the field: `true` after decoding a message where the field was present on the wire, and `true` on a hand-built view whose field is populated. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_tools(&self) -> bool { + self.tools.is_set() + } + /**Whether required field `delegates` is set. + +Mirrors `is_set()` on the field: `true` after decoding a message where the field was present on the wire, and `true` on a hand-built view whose field is populated. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_delegates(&self) -> bool { + self.delegates.is_set() + } +} +impl<'a> ::buffa::MessageView<'a> for CharterView<'a> { + type Owned = super::super::Charter; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + let __limit = ::core::cell::Cell::new(::buffa::DEFAULT_UNKNOWN_FIELD_LIMIT); + ::decode_view_ctx( + buf, + ::buffa::DecodeContext::new(::buffa::RECURSION_LIMIT, &__limit), + ) + } + fn decode_view_with_ctx( + buf: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result { + ::decode_view_ctx(buf, ctx) + } + fn merge_view_field( + &mut self, + tag: ::buffa::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<&'a [u8], ::buffa::DecodeError> { + let _ = ctx; + #[allow(unused_variables)] + let view = self; + let mut cur = cur; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.runtime = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 1u64; + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + let __sub_ctx = ctx.descend()?; + let sub = ::buffa::types::borrow_bytes(&mut cur)?; + match view.model.as_mut() { + Some(existing) => { + ::buffa::MessageView::merge_into_view(existing, sub, __sub_ctx)? + } + None => { + view.model = ::buffa::MessageFieldView::set( + ::decode_view_ctx( + sub, + __sub_ctx, + )?, + ); + } + } + } + 3u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + let __sub_ctx = ctx.descend()?; + let sub = ::buffa::types::borrow_bytes(&mut cur)?; + match view.tools.as_mut() { + Some(existing) => { + ::buffa::MessageView::merge_into_view(existing, sub, __sub_ctx)? + } + None => { + view.tools = ::buffa::MessageFieldView::set( + ::decode_view_ctx( + sub, + __sub_ctx, + )?, + ); + } + } + } + 4u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + let __sub_ctx = ctx.descend()?; + let sub = ::buffa::types::borrow_bytes(&mut cur)?; + match view.delegates.as_mut() { + Some(existing) => { + ::buffa::MessageView::merge_into_view(existing, sub, __sub_ctx)? + } + None => { + view.delegates = ::buffa::MessageFieldView::set( + ::decode_view_ctx( + sub, + __sub_ctx, + )?, + ); + } + } + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, ctx.depth())?; + } + } + ::core::result::Result::Ok(cur) + } + fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.to_owned_from_source(None) + } + #[allow(clippy::useless_conversion, clippy::needless_update)] + fn to_owned_from_source( + &self, + __buffa_src: ::core::option::Option<&::buffa::bytes::Bytes>, + ) -> ::core::result::Result { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + let _ = __buffa_src; + ::core::result::Result::Ok(super::super::Charter { + runtime: self.runtime.to_string(), + model: match self.model.as_option() { + Some(v) => { + ::buffa::MessageField::< + super::super::Model, + >::some(v.to_owned_from_source(__buffa_src)?) + } + None => ::buffa::MessageField::none(), + }, + tools: match self.tools.as_option() { + Some(v) => { + ::buffa::MessageField::< + super::super::ToolDependencies, + >::some(v.to_owned_from_source(__buffa_src)?) + } + None => ::buffa::MessageField::none(), + }, + delegates: match self.delegates.as_option() { + Some(v) => { + ::buffa::MessageField::< + super::super::DelegateDependencies, + >::some(v.to_owned_from_source(__buffa_src)?) + } + None => ::buffa::MessageField::none(), + }, + ..::core::default::Default::default() + }) + } +} +impl<'a> ::buffa::ViewEncode<'a> for CharterView<'a> { + #[allow(clippy::needless_borrow, clippy::let_and_return)] + fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.runtime) as u32; + if self.model.is_set() { + let __slot = __cache.reserve(); + let inner_size = self.model.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + if self.tools.is_set() { + let __slot = __cache.reserve(); + let inner_size = self.tools.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + if self.delegates.is_set() { + let __slot = __cache.reserve(); + let inner_size = self.delegates.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + size + } + #[allow(clippy::needless_borrow)] + fn write_to( + &self, + __cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + ::buffa::types::put_string_field(1u32, &self.runtime, buf); + if self.model.is_set() { + ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + self.model.write_to(__cache, buf); + } + if self.tools.is_set() { + ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + self.tools.write_to(__cache, buf); + } + if self.delegates.is_set() { + ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + self.delegates.write_to(__cache, buf); + } + } +} +/// Serializes this view as protobuf JSON. +/// +/// Implicit-presence fields with default values are omitted, `required` +/// fields are always emitted, explicit-presence (`optional`) fields are +/// emitted only when set, bytes fields are base64-encoded, and enum +/// values are their proto name strings. +/// +/// This impl uses `serialize_map(None)` because the number of emitted +/// fields depends on default-omission rules; serializers that require +/// known map lengths (e.g. `bincode`) will return a runtime error. +/// Use the owned message type for those formats. +impl<'__a> ::serde::Serialize for CharterView<'__a> { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + use ::serde::ser::SerializeMap as _; + let mut __map = __s.serialize_map(::core::option::Option::None)?; + { + __map.serialize_entry("runtime", self.runtime)?; + } + { + if let ::core::option::Option::Some(__v) = self.model.as_option() { + __map.serialize_entry("model", __v)?; + } + } + { + if let ::core::option::Option::Some(__v) = self.tools.as_option() { + __map.serialize_entry("tools", __v)?; + } + } + { + if let ::core::option::Option::Some(__v) = self.delegates.as_option() { + __map.serialize_entry("delegates", __v)?; + } + } + __map.end() + } +} +impl<'a> ::buffa::MessageName for CharterView<'a> { + const PACKAGE: &'static str = "trogonai.agents.agents.v1"; + const NAME: &'static str = "Charter"; + const FULL_NAME: &'static str = "trogonai.agents.agents.v1.Charter"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.Charter"; +} +::buffa::impl_default_view_instance!(CharterView); +::buffa::impl_view_reborrow!(CharterView); +/** Self-contained, `'static` owned view of a `Charter` message. + + Wraps [`::buffa::OwnedView`]`<`[`CharterView`]`<'static>>`: the decoded view and the [`::buffa::bytes::Bytes`] buffer it borrows from travel together, so the handle is `'static` and `Send + Sync` — suitable for async handlers, spawned tasks, and anywhere a `'static` bound is required. + + Field accessors return borrows tied to `&self`. Use [`Self::view`] to get the full [`CharterView`] when you need struct patterns, iteration helpers, or to pass the view to lifetime-parameterised code.*/ +#[derive(Clone, Debug)] +pub struct CharterOwnedView(::buffa::OwnedView>); +impl CharterOwnedView { + /// Decode an owned view from a [`::buffa::bytes::Bytes`] buffer. + /// + /// The view borrows directly from the buffer's data; the buffer is + /// retained inside the returned handle. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer contains invalid + /// protobuf data. + pub fn decode( + bytes: ::buffa::bytes::Bytes, + ) -> ::core::result::Result { + ::core::result::Result::Ok(CharterOwnedView(::buffa::OwnedView::decode(bytes)?)) + } + /// Decode with custom [`::buffa::DecodeOptions`] (recursion limit, + /// max message size). + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer is invalid or + /// exceeds the configured limits. + pub fn decode_with_options( + bytes: ::buffa::bytes::Bytes, + opts: &::buffa::DecodeOptions, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + CharterOwnedView(::buffa::OwnedView::decode_with_options(bytes, opts)?), + ) + } + /// Build from an owned message via an encode → decode round-trip. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// somehow invalid (should not happen for well-formed messages). + pub fn from_owned( + msg: &super::super::Charter, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + CharterOwnedView(::buffa::OwnedView::from_owned(msg)?), + ) + } + /// Borrow the full [`CharterView`] with its lifetime tied to `&self`. + #[must_use] + pub fn view(&self) -> &CharterView<'_> { + self.0.reborrow() + } + /// Convert to the owned message type. + /// + /// # Errors + /// + /// Returns an error if re-materializing preserved unknown fields + /// fails (e.g. the unknown-field limit is exceeded). + pub fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.0.to_owned_message() + } + /// The underlying bytes buffer. + #[must_use] + pub fn bytes(&self) -> &::buffa::bytes::Bytes { + self.0.bytes() + } + /// Consume the handle, returning the underlying bytes buffer. + #[must_use] + pub fn into_bytes(self) -> ::buffa::bytes::Bytes { + self.0.into_bytes() + } + /// Execution engine. Immutable for the agent's v1 lifetime; switching + /// engines mints a new sibling agent rather than a revision (design Q24). + /// + /// Field 1: `runtime` + #[must_use] + pub fn runtime(&self) -> &'_ str { + self.0.reborrow().runtime + } + /// Default model; a per-session override does not mint a revision. + /// + /// Field 2: `model` + #[must_use] + pub fn model( + &self, + ) -> &::buffa::MessageFieldView> { + &self.0.reborrow().model + } + /// Field 3: `tools` + #[must_use] + pub fn tools( + &self, + ) -> &::buffa::MessageFieldView< + super::super::__buffa::view::ToolDependenciesView<'_>, + > { + &self.0.reborrow().tools + } + /// Field 4: `delegates` + #[must_use] + pub fn delegates( + &self, + ) -> &::buffa::MessageFieldView< + super::super::__buffa::view::DelegateDependenciesView<'_>, + > { + &self.0.reborrow().delegates + } +} +impl ::core::convert::From<::buffa::OwnedView>> +for CharterOwnedView { + fn from(inner: ::buffa::OwnedView>) -> Self { + CharterOwnedView(inner) + } +} +impl ::core::convert::From +for ::buffa::OwnedView> { + fn from(wrapper: CharterOwnedView) -> Self { + wrapper.0 + } +} +impl ::core::convert::AsRef<::buffa::OwnedView>> +for CharterOwnedView { + fn as_ref(&self) -> &::buffa::OwnedView> { + &self.0 + } +} +impl ::buffa::HasMessageView for super::super::Charter { + type View<'a> = CharterView<'a>; + type ViewHandle = CharterOwnedView; +} +impl ::serde::Serialize for CharterOwnedView { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + ::serde::Serialize::serialize(&self.0, __s) + } +} +/// ModelParameter is one deterministic model-configuration entry. Entries must +/// have unique keys and be ordered by key before they cross the wire boundary. +#[derive(Clone, Debug, Default)] +pub struct ModelParameterView<'a> { + /// Field 1: `key` + pub key: &'a str, + /// Field 2: `value` + pub value: &'a str, + #[doc(hidden)] + pub __buffa_required_seen_0: u64, +} +impl<'a> ModelParameterView<'a> { + /**Whether required field `key` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_key(&self) -> bool { + self.__buffa_required_seen_0 & 1u64 != 0 + } + /**Whether required field `value` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_value(&self) -> bool { + self.__buffa_required_seen_0 & 2u64 != 0 + } +} +impl<'a> ::buffa::MessageView<'a> for ModelParameterView<'a> { + type Owned = super::super::ModelParameter; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + let __limit = ::core::cell::Cell::new(::buffa::DEFAULT_UNKNOWN_FIELD_LIMIT); + ::decode_view_ctx( + buf, + ::buffa::DecodeContext::new(::buffa::RECURSION_LIMIT, &__limit), + ) + } + fn decode_view_with_ctx( + buf: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result { + ::decode_view_ctx(buf, ctx) + } + fn merge_view_field( + &mut self, + tag: ::buffa::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<&'a [u8], ::buffa::DecodeError> { + let _ = ctx; + #[allow(unused_variables)] + let view = self; + let mut cur = cur; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.key = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 1u64; + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.value = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 2u64; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, ctx.depth())?; + } + } + ::core::result::Result::Ok(cur) + } + fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.to_owned_from_source(None) + } + #[allow(clippy::useless_conversion, clippy::needless_update)] + fn to_owned_from_source( + &self, + __buffa_src: ::core::option::Option<&::buffa::bytes::Bytes>, + ) -> ::core::result::Result { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + let _ = __buffa_src; + ::core::result::Result::Ok(super::super::ModelParameter { + key: self.key.to_string(), + value: self.value.to_string(), + ..::core::default::Default::default() + }) + } +} +impl<'a> ::buffa::ViewEncode<'a> for ModelParameterView<'a> { + #[allow(clippy::needless_borrow, clippy::let_and_return)] + fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.key) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.value) as u32; + size + } + #[allow(clippy::needless_borrow)] + fn write_to( + &self, + _cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + ::buffa::types::put_string_field(1u32, &self.key, buf); + ::buffa::types::put_string_field(2u32, &self.value, buf); + } +} +/// Serializes this view as protobuf JSON. +/// +/// Implicit-presence fields with default values are omitted, `required` +/// fields are always emitted, explicit-presence (`optional`) fields are +/// emitted only when set, bytes fields are base64-encoded, and enum +/// values are their proto name strings. +/// +/// This impl uses `serialize_map(None)` because the number of emitted +/// fields depends on default-omission rules; serializers that require +/// known map lengths (e.g. `bincode`) will return a runtime error. +/// Use the owned message type for those formats. +impl<'__a> ::serde::Serialize for ModelParameterView<'__a> { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + use ::serde::ser::SerializeMap as _; + let mut __map = __s.serialize_map(::core::option::Option::None)?; + { + __map.serialize_entry("key", self.key)?; + } + { + __map.serialize_entry("value", self.value)?; + } + __map.end() + } +} +impl<'a> ::buffa::MessageName for ModelParameterView<'a> { + const PACKAGE: &'static str = "trogonai.agents.agents.v1"; + const NAME: &'static str = "ModelParameter"; + const FULL_NAME: &'static str = "trogonai.agents.agents.v1.ModelParameter"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.ModelParameter"; +} +::buffa::impl_default_view_instance!(ModelParameterView); +::buffa::impl_view_reborrow!(ModelParameterView); +/** Self-contained, `'static` owned view of a `ModelParameter` message. + + Wraps [`::buffa::OwnedView`]`<`[`ModelParameterView`]`<'static>>`: the decoded view and the [`::buffa::bytes::Bytes`] buffer it borrows from travel together, so the handle is `'static` and `Send + Sync` — suitable for async handlers, spawned tasks, and anywhere a `'static` bound is required. + + Field accessors return borrows tied to `&self`. Use [`Self::view`] to get the full [`ModelParameterView`] when you need struct patterns, iteration helpers, or to pass the view to lifetime-parameterised code.*/ +#[derive(Clone, Debug)] +pub struct ModelParameterOwnedView(::buffa::OwnedView>); +impl ModelParameterOwnedView { + /// Decode an owned view from a [`::buffa::bytes::Bytes`] buffer. + /// + /// The view borrows directly from the buffer's data; the buffer is + /// retained inside the returned handle. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer contains invalid + /// protobuf data. + pub fn decode( + bytes: ::buffa::bytes::Bytes, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ModelParameterOwnedView(::buffa::OwnedView::decode(bytes)?), + ) + } + /// Decode with custom [`::buffa::DecodeOptions`] (recursion limit, + /// max message size). + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer is invalid or + /// exceeds the configured limits. + pub fn decode_with_options( + bytes: ::buffa::bytes::Bytes, + opts: &::buffa::DecodeOptions, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ModelParameterOwnedView( + ::buffa::OwnedView::decode_with_options(bytes, opts)?, + ), + ) + } + /// Build from an owned message via an encode → decode round-trip. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// somehow invalid (should not happen for well-formed messages). + pub fn from_owned( + msg: &super::super::ModelParameter, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ModelParameterOwnedView(::buffa::OwnedView::from_owned(msg)?), + ) + } + /// Borrow the full [`ModelParameterView`] with its lifetime tied to `&self`. + #[must_use] + pub fn view(&self) -> &ModelParameterView<'_> { + self.0.reborrow() + } + /// Convert to the owned message type. + /// + /// # Errors + /// + /// Returns an error if re-materializing preserved unknown fields + /// fails (e.g. the unknown-field limit is exceeded). + pub fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.0.to_owned_message() + } + /// The underlying bytes buffer. + #[must_use] + pub fn bytes(&self) -> &::buffa::bytes::Bytes { + self.0.bytes() + } + /// Consume the handle, returning the underlying bytes buffer. + #[must_use] + pub fn into_bytes(self) -> ::buffa::bytes::Bytes { + self.0.into_bytes() + } + /// Field 1: `key` + #[must_use] + pub fn key(&self) -> &'_ str { + self.0.reborrow().key + } + /// Field 2: `value` + #[must_use] + pub fn value(&self) -> &'_ str { + self.0.reborrow().value + } +} +impl ::core::convert::From<::buffa::OwnedView>> +for ModelParameterOwnedView { + fn from(inner: ::buffa::OwnedView>) -> Self { + ModelParameterOwnedView(inner) + } +} +impl ::core::convert::From +for ::buffa::OwnedView> { + fn from(wrapper: ModelParameterOwnedView) -> Self { + wrapper.0 + } +} +impl ::core::convert::AsRef<::buffa::OwnedView>> +for ModelParameterOwnedView { + fn as_ref(&self) -> &::buffa::OwnedView> { + &self.0 + } +} +impl ::buffa::HasMessageView for super::super::ModelParameter { + type View<'a> = ModelParameterView<'a>; + type ViewHandle = ModelParameterOwnedView; +} +impl ::serde::Serialize for ModelParameterOwnedView { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + ::serde::Serialize::serialize(&self.0, __s) + } +} +/// Label is selectable metadata. Entries must have unique keys and be ordered +/// by key so guest Wasm receives a deterministic sequence. +#[derive(Clone, Debug, Default)] +pub struct LabelView<'a> { + /// Field 1: `key` + pub key: &'a str, + /// Field 2: `value` + pub value: &'a str, + #[doc(hidden)] + pub __buffa_required_seen_0: u64, +} +impl<'a> LabelView<'a> { + /**Whether required field `key` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_key(&self) -> bool { + self.__buffa_required_seen_0 & 1u64 != 0 + } + /**Whether required field `value` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_value(&self) -> bool { + self.__buffa_required_seen_0 & 2u64 != 0 + } +} +impl<'a> ::buffa::MessageView<'a> for LabelView<'a> { + type Owned = super::super::Label; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + let __limit = ::core::cell::Cell::new(::buffa::DEFAULT_UNKNOWN_FIELD_LIMIT); + ::decode_view_ctx( + buf, + ::buffa::DecodeContext::new(::buffa::RECURSION_LIMIT, &__limit), + ) + } + fn decode_view_with_ctx( + buf: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result { + ::decode_view_ctx(buf, ctx) + } + fn merge_view_field( + &mut self, + tag: ::buffa::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<&'a [u8], ::buffa::DecodeError> { + let _ = ctx; + #[allow(unused_variables)] + let view = self; + let mut cur = cur; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.key = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 1u64; + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.value = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 2u64; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, ctx.depth())?; + } + } + ::core::result::Result::Ok(cur) + } + fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.to_owned_from_source(None) + } + #[allow(clippy::useless_conversion, clippy::needless_update)] + fn to_owned_from_source( + &self, + __buffa_src: ::core::option::Option<&::buffa::bytes::Bytes>, + ) -> ::core::result::Result { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + let _ = __buffa_src; + ::core::result::Result::Ok(super::super::Label { + key: self.key.to_string(), + value: self.value.to_string(), + ..::core::default::Default::default() + }) + } +} +impl<'a> ::buffa::ViewEncode<'a> for LabelView<'a> { + #[allow(clippy::needless_borrow, clippy::let_and_return)] + fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.key) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.value) as u32; + size + } + #[allow(clippy::needless_borrow)] + fn write_to( + &self, + _cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + ::buffa::types::put_string_field(1u32, &self.key, buf); + ::buffa::types::put_string_field(2u32, &self.value, buf); + } +} +/// Serializes this view as protobuf JSON. +/// +/// Implicit-presence fields with default values are omitted, `required` +/// fields are always emitted, explicit-presence (`optional`) fields are +/// emitted only when set, bytes fields are base64-encoded, and enum +/// values are their proto name strings. +/// +/// This impl uses `serialize_map(None)` because the number of emitted +/// fields depends on default-omission rules; serializers that require +/// known map lengths (e.g. `bincode`) will return a runtime error. +/// Use the owned message type for those formats. +impl<'__a> ::serde::Serialize for LabelView<'__a> { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + use ::serde::ser::SerializeMap as _; + let mut __map = __s.serialize_map(::core::option::Option::None)?; + { + __map.serialize_entry("key", self.key)?; + } + { + __map.serialize_entry("value", self.value)?; + } + __map.end() + } +} +impl<'a> ::buffa::MessageName for LabelView<'a> { + const PACKAGE: &'static str = "trogonai.agents.agents.v1"; + const NAME: &'static str = "Label"; + const FULL_NAME: &'static str = "trogonai.agents.agents.v1.Label"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.Label"; +} +::buffa::impl_default_view_instance!(LabelView); +::buffa::impl_view_reborrow!(LabelView); +/** Self-contained, `'static` owned view of a `Label` message. + + Wraps [`::buffa::OwnedView`]`<`[`LabelView`]`<'static>>`: the decoded view and the [`::buffa::bytes::Bytes`] buffer it borrows from travel together, so the handle is `'static` and `Send + Sync` — suitable for async handlers, spawned tasks, and anywhere a `'static` bound is required. + + Field accessors return borrows tied to `&self`. Use [`Self::view`] to get the full [`LabelView`] when you need struct patterns, iteration helpers, or to pass the view to lifetime-parameterised code.*/ +#[derive(Clone, Debug)] +pub struct LabelOwnedView(::buffa::OwnedView>); +impl LabelOwnedView { + /// Decode an owned view from a [`::buffa::bytes::Bytes`] buffer. + /// + /// The view borrows directly from the buffer's data; the buffer is + /// retained inside the returned handle. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer contains invalid + /// protobuf data. + pub fn decode( + bytes: ::buffa::bytes::Bytes, + ) -> ::core::result::Result { + ::core::result::Result::Ok(LabelOwnedView(::buffa::OwnedView::decode(bytes)?)) + } + /// Decode with custom [`::buffa::DecodeOptions`] (recursion limit, + /// max message size). + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer is invalid or + /// exceeds the configured limits. + pub fn decode_with_options( + bytes: ::buffa::bytes::Bytes, + opts: &::buffa::DecodeOptions, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + LabelOwnedView(::buffa::OwnedView::decode_with_options(bytes, opts)?), + ) + } + /// Build from an owned message via an encode → decode round-trip. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// somehow invalid (should not happen for well-formed messages). + pub fn from_owned( + msg: &super::super::Label, + ) -> ::core::result::Result { + ::core::result::Result::Ok(LabelOwnedView(::buffa::OwnedView::from_owned(msg)?)) + } + /// Borrow the full [`LabelView`] with its lifetime tied to `&self`. + #[must_use] + pub fn view(&self) -> &LabelView<'_> { + self.0.reborrow() + } + /// Convert to the owned message type. + /// + /// # Errors + /// + /// Returns an error if re-materializing preserved unknown fields + /// fails (e.g. the unknown-field limit is exceeded). + pub fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.0.to_owned_message() + } + /// The underlying bytes buffer. + #[must_use] + pub fn bytes(&self) -> &::buffa::bytes::Bytes { + self.0.bytes() + } + /// Consume the handle, returning the underlying bytes buffer. + #[must_use] + pub fn into_bytes(self) -> ::buffa::bytes::Bytes { + self.0.into_bytes() + } + /// Field 1: `key` + #[must_use] + pub fn key(&self) -> &'_ str { + self.0.reborrow().key + } + /// Field 2: `value` + #[must_use] + pub fn value(&self) -> &'_ str { + self.0.reborrow().value + } +} +impl ::core::convert::From<::buffa::OwnedView>> for LabelOwnedView { + fn from(inner: ::buffa::OwnedView>) -> Self { + LabelOwnedView(inner) + } +} +impl ::core::convert::From for ::buffa::OwnedView> { + fn from(wrapper: LabelOwnedView) -> Self { + wrapper.0 + } +} +impl ::core::convert::AsRef<::buffa::OwnedView>> for LabelOwnedView { + fn as_ref(&self) -> &::buffa::OwnedView> { + &self.0 + } +} +impl ::buffa::HasMessageView for super::super::Label { + type View<'a> = LabelView<'a>; + type ViewHandle = LabelOwnedView; +} +impl ::serde::Serialize for LabelOwnedView { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + ::serde::Serialize::serialize(&self.0, __s) + } +} +/// Model is a default, not a constraint; sessions may override it without +/// minting a revision. +#[derive(Clone, Debug, Default)] +pub struct ModelView<'a> { + /// Field 1: `id` + pub id: &'a str, + /// Field 2: `params` + pub params: ::buffa::RepeatedView< + 'a, + super::super::__buffa::view::ModelParameterView<'a>, + >, + #[doc(hidden)] + pub __buffa_required_seen_0: u64, +} +impl<'a> ModelView<'a> { + /**Whether required field `id` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_id(&self) -> bool { + self.__buffa_required_seen_0 & 1u64 != 0 + } +} +impl<'a> ::buffa::MessageView<'a> for ModelView<'a> { + type Owned = super::super::Model; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + let __limit = ::core::cell::Cell::new(::buffa::DEFAULT_UNKNOWN_FIELD_LIMIT); + ::decode_view_ctx( + buf, + ::buffa::DecodeContext::new(::buffa::RECURSION_LIMIT, &__limit), + ) + } + fn decode_view_with_ctx( + buf: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result { + ::decode_view_ctx(buf, ctx) + } + fn merge_view_field( + &mut self, + tag: ::buffa::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<&'a [u8], ::buffa::DecodeError> { + let _ = ctx; + #[allow(unused_variables)] + let view = self; + let mut cur = cur; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.id = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 1u64; + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + let __sub_ctx = ctx.descend()?; + let sub = ::buffa::types::borrow_bytes(&mut cur)?; + view.params + .push( + ::decode_view_ctx( + sub, + __sub_ctx, + )?, + ); + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, ctx.depth())?; + } + } + ::core::result::Result::Ok(cur) + } + fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.to_owned_from_source(None) + } + #[allow(clippy::useless_conversion, clippy::needless_update)] + fn to_owned_from_source( + &self, + __buffa_src: ::core::option::Option<&::buffa::bytes::Bytes>, + ) -> ::core::result::Result { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + let _ = __buffa_src; + ::core::result::Result::Ok(super::super::Model { + id: self.id.to_string(), + params: self + .params + .iter() + .map(|v| v.to_owned_from_source(__buffa_src)) + .collect::<::core::result::Result<_, ::buffa::DecodeError>>()?, + ..::core::default::Default::default() + }) + } +} +impl<'a> ::buffa::ViewEncode<'a> for ModelView<'a> { + #[allow(clippy::needless_borrow, clippy::let_and_return)] + fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.id) as u32; + for v in &self.params { + let __slot = __cache.reserve(); + let inner_size = v.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + size + } + #[allow(clippy::needless_borrow)] + fn write_to( + &self, + __cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + ::buffa::types::put_string_field(1u32, &self.id, buf); + for v in &self.params { + ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + v.write_to(__cache, buf); + } + } +} +/// Serializes this view as protobuf JSON. +/// +/// Implicit-presence fields with default values are omitted, `required` +/// fields are always emitted, explicit-presence (`optional`) fields are +/// emitted only when set, bytes fields are base64-encoded, and enum +/// values are their proto name strings. +/// +/// This impl uses `serialize_map(None)` because the number of emitted +/// fields depends on default-omission rules; serializers that require +/// known map lengths (e.g. `bincode`) will return a runtime error. +/// Use the owned message type for those formats. +impl<'__a> ::serde::Serialize for ModelView<'__a> { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + use ::serde::ser::SerializeMap as _; + let mut __map = __s.serialize_map(::core::option::Option::None)?; + { + __map.serialize_entry("id", self.id)?; + } + if !self.params.is_empty() { + __map.serialize_entry("params", &*self.params)?; + } + __map.end() + } +} +impl<'a> ::buffa::MessageName for ModelView<'a> { + const PACKAGE: &'static str = "trogonai.agents.agents.v1"; + const NAME: &'static str = "Model"; + const FULL_NAME: &'static str = "trogonai.agents.agents.v1.Model"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.Model"; +} +::buffa::impl_default_view_instance!(ModelView); +::buffa::impl_view_reborrow!(ModelView); +/** Self-contained, `'static` owned view of a `Model` message. + + Wraps [`::buffa::OwnedView`]`<`[`ModelView`]`<'static>>`: the decoded view and the [`::buffa::bytes::Bytes`] buffer it borrows from travel together, so the handle is `'static` and `Send + Sync` — suitable for async handlers, spawned tasks, and anywhere a `'static` bound is required. + + Field accessors return borrows tied to `&self`. Use [`Self::view`] to get the full [`ModelView`] when you need struct patterns, iteration helpers, or to pass the view to lifetime-parameterised code.*/ +#[derive(Clone, Debug)] +pub struct ModelOwnedView(::buffa::OwnedView>); +impl ModelOwnedView { + /// Decode an owned view from a [`::buffa::bytes::Bytes`] buffer. + /// + /// The view borrows directly from the buffer's data; the buffer is + /// retained inside the returned handle. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer contains invalid + /// protobuf data. + pub fn decode( + bytes: ::buffa::bytes::Bytes, + ) -> ::core::result::Result { + ::core::result::Result::Ok(ModelOwnedView(::buffa::OwnedView::decode(bytes)?)) + } + /// Decode with custom [`::buffa::DecodeOptions`] (recursion limit, + /// max message size). + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer is invalid or + /// exceeds the configured limits. + pub fn decode_with_options( + bytes: ::buffa::bytes::Bytes, + opts: &::buffa::DecodeOptions, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ModelOwnedView(::buffa::OwnedView::decode_with_options(bytes, opts)?), + ) + } + /// Build from an owned message via an encode → decode round-trip. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// somehow invalid (should not happen for well-formed messages). + pub fn from_owned( + msg: &super::super::Model, + ) -> ::core::result::Result { + ::core::result::Result::Ok(ModelOwnedView(::buffa::OwnedView::from_owned(msg)?)) + } + /// Borrow the full [`ModelView`] with its lifetime tied to `&self`. + #[must_use] + pub fn view(&self) -> &ModelView<'_> { + self.0.reborrow() + } + /// Convert to the owned message type. + /// + /// # Errors + /// + /// Returns an error if re-materializing preserved unknown fields + /// fails (e.g. the unknown-field limit is exceeded). + pub fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.0.to_owned_message() + } + /// The underlying bytes buffer. + #[must_use] + pub fn bytes(&self) -> &::buffa::bytes::Bytes { + self.0.bytes() + } + /// Consume the handle, returning the underlying bytes buffer. + #[must_use] + pub fn into_bytes(self) -> ::buffa::bytes::Bytes { + self.0.into_bytes() + } + /// Field 1: `id` + #[must_use] + pub fn id(&self) -> &'_ str { + self.0.reborrow().id + } + /// Field 2: `params` + #[must_use] + pub fn params( + &self, + ) -> &::buffa::RepeatedView< + '_, + super::super::__buffa::view::ModelParameterView<'_>, + > { + &self.0.reborrow().params + } +} +impl ::core::convert::From<::buffa::OwnedView>> for ModelOwnedView { + fn from(inner: ::buffa::OwnedView>) -> Self { + ModelOwnedView(inner) + } +} +impl ::core::convert::From for ::buffa::OwnedView> { + fn from(wrapper: ModelOwnedView) -> Self { + wrapper.0 + } +} +impl ::core::convert::AsRef<::buffa::OwnedView>> for ModelOwnedView { + fn as_ref(&self) -> &::buffa::OwnedView> { + &self.0 + } +} +impl ::buffa::HasMessageView for super::super::Model { + type View<'a> = ModelView<'a>; + type ViewHandle = ModelOwnedView; +} +impl ::serde::Serialize for ModelOwnedView { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + ::serde::Serialize::serialize(&self.0, __s) + } +} +/// ToolDependencies are declarations, not grants. Grants are human-held, +/// evaluated live at every tool call on the Grants stream (design Q8/Q16). +#[derive(Clone, Debug, Default)] +pub struct ToolDependenciesView<'a> { + /// Field 1: `required` + pub required: ::buffa::RepeatedView<'a, &'a str>, + /// Field 2: `optional` + pub optional: ::buffa::RepeatedView<'a, &'a str>, +} +impl<'a> ::buffa::MessageView<'a> for ToolDependenciesView<'a> { + type Owned = super::super::ToolDependencies; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + let __limit = ::core::cell::Cell::new(::buffa::DEFAULT_UNKNOWN_FIELD_LIMIT); + ::decode_view_ctx( + buf, + ::buffa::DecodeContext::new(::buffa::RECURSION_LIMIT, &__limit), + ) + } + fn decode_view_with_ctx( + buf: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result { + ::decode_view_ctx(buf, ctx) + } + fn merge_view_field( + &mut self, + tag: ::buffa::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<&'a [u8], ::buffa::DecodeError> { + let _ = ctx; + #[allow(unused_variables)] + let view = self; + let mut cur = cur; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.required.push(::buffa::types::borrow_str(&mut cur)?); + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.optional.push(::buffa::types::borrow_str(&mut cur)?); + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, ctx.depth())?; + } + } + ::core::result::Result::Ok(cur) + } + fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.to_owned_from_source(None) + } + #[allow(clippy::useless_conversion, clippy::needless_update)] + fn to_owned_from_source( + &self, + __buffa_src: ::core::option::Option<&::buffa::bytes::Bytes>, + ) -> ::core::result::Result { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + let _ = __buffa_src; + ::core::result::Result::Ok(super::super::ToolDependencies { + required: self.required.iter().map(|s| s.to_string()).collect(), + optional: self.optional.iter().map(|s| s.to_string()).collect(), + ..::core::default::Default::default() + }) + } +} +impl<'a> ::buffa::ViewEncode<'a> for ToolDependenciesView<'a> { + #[allow(clippy::needless_borrow, clippy::let_and_return)] + fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + for v in &self.required { + size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + } + for v in &self.optional { + size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + } + size + } + #[allow(clippy::needless_borrow)] + fn write_to( + &self, + _cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + for v in &self.required { + ::buffa::types::put_string_field(1u32, v, buf); + } + for v in &self.optional { + ::buffa::types::put_string_field(2u32, v, buf); + } + } +} +/// Serializes this view as protobuf JSON. +/// +/// Implicit-presence fields with default values are omitted, `required` +/// fields are always emitted, explicit-presence (`optional`) fields are +/// emitted only when set, bytes fields are base64-encoded, and enum +/// values are their proto name strings. +/// +/// This impl uses `serialize_map(None)` because the number of emitted +/// fields depends on default-omission rules; serializers that require +/// known map lengths (e.g. `bincode`) will return a runtime error. +/// Use the owned message type for those formats. +impl<'__a> ::serde::Serialize for ToolDependenciesView<'__a> { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + use ::serde::ser::SerializeMap as _; + let mut __map = __s.serialize_map(::core::option::Option::None)?; + if !self.required.is_empty() { + __map.serialize_entry("required", &*self.required)?; + } + if !self.optional.is_empty() { + __map.serialize_entry("optional", &*self.optional)?; + } + __map.end() + } +} +impl<'a> ::buffa::MessageName for ToolDependenciesView<'a> { + const PACKAGE: &'static str = "trogonai.agents.agents.v1"; + const NAME: &'static str = "ToolDependencies"; + const FULL_NAME: &'static str = "trogonai.agents.agents.v1.ToolDependencies"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.ToolDependencies"; +} +::buffa::impl_default_view_instance!(ToolDependenciesView); +::buffa::impl_view_reborrow!(ToolDependenciesView); +/** Self-contained, `'static` owned view of a `ToolDependencies` message. + + Wraps [`::buffa::OwnedView`]`<`[`ToolDependenciesView`]`<'static>>`: the decoded view and the [`::buffa::bytes::Bytes`] buffer it borrows from travel together, so the handle is `'static` and `Send + Sync` — suitable for async handlers, spawned tasks, and anywhere a `'static` bound is required. + + Field accessors return borrows tied to `&self`. Use [`Self::view`] to get the full [`ToolDependenciesView`] when you need struct patterns, iteration helpers, or to pass the view to lifetime-parameterised code.*/ +#[derive(Clone, Debug)] +pub struct ToolDependenciesOwnedView(::buffa::OwnedView>); +impl ToolDependenciesOwnedView { + /// Decode an owned view from a [`::buffa::bytes::Bytes`] buffer. + /// + /// The view borrows directly from the buffer's data; the buffer is + /// retained inside the returned handle. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer contains invalid + /// protobuf data. + pub fn decode( + bytes: ::buffa::bytes::Bytes, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ToolDependenciesOwnedView(::buffa::OwnedView::decode(bytes)?), + ) + } + /// Decode with custom [`::buffa::DecodeOptions`] (recursion limit, + /// max message size). + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer is invalid or + /// exceeds the configured limits. + pub fn decode_with_options( + bytes: ::buffa::bytes::Bytes, + opts: &::buffa::DecodeOptions, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ToolDependenciesOwnedView( + ::buffa::OwnedView::decode_with_options(bytes, opts)?, + ), + ) + } + /// Build from an owned message via an encode → decode round-trip. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// somehow invalid (should not happen for well-formed messages). + pub fn from_owned( + msg: &super::super::ToolDependencies, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + ToolDependenciesOwnedView(::buffa::OwnedView::from_owned(msg)?), + ) + } + /// Borrow the full [`ToolDependenciesView`] with its lifetime tied to `&self`. + #[must_use] + pub fn view(&self) -> &ToolDependenciesView<'_> { + self.0.reborrow() + } + /// Convert to the owned message type. + /// + /// # Errors + /// + /// Returns an error if re-materializing preserved unknown fields + /// fails (e.g. the unknown-field limit is exceeded). + pub fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.0.to_owned_message() + } + /// The underlying bytes buffer. + #[must_use] + pub fn bytes(&self) -> &::buffa::bytes::Bytes { + self.0.bytes() + } + /// Consume the handle, returning the underlying bytes buffer. + #[must_use] + pub fn into_bytes(self) -> ::buffa::bytes::Bytes { + self.0.into_bytes() + } + /// Field 1: `required` + #[must_use] + pub fn required(&self) -> &::buffa::RepeatedView<'_, &'_ str> { + &self.0.reborrow().required + } + /// Field 2: `optional` + #[must_use] + pub fn optional(&self) -> &::buffa::RepeatedView<'_, &'_ str> { + &self.0.reborrow().optional + } +} +impl ::core::convert::From<::buffa::OwnedView>> +for ToolDependenciesOwnedView { + fn from(inner: ::buffa::OwnedView>) -> Self { + ToolDependenciesOwnedView(inner) + } +} +impl ::core::convert::From +for ::buffa::OwnedView> { + fn from(wrapper: ToolDependenciesOwnedView) -> Self { + wrapper.0 + } +} +impl ::core::convert::AsRef<::buffa::OwnedView>> +for ToolDependenciesOwnedView { + fn as_ref(&self) -> &::buffa::OwnedView> { + &self.0 + } +} +impl ::buffa::HasMessageView for super::super::ToolDependencies { + type View<'a> = ToolDependenciesView<'a>; + type ViewHandle = ToolDependenciesOwnedView; +} +impl ::serde::Serialize for ToolDependenciesOwnedView { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + ::serde::Serialize::serialize(&self.0, __s) + } +} +/// DelegateDependencies are declarations, not permissions. +#[derive(Clone, Debug, Default)] +pub struct DelegateDependenciesView<'a> { + /// Field 1: `required` + pub required: ::buffa::RepeatedView<'a, &'a str>, + /// Field 2: `optional` + pub optional: ::buffa::RepeatedView<'a, &'a str>, +} +impl<'a> ::buffa::MessageView<'a> for DelegateDependenciesView<'a> { + type Owned = super::super::DelegateDependencies; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + let __limit = ::core::cell::Cell::new(::buffa::DEFAULT_UNKNOWN_FIELD_LIMIT); + ::decode_view_ctx( + buf, + ::buffa::DecodeContext::new(::buffa::RECURSION_LIMIT, &__limit), + ) + } + fn decode_view_with_ctx( + buf: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result { + ::decode_view_ctx(buf, ctx) + } + fn merge_view_field( + &mut self, + tag: ::buffa::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<&'a [u8], ::buffa::DecodeError> { + let _ = ctx; + #[allow(unused_variables)] + let view = self; + let mut cur = cur; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.required.push(::buffa::types::borrow_str(&mut cur)?); + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.optional.push(::buffa::types::borrow_str(&mut cur)?); + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, ctx.depth())?; + } + } + ::core::result::Result::Ok(cur) + } + fn to_owned_message( + &self, + ) -> ::core::result::Result< + super::super::DelegateDependencies, + ::buffa::DecodeError, + > { + self.to_owned_from_source(None) + } + #[allow(clippy::useless_conversion, clippy::needless_update)] + fn to_owned_from_source( + &self, + __buffa_src: ::core::option::Option<&::buffa::bytes::Bytes>, + ) -> ::core::result::Result< + super::super::DelegateDependencies, + ::buffa::DecodeError, + > { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + let _ = __buffa_src; + ::core::result::Result::Ok(super::super::DelegateDependencies { + required: self.required.iter().map(|s| s.to_string()).collect(), + optional: self.optional.iter().map(|s| s.to_string()).collect(), + ..::core::default::Default::default() + }) + } +} +impl<'a> ::buffa::ViewEncode<'a> for DelegateDependenciesView<'a> { + #[allow(clippy::needless_borrow, clippy::let_and_return)] + fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + for v in &self.required { + size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + } + for v in &self.optional { + size += 1u32 + ::buffa::types::string_encoded_len(v) as u32; + } + size + } + #[allow(clippy::needless_borrow)] + fn write_to( + &self, + _cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + for v in &self.required { + ::buffa::types::put_string_field(1u32, v, buf); + } + for v in &self.optional { + ::buffa::types::put_string_field(2u32, v, buf); + } + } +} +/// Serializes this view as protobuf JSON. +/// +/// Implicit-presence fields with default values are omitted, `required` +/// fields are always emitted, explicit-presence (`optional`) fields are +/// emitted only when set, bytes fields are base64-encoded, and enum +/// values are their proto name strings. +/// +/// This impl uses `serialize_map(None)` because the number of emitted +/// fields depends on default-omission rules; serializers that require +/// known map lengths (e.g. `bincode`) will return a runtime error. +/// Use the owned message type for those formats. +impl<'__a> ::serde::Serialize for DelegateDependenciesView<'__a> { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + use ::serde::ser::SerializeMap as _; + let mut __map = __s.serialize_map(::core::option::Option::None)?; + if !self.required.is_empty() { + __map.serialize_entry("required", &*self.required)?; + } + if !self.optional.is_empty() { + __map.serialize_entry("optional", &*self.optional)?; + } + __map.end() + } +} +impl<'a> ::buffa::MessageName for DelegateDependenciesView<'a> { + const PACKAGE: &'static str = "trogonai.agents.agents.v1"; + const NAME: &'static str = "DelegateDependencies"; + const FULL_NAME: &'static str = "trogonai.agents.agents.v1.DelegateDependencies"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.DelegateDependencies"; +} +::buffa::impl_default_view_instance!(DelegateDependenciesView); +::buffa::impl_view_reborrow!(DelegateDependenciesView); +/** Self-contained, `'static` owned view of a `DelegateDependencies` message. + + Wraps [`::buffa::OwnedView`]`<`[`DelegateDependenciesView`]`<'static>>`: the decoded view and the [`::buffa::bytes::Bytes`] buffer it borrows from travel together, so the handle is `'static` and `Send + Sync` — suitable for async handlers, spawned tasks, and anywhere a `'static` bound is required. + + Field accessors return borrows tied to `&self`. Use [`Self::view`] to get the full [`DelegateDependenciesView`] when you need struct patterns, iteration helpers, or to pass the view to lifetime-parameterised code.*/ +#[derive(Clone, Debug)] +pub struct DelegateDependenciesOwnedView( + ::buffa::OwnedView>, +); +impl DelegateDependenciesOwnedView { + /// Decode an owned view from a [`::buffa::bytes::Bytes`] buffer. + /// + /// The view borrows directly from the buffer's data; the buffer is + /// retained inside the returned handle. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer contains invalid + /// protobuf data. + pub fn decode( + bytes: ::buffa::bytes::Bytes, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + DelegateDependenciesOwnedView(::buffa::OwnedView::decode(bytes)?), + ) + } + /// Decode with custom [`::buffa::DecodeOptions`] (recursion limit, + /// max message size). + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer is invalid or + /// exceeds the configured limits. + pub fn decode_with_options( + bytes: ::buffa::bytes::Bytes, + opts: &::buffa::DecodeOptions, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + DelegateDependenciesOwnedView( + ::buffa::OwnedView::decode_with_options(bytes, opts)?, + ), + ) + } + /// Build from an owned message via an encode → decode round-trip. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// somehow invalid (should not happen for well-formed messages). + pub fn from_owned( + msg: &super::super::DelegateDependencies, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + DelegateDependenciesOwnedView(::buffa::OwnedView::from_owned(msg)?), + ) + } + /// Borrow the full [`DelegateDependenciesView`] with its lifetime tied to `&self`. + #[must_use] + pub fn view(&self) -> &DelegateDependenciesView<'_> { + self.0.reborrow() + } + /// Convert to the owned message type. + /// + /// # Errors + /// + /// Returns an error if re-materializing preserved unknown fields + /// fails (e.g. the unknown-field limit is exceeded). + pub fn to_owned_message( + &self, + ) -> ::core::result::Result< + super::super::DelegateDependencies, + ::buffa::DecodeError, + > { + self.0.to_owned_message() + } + /// The underlying bytes buffer. + #[must_use] + pub fn bytes(&self) -> &::buffa::bytes::Bytes { + self.0.bytes() + } + /// Consume the handle, returning the underlying bytes buffer. + #[must_use] + pub fn into_bytes(self) -> ::buffa::bytes::Bytes { + self.0.into_bytes() + } + /// Field 1: `required` + #[must_use] + pub fn required(&self) -> &::buffa::RepeatedView<'_, &'_ str> { + &self.0.reborrow().required + } + /// Field 2: `optional` + #[must_use] + pub fn optional(&self) -> &::buffa::RepeatedView<'_, &'_ str> { + &self.0.reborrow().optional + } +} +impl ::core::convert::From<::buffa::OwnedView>> +for DelegateDependenciesOwnedView { + fn from(inner: ::buffa::OwnedView>) -> Self { + DelegateDependenciesOwnedView(inner) + } +} +impl ::core::convert::From +for ::buffa::OwnedView> { + fn from(wrapper: DelegateDependenciesOwnedView) -> Self { + wrapper.0 + } +} +impl ::core::convert::AsRef<::buffa::OwnedView>> +for DelegateDependenciesOwnedView { + fn as_ref(&self) -> &::buffa::OwnedView> { + &self.0 + } +} +impl ::buffa::HasMessageView for super::super::DelegateDependencies { + type View<'a> = DelegateDependenciesView<'a>; + type ViewHandle = DelegateDependenciesOwnedView; +} +impl ::serde::Serialize for DelegateDependenciesOwnedView { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + ::serde::Serialize::serialize(&self.0, __s) + } +} +/// RevisionRef pins a specific revision by number and content digest. +#[derive(Clone, Debug, Default)] +pub struct RevisionRefView<'a> { + /// Field 1: `number` + pub number: u64, + /// Field 2: `content_digest` + pub content_digest: &'a str, + #[doc(hidden)] + pub __buffa_required_seen_0: u64, +} +impl<'a> RevisionRefView<'a> { + /**Whether required field `number` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_number(&self) -> bool { + self.__buffa_required_seen_0 & 1u64 != 0 + } + /**Whether required field `content_digest` was present on the wire. + +Distinguishes a field that was absent from one explicitly encoded with its default value (required scalar fields are stored as bare, non-`Option` types, so the value alone cannot tell the two apart). Presence is recorded only by the wire decoder: a default or hand-built view reports `false`. Encoding is unaffected — required fields are always written.*/ + #[must_use] + #[inline] + pub const fn has_content_digest(&self) -> bool { + self.__buffa_required_seen_0 & 2u64 != 0 + } +} +impl<'a> ::buffa::MessageView<'a> for RevisionRefView<'a> { + type Owned = super::super::RevisionRef; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + let __limit = ::core::cell::Cell::new(::buffa::DEFAULT_UNKNOWN_FIELD_LIMIT); + ::decode_view_ctx( + buf, + ::buffa::DecodeContext::new(::buffa::RECURSION_LIMIT, &__limit), + ) + } + fn decode_view_with_ctx( + buf: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result { + ::decode_view_ctx(buf, ctx) + } + fn merge_view_field( + &mut self, + tag: ::buffa::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<&'a [u8], ::buffa::DecodeError> { + let _ = ctx; + #[allow(unused_variables)] + let view = self; + let mut cur = cur; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::Varint, + )?; + view.number = ::buffa::types::decode_uint64(&mut cur)?; + view.__buffa_required_seen_0 |= 1u64; + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + view.content_digest = ::buffa::types::borrow_str(&mut cur)?; + view.__buffa_required_seen_0 |= 2u64; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, ctx.depth())?; + } + } + ::core::result::Result::Ok(cur) + } + fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.to_owned_from_source(None) + } + #[allow(clippy::useless_conversion, clippy::needless_update)] + fn to_owned_from_source( + &self, + __buffa_src: ::core::option::Option<&::buffa::bytes::Bytes>, + ) -> ::core::result::Result { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + let _ = __buffa_src; + ::core::result::Result::Ok(super::super::RevisionRef { + number: self.number, + content_digest: self.content_digest.to_string(), + ..::core::default::Default::default() + }) + } +} +impl<'a> ::buffa::ViewEncode<'a> for RevisionRefView<'a> { + #[allow(clippy::needless_borrow, clippy::let_and_return)] + fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + size += 1u32 + ::buffa::types::uint64_encoded_len(self.number) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.content_digest) as u32; + size + } + #[allow(clippy::needless_borrow)] + fn write_to( + &self, + _cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + ::buffa::types::put_uint64_field(1u32, self.number, buf); + ::buffa::types::put_string_field(2u32, &self.content_digest, buf); + } +} +/// Serializes this view as protobuf JSON. +/// +/// Implicit-presence fields with default values are omitted, `required` +/// fields are always emitted, explicit-presence (`optional`) fields are +/// emitted only when set, bytes fields are base64-encoded, and enum +/// values are their proto name strings. +/// +/// This impl uses `serialize_map(None)` because the number of emitted +/// fields depends on default-omission rules; serializers that require +/// known map lengths (e.g. `bincode`) will return a runtime error. +/// Use the owned message type for those formats. +impl<'__a> ::serde::Serialize for RevisionRefView<'__a> { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + use ::serde::ser::SerializeMap as _; + let mut __map = __s.serialize_map(::core::option::Option::None)?; + { + __map + .serialize_entry( + "number", + &::buffa::json_helpers::ProtoJson(&self.number), + )?; + } + { + __map.serialize_entry("contentDigest", self.content_digest)?; + } + __map.end() + } +} +impl<'a> ::buffa::MessageName for RevisionRefView<'a> { + const PACKAGE: &'static str = "trogonai.agents.agents.v1"; + const NAME: &'static str = "RevisionRef"; + const FULL_NAME: &'static str = "trogonai.agents.agents.v1.RevisionRef"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.RevisionRef"; +} +::buffa::impl_default_view_instance!(RevisionRefView); +::buffa::impl_view_reborrow!(RevisionRefView); +/** Self-contained, `'static` owned view of a `RevisionRef` message. + + Wraps [`::buffa::OwnedView`]`<`[`RevisionRefView`]`<'static>>`: the decoded view and the [`::buffa::bytes::Bytes`] buffer it borrows from travel together, so the handle is `'static` and `Send + Sync` — suitable for async handlers, spawned tasks, and anywhere a `'static` bound is required. + + Field accessors return borrows tied to `&self`. Use [`Self::view`] to get the full [`RevisionRefView`] when you need struct patterns, iteration helpers, or to pass the view to lifetime-parameterised code.*/ +#[derive(Clone, Debug)] +pub struct RevisionRefOwnedView(::buffa::OwnedView>); +impl RevisionRefOwnedView { + /// Decode an owned view from a [`::buffa::bytes::Bytes`] buffer. + /// + /// The view borrows directly from the buffer's data; the buffer is + /// retained inside the returned handle. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer contains invalid + /// protobuf data. + pub fn decode( + bytes: ::buffa::bytes::Bytes, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + RevisionRefOwnedView(::buffa::OwnedView::decode(bytes)?), + ) + } + /// Decode with custom [`::buffa::DecodeOptions`] (recursion limit, + /// max message size). + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the buffer is invalid or + /// exceeds the configured limits. + pub fn decode_with_options( + bytes: ::buffa::bytes::Bytes, + opts: &::buffa::DecodeOptions, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + RevisionRefOwnedView(::buffa::OwnedView::decode_with_options(bytes, opts)?), + ) + } + /// Build from an owned message via an encode → decode round-trip. + /// + /// # Errors + /// + /// Returns [`::buffa::DecodeError`] if the re-encoded bytes are + /// somehow invalid (should not happen for well-formed messages). + pub fn from_owned( + msg: &super::super::RevisionRef, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + RevisionRefOwnedView(::buffa::OwnedView::from_owned(msg)?), + ) + } + /// Borrow the full [`RevisionRefView`] with its lifetime tied to `&self`. + #[must_use] + pub fn view(&self) -> &RevisionRefView<'_> { + self.0.reborrow() + } + /// Convert to the owned message type. + /// + /// # Errors + /// + /// Returns an error if re-materializing preserved unknown fields + /// fails (e.g. the unknown-field limit is exceeded). + pub fn to_owned_message( + &self, + ) -> ::core::result::Result { + self.0.to_owned_message() + } + /// The underlying bytes buffer. + #[must_use] + pub fn bytes(&self) -> &::buffa::bytes::Bytes { + self.0.bytes() + } + /// Consume the handle, returning the underlying bytes buffer. + #[must_use] + pub fn into_bytes(self) -> ::buffa::bytes::Bytes { + self.0.into_bytes() + } + /// Field 1: `number` + #[must_use] + pub fn number(&self) -> u64 { + self.0.reborrow().number + } + /// Field 2: `content_digest` + #[must_use] + pub fn content_digest(&self) -> &'_ str { + self.0.reborrow().content_digest + } +} +impl ::core::convert::From<::buffa::OwnedView>> +for RevisionRefOwnedView { + fn from(inner: ::buffa::OwnedView>) -> Self { + RevisionRefOwnedView(inner) + } +} +impl ::core::convert::From +for ::buffa::OwnedView> { + fn from(wrapper: RevisionRefOwnedView) -> Self { + wrapper.0 + } +} +impl ::core::convert::AsRef<::buffa::OwnedView>> +for RevisionRefOwnedView { + fn as_ref(&self) -> &::buffa::OwnedView> { + &self.0 + } +} +impl ::buffa::HasMessageView for super::super::RevisionRef { + type View<'a> = RevisionRefView<'a>; + type ViewHandle = RevisionRefOwnedView; +} +impl ::serde::Serialize for RevisionRefOwnedView { + fn serialize<__S: ::serde::Serializer>( + &self, + __s: __S, + ) -> ::core::result::Result<__S::Ok, __S::Error> { + ::serde::Serialize::serialize(&self.0, __s) + } +} diff --git a/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.agent.rs b/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.agent.rs new file mode 100644 index 0000000000..13b5c26ec3 --- /dev/null +++ b/rsworkspace/crates/platform/trogonai-proto/src/gen/trogonai.agents.agents.v1.agent.rs @@ -0,0 +1,942 @@ +// @generated by buffa-codegen. DO NOT EDIT. +// source: trogonai/agents/agents/v1/agent.proto + +/// Charter is the minimal, versioned declaration of an agent (design Q16): +/// identity, engine, and dependency declarations. Nothing economic, temporal, +/// evaluative, or reputational lives here; those are stances on their own +/// planes (policy, evaluation, scheduling), selector-bound and human-owned. +#[derive(Clone, PartialEq, Default)] +#[derive(::serde::Serialize, ::serde::Deserialize)] +#[serde(default)] +pub struct Charter { + /// Execution engine. Immutable for the agent's v1 lifetime; switching + /// engines mints a new sibling agent rather than a revision (design Q24). + /// + /// Field 1: `runtime` + #[serde(rename = "runtime", with = "::buffa::json_helpers::proto_string")] + pub runtime: ::buffa::alloc::string::String, + /// Default model; a per-session override does not mint a revision. + /// + /// Field 2: `model` + #[serde(rename = "model")] + pub model: ::buffa::MessageField, + /// Field 3: `tools` + #[serde(rename = "tools")] + pub tools: ::buffa::MessageField, + /// Field 4: `delegates` + #[serde(rename = "delegates")] + pub delegates: ::buffa::MessageField, +} +impl ::core::fmt::Debug for Charter { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_struct("Charter") + .field("runtime", &self.runtime) + .field("model", &self.model) + .field("tools", &self.tools) + .field("delegates", &self.delegates) + .finish() + } +} +impl Charter { + /// Protobuf type URL for this message, for use with `Any::pack` and + /// `Any::unpack_if`. + /// + /// Format: `type.googleapis.com/` + pub const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.Charter"; +} +::buffa::impl_default_instance!(Charter); +impl ::buffa::MessageName for Charter { + const PACKAGE: &'static str = "trogonai.agents.agents.v1"; + const NAME: &'static str = "Charter"; + const FULL_NAME: &'static str = "trogonai.agents.agents.v1.Charter"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.Charter"; +} +impl ::buffa::Message for Charter { + /// Returns the total encoded size in bytes. + /// + /// The result is a `u32`; the protobuf specification requires all + /// messages to fit within 2 GiB (2,147,483,647 bytes), so a + /// compliant message will never overflow this type. + #[allow(clippy::let_and_return)] + fn compute_size(&self, __cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.runtime) as u32; + if self.model.is_set() { + let __slot = __cache.reserve(); + let inner_size = self.model.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + if self.tools.is_set() { + let __slot = __cache.reserve(); + let inner_size = self.tools.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + if self.delegates.is_set() { + let __slot = __cache.reserve(); + let inner_size = self.delegates.compute_size(__cache); + __cache.set(__slot, inner_size); + size + += 1u32 + ::buffa::encoding::varint_len(inner_size as u64) as u32 + + inner_size; + } + size + } + fn write_to( + &self, + __cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + ::buffa::types::put_string_field(1u32, &self.runtime, buf); + if self.model.is_set() { + ::buffa::types::put_len_delimited_header(2u32, __cache.consume_next(), buf); + self.model.write_to(__cache, buf); + } + if self.tools.is_set() { + ::buffa::types::put_len_delimited_header(3u32, __cache.consume_next(), buf); + self.tools.write_to(__cache, buf); + } + if self.delegates.is_set() { + ::buffa::types::put_len_delimited_header(4u32, __cache.consume_next(), buf); + self.delegates.write_to(__cache, buf); + } + } + fn merge_field( + &mut self, + tag: ::buffa::encoding::Tag, + buf: &mut impl ::buffa::bytes::Buf, + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + #[allow(unused_imports)] + use ::buffa::bytes::Buf as _; + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::types::merge_string(&mut self.runtime, buf)?; + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::Message::merge_length_delimited( + self.model.get_or_insert_default(), + buf, + ctx, + )?; + } + 3u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::Message::merge_length_delimited( + self.tools.get_or_insert_default(), + buf, + ctx, + )?; + } + 4u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::Message::merge_length_delimited( + self.delegates.get_or_insert_default(), + buf, + ctx, + )?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, buf, ctx.depth())?; + } + } + ::core::result::Result::Ok(()) + } + fn clear(&mut self) { + self.runtime.clear(); + self.model = ::buffa::MessageField::none(); + self.tools = ::buffa::MessageField::none(); + self.delegates = ::buffa::MessageField::none(); + } +} +impl ::buffa::json_helpers::ProtoElemJson for Charter { + fn serialize_proto_json( + v: &Self, + s: S, + ) -> ::core::result::Result { + ::serde::Serialize::serialize(v, s) + } + fn deserialize_proto_json<'de, D: ::serde::Deserializer<'de>>( + d: D, + ) -> ::core::result::Result { + ::deserialize(d) + } +} +#[doc(hidden)] +pub const __CHARTER_JSON_ANY: ::buffa::type_registry::JsonAnyEntry = ::buffa::type_registry::JsonAnyEntry { + type_url: "type.googleapis.com/trogonai.agents.agents.v1.Charter", + to_json: ::buffa::type_registry::any_to_json::, + from_json: ::buffa::type_registry::any_from_json::, + is_wkt: false, +}; +/// ModelParameter is one deterministic model-configuration entry. Entries must +/// have unique keys and be ordered by key before they cross the wire boundary. +#[derive(Clone, PartialEq, Default)] +#[derive(::serde::Serialize, ::serde::Deserialize)] +#[serde(default)] +pub struct ModelParameter { + /// Field 1: `key` + #[serde(rename = "key", with = "::buffa::json_helpers::proto_string")] + pub key: ::buffa::alloc::string::String, + /// Field 2: `value` + #[serde(rename = "value", with = "::buffa::json_helpers::proto_string")] + pub value: ::buffa::alloc::string::String, +} +impl ::core::fmt::Debug for ModelParameter { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_struct("ModelParameter") + .field("key", &self.key) + .field("value", &self.value) + .finish() + } +} +impl ModelParameter { + /// Protobuf type URL for this message, for use with `Any::pack` and + /// `Any::unpack_if`. + /// + /// Format: `type.googleapis.com/` + pub const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.ModelParameter"; +} +::buffa::impl_default_instance!(ModelParameter); +impl ::buffa::MessageName for ModelParameter { + const PACKAGE: &'static str = "trogonai.agents.agents.v1"; + const NAME: &'static str = "ModelParameter"; + const FULL_NAME: &'static str = "trogonai.agents.agents.v1.ModelParameter"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.ModelParameter"; +} +impl ::buffa::Message for ModelParameter { + /// Returns the total encoded size in bytes. + /// + /// The result is a `u32`; the protobuf specification requires all + /// messages to fit within 2 GiB (2,147,483,647 bytes), so a + /// compliant message will never overflow this type. + #[allow(clippy::let_and_return)] + fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.key) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.value) as u32; + size + } + fn write_to( + &self, + _cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + ::buffa::types::put_string_field(1u32, &self.key, buf); + ::buffa::types::put_string_field(2u32, &self.value, buf); + } + fn merge_field( + &mut self, + tag: ::buffa::encoding::Tag, + buf: &mut impl ::buffa::bytes::Buf, + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + #[allow(unused_imports)] + use ::buffa::bytes::Buf as _; + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::types::merge_string(&mut self.key, buf)?; + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::types::merge_string(&mut self.value, buf)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, buf, ctx.depth())?; + } + } + ::core::result::Result::Ok(()) + } + fn clear(&mut self) { + self.key.clear(); + self.value.clear(); + } +} +impl ::buffa::json_helpers::ProtoElemJson for ModelParameter { + fn serialize_proto_json( + v: &Self, + s: S, + ) -> ::core::result::Result { + ::serde::Serialize::serialize(v, s) + } + fn deserialize_proto_json<'de, D: ::serde::Deserializer<'de>>( + d: D, + ) -> ::core::result::Result { + ::deserialize(d) + } +} +#[doc(hidden)] +pub const __MODEL_PARAMETER_JSON_ANY: ::buffa::type_registry::JsonAnyEntry = ::buffa::type_registry::JsonAnyEntry { + type_url: "type.googleapis.com/trogonai.agents.agents.v1.ModelParameter", + to_json: ::buffa::type_registry::any_to_json::, + from_json: ::buffa::type_registry::any_from_json::, + is_wkt: false, +}; +/// Label is selectable metadata. Entries must have unique keys and be ordered +/// by key so guest Wasm receives a deterministic sequence. +#[derive(Clone, PartialEq, Default)] +#[derive(::serde::Serialize, ::serde::Deserialize)] +#[serde(default)] +pub struct Label { + /// Field 1: `key` + #[serde(rename = "key", with = "::buffa::json_helpers::proto_string")] + pub key: ::buffa::alloc::string::String, + /// Field 2: `value` + #[serde(rename = "value", with = "::buffa::json_helpers::proto_string")] + pub value: ::buffa::alloc::string::String, +} +impl ::core::fmt::Debug for Label { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_struct("Label") + .field("key", &self.key) + .field("value", &self.value) + .finish() + } +} +impl Label { + /// Protobuf type URL for this message, for use with `Any::pack` and + /// `Any::unpack_if`. + /// + /// Format: `type.googleapis.com/` + pub const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.Label"; +} +::buffa::impl_default_instance!(Label); +impl ::buffa::MessageName for Label { + const PACKAGE: &'static str = "trogonai.agents.agents.v1"; + const NAME: &'static str = "Label"; + const FULL_NAME: &'static str = "trogonai.agents.agents.v1.Label"; + const TYPE_URL: &'static str = "type.googleapis.com/trogonai.agents.agents.v1.Label"; +} +impl ::buffa::Message for Label { + /// Returns the total encoded size in bytes. + /// + /// The result is a `u32`; the protobuf specification requires all + /// messages to fit within 2 GiB (2,147,483,647 bytes), so a + /// compliant message will never overflow this type. + #[allow(clippy::let_and_return)] + fn compute_size(&self, _cache: &mut ::buffa::SizeCache) -> u32 { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + let mut size = 0u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.key) as u32; + size += 1u32 + ::buffa::types::string_encoded_len(&self.value) as u32; + size + } + fn write_to( + &self, + _cache: &mut ::buffa::SizeCache, + buf: &mut impl ::buffa::bytes::BufMut, + ) { + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + ::buffa::types::put_string_field(1u32, &self.key, buf); + ::buffa::types::put_string_field(2u32, &self.value, buf); + } + fn merge_field( + &mut self, + tag: ::buffa::encoding::Tag, + buf: &mut impl ::buffa::bytes::Buf, + ctx: ::buffa::DecodeContext<'_>, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + #[allow(unused_imports)] + use ::buffa::bytes::Buf as _; + #[allow(unused_imports)] + use ::buffa::Enumeration as _; + match tag.field_number() { + 1u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::types::merge_string(&mut self.key, buf)?; + } + 2u32 => { + ::buffa::encoding::check_wire_type( + tag, + ::buffa::encoding::WireType::LengthDelimited, + )?; + ::buffa::types::merge_string(&mut self.value, buf)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, buf, ctx.depth())?; + } + } + ::core::result::Result::Ok(()) + } + fn clear(&mut self) { + self.key.clear(); + self.value.clear(); + } +} +impl ::buffa::json_helpers::ProtoElemJson for Label { + fn serialize_proto_json( + v: &Self, + s: S, + ) -> ::core::result::Result { + ::serde::Serialize::serialize(v, s) + } + fn deserialize_proto_json<'de, D: ::serde::Deserializer<'de>>( + d: D, + ) -> ::core::result::Result { + ::deserialize(d) + } +} +#[doc(hidden)] +pub const __LABEL_JSON_ANY: ::buffa::type_registry::JsonAnyEntry = ::buffa::type_registry::JsonAnyEntry { + type_url: "type.googleapis.com/trogonai.agents.agents.v1.Label", + to_json: ::buffa::type_registry::any_to_json::