6411 lines · 262.9 KB · Zig
RawDownload
const std = @import("std");const question_prompt = @import("../agent/question_prompt.zig");const input_completion_runtime = @import("input_completion_runtime.zig");const input_queue_runtime = @import("input_queue_runtime.zig");const app_commands = @import("app_commands.zig");const app_lifecycle = @import("app_lifecycle.zig");const app_permission_runtime = @import("app_permission_runtime.zig");const app_session_runtime = @import("app_session_runtime.zig");const terminal_ui_projection = @import("../terminal/ui_projection.zig");const worker_runtime = @import("../agent/worker_runtime.zig");const auth_runtime = @import("../auth/auth_runtime.zig");const model_capabilities = @import("../config/model_capabilities.zig");const picker_state = @import("../input/picker_state.zig");const core_input_runtime = @import("../input/runtime.zig");const command_specs = @import("../slash_commands/command_specs.zig");const model_cache_runtime = @import("model_cache_runtime.zig");const debug_trace = @import("../shared/debug_trace.zig");const diff_mod = @import("../output/diff.zig");const io_mod = @import("../shared/io.zig");const text_utils = @import("../shared/text_utils.zig");const permission_request = @import("../permissions/permission_request.zig");const sandbox = @import("../permissions/sandbox.zig");const skill_runtime = @import("../skills/skill_runtime.zig");const types = @import("../shared/types.zig");const subagent_domain = @import("../subagent/domain.zig");const subagent_projection = @import("../subagent/ui_projection.zig");const file_index = @import("../workspace/file_index.zig");const activity_runtime = @import("../output/activity_runtime.zig");const transcript_presentation = @import("../output/transcript_presentation.zig");const event_loop = @import("../../ui/event_loop.zig");const surface_frame = @import("../../ui/footer/surface_frame.zig");const surface_invalidation = @import("../../ui/footer/surface_invalidation.zig");const interaction_state = @import("../../ui/footer/interaction_state.zig");const approval_prompt = @import("../permissions/approval_prompt.zig");const render_input = @import("../../ui/footer/render_input.zig");const input_presentation = @import("../../ui/footer/input_presentation.zig");const footer_viewport = @import("../../ui/footer/viewport.zig");const render_request = @import("../../ui/render_request.zig");const ui_input = @import("../../ui/input/runtime.zig");const input_visual_layout = @import("../../ui/input/visual_layout.zig");const registered_entities = @import("../input/registered_entities.zig");const approval_screen = @import("../../ui/approval_screen.zig");const models_screen = @import("../../ui/models_screen.zig");const resume_screen = @import("../../ui/resume_screen.zig");const skills_screen = @import("../../ui/skills_screen.zig");const help_screen = @import("../../ui/help_screen.zig");const settings_screen = @import("../../ui/settings_screen.zig");const full_transcript_screen = @import("../../ui/full_transcript_screen.zig");const render_engine = @import("../../ui/render_engine.zig");const build_checkpoint = @import("../../ui/render_engine/build_checkpoint.zig");const shell_runtime = @import("../../ui/shell_runtime.zig");const shimmer_runtime = @import("../../ui/transcript/shimmer_runtime.zig");const ui_subagents = @import("../../ui/subagent/controller.zig");const ui_terminal = @import("../../ui/terminal/terminal.zig");const vt_emulator = @import("../terminal/engine.zig");const transcript_painter = @import("../../ui/transcript/painter.zig");const command_output_runtime = @import("../../ui/transcript/command_output_runtime.zig");const resume_projection = @import("../../ui/transcript/resume_projection.zig");const transcript_runtime = @import("../../ui/transcript/runtime.zig");const ui_render = @import("../../ui/render.zig");const assistant_pacer = @import("../../ui/assistant/pacer.zig"); fn set_transcript_assistant_tail_writable(    runtime: *transcript_runtime.TranscriptRuntime,    writable: bool,) void {    if (runtime.transcript_release.assistant_tail_writable == writable) return;    runtime.transcript_release = runtime.transcript_release.with_assistant_tail_writable(writable);    debug_trace.logf(        "scroll",        "assistant tail writability changed writable={s}",        .{if (writable) "true" else "false"},    );} pub const VisualEpochResetTrigger = enum {    native_clear_probe,    ctrl_l,}; const FrameAttemptResult = struct {    shadow_state: render_engine.terminal_diff.ShadowCommitState,    animation_visible: bool,    yolo_warning_visible: bool = false,     fn is_committed(self: FrameAttemptResult) bool {        return self.shadow_state.is_committed();    }}; const InlineRenderReconciliation = struct {    alternate_screen_owns_rendering: bool = false,    terminal_transition: render_engine.terminal_diff.FrameTerminalTransition = .none,}; const PreparedChildConversation = struct {    runtime: transcript_runtime.TranscriptRuntime,    diff_entries: std.ArrayList(diff_mod.DiffEntry),    next_diff_id: u32,}; fn encodeSelectedChildDisplayName(    alloc: std.mem.Allocator,    raw_name: []const u8,) error{OutOfMemory}!text_utils.EncodedText {    return text_utils.encodeTerminalSafe(        alloc,        raw_name,        std.math.maxInt(usize),    );} /// Couples the physical terminal buffer with the logical presentation that/// owns retry invalidations. Ctrl-X keeps one terminal shadow with native/// primary/alternate buffers while main and child conversations retain/// independent render queues.const SurfaceFrameShell = struct {    output: *transcript_runtime.TranscriptRuntime,    invalidation_owner: *transcript_runtime.TranscriptRuntime,    shadow_vt: ?*vt_emulator.Grid,    committed_frame_layout: render_engine.frame_layout.CommittedLayoutSnapshot,    history_reset_uses_ris: bool,     fn init(        output: *transcript_runtime.TranscriptRuntime,        invalidation_owner: *transcript_runtime.TranscriptRuntime,        committed_frame_layout: render_engine.frame_layout.CommittedLayoutSnapshot,    ) SurfaceFrameShell {        return .{            .output = output,            .invalidation_owner = invalidation_owner,            .shadow_vt = output.shadow_vt,            .committed_frame_layout = committed_frame_layout,            .history_reset_uses_ris = output.history_reset_uses_ris,        };    }     pub fn frameSink(        self: *SurfaceFrameShell,    ) render_engine.terminal_diff.FrameSink {        return self.output.frameSink();    }     pub fn recordFrameInvalidation(        self: *SurfaceFrameShell,        range: render_engine.paint_plan.FrameInvalidationRange,    ) !void {        try self.invalidation_owner.recordFrameInvalidation(range);    }}; const RenderReconciliation = union(enum) {    inline_render: InlineRenderReconciliation,    file_approval_screen,    skills_screen,    models_screen,    resume_screen,    help_screen,    settings_screen,    frame_result: FrameAttemptResult,}; const QueuedCardProjection = struct {    cards: []render_input.QueuedPromptCard = &.{},    row_count: u16 = 0,    editor_active: bool = false,     fn deinit(self: *QueuedCardProjection, alloc: std.mem.Allocator) void {        for (self.cards) |card| alloc.free(card.bytes);        if (self.cards.len > 0) alloc.free(self.cards);        self.* = .{};    }}; fn composerInputAppearance(comptime App: type, app: *App) render_input.InputAppearance {    return if (app.shell.maxxing_mode == .minimal) .lines else app.input_runtime.input_appearance;} fn composerPrefixStyle(comptime App: type, app: *App) input_presentation.ComposerPrefixStyle {    return if (app.shell.maxxing_mode == .minimal) .rail else .arrow;} // Queued prompts stay collapsed behind their summary row until the review is// opened; only then does the banner expand into one card per queued prompt.fn buildQueuedCardProjection(comptime App: type, app: *App) !QueuedCardProjection {    if (comptime !@hasField(App, "queued_prompt_review")) return .{};    const review_entries = app.queued_prompt_review.entries;    if (!app.queued_prompt_review.visible or        !app.queued_prompt_review.active() or        review_entries.len == 0) return .{};    const draft_count = review_entries.len;     const cards = try app.alloc.alloc(render_input.QueuedPromptCard, draft_count);    var built: usize = 0;    errdefer {        for (cards[0..built]) |card| app.alloc.free(card.bytes);        app.alloc.free(cards);    }     const selected_turn_id: ?u64 = blk: {        const selected_index = app.queued_prompt_review.selected_index orelse break :blk null;        if (selected_index >= review_entries.len) break :blk null;        break :blk review_entries[selected_index].draft.turn_id;    };     var total_rows: u16 = 0;    var editor_active = false;    while (built < draft_count) : (built += 1) {        const draft = review_entries[built].draft;        const editing = selected_turn_id != null and draft.turn_id == selected_turn_id.?;        if (editing) {            const source = input_visual_layout.Source{                .input = app.input_runtime.edit_state.input.items,                .cursor = app.input_runtime.edit_state.cursor,                .terminal_cols = app.shell.layout.cols,                .images = app.pending_images.items,                .pasted_blocks = app.input_runtime.entities.pasted_blocks.items,                .image_tokens = app.input_runtime.entities.image_tokens.items,                .skill_tokens = app.input_runtime.entities.skill_tokens.items,            };            const summary = input_visual_layout.summarize(source, null);            const row_count: u16 = @intCast(@min(summary.total_rows, std.math.maxInt(u16)));            cards[built] = .{                .bytes = try app.alloc.dupe(u8, ""),                .row_count = @max(row_count, 1),                .editing = true,            };            total_rows +|= cards[built].row_count;            editor_active = true;            continue;        }         const review_input = draft.reviewInput();        const review_skill_spans = draft.reviewSkillDisplaySpans();        var skill_tokens: []registered_entities.SkillTokenSpan = if (review_skill_spans.len > 0)            try app.alloc.alloc(registered_entities.SkillTokenSpan, review_skill_spans.len)        else            &.{};        defer if (skill_tokens.len > 0) app.alloc.free(skill_tokens);        for (review_skill_spans, 0..) |span, i| {            skill_tokens[i] = .{                .raw_start = span.raw_start,                .raw_end = span.raw_end,                .name = span.name,                .path = span.path,                .display_source = span.display_source,                .owns_trailing_separator = span.owns_trailing_separator,            };        }         const bytes = try input_presentation.composeQueuedPromptCard(            app.alloc,            .{                .input = review_input,                .cursor = 0,                .terminal_cols = app.shell.layout.cols,                .images = draft.images,                .pasted_blocks = review_entries[built].pasted_blocks.items,                .image_tokens = review_entries[built].image_tokens.items,                .skill_tokens = skill_tokens,            },            composerInputAppearance(App, app),            composerPrefixStyle(App, app),        );        const row_count: u16 = @intCast(@min(            std.mem.count(u8, bytes, "\n"),            std.math.maxInt(u16),        ));        cards[built] = .{ .bytes = bytes, .row_count = row_count };        total_rows +|= row_count;    }     return .{ .cards = cards, .row_count = total_rows, .editor_active = editor_active };} noinline fn approvalScreenNeedsClear(    screen_active: bool,    request: permission_request.PermissionRequest,    layout: types.Layout,    commit: ?interaction_state.ApprovalScreenCommit,) bool {    if (!screen_active) return true;    const prior = commit orelse return true;    return prior.request_id != request.id or        prior.rows != layout.rows or        prior.cols != layout.cols;} pub fn Runtime(comptime App: type) type {    return struct {        fn appendDeferredHistoryForVisualEpoch(            ctx: *anyopaque,            finished: *const types.FinishedPrompt,        ) anyerror!assistant_pacer.DeferredFinishCommit {            const app: *App = @ptrCast(@alignCast(ctx));            return switch (try app_session_runtime.Runtime(App).appendFinishedPromptForVisualEpoch(                app,                finished.*,            )) {                .uncommitted => .uncommitted,                .committed, .committed_degraded => .committed,            };        }         pub fn resetVisualEpoch(            app: *App,            trigger: VisualEpochResetTrigger,        ) !bool {            const welcome = try ui_render.welcomeMessage(app.alloc);            defer app.alloc.free(welcome);             const committed = try app.pacer.discardDeferredPresentationForVisualEpoch(                app.alloc,                .{                    .ctx = app,                    .append_history = appendDeferredHistoryForVisualEpoch,                },            );            if (!committed) {                switch (trigger) {                    .native_clear_probe => debug_trace.logf(                        "native_clear",                        "native_clear_recovery_skipped reason=history_uncommitted",                        .{},                    ),                    .ctrl_l => debug_trace.logf(                        "render",                        "visual_epoch_reset_skipped trigger=ctrl_l reason=history_uncommitted",                        .{},                    ),                }                return false;            }             try app.shell.resetVisualEpoch(app.alloc, welcome);            try app.shell.requestTerminalReset(&app.metrics);            switch (trigger) {                .native_clear_probe => debug_trace.logf(                    "native_clear",                    "native_clear_recovery_requested",                    .{},                ),                .ctrl_l => debug_trace.logf(                    "render",                    "visual_epoch_reset_requested trigger=ctrl_l",                    .{},                ),            }            return true;        }         pub fn applyThemeUpdate(app: *App, light: bool, rgb: ?ui_render.TerminalRgb) !void {            if (!ui_render.themeNeedsUpdate(light, rgb)) return;             const prior_light = ui_render.is_light;            app.shell.retintEntriesForTheme(app.alloc, prior_light, light) catch |err| {                debug_trace.logf("theme", "theme_transcript_retint_failed err={s}", .{@errorName(err)});                return;            };            app.pacer.rethemeInlineCode(light);            ui_render.initTheme(light, rgb);            app.shell.setCommandOutputRenderPolicy(shellStyles());            try app.shell.requestTerminalReset(&app.metrics);            app.shell.render_requests.request(.transcript);        }         pub fn shellStyles() transcript_runtime.Styles {            return .{                .system_notice_label_style = ui_render.system_notice_label_style,                .system_notice_text_style = ui_render.system_notice_text_style,                .reset_style = ui_render.reset_style,                .dim_style = ui_render.dim_style,                .red_style = ui_render.red_style,                .cancelled_text_style = ui_render.hint_style,                .notice_information_style = ui_render.system_notice_label_style,                .notice_success_style = ui_render.green_style,                .notice_warning_style = ui_render.warning_style,                .notice_error_style = ui_render.red_style,                .notice_cancelled_style = ui_render.dim_style,                .code_highlight_theme = if (ui_render.is_light) .light else .dark,            };        }         var model_completions_buf: [32][]const u8 = undefined;        var effort_picker_values_buf: [types.ReasoningEffort.max_options + 1]types.ReasoningEffort = undefined;        var effort_picker_labels_buf: [types.ReasoningEffort.max_options + 1][]const u8 = undefined;        var fast_picker_labels_buf: [2][]const u8 = undefined;        var file_completions_buf: [input_completion_runtime.file_picker_completion_cap]file_index.SearchResult = undefined;        var file_match_spans_buf: [input_completion_runtime.file_picker_completion_cap * file_index.max_path_len]file_index.MatchSpan = undefined;        var file_path_storage_buf: [input_completion_runtime.file_picker_path_storage_cap]u8 = undefined;        noinline fn footerContext(            app: *App,            upgrade_status_buf: *[64]u8,            shimmer_pos: i16,            queued_cards: *const QueuedCardProjection,        ) render_input.RenderContext {            const queue_preview = app.worker.queuePreview();             const model_query = app.input_runtime.picker.activeModelPickerQuery(&app.input_runtime.edit_state);            const pending_model = if (app.input_runtime.picker.hasPendingModelPickerSelection()) app.input_runtime.picker.model_picker_pending_model.items else null;            var model_picker_stage: picker_state.ModelPickerStage = .model;            var picker_items: []const []const u8 = &.{};            var picker_index: usize = 0;            var picker_window_start: usize = 0;            var picker_anchor: usize = 0;             if (model_query) |picker_query| {                model_picker_stage = picker_query.stage;                picker_anchor = picker_query.token_start;                switch (picker_query.stage) {                    .model => {                        const count = input_completion_runtime.CompletionRuntime(App).modelPickerCompletions(app, picker_query.query, &model_completions_buf);                        picker_items = model_completions_buf[0..count];                        picker_index = input_completion_runtime.CompletionRuntime(App).modelPickerIndex(app, picker_items);                        picker_window_start = input_completion_runtime.CompletionRuntime(App).modelPickerWindowStart(app, count, picker_index);                    },                    .effort => {                        const target = if (app.input_runtime.picker.hasPendingModelPickerSelection()) app.input_runtime.picker.model_picker_pending_model.items else app.selected_model.items;                        const capabilities = model_capabilities.resolveForApp(App, app, target);                        const effort_count = model_capabilities.reasoningEffortOptionCount(capabilities);                        for (0..effort_count) |i| {                            effort_picker_values_buf[i] = model_capabilities.reasoningEffortAtIndex(capabilities, i);                            effort_picker_labels_buf[i] = effort_picker_values_buf[i].displayLabel();                        }                        const count = picker_state.filterCompletionLabels(picker_query.query, effort_picker_labels_buf[0..effort_count], effort_picker_labels_buf[0..]);                        picker_items = effort_picker_labels_buf[0..count];                        picker_index = app.input_runtime.picker.model_picker_effort_index;                        picker_window_start = app.input_runtime.picker.model_picker_effort_window_start;                    },                    .fast => {                        for (picker_state.model_picker_fast_options, 0..) |option, i| fast_picker_labels_buf[i] = option;                        const count = picker_state.filterCompletionLabels(picker_query.query, fast_picker_labels_buf[0..], fast_picker_labels_buf[0..]);                        picker_items = fast_picker_labels_buf[0..count];                        picker_index = app.input_runtime.picker.model_picker_fast_index;                        picker_window_start = app.input_runtime.picker.model_picker_fast_window_start;                    },                }            }             const file_query = if (model_query == null) app.input_runtime.picker.activeFilePickerQuery(&app.input_runtime.edit_state) else null;            var file_items: []const file_index.SearchResult = &.{};            var file_anchor: usize = 0;            var file_selection_index: usize = 0;            if (file_query) |fq| {                file_anchor = fq.at_offset;                const count = app.fileCompletions(fq.query, &file_completions_buf, &file_match_spans_buf, &file_path_storage_buf) catch |err| failed: {                    debug_trace.logf("render", "file picker render search failed err={s}", .{@errorName(err)});                    break :failed 0;                };                file_items = file_completions_buf[0..count];                file_selection_index = app.input_runtime.picker.file_completion_index;            }            const inline_completion =                input_completion_runtime.CompletionRuntime(App).visibleInlineCompletion(app);             const visible_model = pending_model orelse app.selected_model.items;            const visible_capabilities = model_capabilities.resolveForApp(App, app, visible_model);            const model_supports_fast = visible_capabilities.supports_fast_mode;            const model_supports_effort = visible_capabilities.reasoning_efforts.len > 0;            const visible_effort = if (pending_model != null and model_supports_effort)                pendingPickerEffort(app, visible_model, model_query, app.input_runtime.picker.model_picker_effort_index)            else if (model_capabilities.reasoningEffortSupported(visible_capabilities, app.effort))                app.effort            else                .auto;            const visible_fast_mode = if (pending_model != null and model_supports_fast)                pendingPickerFastMode(model_query, app.input_runtime.picker.model_picker_fast_index)            else                app.fast_mode;             const upgrade_label = app.upgrader.statusLabel(upgrade_status_buf);            const yolo_warning_active =                if (comptime @hasField(App, "permission_state") and                @hasField(App, "permission_engine"))                    app_permission_runtime.Runtime(App).yoloWarningActive(app)                else                    false;            const settings_snapshot = app_commands.settingsCatalogSnapshot(app);            const now_ms = io_mod.milliTimestamp();            var visible_stream = app.stream;            if (!visible_stream.active) {                if (app.pacer.completedAssistantPresentationTokenProgress()) |progress| {                    visible_stream.token_progress = progress;                }            }             return .{                .slash_registry = app.slashRegistry(),                .stream = visible_stream,                .completed_assistant_presentation_tail = app.pacer.hasCompletedAssistantPresentationTail(),                .writing_response = app.pacer.hasPending(),                .has_api_key = app.auth.credentialSource() != null,                .model = visible_model,                .pending_images = app.pending_images.items,                .input_appearance = app.input_runtime.input_appearance,                .maxxing_mode = app.shell.maxxing_mode,                .permission_mode = if (comptime @hasField(App, "permission_engine"))                    app.permission_engine.mode                else                    .ask,

Lines 1–500 of 6411.