const std = @import("std");const debug_trace = @import("../../core/shared/debug_trace.zig");const display_width = @import("../../core/shared/display_width.zig");const input_action = @import("../../core/input/input_action.zig");const io_mod = @import("../../core/shared/io.zig");const activity_status = @import("../../core/output/activity_status.zig");const activity_runtime = @import("../../core/output/activity_runtime.zig");const transcript_release = @import("../../core/output/transcript_release.zig");const transcript_presentation = @import("../../core/output/transcript_presentation.zig");const worker_status = @import("../../core/output/worker_status.zig");const footer_viewport_runtime = @import("../footer/viewport.zig");const render_request = @import("../render_request.zig");const presentation_mode = @import("../../core/config/presentation_mode.zig");const command_output_runtime = @import("command_output_runtime.zig");const input_visual_layout = @import("../input/visual_layout.zig");const user_message_card = @import("../assistant/user_message_card.zig");const render_engine = @import("../render_engine.zig");const build_checkpoint = @import("../render_engine/build_checkpoint.zig");const transcript_io = @import("io.zig");const transcript_painter = @import("painter.zig");const source_preparation = @import("source_preparation.zig");const tool_group_projection = @import("tool_group_projection.zig");const transcript_store = @import("store.zig");const full_transcript_screen = @import("../full_transcript_screen.zig");const transcript_viewport_runtime = @import("viewport_runtime.zig");const transcript_writer = @import("writer.zig");const ui_render = @import("../render.zig");const types = @import("../../core/shared/types.zig");const command_output_content = @import("../../core/tooling/command_output_content.zig");const captured_command = @import("../../core/tooling/captured_command.zig");const vt_emulator = @import("../../core/terminal/engine.zig");const result_store = @import("../../core/session/result_store.zig");const session_child_store = @import("../../core/session/session_child_store.zig"); const Allocator = std.mem.Allocator;const Layout = types.Layout;const Metrics = types.Metrics;const transcript_blocks = render_engine.transcript_blocks;const viewport_selection = render_engine.viewport_selection; pub const CommandOutputDisplayState = transcript_blocks.CommandOutputDisplayState;pub const Styles = transcript_blocks.Styles;pub const AssistantTurnSegments = transcript_blocks.AssistantTurnSegments;pub const RawEntryClass = transcript_blocks.RawEntryClass;pub const TranscriptEntry = transcript_blocks.TranscriptEntry;pub const TranscriptPreparationSource = source_preparation.TranscriptPreparationSource; pub const VisibleTranscriptSnapshot = struct { text: []u8, terminal_rows: u16, terminal_cols: u16, complete: bool, pub fn deinit(self: *VisibleTranscriptSnapshot, alloc: Allocator) void { alloc.free(self.text); self.* = undefined; }}; pub const ResizeObservationPhase = enum { live, settle_pending, reset_queued,}; pub const ResizeObservationResult = union(enum) { awaiting, measured: i32, unavailable,}; pub const ResizeObservation = struct { layout: types.Layout, apply_after_ms: i64, expected_row: ?u32, result: ResizeObservationResult, phase: ResizeObservationPhase,}; const ViewportSelection = viewport_selection.ViewportSelection; pub const TranscriptCommitDiagnosticState = enum { invalid, stable, recovering,}; pub const TranscriptCommitDiagnostic = struct { state: TranscriptCommitDiagnosticState, stable_start_line: ?usize = null, visual_offset: u32 = 0, layout_id: u64 = 0, remaining_inline_rows: u32 = 0, remaining_unplanned_scroll_rows: u16 = 0,}; pub const StableTranscriptProjection = struct { selection: ViewportSelection, visual_offset: u32, history_visual_offset: u32, total_visual_rows: u32, flow_unchanged: bool, cursor_row: u16, cursor_col: u16, occupied_last_row: u16, occupied_last_row_blank: bool, layout_id: u64, measured_history_origin: ?transcript_painter.MeasuredHistoryOrigin = null,}; pub const MeasuredResizeProjection = union(enum) { stable, recovering: struct { accepted_visual_offset: u32, source_cols: u16, },}; const CommittedTranscriptAnchor = struct { selection: ViewportSelection, visual_offset: u32, // Footer reflow can move the logical tail without advancing rows into // terminal history. The committed grid projection resumes from this offset. history_visual_offset: u32, total_visual_rows: u32, flow: []u8, cursor_row: u16, cursor_col: u16, occupied_last_row: u16, occupied_last_row_blank: bool, layout_id: u64, measured_history_origin: ?transcript_painter.MeasuredHistoryOrigin = null, row_provenance: []transcript_blocks.RowProvenance = &.{}, normal_buffer_recovery_pending: bool = false, /// Stable-anchor settlement obligation. It is preserved while final rows /// remain ahead of history and cleared once history reaches the viewport. /// The offset gap alone is insufficient because footer displacement can /// create the same shape without content debt. history_catchup_pending: bool = false, fn deinit(self: *CommittedTranscriptAnchor, alloc: Allocator) void { if (self.flow.len > 0) alloc.free(self.flow); if (self.row_provenance.len > 0) alloc.free(self.row_provenance); self.* = undefined; }}; fn committedProjectionVisualOffset(anchor: CommittedTranscriptAnchor) u32 { return if (anchor.normal_buffer_recovery_pending) anchor.history_visual_offset else anchor.visual_offset;} const TranscriptRecoveryReceipt = struct { accepted_visual_offset: u32 = 0, accepted_history_visual_offset: u32 = 0, attempt_visual_offset: u32 = 0, attempt_total_visual_rows: u32 = 0, materialized_visual_offset: u32 = 0, materialized_visual_rows: u16 = 0, materialized_flow_len: ?usize = null, remaining_resize_reflow_rows: u16 = 0, remaining_semantic_rows: u32 = 0, // Only this remaining semantic suffix may advance the current projection. remaining_semantic_progress_rows: u32 = 0, remaining_unplanned_scroll_rows: u16 = 0, attempt_cols: u16 = 0, attempt_resize_history_row_delta: ?i32 = null, attempt_resize_history_committed: bool = false, projection_rebase_pending: bool = false, tracks_semantic_progress: bool = false, flow: []u8 = &.{}, flow_owned: bool = true, presentation_resume_bytes: []u8 = &.{}, presentation_pending_wrap: bool = false, presentation_valid: bool = false, cursor_row: u16 = 1, cursor_col: u16 = 1, document_append_committed: bool = false, measured_history_origin: ?transcript_painter.MeasuredHistoryOrigin = null, row_provenance: []transcript_blocks.RowProvenance = &.{}, normal_buffer_recovery_pending: bool = false, fn deinit(self: *TranscriptRecoveryReceipt, alloc: Allocator) void { if (self.flow_owned and self.flow.len > 0) alloc.free(self.flow); if (self.presentation_resume_bytes.len > 0) alloc.free(self.presentation_resume_bytes); if (self.row_provenance.len > 0) alloc.free(self.row_provenance); self.* = undefined; }}; const TranscriptCommitState = union(enum) { invalid, stable: CommittedTranscriptAnchor, recovering: TranscriptRecoveryReceipt, fn deinit(self: *TranscriptCommitState, alloc: Allocator) void { switch (self.*) { .invalid => {}, .stable => |*anchor| anchor.deinit(alloc), .recovering => |*receipt| receipt.deinit(alloc), } self.* = .invalid; }}; fn recoveryAppendPrefixMatches( target_flow: []const u8, receipt: TranscriptRecoveryReceipt, materialized_flow_len: usize,) bool { return materialized_flow_len <= receipt.flow.len and materialized_flow_len <= target_flow.len and std.mem.eql( u8, target_flow[0..materialized_flow_len], receipt.flow[0..materialized_flow_len], );} pub const TranscriptScrollFacts = struct { source_visual_offset: u32 = 0, target_visual_offset: u32 = 0, resize_reflow_rows: u16 = 0, semantic_rows: u32 = 0, semantic_progress_rows: u32 = 0, planned_rows: u16 = 0, source_compatible: bool = false, geometry_rebase: bool = false, recovery_rebase: bool = false, recovery_progress_compatible: bool = false, recovery_tracks_semantic_progress: bool = false, recovery_projection_deferred: bool = false, /// The finality floor withheld release this frame (mutable rows above /// the viewport top), or final rows remain to settle beyond what this /// frame planned. Routes into history_catchup_pending at commit so the /// existing catch-up machinery settles the debt; footer displacement /// (visual ahead of history with nothing to settle) stays unflagged. finality_hold: bool = false,}; pub const RetainedTranscriptBody = render_engine.frame_retention.RetainedTranscriptBody; pub const TranscriptBodyDisposition = union(enum) { paint, retain_committed: RetainedTranscriptBody,}; fn nextResizeBottomAnchorPending( pending: bool, terminal_reset_commit: bool, resize_reset_commit: bool, tail_kind: ?TranscriptBlockKind, occupied_last_row: u16, content_bottom_row: u16,) bool { const next = if (terminal_reset_commit) resize_reset_commit else pending; if (tail_kind == .cancel_notice and occupied_last_row == content_bottom_row) { return false; } return next;} test "resize bottom anchor remains pending until a cancel projection reaches it" { const Case = struct { pending: bool, terminal_reset_commit: bool, resize_reset_commit: bool, tail_kind: ?TranscriptBlockKind, occupied_last_row: u16, content_bottom_row: u16, expected: bool, }; const cases = [_]Case{ .{ .pending = false, .terminal_reset_commit = false, .resize_reset_commit = false, .tail_kind = null, .occupied_last_row = 4, .content_bottom_row = 8, .expected = false }, .{ .pending = false, .terminal_reset_commit = true, .resize_reset_commit = true, .tail_kind = null, .occupied_last_row = 4, .content_bottom_row = 8, .expected = true }, .{ .pending = true, .terminal_reset_commit = false, .resize_reset_commit = false, .tail_kind = .assistant_turn, .occupied_last_row = 4, .content_bottom_row = 8, .expected = true }, .{ .pending = true, .terminal_reset_commit = false, .resize_reset_commit = false, .tail_kind = .cancel_notice, .occupied_last_row = 4, .content_bottom_row = 8, .expected = true }, .{ .pending = true, .terminal_reset_commit = false, .resize_reset_commit = false, .tail_kind = .cancel_notice, .occupied_last_row = 8, .content_bottom_row = 8, .expected = false }, .{ .pending = true, .terminal_reset_commit = true, .resize_reset_commit = false, .tail_kind = null, .occupied_last_row = 4, .content_bottom_row = 8, .expected = false }, }; for (cases) |case| { try std.testing.expectEqual( case.expected, nextResizeBottomAnchorPending( case.pending, case.terminal_reset_commit, case.resize_reset_commit, case.tail_kind, case.occupied_last_row, case.content_bottom_row, ), ); }} const MaterializedEndpoint = struct { visual_offset: u32 = 0, visual_rows: u16 = 0, flow_len: ?usize = null, cursor_row: u16 = 1, cursor_col: u16 = 1,}; pub const TranscriptTransition = struct { scroll_plan: render_engine.frame_scroll_plan.FrameScrollPlan, document_append: render_engine.frame_scroll_plan.FrameDocumentAppend, /// Final rows exist beyond materialized history that this frame could /// not release; consuming the transition requests a follow-up frame so /// the debt settles promptly instead of waiting for outside activity. history_settle_pending: bool = false, /// Owns `document_append.bytes` when append preparation allocates wire bytes. document_append_bytes: []u8 = &.{}, resize_reflow_rows: u16 = 0, semantic_rows: u32 = 0, semantic_progress_rows: u32 = 0, recovery_rebase: bool = false, recovery_progress_compatible: bool = false, recovery_tracks_semantic_progress: bool = false, recovery_projection_deferred: bool = false, body_disposition: TranscriptBodyDisposition = .paint, target_flow: []u8, target_flow_owned: bool = true, presentation_resume_bytes: []u8 = &.{}, presentation_pending_wrap: bool = false, presentation_valid: bool = false, target_cache: std.ArrayList(u8), target_cache_unchanged: bool = false, target_cache_origin_untrimmed: bool = false, folded_summary_indices: []usize, target_layout: render_engine.frame_layout.CommittedLayoutSnapshot, selection: ViewportSelection, visual_offset: u32, history_visual_offset: u32, source_endpoint_visual_offset: u32 = 0, total_visual_rows: u32, materialized: MaterializedEndpoint = .{}, // The exact transcript endpoint if this frame appends no transcript bytes. materialized_without_append: MaterializedEndpoint, cursor_row: u16, cursor_col: u16, replaceable_last_line: bool, replaceable_start: usize, replaceable_row: u16, trace_transcript_frame: bool, trace_visible_rows: u16, measured_history_origin: ?transcript_painter.MeasuredHistoryOrigin = null, row_provenance: []transcript_blocks.RowProvenance = &.{}, normal_buffer_recovery_pending: bool = false, history_catchup_pending: bool = false, consumed: bool = false, pub fn deinit(self: *TranscriptTransition, alloc: Allocator) void { if (self.target_flow_owned and self.target_flow.len > 0) alloc.free(self.target_flow); if (self.presentation_resume_bytes.len > 0) alloc.free(self.presentation_resume_bytes); if (self.document_append_bytes.len > 0) { alloc.free(self.document_append_bytes); } self.target_cache.deinit(alloc); if (self.folded_summary_indices.len > 0) alloc.free(self.folded_summary_indices); if (self.row_provenance.len > 0) alloc.free(self.row_provenance); self.* = undefined; }}; const MaterializedTranscriptProjection = struct { raw_flow_exact: bool = false, visual_rows: u16 = 0,}; const TranscriptBlockKind = transcript_blocks.TranscriptBlockKind;const WalkResult = transcript_blocks.WalkResult;const walkText = transcript_blocks.walkText; fn formatDurationCompact(buf: []u8, duration_ms: u64) []const u8 { const total_seconds = duration_ms / std.time.ms_per_s; if (total_seconds < 60) { return std.fmt.bufPrint(buf, "{d}s", .{total_seconds}) catch "0s"; } if (total_seconds < 60 * 60) { const minutes = total_seconds / 60; const seconds = total_seconds % 60; return std.fmt.bufPrint(buf, "{d}m {d}s", .{ minutes, seconds }) catch "1m 0s"; } const hours = total_seconds / (60 * 60); const minutes = (total_seconds % (60 * 60)) / 60; return std.fmt.bufPrint(buf, "{d}h {d:0>2}m", .{ hours, minutes }) catch "1h 00m";} fn formatTurnSummaryLine(buf: []u8, summary: types.TurnSummary) []const u8 { var turn_buf: [24]u8 = undefined; const turn = formatDurationCompact(&turn_buf, summary.turn_duration_ms); var out: std.Io.Writer = .fixed(buf); out.print(" {s}", .{turn}) catch return " "; activity_status.appendTokenProgressSuffix(&out, summary.token_progress) catch return out.buffered(); return out.buffered();} fn lifecycleEventTurnId(event: types.ToolLifecycleEvent) u64 { return switch (event) { .provisional => |payload| payload.id.turn_id, .authoritative_started => |payload| payload.id.turn_id, .progress => |payload| payload.id.turn_id, .terminal => |payload| payload.id.turn_id, .turn_finished => |payload| payload.turn_id, };} fn admitLifecycleStart( state: *const activity_runtime.ToolActivityState, id: types.ToolLifecycleId,) bool { return switch (state.admitStart(id)) { .accepted => true, .invalid_identity => false, .finalized => |fence| blk: { debug_trace.logf( "ui_activity", "lifecycle rejected by finalized fence turn_id={d} fence={d} kind=start", .{ id.turn_id, fence }, ); break :blk false; }, };} fn traceLateLifecycleEvent(id: types.ToolLifecycleId, event_kind: []const u8) void { debug_trace.logf( "ui_activity", "late lifecycle event ignored turn_id={d} kind={s}", .{ id.turn_id, event_kind }, );} fn lifecycleStartLine( alloc: Allocator, tool_name: ?[]const u8,) ![]u8 { var label_buf: [512]u8 = undefined; const label = std.fmt.bufPrint( &label_buf, "● {s}", .{tool_name orelse "tool"}, ) catch "● tool"; return std.fmt.allocPrint( alloc, "{s}{s}\n", .{ label, ui_render.reset_style }, );} fn lifecycleLine(alloc: Allocator, text: []const u8) ![]u8 { if (std.mem.endsWith(u8, text, "\n")) return alloc.dupe(u8, text); return std.fmt.allocPrint(alloc, "{s}\n", .{text});} const LifecycleMarker = struct { style: []const u8, glyph: []const u8,}; fn lifecycleMarker(kind: types.ToolOutcomeKind) LifecycleMarker { return switch (kind) { .completed => .{ .style = ui_render.system_notice_text_style, .glyph = "●" }, .failed => .{ .style = ui_render.red_style, .glyph = "●" }, .denied => .{ .style = ui_render.red_style, .glyph = "⊘" }, .cancelled => .{ .style = ui_render.warning_style, .glyph = "■" }, .deferred => .{ .style = ui_render.warning_style, .glyph = "↻" }, };} fn leadingSgrPrefixEnd(text: []const u8) usize { var index: usize = 0; while (index + 2 <= text.len and text[index] == 0x1b and text[index + 1] == '[') { var end = index + 2; while (end < text.len and (text[end] < 0x40 or text[end] > 0x7e)) : (end += 1) {} if (end == text.len or text[end] != 'm') break; index = end + 1; } return index;} fn lifecycleTerminalLine( alloc: Allocator, kind: types.ToolOutcomeKind, summary: []const u8,) ![]u8 { const marker = lifecycleMarker(kind); const prefix_end = leadingSgrPrefixEnd(summary); const has_marker = std.mem.startsWith(u8, summary[prefix_end..], "●"); const cancelled = kind == .cancelled; const text_style = if (cancelled) ui_render.hint_style else ""; const text_reset = if (cancelled) ui_render.reset_style else "";Lines 1–500 of 11931. Load more
const std = @import("std"); const debug_trace = @import("../../core/shared/debug_trace.zig"); const display_width = @import("../../core/shared/display_width.zig"); const input_action = @import("../../core/input/input_action.zig"); const io_mod = @import("../../core/shared/io.zig"); const activity_status = @import("../../core/output/activity_status.zig"); const activity_runtime = @import("../../core/output/activity_runtime.zig"); const transcript_release = @import("../../core/output/transcript_release.zig"); const transcript_presentation = @import("../../core/output/transcript_presentation.zig"); const worker_status = @import("../../core/output/worker_status.zig"); const footer_viewport_runtime = @import("../footer/viewport.zig"); const render_request = @import("../render_request.zig"); const presentation_mode = @import("../../core/config/presentation_mode.zig"); const command_output_runtime = @import("command_output_runtime.zig"); const input_visual_layout = @import("../input/visual_layout.zig"); const user_message_card = @import("../assistant/user_message_card.zig"); const render_engine = @import("../render_engine.zig"); const build_checkpoint = @import("../render_engine/build_checkpoint.zig"); const transcript_io = @import("io.zig"); const transcript_painter = @import("painter.zig"); const source_preparation = @import("source_preparation.zig"); const tool_group_projection = @import("tool_group_projection.zig"); const transcript_store = @import("store.zig"); const full_transcript_screen = @import("../full_transcript_screen.zig"); const transcript_viewport_runtime = @import("viewport_runtime.zig"); const transcript_writer = @import("writer.zig"); const ui_render = @import("../render.zig"); const types = @import("../../core/shared/types.zig"); const command_output_content = @import("../../core/tooling/command_output_content.zig"); const captured_command = @import("../../core/tooling/captured_command.zig"); const vt_emulator = @import("../../core/terminal/engine.zig"); const result_store = @import("../../core/session/result_store.zig"); const session_child_store = @import("../../core/session/session_child_store.zig"); const Allocator = std.mem.Allocator; const Layout = types.Layout; const Metrics = types.Metrics; const transcript_blocks = render_engine.transcript_blocks; const viewport_selection = render_engine.viewport_selection; pub const CommandOutputDisplayState = transcript_blocks.CommandOutputDisplayState; pub const Styles = transcript_blocks.Styles; pub const AssistantTurnSegments = transcript_blocks.AssistantTurnSegments; pub const RawEntryClass = transcript_blocks.RawEntryClass; pub const TranscriptEntry = transcript_blocks.TranscriptEntry; pub const TranscriptPreparationSource = source_preparation.TranscriptPreparationSource; pub const VisibleTranscriptSnapshot = struct { text: []u8, terminal_rows: u16, terminal_cols: u16, complete: bool, pub fn deinit(self: *VisibleTranscriptSnapshot, alloc: Allocator) void { alloc.free(self.text); self.* = undefined; } }; pub const ResizeObservationPhase = enum { live, settle_pending, reset_queued, }; pub const ResizeObservationResult = union(enum) { awaiting, measured: i32, unavailable, }; pub const ResizeObservation = struct { layout: types.Layout, apply_after_ms: i64, expected_row: ?u32, result: ResizeObservationResult, phase: ResizeObservationPhase, }; const ViewportSelection = viewport_selection.ViewportSelection; pub const TranscriptCommitDiagnosticState = enum { invalid, stable, recovering, }; pub const TranscriptCommitDiagnostic = struct { state: TranscriptCommitDiagnosticState, stable_start_line: ?usize = null, visual_offset: u32 = 0, layout_id: u64 = 0, remaining_inline_rows: u32 = 0, remaining_unplanned_scroll_rows: u16 = 0, }; pub const StableTranscriptProjection = struct { selection: ViewportSelection, visual_offset: u32, history_visual_offset: u32, total_visual_rows: u32, flow_unchanged: bool, cursor_row: u16, cursor_col: u16, occupied_last_row: u16, occupied_last_row_blank: bool, layout_id: u64, measured_history_origin: ?transcript_painter.MeasuredHistoryOrigin = null, }; pub const MeasuredResizeProjection = union(enum) { stable, recovering: struct { accepted_visual_offset: u32, source_cols: u16, }, }; const CommittedTranscriptAnchor = struct { selection: ViewportSelection, visual_offset: u32, // Footer reflow can move the logical tail without advancing rows into // terminal history. The committed grid projection resumes from this offset. history_visual_offset: u32, total_visual_rows: u32, flow: []u8, cursor_row: u16, cursor_col: u16, occupied_last_row: u16, occupied_last_row_blank: bool, layout_id: u64, measured_history_origin: ?transcript_painter.MeasuredHistoryOrigin = null, row_provenance: []transcript_blocks.RowProvenance = &.{}, normal_buffer_recovery_pending: bool = false, /// Stable-anchor settlement obligation. It is preserved while final rows /// remain ahead of history and cleared once history reaches the viewport. /// The offset gap alone is insufficient because footer displacement can /// create the same shape without content debt. history_catchup_pending: bool = false, fn deinit(self: *CommittedTranscriptAnchor, alloc: Allocator) void { if (self.flow.len > 0) alloc.free(self.flow); if (self.row_provenance.len > 0) alloc.free(self.row_provenance); self.* = undefined; } }; fn committedProjectionVisualOffset(anchor: CommittedTranscriptAnchor) u32 { return if (anchor.normal_buffer_recovery_pending) anchor.history_visual_offset else anchor.visual_offset; } const TranscriptRecoveryReceipt = struct { accepted_visual_offset: u32 = 0, accepted_history_visual_offset: u32 = 0, attempt_visual_offset: u32 = 0, attempt_total_visual_rows: u32 = 0, materialized_visual_offset: u32 = 0, materialized_visual_rows: u16 = 0, materialized_flow_len: ?usize = null, remaining_resize_reflow_rows: u16 = 0, remaining_semantic_rows: u32 = 0, // Only this remaining semantic suffix may advance the current projection. remaining_semantic_progress_rows: u32 = 0, remaining_unplanned_scroll_rows: u16 = 0, attempt_cols: u16 = 0, attempt_resize_history_row_delta: ?i32 = null, attempt_resize_history_committed: bool = false, projection_rebase_pending: bool = false, tracks_semantic_progress: bool = false, flow: []u8 = &.{}, flow_owned: bool = true, presentation_resume_bytes: []u8 = &.{}, presentation_pending_wrap: bool = false, presentation_valid: bool = false, cursor_row: u16 = 1, cursor_col: u16 = 1, document_append_committed: bool = false, measured_history_origin: ?transcript_painter.MeasuredHistoryOrigin = null, row_provenance: []transcript_blocks.RowProvenance = &.{}, normal_buffer_recovery_pending: bool = false, fn deinit(self: *TranscriptRecoveryReceipt, alloc: Allocator) void { if (self.flow_owned and self.flow.len > 0) alloc.free(self.flow); if (self.presentation_resume_bytes.len > 0) alloc.free(self.presentation_resume_bytes); if (self.row_provenance.len > 0) alloc.free(self.row_provenance); self.* = undefined; } }; const TranscriptCommitState = union(enum) { invalid, stable: CommittedTranscriptAnchor, recovering: TranscriptRecoveryReceipt, fn deinit(self: *TranscriptCommitState, alloc: Allocator) void { switch (self.*) { .invalid => {}, .stable => |*anchor| anchor.deinit(alloc), .recovering => |*receipt| receipt.deinit(alloc), } self.* = .invalid; } }; fn recoveryAppendPrefixMatches( target_flow: []const u8, receipt: TranscriptRecoveryReceipt, materialized_flow_len: usize, ) bool { return materialized_flow_len <= receipt.flow.len and materialized_flow_len <= target_flow.len and std.mem.eql( u8, target_flow[0..materialized_flow_len], receipt.flow[0..materialized_flow_len], ); } pub const TranscriptScrollFacts = struct { source_visual_offset: u32 = 0, target_visual_offset: u32 = 0, resize_reflow_rows: u16 = 0, semantic_rows: u32 = 0, semantic_progress_rows: u32 = 0, planned_rows: u16 = 0, source_compatible: bool = false, geometry_rebase: bool = false, recovery_rebase: bool = false, recovery_progress_compatible: bool = false, recovery_tracks_semantic_progress: bool = false, recovery_projection_deferred: bool = false, /// The finality floor withheld release this frame (mutable rows above /// the viewport top), or final rows remain to settle beyond what this /// frame planned. Routes into history_catchup_pending at commit so the /// existing catch-up machinery settles the debt; footer displacement /// (visual ahead of history with nothing to settle) stays unflagged. finality_hold: bool = false, }; pub const RetainedTranscriptBody = render_engine.frame_retention.RetainedTranscriptBody; pub const TranscriptBodyDisposition = union(enum) { paint, retain_committed: RetainedTranscriptBody, }; fn nextResizeBottomAnchorPending( pending: bool, terminal_reset_commit: bool, resize_reset_commit: bool, tail_kind: ?TranscriptBlockKind, occupied_last_row: u16, content_bottom_row: u16, ) bool { const next = if (terminal_reset_commit) resize_reset_commit else pending; if (tail_kind == .cancel_notice and occupied_last_row == content_bottom_row) { return false; } return next; } test "resize bottom anchor remains pending until a cancel projection reaches it" { const Case = struct { pending: bool, terminal_reset_commit: bool, resize_reset_commit: bool, tail_kind: ?TranscriptBlockKind, occupied_last_row: u16, content_bottom_row: u16, expected: bool, }; const cases = [_]Case{ .{ .pending = false, .terminal_reset_commit = false, .resize_reset_commit = false, .tail_kind = null, .occupied_last_row = 4, .content_bottom_row = 8, .expected = false }, .{ .pending = false, .terminal_reset_commit = true, .resize_reset_commit = true, .tail_kind = null, .occupied_last_row = 4, .content_bottom_row = 8, .expected = true }, .{ .pending = true, .terminal_reset_commit = false, .resize_reset_commit = false, .tail_kind = .assistant_turn, .occupied_last_row = 4, .content_bottom_row = 8, .expected = true }, .{ .pending = true, .terminal_reset_commit = false, .resize_reset_commit = false, .tail_kind = .cancel_notice, .occupied_last_row = 4, .content_bottom_row = 8, .expected = true }, .{ .pending = true, .terminal_reset_commit = false, .resize_reset_commit = false, .tail_kind = .cancel_notice, .occupied_last_row = 8, .content_bottom_row = 8, .expected = false }, .{ .pending = true, .terminal_reset_commit = true, .resize_reset_commit = false, .tail_kind = null, .occupied_last_row = 4, .content_bottom_row = 8, .expected = false }, }; for (cases) |case| { try std.testing.expectEqual( case.expected, nextResizeBottomAnchorPending( case.pending, case.terminal_reset_commit, case.resize_reset_commit, case.tail_kind, case.occupied_last_row, case.content_bottom_row, ), ); } } const MaterializedEndpoint = struct { visual_offset: u32 = 0, visual_rows: u16 = 0, flow_len: ?usize = null, cursor_row: u16 = 1, cursor_col: u16 = 1, }; pub const TranscriptTransition = struct { scroll_plan: render_engine.frame_scroll_plan.FrameScrollPlan, document_append: render_engine.frame_scroll_plan.FrameDocumentAppend, /// Final rows exist beyond materialized history that this frame could /// not release; consuming the transition requests a follow-up frame so /// the debt settles promptly instead of waiting for outside activity. history_settle_pending: bool = false, /// Owns `document_append.bytes` when append preparation allocates wire bytes. document_append_bytes: []u8 = &.{}, resize_reflow_rows: u16 = 0, semantic_rows: u32 = 0, semantic_progress_rows: u32 = 0, recovery_rebase: bool = false, recovery_progress_compatible: bool = false, recovery_tracks_semantic_progress: bool = false, recovery_projection_deferred: bool = false, body_disposition: TranscriptBodyDisposition = .paint, target_flow: []u8, target_flow_owned: bool = true, presentation_resume_bytes: []u8 = &.{}, presentation_pending_wrap: bool = false, presentation_valid: bool = false, target_cache: std.ArrayList(u8), target_cache_unchanged: bool = false, target_cache_origin_untrimmed: bool = false, folded_summary_indices: []usize, target_layout: render_engine.frame_layout.CommittedLayoutSnapshot, selection: ViewportSelection, visual_offset: u32, history_visual_offset: u32, source_endpoint_visual_offset: u32 = 0, total_visual_rows: u32, materialized: MaterializedEndpoint = .{}, // The exact transcript endpoint if this frame appends no transcript bytes. materialized_without_append: MaterializedEndpoint, cursor_row: u16, cursor_col: u16, replaceable_last_line: bool, replaceable_start: usize, replaceable_row: u16, trace_transcript_frame: bool, trace_visible_rows: u16, measured_history_origin: ?transcript_painter.MeasuredHistoryOrigin = null, row_provenance: []transcript_blocks.RowProvenance = &.{}, normal_buffer_recovery_pending: bool = false, history_catchup_pending: bool = false, consumed: bool = false, pub fn deinit(self: *TranscriptTransition, alloc: Allocator) void { if (self.target_flow_owned and self.target_flow.len > 0) alloc.free(self.target_flow); if (self.presentation_resume_bytes.len > 0) alloc.free(self.presentation_resume_bytes); if (self.document_append_bytes.len > 0) { alloc.free(self.document_append_bytes); } self.target_cache.deinit(alloc); if (self.folded_summary_indices.len > 0) alloc.free(self.folded_summary_indices); if (self.row_provenance.len > 0) alloc.free(self.row_provenance); self.* = undefined; } }; const MaterializedTranscriptProjection = struct { raw_flow_exact: bool = false, visual_rows: u16 = 0, }; const TranscriptBlockKind = transcript_blocks.TranscriptBlockKind; const WalkResult = transcript_blocks.WalkResult; const walkText = transcript_blocks.walkText; fn formatDurationCompact(buf: []u8, duration_ms: u64) []const u8 { const total_seconds = duration_ms / std.time.ms_per_s; if (total_seconds < 60) { return std.fmt.bufPrint(buf, "{d}s", .{total_seconds}) catch "0s"; } if (total_seconds < 60 * 60) { const minutes = total_seconds / 60; const seconds = total_seconds % 60; return std.fmt.bufPrint(buf, "{d}m {d}s", .{ minutes, seconds }) catch "1m 0s"; } const hours = total_seconds / (60 * 60); const minutes = (total_seconds % (60 * 60)) / 60; return std.fmt.bufPrint(buf, "{d}h {d:0>2}m", .{ hours, minutes }) catch "1h 00m"; } fn formatTurnSummaryLine(buf: []u8, summary: types.TurnSummary) []const u8 { var turn_buf: [24]u8 = undefined; const turn = formatDurationCompact(&turn_buf, summary.turn_duration_ms); var out: std.Io.Writer = .fixed(buf); out.print(" {s}", .{turn}) catch return " "; activity_status.appendTokenProgressSuffix(&out, summary.token_progress) catch return out.buffered(); return out.buffered(); } fn lifecycleEventTurnId(event: types.ToolLifecycleEvent) u64 { return switch (event) { .provisional => |payload| payload.id.turn_id, .authoritative_started => |payload| payload.id.turn_id, .progress => |payload| payload.id.turn_id, .terminal => |payload| payload.id.turn_id, .turn_finished => |payload| payload.turn_id, }; } fn admitLifecycleStart( state: *const activity_runtime.ToolActivityState, id: types.ToolLifecycleId, ) bool { return switch (state.admitStart(id)) { .accepted => true, .invalid_identity => false, .finalized => |fence| blk: { debug_trace.logf( "ui_activity", "lifecycle rejected by finalized fence turn_id={d} fence={d} kind=start", .{ id.turn_id, fence }, ); break :blk false; }, }; } fn traceLateLifecycleEvent(id: types.ToolLifecycleId, event_kind: []const u8) void { debug_trace.logf( "ui_activity", "late lifecycle event ignored turn_id={d} kind={s}", .{ id.turn_id, event_kind }, ); } fn lifecycleStartLine( alloc: Allocator, tool_name: ?[]const u8, ) ![]u8 { var label_buf: [512]u8 = undefined; const label = std.fmt.bufPrint( &label_buf, "● {s}", .{tool_name orelse "tool"}, ) catch "● tool"; return std.fmt.allocPrint( alloc, "{s}{s}\n", .{ label, ui_render.reset_style }, ); } fn lifecycleLine(alloc: Allocator, text: []const u8) ![]u8 { if (std.mem.endsWith(u8, text, "\n")) return alloc.dupe(u8, text); return std.fmt.allocPrint(alloc, "{s}\n", .{text}); } const LifecycleMarker = struct { style: []const u8, glyph: []const u8, }; fn lifecycleMarker(kind: types.ToolOutcomeKind) LifecycleMarker { return switch (kind) { .completed => .{ .style = ui_render.system_notice_text_style, .glyph = "●" }, .failed => .{ .style = ui_render.red_style, .glyph = "●" }, .denied => .{ .style = ui_render.red_style, .glyph = "⊘" }, .cancelled => .{ .style = ui_render.warning_style, .glyph = "■" }, .deferred => .{ .style = ui_render.warning_style, .glyph = "↻" }, }; } fn leadingSgrPrefixEnd(text: []const u8) usize { var index: usize = 0; while (index + 2 <= text.len and text[index] == 0x1b and text[index + 1] == '[') { var end = index + 2; while (end < text.len and (text[end] < 0x40 or text[end] > 0x7e)) : (end += 1) {} if (end == text.len or text[end] != 'm') break; index = end + 1; } return index; } fn lifecycleTerminalLine( alloc: Allocator, kind: types.ToolOutcomeKind, summary: []const u8, ) ![]u8 { const marker = lifecycleMarker(kind); const prefix_end = leadingSgrPrefixEnd(summary); const has_marker = std.mem.startsWith(u8, summary[prefix_end..], "●"); const cancelled = kind == .cancelled; const text_style = if (cancelled) ui_render.hint_style else ""; const text_reset = if (cancelled) ui_render.reset_style else "";