@@ -177,119 +177,121 @@ func LoadTranscript(path string) (Transcript, error) {
177177
178178 scanner := newTranscriptScanner (f )
179179 for scanner .Scan () {
180- line := bytes .TrimSpace (scanner .Bytes ())
181- if len (line ) == 0 {
182- continue
183- }
184- var envelope transcriptEnvelope
185- if err := json .Unmarshal (line , & envelope ); err != nil {
186- continue
187- }
188- if envelope .Type == "progress" && envelope .UUID != "" {
189- progressBridge [envelope .UUID ] = resolveProgressParent (progressBridge , envelope .ParentUUID )
180+ physicalLine := bytes .TrimSpace (scanner .Bytes ())
181+ if len (physicalLine ) == 0 {
190182 continue
191183 }
192- metadataType := normalizeTranscriptMetadataType (envelope .Type )
193- switch {
194- case isTranscriptType (envelope .Type ):
195- var msg TranscriptMessage
196- if err := json .Unmarshal (line , & msg ); err != nil || msg .UUID == "" {
184+ for _ , line := range transcriptRecordLines (physicalLine ) {
185+ var envelope transcriptEnvelope
186+ if err := json .Unmarshal (line , & envelope ); err != nil {
197187 continue
198188 }
199- if msg .ParentUUID != nil {
200- if bridged , ok := progressBridge [* msg .ParentUUID ]; ok {
201- msg .ParentUUID = cloneIDPtr (bridged )
202- }
203- }
204- msg .Raw = append (json .RawMessage (nil ), line ... )
205- transcript .addMessage (& msg )
206- if msg .IsCompactBoundary () {
207- transcript .ContextCollapseCommits = nil
208- transcript .ContextCollapseSnapshot = nil
209- }
210- case metadataType == "summary" :
211- if leafUUID , summary , ok := parseSummaryMetadata (line ); ok && leafUUID != "" {
212- transcript .Summaries [leafUUID ] = summary
213- }
214- case metadataType == "custom-title" :
215- if sessionID , title , ok := parseSessionStringMetadata (line , "customTitle" , "custom_title" , "title" , "name" ); ok && sessionID != "" {
216- transcript .CustomTitles [sessionID ] = title
217- }
218- case metadataType == "ai-title" :
219- if sessionID , title , ok := parseSessionStringMetadata (line , "aiTitle" , "ai_title" , "title" , "name" ); ok && sessionID != "" {
220- transcript .AITitles [sessionID ] = title
221- }
222- case metadataType == "last-prompt" :
223- if sessionID , prompt , ok := parseSessionStringMetadata (line , "lastPrompt" , "last_prompt" , "prompt" , "input" , "text" , "content" , "message" ); ok && sessionID != "" {
224- transcript .LastPrompts [sessionID ] = prompt
225- }
226- case metadataType == "task-summary" :
227- if entry , ok := parseTaskSummaryMetadata (line ); ok && entry .SessionID != "" {
228- transcript .TaskSummaries [entry .SessionID ] = entry
229- }
230- case metadataType == "tag" :
231- if sessionID , tag , ok := parseSessionStringMetadata (line , "tag" , "value" , "name" , "label" ); ok && sessionID != "" {
232- transcript .Tags [sessionID ] = tag
233- }
234- case metadataType == "agent-name" :
235- if sessionID , name , ok := parseSessionStringMetadata (line , "agentName" , "agent_name" , "name" , "agent" , "title" ); ok && sessionID != "" {
236- transcript .AgentNames [sessionID ] = name
237- }
238- case metadataType == "agent-color" :
239- if sessionID , color , ok := parseSessionStringMetadata (line , "agentColor" , "agent_color" , "color" , "colour" , "value" ); ok && sessionID != "" {
240- transcript .AgentColors [sessionID ] = color
241- }
242- case metadataType == "agent-setting" :
243- if sessionID , setting , ok := parseSessionStringMetadata (line , "agentSetting" , "agent_setting" , "setting" , "value" , "mode" ); ok && sessionID != "" {
244- transcript .AgentSettings [sessionID ] = setting
245- }
246- case metadataType == "pr-link" :
247- if entry , ok := parsePRLinkMetadata (line ); ok && entry .SessionID != "" {
248- transcript .PRLinks [entry .SessionID ] = entry
249- }
250- case metadataType == "mode" :
251- if sessionID , mode , ok := parseSessionStringMetadata (line , "mode" , "value" , "name" , "status" ); ok && sessionID != "" {
252- transcript .Modes [sessionID ] = mode
253- }
254- case metadataType == "worktree-state" :
255- if entry , ok := parseWorktreeStateMetadata (line ); ok && entry .SessionID != "" {
256- transcript .WorktreeStates [entry .SessionID ] = entry
189+ if envelope .Type == "progress" && envelope .UUID != "" {
190+ progressBridge [envelope .UUID ] = resolveProgressParent (progressBridge , envelope .ParentUUID )
191+ continue
257192 }
258- case metadataType == "content-replacement" :
259- if entry , ok := parseContentReplacementMetadata (line ); ok && entry .SessionID != "" {
260- key := entry .SessionID
261- if entry .AgentID != "" {
262- key = contracts .ID (entry .AgentID )
193+ metadataType := normalizeTranscriptMetadataType (envelope .Type )
194+ switch {
195+ case isTranscriptType (envelope .Type ):
196+ var msg TranscriptMessage
197+ if err := json .Unmarshal (line , & msg ); err != nil || msg .UUID == "" {
198+ continue
199+ }
200+ if msg .ParentUUID != nil {
201+ if bridged , ok := progressBridge [* msg .ParentUUID ]; ok {
202+ msg .ParentUUID = cloneIDPtr (bridged )
203+ }
204+ }
205+ msg .Raw = append (json .RawMessage (nil ), line ... )
206+ transcript .addMessage (& msg )
207+ if msg .IsCompactBoundary () {
208+ transcript .ContextCollapseCommits = nil
209+ transcript .ContextCollapseSnapshot = nil
210+ }
211+ case metadataType == "summary" :
212+ if leafUUID , summary , ok := parseSummaryMetadata (line ); ok && leafUUID != "" {
213+ transcript .Summaries [leafUUID ] = summary
214+ }
215+ case metadataType == "custom-title" :
216+ if sessionID , title , ok := parseSessionStringMetadata (line , "customTitle" , "custom_title" , "title" , "name" ); ok && sessionID != "" {
217+ transcript .CustomTitles [sessionID ] = title
218+ }
219+ case metadataType == "ai-title" :
220+ if sessionID , title , ok := parseSessionStringMetadata (line , "aiTitle" , "ai_title" , "title" , "name" ); ok && sessionID != "" {
221+ transcript .AITitles [sessionID ] = title
222+ }
223+ case metadataType == "last-prompt" :
224+ if sessionID , prompt , ok := parseSessionStringMetadata (line , "lastPrompt" , "last_prompt" , "prompt" , "input" , "text" , "content" , "message" ); ok && sessionID != "" {
225+ transcript .LastPrompts [sessionID ] = prompt
226+ }
227+ case metadataType == "task-summary" :
228+ if entry , ok := parseTaskSummaryMetadata (line ); ok && entry .SessionID != "" {
229+ transcript .TaskSummaries [entry .SessionID ] = entry
230+ }
231+ case metadataType == "tag" :
232+ if sessionID , tag , ok := parseSessionStringMetadata (line , "tag" , "value" , "name" , "label" ); ok && sessionID != "" {
233+ transcript .Tags [sessionID ] = tag
234+ }
235+ case metadataType == "agent-name" :
236+ if sessionID , name , ok := parseSessionStringMetadata (line , "agentName" , "agent_name" , "name" , "agent" , "title" ); ok && sessionID != "" {
237+ transcript .AgentNames [sessionID ] = name
238+ }
239+ case metadataType == "agent-color" :
240+ if sessionID , color , ok := parseSessionStringMetadata (line , "agentColor" , "agent_color" , "color" , "colour" , "value" ); ok && sessionID != "" {
241+ transcript .AgentColors [sessionID ] = color
242+ }
243+ case metadataType == "agent-setting" :
244+ if sessionID , setting , ok := parseSessionStringMetadata (line , "agentSetting" , "agent_setting" , "setting" , "value" , "mode" ); ok && sessionID != "" {
245+ transcript .AgentSettings [sessionID ] = setting
246+ }
247+ case metadataType == "pr-link" :
248+ if entry , ok := parsePRLinkMetadata (line ); ok && entry .SessionID != "" {
249+ transcript .PRLinks [entry .SessionID ] = entry
250+ }
251+ case metadataType == "mode" :
252+ if sessionID , mode , ok := parseSessionStringMetadata (line , "mode" , "value" , "name" , "status" ); ok && sessionID != "" {
253+ transcript .Modes [sessionID ] = mode
254+ }
255+ case metadataType == "worktree-state" :
256+ if entry , ok := parseWorktreeStateMetadata (line ); ok && entry .SessionID != "" {
257+ transcript .WorktreeStates [entry .SessionID ] = entry
258+ }
259+ case metadataType == "content-replacement" :
260+ if entry , ok := parseContentReplacementMetadata (line ); ok && entry .SessionID != "" {
261+ key := entry .SessionID
262+ if entry .AgentID != "" {
263+ key = contracts .ID (entry .AgentID )
264+ }
265+ transcript .ContentReplacements [key ] = append (transcript .ContentReplacements [key ], entry .Replacements ... )
266+ }
267+ case metadataType == "tombstone" :
268+ if entry , ok := parseTombstoneMetadata (line ); ok && entry .TargetUUID != "" {
269+ transcript .Tombstones [entry .TargetUUID ] = entry
270+ }
271+ case metadataType == "file-history-snapshot" :
272+ snapshot := append (json .RawMessage (nil ), line ... )
273+ transcript .FileHistorySnapshots = append (transcript .FileHistorySnapshots , snapshot )
274+ if messageID := parseSnapshotMessageID (line ); messageID != "" {
275+ transcript .FileHistoryByMessageID [messageID ] = snapshot
276+ }
277+ case metadataType == "attribution-snapshot" :
278+ snapshot := append (json .RawMessage (nil ), line ... )
279+ transcript .AttributionSnapshots = append (transcript .AttributionSnapshots , snapshot )
280+ if messageID := parseSnapshotMessageID (line ); messageID != "" {
281+ transcript .AttributionByMessageID [messageID ] = snapshot
282+ }
283+ case metadataType == "speculation-accept" :
284+ if entry , ok := parseSpeculationAcceptMetadata (line ); ok {
285+ transcript .SpeculationAccepts = append (transcript .SpeculationAccepts , entry )
286+ }
287+ case metadataType == "marble-origami-commit" :
288+ if entry , ok := parseContextCollapseCommitMetadata (line ); ok {
289+ transcript .ContextCollapseCommits = append (transcript .ContextCollapseCommits , entry )
290+ }
291+ case metadataType == "marble-origami-snapshot" :
292+ if entry , ok := parseContextCollapseSnapshotMetadata (line ); ok {
293+ transcript .ContextCollapseSnapshot = & entry
263294 }
264- transcript .ContentReplacements [key ] = append (transcript .ContentReplacements [key ], entry .Replacements ... )
265- }
266- case metadataType == "tombstone" :
267- if entry , ok := parseTombstoneMetadata (line ); ok && entry .TargetUUID != "" {
268- transcript .Tombstones [entry .TargetUUID ] = entry
269- }
270- case metadataType == "file-history-snapshot" :
271- snapshot := append (json .RawMessage (nil ), line ... )
272- transcript .FileHistorySnapshots = append (transcript .FileHistorySnapshots , snapshot )
273- if messageID := parseSnapshotMessageID (line ); messageID != "" {
274- transcript .FileHistoryByMessageID [messageID ] = snapshot
275- }
276- case metadataType == "attribution-snapshot" :
277- snapshot := append (json .RawMessage (nil ), line ... )
278- transcript .AttributionSnapshots = append (transcript .AttributionSnapshots , snapshot )
279- if messageID := parseSnapshotMessageID (line ); messageID != "" {
280- transcript .AttributionByMessageID [messageID ] = snapshot
281- }
282- case metadataType == "speculation-accept" :
283- if entry , ok := parseSpeculationAcceptMetadata (line ); ok {
284- transcript .SpeculationAccepts = append (transcript .SpeculationAccepts , entry )
285- }
286- case metadataType == "marble-origami-commit" :
287- if entry , ok := parseContextCollapseCommitMetadata (line ); ok {
288- transcript .ContextCollapseCommits = append (transcript .ContextCollapseCommits , entry )
289- }
290- case metadataType == "marble-origami-snapshot" :
291- if entry , ok := parseContextCollapseSnapshotMetadata (line ); ok {
292- transcript .ContextCollapseSnapshot = & entry
293295 }
294296 }
295297 }
0 commit comments