#[derive(Debug, Clone)]
pub(crate) struct FileEvent {
pub(crate) line_no: usize,
pub(crate) turn_index: usize,
pub(crate) timestamp_utc: Option<String>,
pub(crate) kind: EventKind,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct EventStamp {
pub(crate) turn: crate::model::TurnStamp,
pub(crate) survival: &'static str,
}
impl Default for EventStamp {
fn default() -> Self {
EventStamp {
turn: crate::model::TurnStamp::Live(0),
survival: "live",
}
}
}
#[derive(Debug, Clone)]
pub(crate) enum EventKind {
FullSnapshot {
content: String,
total_lines: usize,
source: SnapSource,
},
PartialRead {
start_line: usize,
lines: Vec<String>,
total_lines: usize,
},
BlankedRead { total_lines: Option<usize> },
Edit {
hunks: Vec<EditHunk>,
original_file: Option<String>,
structured_patch: Option<Vec<PatchHunk>>,
},
IntegrityError { kind: IntegrityKind, raw: String },
BashTouch {
verb: String,
path: String,
resolution: &'static str,
},
ExternalEdit { snippet: Vec<(usize, String)> },
StaleReadHint { path: String },
StaleRecovered,
BashWindowRead {
start_line: usize,
lines: Vec<String>,
},
BashAppend { content: String },
HistorySnapshotMarker {
version: Option<u64>,
backup_file: Option<String>,
backup_time: Option<String>,
content: Option<String>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum SnapSource {
Write,
FullRead,
FileAttachment,
BashCat,
BashHeredoc,
BashWrite,
HistorySnapshot,
}
impl SnapSource {
pub(crate) fn label(self) -> &'static str {
match self {
SnapSource::Write => "write",
SnapSource::FullRead => "full-read",
SnapSource::FileAttachment => "file-attachment",
SnapSource::BashCat => "bash-cat",
SnapSource::BashHeredoc => "bash-heredoc",
SnapSource::BashWrite => "bash-write",
SnapSource::HistorySnapshot => "file-history-snapshot",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum IntegrityKind {
ModifiedSinceRead,
NotReadYet,
StringNotFound,
FileDoesNotExist,
}
#[derive(Debug, Clone)]
pub(crate) struct EditHunk {
pub(crate) old_string: String,
pub(crate) new_string: String,
pub(crate) replace_all: bool,
}
#[derive(Debug, Clone)]
pub(crate) struct PatchHunk {
pub(crate) old_start: usize,
pub(crate) old_lines: usize,
pub(crate) new_lines: usize,
pub(crate) lines: Vec<String>,
}
#[derive(Debug, Clone)]
pub(crate) struct OpaqueCommand {
pub(crate) session_id: String,
pub(crate) line_no: usize,
pub(crate) turn_index: usize,
pub(crate) timestamp_utc: Option<String>,
pub(crate) marker: String,
}
#[derive(Debug)]
pub(crate) struct ScanResult {
pub(crate) session_id: String,
pub(crate) is_subagent: bool,
pub(crate) parent_session_id: String,
pub(crate) events: Vec<FileEvent>,
pub(crate) stamps: std::collections::BTreeMap<usize, EventStamp>,
pub(crate) opaque: Vec<OpaqueCommand>,
pub(crate) merged_line_origin: std::collections::BTreeMap<usize, (String, usize)>,
pub(crate) skipped_lines: usize,
}
impl ScanResult {
pub(crate) fn stamp_at(&self, line_no: usize) -> EventStamp {
self.stamps.get(&line_no).copied().unwrap_or_default()
}
pub(crate) fn abandoned_events(&self) -> usize {
self.events
.iter()
.filter(|e| self.stamp_at(e.line_no).turn.is_abandoned())
.count()
}
}