| 1 | package transcript |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | |
| 6 | "reasonix/internal/event" |
| 7 | "reasonix/internal/turnevent" |
| 8 | ) |
| 9 | |
| 10 | // PendingDisplayMessages is the legacy migration path. Modern recovery uses |
| 11 | // the whole identified event stream from a durable projection checkpoint. |
| 12 | func PendingDisplayMessages(projection turnevent.PendingProjection, format Formatter) []Message { |
| 13 | planner, executor := Buffer{Format: format}, Buffer{Format: format} |
| 14 | for _, envelope := range projection.Events { |
| 15 | e, ok := EventFromEnvelope(envelope) |
| 16 | if !ok { |
| 17 | continue |
| 18 | } |
| 19 | buffer := &executor |
| 20 | if strings.TrimSpace(e.Source) == event.UsageSourcePlanner { |
| 21 | buffer = &planner |
| 22 | } |
| 23 | buffer.Apply(e) |
| 24 | } |
| 25 | out := LegacyDisplayMessages(planner.Messages()) |
| 26 | interrupted := projection.Status == event.TurnInterrupted || projection.Status == event.TurnRecoveryRequired |
| 27 | if !interrupted { |
| 28 | out = append(out, executor.ResultMessages()...) |
| 29 | } |
| 30 | if interrupted { |
| 31 | out = append(out, LegacyDisplayMessages(executor.Messages())...) |
| 32 | if len(out) > 0 { |
| 33 | out = append(out, Message{Role: "notice", Level: "info", Code: event.NoticeCodeCancelledTurn, |
| 34 | Content: "This turn was interrupted. Partial output is kept for reference; only completed tool pairs and a bounded recovery summary enter the next model turn. Inspect the workspace before continuing or reverting changes."}) |
| 35 | } |
| 36 | } |
| 37 | return out |
| 38 | } |
| 39 | |
| 40 | // Legacy sidecars supplement provider history. User rows already have a |
| 41 | // canonical owner, and empty sampling placeholders are never persisted. |
| 42 | func LegacyDisplayMessages(messages []Message) []Message { |
| 43 | out := make([]Message, 0, len(messages)) |
| 44 | for _, message := range messages { |
| 45 | if message.Role == "user" || (message.Role == "assistant" && strings.TrimSpace(message.Content+message.Reasoning) == "" && len(message.ToolCalls) == 0 && len(message.MemoryCitations) == 0) { |
| 46 | continue |
| 47 | } |
| 48 | message.Pending = false |
| 49 | for i := range message.ToolCalls { |
| 50 | message.ToolCalls[i].Pending = false |
| 51 | } |
| 52 | out = append(out, message) |
| 53 | } |
| 54 | return out |
| 55 | } |
| 56 |