1use serde::{Deserialize, Serialize};
8
9use crate::JsonObject;
10use crate::output::OutputField;
11use crate::schema::{NonEmptyString, RunxSchema};
12use std::collections::BTreeMap;
13
14#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
16pub enum ContextEntryVersion {
17 #[serde(rename = "1")]
18 V1,
19}
20
21#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
23#[serde(deny_unknown_fields)]
24pub struct ContextArtifactProducer {
25 pub skill: NonEmptyString,
26 pub runner: NonEmptyString,
27}
28
29#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
32#[serde(deny_unknown_fields)]
33pub struct ContextArtifactMeta {
34 pub artifact_id: NonEmptyString,
35 pub run_id: NonEmptyString,
36 pub step_id: Option<NonEmptyString>,
37 pub producer: ContextArtifactProducer,
38 pub created_at: NonEmptyString,
39 pub hash: NonEmptyString,
40 pub size_bytes: u64,
41 pub parent_artifact_id: Option<NonEmptyString>,
42 pub receipt_id: Option<NonEmptyString>,
43 pub redacted: bool,
44}
45
46#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, RunxSchema)]
48#[serde(deny_unknown_fields)]
49pub struct ContextEntry {
50 #[serde(rename = "type")]
51 pub entry_type: Option<NonEmptyString>,
52 pub version: ContextEntryVersion,
53 pub data: JsonObject,
54 pub meta: ContextArtifactMeta,
55}
56
57#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
59#[serde(deny_unknown_fields)]
60pub struct ProvenanceEntry {
61 pub input: NonEmptyString,
62 pub output: NonEmptyString,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub from_step: Option<String>,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub artifact_id: Option<String>,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub receipt_id: Option<String>,
69}
70
71#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
74#[serde(deny_unknown_fields)]
75pub struct ProfileFile {
76 pub root_path: NonEmptyString,
77 pub path: NonEmptyString,
78 pub sha256: NonEmptyString,
79 pub content: String,
80}
81
82#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
84#[serde(deny_unknown_fields)]
85pub struct AgentContextProfiles {
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub memory: Option<ProfileFile>,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub conventions: Option<ProfileFile>,
90}
91
92#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
95pub enum QualityProfileSource {
96 #[serde(rename = "SKILL.md#quality-profile")]
97 SkillMd,
98}
99
100#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
101#[serde(deny_unknown_fields)]
102pub struct QualityProfile {
103 pub source: QualityProfileSource,
104 pub sha256: NonEmptyString,
105 pub content: String,
106}
107
108#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
110#[serde(deny_unknown_fields)]
111pub struct ExecutionLocation {
112 pub skill_directory: NonEmptyString,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub tool_roots: Option<Vec<NonEmptyString>>,
115}
116
117#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, RunxSchema)]
118#[serde(deny_unknown_fields)]
119#[runx_schema(spec_id = "https://runx.ai/spec/agent-context-envelope.schema.json")]
120pub struct AgentContextEnvelope {
121 pub run_id: NonEmptyString,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub step_id: Option<NonEmptyString>,
124 pub skill: NonEmptyString,
125 pub instructions: NonEmptyString,
126 pub inputs: JsonObject,
127 pub allowed_tools: Vec<NonEmptyString>,
128 pub current_context: Vec<ContextEntry>,
129 pub historical_context: Vec<ContextEntry>,
130 pub provenance: Vec<ProvenanceEntry>,
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub context: Option<AgentContextProfiles>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub voice_profile: Option<ProfileFile>,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub quality_profile: Option<QualityProfile>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub execution_location: Option<ExecutionLocation>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub output: Option<BTreeMap<String, OutputField>>,
141 pub trust_boundary: NonEmptyString,
142}