1use std::collections::BTreeSet;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[non_exhaustive]
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
8pub enum Capability {
9 InvokeTool,
10 InvokeConnector,
11 MemoryRead,
12 MemoryWrite,
13 FilesystemRead,
14 FilesystemWrite,
15 NetworkEgress,
16 ObserveTelemetry,
17 ControlRead,
18 ControlWrite,
19 ControlApprovals,
20 ControlPairing,
21 ControlAcp,
22}
23
24impl Capability {
25 pub const fn as_str(self) -> &'static str {
26 match self {
27 Self::InvokeTool => "invoke_tool",
28 Self::InvokeConnector => "invoke_connector",
29 Self::MemoryRead => "memory_read",
30 Self::MemoryWrite => "memory_write",
31 Self::FilesystemRead => "filesystem_read",
32 Self::FilesystemWrite => "filesystem_write",
33 Self::NetworkEgress => "network_egress",
34 Self::ObserveTelemetry => "observe_telemetry",
35 Self::ControlRead => "control_read",
36 Self::ControlWrite => "control_write",
37 Self::ControlApprovals => "control_approvals",
38 Self::ControlPairing => "control_pairing",
39 Self::ControlAcp => "control_acp",
40 }
41 }
42
43 pub fn parse(raw: &str) -> Option<Self> {
44 match raw.trim().to_ascii_lowercase().replace('-', "_").as_str() {
45 "invoke_tool" => Some(Self::InvokeTool),
46 "invoke_connector" => Some(Self::InvokeConnector),
47 "memory_read" => Some(Self::MemoryRead),
48 "memory_write" => Some(Self::MemoryWrite),
49 "filesystem_read" => Some(Self::FilesystemRead),
50 "filesystem_write" => Some(Self::FilesystemWrite),
51 "network_egress" => Some(Self::NetworkEgress),
52 "observe_telemetry" => Some(Self::ObserveTelemetry),
53 "control_read" => Some(Self::ControlRead),
54 "control_write" => Some(Self::ControlWrite),
55 "control_approvals" => Some(Self::ControlApprovals),
56 "control_pairing" => Some(Self::ControlPairing),
57 "control_acp" => Some(Self::ControlAcp),
58 _ => None,
59 }
60 }
61}
62
63#[non_exhaustive]
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
65pub enum HarnessKind {
66 EmbeddedPi,
67 Acp,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71pub struct ExecutionRoute {
72 pub harness_kind: HarnessKind,
73 pub adapter: Option<String>,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
77pub struct CapabilityToken {
78 pub token_id: String,
79 pub pack_id: String,
80 pub agent_id: String,
81 pub allowed_capabilities: BTreeSet<Capability>,
82 pub issued_at_epoch_s: u64,
83 pub expires_at_epoch_s: u64,
84 pub generation: u64,
85}
86
87#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
88pub struct TaskIntent {
89 pub task_id: String,
90 pub objective: String,
91 pub required_capabilities: BTreeSet<Capability>,
92 pub payload: Value,
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub struct HarnessRequest {
97 pub token_id: String,
98 pub pack_id: String,
99 pub agent_id: String,
100 pub task_id: String,
101 pub objective: String,
102 pub payload: Value,
103}
104
105#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
106pub struct HarnessOutcome {
107 pub status: String,
108 pub output: Value,
109}
110
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112pub struct ConnectorCommand {
113 pub connector_name: String,
114 pub operation: String,
115 pub required_capabilities: BTreeSet<Capability>,
116 pub payload: Value,
117}
118
119#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
120pub struct ConnectorOutcome {
121 pub status: String,
122 pub payload: Value,
123}
124
125#[cfg(test)]
126mod tests {
127 use super::Capability;
128
129 #[test]
130 fn capability_round_trips_through_canonical_names() {
131 let fixtures = [
132 (Capability::InvokeTool, "invoke_tool"),
133 (Capability::InvokeConnector, "invoke_connector"),
134 (Capability::MemoryRead, "memory_read"),
135 (Capability::MemoryWrite, "memory_write"),
136 (Capability::FilesystemRead, "filesystem_read"),
137 (Capability::FilesystemWrite, "filesystem_write"),
138 (Capability::NetworkEgress, "network_egress"),
139 (Capability::ObserveTelemetry, "observe_telemetry"),
140 (Capability::ControlRead, "control_read"),
141 (Capability::ControlWrite, "control_write"),
142 (Capability::ControlApprovals, "control_approvals"),
143 (Capability::ControlPairing, "control_pairing"),
144 (Capability::ControlAcp, "control_acp"),
145 ];
146
147 for (capability, expected_name) in fixtures {
148 assert_eq!(capability.as_str(), expected_name);
149 assert_eq!(Capability::parse(expected_name), Some(capability));
150 assert_eq!(
151 Capability::parse(&expected_name.to_ascii_uppercase()),
152 Some(capability)
153 );
154 assert_eq!(
155 Capability::parse(&expected_name.replace('_', "-")),
156 Some(capability)
157 );
158 }
159 }
160
161 #[test]
162 fn capability_parse_rejects_unknown_values() {
163 assert_eq!(Capability::parse("totally_unknown"), None);
164 assert_eq!(Capability::parse(""), None);
165 assert_eq!(Capability::parse(" "), None);
166 assert_eq!(Capability::parse("schedule_task"), None);
167 }
168}