Skip to main content

aidens_runner/
receipts.rs

1//! Receipt and output DTOs for runner material operations.
2//!
3//! These are AiDENs-local execution receipts; canonical receipt truth remains
4//! with `aidens-receipts` and sibling verification crates.
5
6use super::*;
7
8#[derive(Debug, Clone)]
9pub struct PlanReceiptV1 {
10    pub receipt_id: ArtifactId,
11    pub plan_id: ArtifactId,
12    pub step: u32,
13    pub action: String,
14    pub reason_codes: Vec<String>,
15}
16
17#[derive(Debug, Clone)]
18pub struct ToolRouteReceiptV1 {
19    pub receipt_id: ArtifactId,
20    pub plan_id: ArtifactId,
21    pub step: u32,
22    pub requested_tool_ids: Vec<String>,
23    pub exposed_tool_ids: Vec<String>,
24    pub reason_codes: Vec<String>,
25}
26
27#[derive(Debug, Clone)]
28pub struct ToolCallReceiptV1 {
29    pub receipt_id: ArtifactId,
30    pub plan_id: ArtifactId,
31    pub step: u32,
32    pub run_id: String,
33    pub permitted_tool_calls: usize,
34    pub blocked_tool_calls: usize,
35    pub succeeded_tool_calls: usize,
36    pub failed_tool_calls: usize,
37    pub reason_codes: Vec<String>,
38}
39
40#[derive(Debug, Clone)]
41pub struct VerificationReceiptV1 {
42    pub receipt_id: ArtifactId,
43    pub step: u32,
44    pub check: String,
45    pub passed: bool,
46    pub reason_codes: Vec<String>,
47}
48
49#[derive(Debug, Clone)]
50pub struct AbstentionReceiptV1 {
51    pub receipt_id: ArtifactId,
52    pub step: u32,
53    pub reason_code: String,
54    pub blocked_action: String,
55    pub evidence: Vec<String>,
56    pub required_permits: Vec<String>,
57    pub can_resume: bool,
58    pub support_impact: String,
59}
60
61#[derive(Debug, Clone)]
62pub struct RepairPlanDisplayReceiptV1 {
63    pub repair_id: ArtifactId,
64    pub source_run_id: Option<String>,
65    pub failure_kind: String,
66    pub candidate_repair_actions: Vec<String>,
67    pub required_verification: Vec<String>,
68    pub required_permits: Vec<String>,
69    pub risk_level: String,
70    pub canonical_owner: Option<String>,
71}
72
73#[derive(Debug, Clone)]
74pub struct FinalizationReceiptV1 {
75    pub receipt_id: ArtifactId,
76    pub step: u32,
77    pub outcome: String,
78    pub final_state: String,
79    pub blocked: bool,
80    pub support_label: String,
81    pub reason_codes: Vec<String>,
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub enum PlanActVerifyOutcomeV1 {
86    Success,
87    Abstained,
88    RepairNeeded,
89    Failed,
90}
91
92#[derive(Debug, Clone)]
93pub struct PlanActVerifyLoopV1Output {
94    pub agent_id: String,
95    pub app_id: String,
96    pub profile: String,
97    pub support_label: String,
98    pub outcome: PlanActVerifyOutcomeV1,
99    pub turns_used: u32,
100    pub max_turns: u32,
101    pub plan_receipts: Vec<PlanReceiptV1>,
102    pub tool_route_receipts: Vec<ToolRouteReceiptV1>,
103    pub tool_call_receipts: Vec<ToolCallReceiptV1>,
104    pub verification_receipts: Vec<VerificationReceiptV1>,
105    pub memory_grounding_receipts: Vec<String>,
106    pub abstention_receipt: Option<AbstentionReceiptV1>,
107    pub repair_plan: Option<RepairPlanDisplayReceiptV1>,
108    pub finalization: Option<FinalizationReceiptV1>,
109    pub run_output: Option<AiDENsRunOutput>,
110}
111
112impl PlanActVerifyLoopV1Output {
113    pub(crate) fn abstention(
114        agent: &AgentSpecV1,
115        step: u32,
116        reason_code: impl Into<String>,
117    ) -> Self {
118        Self {
119            agent_id: agent.agent_id.clone(),
120            app_id: "aidens-plan-act-verify-loop".into(),
121            profile: agent.profile.clone(),
122            support_label: agent.support_label.to_string(),
123            outcome: PlanActVerifyOutcomeV1::Abstained,
124            turns_used: 0,
125            max_turns: agent.budget_policy.max_turns,
126            plan_receipts: Vec::new(),
127            tool_route_receipts: Vec::new(),
128            tool_call_receipts: Vec::new(),
129            verification_receipts: Vec::new(),
130            memory_grounding_receipts: Vec::new(),
131            abstention_receipt: Some(AbstentionReceiptV1 {
132                receipt_id: display_only_unstable_id("agent-abstention"),
133                step,
134                reason_code: reason_code.into(),
135                blocked_action: "execution-blocked-before-run".into(),
136                evidence: Vec::new(),
137                required_permits: Vec::new(),
138                can_resume: true,
139                support_impact: "partial".into(),
140            }),
141            repair_plan: None,
142            finalization: None,
143            run_output: None,
144        }
145    }
146
147    #[allow(clippy::too_many_arguments)]
148    pub fn assemble_v3_bundle(
149        &self,
150        run_id: impl Into<String>,
151        profile: impl Into<String>,
152        canonical_execution_context: aidens_contracts::canonical_stack::ForgeExecutionContextV1,
153        event_log: AiDENsRunEventLogDigestV1,
154        budget: AiDENsRunBudgetDeadlineV1,
155        support: AiDENsRunSupportTierEvidenceV1,
156        support_labels: Vec<String>,
157        replay: AiDENsRunReplayNormalizationV1,
158        failure: AiDENsRunFailureTaxonomyV1,
159        attempt_family_id: ArtifactId,
160        attempt_id: StackAttemptId,
161        trial_id: StackTrialId,
162        agent_spec_digest: DisplayDigestV1,
163        memory_grounding_receipts: Vec<String>,
164        outputs: Vec<String>,
165        replay_instructions: Vec<String>,
166        blocked_checks: Vec<String>,
167    ) -> AiDENsRunBundleV3 {
168        let mut bundle = AiDENsRunBundleV3::new(
169            run_id,
170            profile,
171            canonical_execution_context,
172            event_log,
173            budget,
174            support,
175            support_labels,
176            replay,
177            failure,
178            attempt_family_id,
179            attempt_id,
180            trial_id,
181            agent_spec_digest,
182        );
183        bundle.provider_receipts = self
184            .tool_route_receipts
185            .iter()
186            .map(|receipt| receipt.receipt_id.to_string())
187            .collect();
188        bundle.tool_receipts = self
189            .tool_call_receipts
190            .iter()
191            .map(|receipt| receipt.receipt_id.to_string())
192            .collect();
193        bundle.permit_receipts = self
194            .tool_call_receipts
195            .iter()
196            .map(|receipt| format!("permit-use:{}", receipt.receipt_id))
197            .collect();
198        bundle.memory_grounding_receipts = memory_grounding_receipts;
199        bundle.verification_receipts = self
200            .verification_receipts
201            .iter()
202            .map(|receipt| receipt.receipt_id.to_string())
203            .collect();
204        bundle.abstention_receipts = self
205            .abstention_receipt
206            .as_ref()
207            .map(|receipt| vec![receipt.receipt_id.to_string()])
208            .unwrap_or_default();
209        bundle.repair_plan_receipts = self
210            .repair_plan
211            .as_ref()
212            .map(|receipt| vec![receipt.repair_id.to_string()])
213            .unwrap_or_default();
214        bundle.outputs = outputs;
215        bundle.replay_instructions = replay_instructions;
216        bundle.blocked_checks = blocked_checks;
217        bundle
218    }
219}