3281 lines · 110.1 KB · Zig
RawDownload
// Host contract:// - fields: transcript, entries, next_entry_id, last_rendered_cols,//   transcript_cache_origin_untrimmed,//   max_transcript_bytes, max_retained_transcript_bytes,//   folded_command_blocks, command_output_blocks, command_output_display,//   tool_details,//   full_transcript, replaceable_last_line, replaceable_start,//   replaceable_row, transcript_band_dirty, layout, cursor_row, cursor_col,//   owned_top_row, viewport_top_row, pending_scroll_compact,//   has_painted_transcript, last_viewport_selection,//   last_visible_transcript_top_row, last_visible_transcript_start_line,//   last_visible_transcript_partial_skip_rows, last_visible_transcript_split_active,//   last_visible_transcript_split_prefix_lines,//   last_visible_transcript_split_suffix_start_line// - methods: assertCanMutateTranscript, advanceCursor, invalidateTranscriptAnchor,//   recomputeCursorFromTranscript, writeTranscriptBytes,//   writeTranscriptClassified const std = @import("std");const debug_trace = @import("../../core/shared/debug_trace.zig");const io_mod = @import("../../core/shared/io.zig");const types = @import("../../core/shared/types.zig");const command_output_content = @import("../../core/tooling/command_output_content.zig");const command_output_runtime = @import("command_output_runtime.zig");const render_engine = @import("../render_engine.zig");const source_preparation = @import("source_preparation.zig");const user_message_card = @import("../assistant/user_message_card.zig");const input_visual_layout = @import("../input/visual_layout.zig"); const Allocator = std.mem.Allocator;const Metrics = types.Metrics;const transcript_blocks = render_engine.transcript_blocks;const assistant_presentation = @import("../../core/agent/assistant_presentation.zig"); const AssistantTurnSegments = transcript_blocks.AssistantTurnSegments;const RawEntryClass = transcript_blocks.RawEntryClass;const Styles = transcript_blocks.Styles;const ToolDetailRecord = transcript_blocks.ToolDetailRecord;const TranscriptEntry = transcript_blocks.TranscriptEntry;const SkillTokenSpan = input_visual_layout.SkillTokenSpan; pub const TranscriptSourceRewriteMode = enum {    strict,    preserve_same_epoch,}; pub const LifecycleEntryUpdate = struct {    entry_id: u32,    bytes: []const u8,}; noinline fn requestTranscriptPaint(self: anytype) void {    const Runtime = @TypeOf(self.*);    if (comptime @hasDecl(Runtime, "markTranscriptContentDirty")) {        self.markTranscriptContentDirty();    } else if (comptime @hasDecl(Runtime, "markTranscriptDirty")) {        self.markTranscriptDirty();    } else {        self.transcript_band_dirty = true;    }} noinline fn requestTranscriptPaintFrom(self: anytype, entry_id: u32) void {    const Runtime = @TypeOf(self.*);    if (comptime @hasDecl(Runtime, "markTranscriptContentDirtyFrom")) {        self.markTranscriptContentDirtyFrom(entry_id);    } else {        requestTranscriptPaint(self);    }} fn reconcileTranscriptAnchorAfterSourceChange(    self: anytype,    force_rebase: bool,    reason: []const u8,    mode: TranscriptSourceRewriteMode,) void {    reconcileTranscriptAnchorAfterSourceChangeUsingSource(        self,        self.transcript.items,        force_rebase,        reason,        mode,    );} fn reconcileTranscriptAnchorAfterSourceChangeUsingSource(    self: anytype,    reconciliation_source: []const u8,    force_rebase: bool,    reason: []const u8,    mode: TranscriptSourceRewriteMode,) void {    const Runtime = @TypeOf(self.*);    if (comptime @hasDecl(Runtime, "reconcileTranscriptCommitSourceWithRewriteMode")) {        self.reconcileTranscriptCommitSourceWithRewriteMode(            reconciliation_source,            force_rebase,            reason,            mode,        );    } else if (comptime @hasDecl(Runtime, "reconcileTranscriptCommitSource")) {        self.reconcileTranscriptCommitSource(            reconciliation_source,            force_rebase,            reason,        );    } else if (comptime @hasDecl(Runtime, "stableTranscriptProjectionForFlow") and        @hasDecl(Runtime, "invalidateTranscriptAnchor"))    {        if (force_rebase or            self.stableTranscriptProjectionForFlow(reconciliation_source) == null)        {            self.invalidateTranscriptAnchor(reason);        }    }} /// Preserve an already committed prefix while an owner-level detached build/// replaces the transcript state. The complete source is used only for/// reconciliation; retained entries remain authoritative after publication.pub fn reconcileDetachedInstallSource(    self: anytype,    source: []const u8,) void {    reconcileTranscriptAnchorAfterSourceChangeUsingSource(        self,        source,        false,        "resume_projection_install",        .strict,    );} pub fn initBacking(self: anytype, alloc: Allocator) !void {    try self.transcript.ensureTotalCapacity(alloc, self.max_transcript_bytes);} pub fn ensurePaintReservation(self: anytype, alloc: Allocator) !void {    if (self.transcript.capacity < self.max_transcript_bytes) {        try self.transcript.ensureTotalCapacity(alloc, self.max_transcript_bytes);    }} pub fn rebuildTranscriptFromRendered(self: anytype, alloc: Allocator, bytes: []const u8, label: []const u8) !void {    const start = cappedTailStart(bytes, self.max_transcript_bytes);    try self.transcript.ensureTotalCapacity(alloc, bytes.len - start);    self.transcript_cache_origin_untrimmed = false;    self.transcript.clearRetainingCapacity();    if (start > 0) {        debug_trace.logf("transcript_cap", "{s} trimmed {d} leading bytes (bytes.len={d} cap={d})", .{ label, start, bytes.len, self.max_transcript_bytes });    }    self.transcript.appendSliceAssumeCapacity(bytes[start..]);    self.transcript_cache_origin_untrimmed = start == 0;    self.last_rendered_cols = if (self.layout.cols > 0) self.layout.cols else 80;} pub fn retainedStructuredBytes(self: anytype) usize {    var total: usize = 0;    for (self.entries.items) |entry| total += entryRetainedBytes(entry);    for (self.folded_command_blocks.items) |block| total += command_output_runtime.foldedBlockRetainedBytes(block);    for (self.command_output_blocks.items) |block| total += command_output_runtime.commandOutputBlockRetainedBytes(block);    return total;} fn entryRetainedBytes(entry: TranscriptEntry) usize {    return switch (entry) {        .raw_bytes => |e| e.bytes.len,        .semantic_notice => |e| e.topic.len + e.body.len,        .user_turn => |e| blk: {            var total = e.turn.text.len;            for (e.turn.images) |img| {                total += img.path.len;                total += img.media_type.len;            }            for (e.skill_tokens) |token| {                total += token.name.len;                total += token.path.len;            }            break :blk total;        },        .assistant_turn => |e| e.segments.text.items.len,        .assistant_table => |e| tableRetainedBytes(e.table),        .assistant_code_block => |e| e.block.language.len + e.block.code.len,        .assistant_thematic_rule => 0,    };} fn dupeSkillTokenSpans(alloc: Allocator, skill_tokens: []const SkillTokenSpan) ![]SkillTokenSpan {    if (skill_tokens.len == 0) return &.{};    const copy = try alloc.alloc(SkillTokenSpan, skill_tokens.len);    var filled: usize = 0;    errdefer {        var i: usize = 0;        while (i < filled) : (i += 1) {            alloc.free(@constCast(copy[i].name));            alloc.free(@constCast(copy[i].path));        }        alloc.free(copy);    }    while (filled < skill_tokens.len) : (filled += 1) {        const name = try alloc.dupe(u8, skill_tokens[filled].name);        errdefer alloc.free(name);        copy[filled] = .{            .raw_start = skill_tokens[filled].raw_start,            .raw_end = skill_tokens[filled].raw_end,            .name = name,            .path = try alloc.dupe(u8, skill_tokens[filled].path),            .display_source = skill_tokens[filled].display_source,            .owns_trailing_separator = skill_tokens[filled].owns_trailing_separator,        };    }    return copy;} fn freeSkillTokenSpans(alloc: Allocator, skill_tokens: []SkillTokenSpan) void {    for (skill_tokens) |token| {        alloc.free(@constCast(token.name));        alloc.free(@constCast(token.path));    }    if (skill_tokens.len > 0) alloc.free(skill_tokens);} fn tableRetainedBytes(table: assistant_presentation.TablePayload) usize {    var total: usize = table.alignments.len * @sizeOf(assistant_presentation.TableColumnAlign);    for (table.rows) |row| {        for (row.cells) |cell| total += cell.len;    }    return total;} pub fn replaceableEntryId(self: anytype) ?u32 {    if (!self.replaceable_last_line or self.entries.items.len == 0) return null;    const tail = self.entries.items[self.entries.items.len - 1];    return switch (tail) {        .raw_bytes => |e| e.id,        else => null,    };} fn protectedPreviousUserTurnId(self: anytype, protected_id: u32) ?u32 {    for (self.entries.items, 0..) |entry, index| {        if (entry.id() != protected_id) continue;        if (entry != .assistant_turn or index == 0) return null;        const previous = self.entries.items[index - 1];        return if (previous == .user_turn) previous.user_turn.id else null;    }    return null;} fn isProtectedEntry(self: anytype, entry_id: u32, protected_id: ?u32) bool {    if (isLifecyclePinned(self, entry_id)) return true;    for (self.command_output_blocks.items) |block| {        if (command_output_runtime.isPrunedRangeAnchor(block, entry_id)) return true;    }    if (protected_id) |id| {        if (entry_id == id) return true;        if (protectedPreviousUserTurnId(self, id)) |user_id| {            if (entry_id == user_id) return true;        }    }    if (replaceableEntryId(self)) |id| {        if (entry_id == id) return true;    }    return false;} pub fn isLifecyclePinned(self: anytype, entry_id: u32) bool {    for (self.entries.items) |entry| {        if (entry != .raw_bytes or entry.raw_bytes.id != entry_id) continue;        return entry.raw_bytes.lifecycle_pinned;    }    return false;} pub fn toolStatusEntryLabel(self: anytype, entry_id: u32) ?[]const u8 {    for (self.entries.items) |entry| {        if (entry != .raw_bytes or entry.raw_bytes.id != entry_id) continue;        return std.mem.trimEnd(u8, entry.raw_bytes.bytes, "\r\n");    }    return null;} pub fn lifecyclePinCount(self: anytype) usize {    var count: usize = 0;    for (self.entries.items) |entry| {        if (entry == .raw_bytes and entry.raw_bytes.lifecycle_pinned) count += 1;    }    return count;} pub const EntryIdSet = std.AutoHashMapUnmanaged(u32, void); fn rebuildEntryIdSet(self: anytype, alloc: Allocator, entry_ids: *EntryIdSet) !void {    entry_ids.clearRetainingCapacity();    for (self.entries.items) |entry| {        try entry_ids.put(alloc, entry.id(), {});    }} fn compactEntriesToRetainedSet(    self: anytype,    alloc: Allocator,    retained_entry_ids: *const EntryIdSet,) void {    var write_index: usize = 0;    for (self.entries.items, 0..) |entry, read_index| {        if (!retained_entry_ids.contains(entry.id())) {            var removed = entry;            removed.deinit(alloc);            continue;        }        if (write_index != read_index) {            self.entries.items[write_index] = entry;        }        write_index += 1;    }    self.entries.items.len = write_index;} fn buildProtectedEntryIdSet(    self: anytype,    alloc: Allocator,    protected_id: ?u32,) !EntryIdSet {    var protected_entry_ids: EntryIdSet = .empty;    errdefer protected_entry_ids.deinit(alloc);     for (self.entries.items) |entry| {        if (entry == .raw_bytes and entry.raw_bytes.lifecycle_pinned) {            try protected_entry_ids.put(alloc, entry.raw_bytes.id, {});        }    }    for (self.command_output_blocks.items) |block| {        for (block.pruned_ranges.items) |range| {            try protected_entry_ids.put(alloc, range.anchor_entry_id, {});        }    }    if (protected_id) |id| {        try protected_entry_ids.put(alloc, id, {});        if (protectedPreviousUserTurnId(self, id)) |user_id| {            try protected_entry_ids.put(alloc, user_id, {});        }    }    if (replaceableEntryId(self)) |id| {        try protected_entry_ids.put(alloc, id, {});    }    return protected_entry_ids;} fn pruneOrphanedFoldedCommandBlocks(    self: anytype,    alloc: Allocator,    entry_ids: *const EntryIdSet,) bool {    var changed = false;    var i: usize = 0;    while (i < self.folded_command_blocks.items.len) {        const summary_id = self.folded_command_blocks.items[i].summary_entry_id;        if (summary_id) |id| {            if (!entry_ids.contains(id)) {                command_output_runtime.removeFoldedCommandBlock(self, alloc, i);                changed = true;                continue;            }        }        i += 1;    }    return changed;} fn firstExistingCommandOutputSourceEntryId(    entry_ids: *const EntryIdSet,    block: command_output_runtime.CommandOutputBlock,) ?u32 {    for (block.pruned_ranges.items) |range| {        if (entry_ids.contains(range.anchor_entry_id)) return range.anchor_entry_id;    }    for (block.source_entry_ids.items) |entry_id| {        if (entry_ids.contains(entry_id)) return entry_id;    }    return null;} fn retargetCommandOutputDetails(self: anytype, old_entry_id: u32, new_entry_id: u32) void {    if (comptime !@hasField(@TypeOf(self.*), "tool_details")) return;    for (self.tool_details.items) |*detail| {        if (detail.command_output_entry_id == old_entry_id) {            detail.command_output_entry_id = new_entry_id;        }    }} fn commandOutputBlockHasDetail(    self: anytype,    block: command_output_runtime.CommandOutputBlock,) bool {    if (comptime !@hasField(@TypeOf(self.*), "tool_details")) return false;    for (self.tool_details.items) |detail| {        if (detail.command_output_entry_id) |entry_id| {            if (command_output_runtime.commandBlockOwnsEntry(block, entry_id)) {                return true;            }        }        if (detail.lifecycle_id != null and block.lifecycle_id != null and            command_output_runtime.sameLifecycleId(detail.lifecycle_id, block.lifecycle_id))        {            return true;        }    }    return false;} fn removePrunedRangeAnchorEntries(    self: anytype,    alloc: Allocator,    block: command_output_runtime.CommandOutputBlock,    entry_ids: *EntryIdSet,) void {    var entry_index: usize = 0;    while (entry_index < self.entries.items.len) {        const entry_id = self.entries.items[entry_index].id();        if (!command_output_runtime.isPrunedRangeAnchor(block, entry_id)) {            entry_index += 1;            continue;        }        var removed = self.entries.orderedRemove(entry_index);        _ = entry_ids.remove(entry_id);        removed.deinit(alloc);    }} fn pruneOrphanedCommandOutputBlocks(    self: anytype,    alloc: Allocator,    entry_ids: *EntryIdSet,) bool {    var changed = false;    var i: usize = 0;    while (i < self.command_output_blocks.items.len) {        if (self.command_output_display.open_command_block) |open| {            if (open == i) {                i += 1;                continue;            }        }         if (self.command_output_blocks.items[i].lines.items.len == 0 and            self.command_output_blocks.items[i].pruned_ranges.items.len > 0 and            !commandOutputBlockHasDetail(self, self.command_output_blocks.items[i]))        {            removePrunedRangeAnchorEntries(                self,                alloc,                self.command_output_blocks.items[i],                entry_ids,            );            command_output_runtime.removeCommandOutputBlock(self, alloc, i);            changed = true;            continue;        }         const entry_id = self.command_output_blocks.items[i].entry_id;        if (entry_id) |id| {            if (!entry_ids.contains(id)) {                if (firstExistingCommandOutputSourceEntryId(entry_ids, self.command_output_blocks.items[i])) |replacement| {                    self.command_output_blocks.items[i].entry_id = replacement;                    retargetCommandOutputDetails(self, id, replacement);                    changed = true;                    i += 1;                    continue;                }                command_output_runtime.removeCommandOutputBlock(self, alloc, i);                changed = true;                continue;            }        } else {            if (firstExistingCommandOutputSourceEntryId(entry_ids, self.command_output_blocks.items[i])) |replacement| {                self.command_output_blocks.items[i].entry_id = replacement;                changed = true;                i += 1;                continue;            }            command_output_runtime.removeCommandOutputBlock(self, alloc, i);            changed = true;            continue;        }        i += 1;    }    return changed;} fn coalesceCommandOutputPrunedRanges(self: anytype, alloc: Allocator) bool {    var changed = false;    for (0..self.command_output_blocks.items.len) |block_index| {        if (command_output_runtime.coalesceCommandOutputPrunedRanges(            self,            alloc,            block_index,        )) changed = true;

Lines 1–500 of 3281.