MCPcopy Create free account
hub / github.com/ScriptedAlchemy/tracedecay / parse_cursor_jsonl

Function parse_cursor_jsonl

src/sessions/cursor.rs:285–373  ·  view source on GitHub ↗

Parse the newly-appended portion of one Cursor transcript file into a provider-neutral [`ParsedTranscript`]. Shared by the hook path ([`CursorEventSource`]) and the startup catch-up sweep ([`CursorSweepSource`]); both derive identical session/message ids for the same file (the hook event's `session_id` always equals the transcript file stem), so whichever runs second is an idempotent no-op.

(
    event: &Value,
    parent_session_id: &str,
    path: &Path,
    prev: StoredCursor,
    max_new_bytes: Option<u64>,
)

Source from the content-addressed store, hash-verified

283/// same file (the hook event's `session_id` always equals the transcript file
284/// stem), so whichever runs second is an idempotent no-op.
285fn parse_cursor_jsonl(
286 event: &Value,
287 parent_session_id: &str,
288 path: &Path,
289 prev: StoredCursor,
290 max_new_bytes: Option<u64>,
291) -> Option<ParsedTranscript> {
292 let new = stream_new_jsonl(path, prev, max_new_bytes)?;
293 let subagent = cursor_subagent_identity(path, parent_session_id);
294 let session_id = subagent.as_ref().map_or_else(
295 || parent_session_id.to_string(),
296 |(session_id, _agent_id)| session_id.clone(),
297 );
298 let subagent_model = subagent.as_ref().and_then(|(_, agent_id)| {
299 parent_dispatch_model_for_subagent(path, parent_session_id, agent_id)
300 });
301 let event_cwd = event_cwd(event);
302 let event_location_provenance = event_location_provenance(event);
303 let mut carry = TimestampCarry::new(i64::try_from(new.new_cursor.mtime).ok());
304 let mut messages = Vec::new();
305 for line in &new.lines {
306 let derived_timestamp = carry.observe(&line.value);
307 let context = CursorMessageContext {
308 transcript_path: path,
309 source_offset: line.offset,
310 derived_timestamp,
311 model_fallback: subagent_model.as_deref(),
312 event_cwd: event_cwd.as_deref(),
313 event_location_provenance,
314 };
315 // The byte offset doubles as the message ordinal and source_offset,
316 // matching the original Cursor ingestion.
317 if let Some(message) = event_message(&line.value, event, &session_id, line.offset, context)
318 {
319 messages.push(message);
320 }
321 messages.extend(event_dispatch_messages(
322 &line.value,
323 event,
324 &session_id,
325 context,
326 ));
327 }
328
329 // Defer the (filesystem-walking) project/title/metadata derivation until
330 // we actually have new messages; the driver ignores the draft otherwise.
331 let draft = if messages.is_empty() {
332 SessionDraft {
333 session_id,
334 project_key: String::new(),
335 project_path: String::new(),
336 title: None,
337 metadata_json: None,
338 parent_session_id: None,
339 is_subagent: false,
340 agent_id: None,
341 parent_tool_use_id: None,
342 }

Callers 1

parse_newMethod · 0.85

Calls 13

stream_new_jsonlFunction · 0.85
cursor_subagent_identityFunction · 0.85
event_messageFunction · 0.85
event_dispatch_messagesFunction · 0.85
event_projectFunction · 0.85
title_from_messagesFunction · 0.85
pushMethod · 0.80
event_cwdFunction · 0.70
session_metadataFunction · 0.70
observeMethod · 0.45

Tested by

no test coverage detected