kindling_types/
capability.rs1use serde::{Deserialize, Serialize};
8
9#[cfg(feature = "ts-rs")]
10use ts_rs::TS;
11
12use crate::observation::ObservationKind;
13
14pub const OBSERVATION_REQUIRED_FIELDS: &[&str] = &["content", "provenance", "scopeIds", "ts"];
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19#[cfg_attr(feature = "ts-rs", derive(TS), ts(export, export_to = "../bindings/"))]
20#[serde(rename_all = "camelCase")]
21pub struct KindRegistryEntry {
22 pub kind: String,
24 pub required_fields: Vec<String>,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30#[cfg_attr(feature = "ts-rs", derive(TS), ts(export, export_to = "../bindings/"))]
31#[serde(rename_all = "camelCase")]
32pub struct Capability {
33 pub version: String,
34 pub schema_version: u32,
35 pub supported_kinds: Vec<String>,
36 pub storage_path: String,
37 pub kind_registry: Vec<KindRegistryEntry>,
38}
39
40impl ObservationKind {
41 pub fn wire_name(self) -> &'static str {
43 match self {
44 ObservationKind::ToolCall => "tool_call",
45 ObservationKind::Command => "command",
46 ObservationKind::FileDiff => "file_diff",
47 ObservationKind::Error => "error",
48 ObservationKind::Message => "message",
49 ObservationKind::NodeStart => "node_start",
50 ObservationKind::NodeEnd => "node_end",
51 ObservationKind::NodeOutput => "node_output",
52 ObservationKind::NodeError => "node_error",
53 }
54 }
55
56 pub fn documented_provenance_fields(self) -> &'static [&'static str] {
58 match self {
59 ObservationKind::ToolCall => &["toolName", "hasError"],
60 ObservationKind::Command => &["toolName", "hasError", "command"],
61 ObservationKind::FileDiff => &["toolName", "hasError", "filePath"],
62 ObservationKind::Error => &["source"],
63 ObservationKind::Message => &["role", "length"],
64 ObservationKind::NodeStart => &["nodeName"],
65 ObservationKind::NodeEnd => &["nodeName", "duration", "status"],
66 ObservationKind::NodeOutput => &["nodeName", "outputType", "duration"],
67 ObservationKind::NodeError => &["nodeName", "errorType", "errorMessage"],
68 }
69 }
70
71 pub fn required_fields(self) -> Vec<String> {
73 let mut fields: Vec<String> = OBSERVATION_REQUIRED_FIELDS
74 .iter()
75 .map(|s| (*s).to_string())
76 .collect();
77 fields.extend(
78 self.documented_provenance_fields()
79 .iter()
80 .map(|s| (*s).to_string()),
81 );
82 fields
83 }
84}
85
86pub fn supported_kind_names() -> Vec<String> {
88 ObservationKind::ALL
89 .iter()
90 .map(|k| k.wire_name().to_string())
91 .collect()
92}
93
94pub fn kind_registry() -> Vec<KindRegistryEntry> {
96 ObservationKind::ALL
97 .iter()
98 .map(|&kind| KindRegistryEntry {
99 kind: kind.wire_name().to_string(),
100 required_fields: kind.required_fields(),
101 })
102 .collect()
103}
104
105pub fn build_capability(
107 version: impl Into<String>,
108 schema_version: u32,
109 storage_path: impl Into<String>,
110) -> Capability {
111 Capability {
112 version: version.into(),
113 schema_version,
114 supported_kinds: supported_kind_names(),
115 storage_path: storage_path.into(),
116 kind_registry: kind_registry(),
117 }
118}