artifactize_core/monitor/
evidence.rs1use crate::{
3 agent::session,
4 config::ProfileKind,
5 store::{self, RequestView},
6 types::RequestStatus,
7};
8
9use std::path::Path;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct EvidenceStamp {
15 status: RequestStatus,
16 completed_at: Option<crate::types::Timestamp>,
17 session: Option<session::SessionRef>,
18 session_id: Option<crate::types::SessionId>,
19 producer: Option<store::Producer>,
20 original: Option<crate::types::RequestId>,
21}
22impl EvidenceStamp {
23 pub fn new(view: &RequestView) -> Self {
24 Self {
25 status: view.request.status(),
26 completed_at: view.request.completed_at(),
27 session: view.request.session.clone(),
28 session_id: view.request.session_id.clone(),
29 producer: view.request.producer.clone(),
30 original: view
31 .execution
32 .as_ref()
33 .map(|execution| execution.provenance.request_id.clone())
34 .or_else(|| {
35 view.request
36 .provenance
37 .as_ref()
38 .map(|source| source.request_id.clone())
39 }),
40 }
41 }
42}
43
44#[derive(Debug, Clone, Default)]
45pub struct Evidence {
46 pub title: String,
47 pub text: String,
48}
49impl Evidence {
50 pub fn is_empty(&self) -> bool {
52 self.title.is_empty() && self.text.is_empty()
53 }
54}
55
56pub async fn original(state: &Path, view: &RequestView) -> Result<RequestView, String> {
57 store::read_original(state, view).await
58}
59
60pub fn evidence(_state: &Path, view: &RequestView) -> Evidence {
61 match view.request.profile.kind() {
62 ProfileKind::Agent => Evidence {
63 title: "Agent session".into(),
64 text: "Loading session…".into(),
65 },
66 ProfileKind::Runtime => {
67 let logs = view
68 .request
69 .result()
70 .cloned()
71 .and_then(|result| match result {
72 store::ExecutionResult::Runtime(logs)
73 if logs.stdout.value().is_some() && logs.stderr.value().is_some() =>
74 {
75 Some(logs)
76 }
77 _ => None,
78 });
79 let text = match logs {
80 Some(logs) => format!(
81 "exit code: {}\ncapture truncated: {}\n\nstdout\n{}\n\nstderr\n{}",
82 logs.exit_code.value()
83 .map_or("unreported".into(), |code| code.to_string()),
84 logs.truncated.value().copied().unwrap_or(false),
85 logs.stdout.value().expect("filtered logs"),
86 logs.stderr.value().expect("filtered logs")
87 ),
88 None if view.request.origin.is_some() => {
89 "Logs unavailable: this remote result contains only a summary; stdout/stderr were not saved here."
90 .into()
91 }
92 None if view.request.status() == RequestStatus::Running => {
93 "Logs unavailable while running: only completed runtime output is saved; this view does not stream live pipes."
94 .into()
95 }
96 None => {
97 "Logs unavailable: stdout/stderr were not saved for this request (for example timeout, cancellation, operational failure, or a summary-only reused result)."
98 .into()
99 }
100 };
101 Evidence {
102 title: "Saved runtime stdout / stderr".into(),
103 text,
104 }
105 }
106 ProfileKind::Human => Evidence::default(),
107 }
108}