11931 lines · 457.6 KB · Zig
RawDownload
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.