1052 lines · 41.5 KB · Zig
RawDownload
const std = @import("std");const input_action = @import("../input/input_action.zig");const core_input_runtime = @import("../input/runtime.zig");const ui_input = @import("../../ui/input/runtime.zig");const input_visual_layout = @import("../../ui/input/visual_layout.zig");const shell_runtime = @import("../../ui/shell_runtime.zig");const transcript_runtime = @import("../../ui/transcript/runtime.zig");const app_render_runtime = @import("app_render_runtime.zig");const app_terminal_runtime = @import("app_terminal_runtime.zig");const input_selection_runtime = @import("input_selection_runtime.zig");const app_session_runtime = @import("app_session_runtime.zig");const command_specs = @import("../slash_commands/command_specs.zig");const debug_trace = @import("../shared/debug_trace.zig");const skill_runtime = @import("../skills/skill_runtime.zig");const types = @import("../shared/types.zig");const subagent_domain = @import("../subagent/domain.zig");const subagent_input = @import("../subagent/input_action.zig");const catalog_screen_layout = @import("../../ui/catalog_screen_layout.zig");const input_presentation = @import("../../ui/footer/input_presentation.zig");const model_menu_presentation = @import("../../ui/footer/model_menu_presentation.zig");const skills_menu_presentation = @import("../../ui/footer/skills_menu_presentation.zig");const render_input = @import("../../ui/footer/render_input.zig"); pub fn SubagentRuntime(comptime App: type) type {    return struct {        pub fn routeSubagentEscapeAction(            app: *App,            action: input_action.Action,            shortcut: ?input_action.ShortcutAction,            typed_action: ?subagent_input.Action,        ) !void {            if (comptime !@hasDecl(@TypeOf(app.subagents), "handleAction")) return;            app.input_runtime.vertical_navigation.reset();            if (comptime @hasField(App, "model_cache") and                @hasDecl(@TypeOf(app.subagents), "childRouteId") and                @hasDecl(@TypeOf(app.subagents), "childPresentationView"))            {                if (childSkillsMenuActive(app)) {                    switch (action) {                        .escape => {                            closeChildSkillsMenu(app);                            try childChangedAndRedraw(app);                        },                        .history_up, .cursor_up => moveChildSkillsMenu(app, -1),                        .history_down, .cursor_down => moveChildSkillsMenu(app, 1),                        .cursor_left,                        .cursor_right,                        .home,                        .end,                        .word_left,                        .word_right,                        .delete_next,                        .delete_word_left,                        .delete_word_right,                        .delete_to_line_start,                        .delete_to_line_end,                        .clear_line,                        => try editChildSkillsQuery(app, action),                        else => {},                    }                    return;                }                if (childModelMenuActive(app)) {                    switch (action) {                        .escape => {                            closeChildModelMenu(app);                            try childChangedAndRedraw(app);                        },                        .history_up, .cursor_up => moveChildModelMenu(app, -1),                        .history_down, .cursor_down => moveChildModelMenu(app, 1),                        .cursor_left,                        .cursor_right,                        .home,                        .end,                        .word_left,                        .word_right,                        .delete_next,                        .delete_word_left,                        .delete_word_right,                        .delete_to_line_start,                        .delete_to_line_end,                        .clear_line,                        => try editChildModelQuery(app, action),                        else => {},                    }                    return;                }            }            if (shortcut) |composer_action| {                if (try routeChildComposerShortcut(app, composer_action)) return;            }            if (typed_action) |resolved| {                try handleSubagentAction(app, resolved);                return;            }            if (action == .mouse_pointer) {                _ = routeChildComposerPointerAction(app, action.mouse_pointer);            }        }         fn routeChildComposerShortcut(            app: *App,            shortcut: input_action.ShortcutAction,        ) !bool {            if (comptime !@hasDecl(@TypeOf(app.subagents), "childComposerFocused")) {                return false;            }            if (!app.subagents.childComposerFocused()) return false;             switch (shortcut) {                .move => |intent| {                    const child_shell = app.subagents.childConversationRuntime() orelse return false;                    if (!app.subagents.moveChildInputCursor(                        intent,                        child_shell.layout.cols,                        input_presentation.inputRowLimit(child_shell.layout.content_bottom),                    )) return false;                },                .select_all => {                    if (comptime !@hasDecl(                        @TypeOf(app.subagents),                        "childComposerEditor",                    )) return false;                    const editor = app.subagents.childComposerEditor() orelse return false;                    _ = editor.selectionState().selectAll();                },                .copy_selection => {                    if (comptime @hasDecl(App, "clipboard")) {                        const view = app.subagents.childPresentationView() orelse return false;                        if (input_selection_runtime.copySelection(                            &view.editor.edit_state,                            app.clipboard(),                        ) == .copy_failed) {                            debug_trace.logf(                                "subagent",                                "child selection copy preserved reason=clipboard_copy_failed",                                .{},                            );                        }                    }                },                .cut_selection => {                    if (comptime @hasDecl(App, "clipboard")) {                        const editor = app.subagents.childComposerEditor() orelse return false;                        switch (input_selection_runtime.cutSelection(                            app.alloc,                            editor.selectionState(),                            null,                            app.clipboard(),                        )) {                            .cut => app.subagents.commitChildEditorEdit(app.alloc),                            .copy_failed => debug_trace.logf(                                "subagent",                                "child selection cut preserved reason=clipboard_copy_failed",                                .{},                            ),                            .delete_failed => debug_trace.logf(                                "subagent",                                "child selection cut delete skipped reason=delete_failed",                                .{},                            ),                            .inactive, .copied => {},                        }                    }                },                .delete_forward => try handleSubagentAction(app, .delete_next),                .delete_word_left => try handleSubagentAction(app, .delete_word_left),                .delete_whitespace_word_left => {                    const editor = app.subagents.childComposerEditor() orelse return false;                    if (try editor.killRingState(null).delete(                        app.alloc,                        .whitespace_word_left,                    )) {                        app.subagents.commitChildEditorEdit(app.alloc);                    }                },                .delete_word_right => try handleSubagentAction(app, .delete_word_right),                .delete_to_line_start => try handleSubagentAction(app, .delete_to_line_start),                .delete_to_line_end => try handleSubagentAction(app, .delete_to_line_end),                .insert_newline => try handleSubagentAction(app, .insert_newline),                .undo => {                    const editor = app.subagents.childComposerEditor() orelse return false;                    if (try editor.undoState().undo(app.alloc)) {                        app.subagents.commitChildEditorEdit(app.alloc);                    }                },                .redo => {                    const editor = app.subagents.childComposerEditor() orelse return false;                    if (try editor.undoState().redo(app.alloc)) {                        app.subagents.commitChildEditorEdit(app.alloc);                    }                },                .yank => {                    const editor = app.subagents.childComposerEditor() orelse return false;                    switch (try editor.killRingState(null).yank(                        app.alloc,                        1,                        subagent_domain.max_message_bytes,                    )) {                        .inserted => app.subagents.commitChildEditorEdit(app.alloc),                        .inactive, .limit_exceeded => {},                    }                },                .delete_backward,                .history_previous,                .history_next,                .redraw,                => return false,            }            app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                app,                .subagent_panel,            );            return true;        }         fn routeChildComposerPointerAction(            app: *App,            pointer: input_action.MousePointer,        ) bool {            if (pointer.shift) return false;            if (comptime !@hasDecl(                @TypeOf(app.subagents),                "childComposerEditor",            )) return false;            const editor = app.subagents.childComposerEditor() orelse return false;            const child_shell = app.subagents.childConversationRuntime() orelse return false;            if (!child_shell.footer_viewport.has_frame) return false;             const prefix_cells: u16 = @intCast(input_visual_layout.inputPrefix(0).cell_width);            const position = child_shell.footer_viewport.geometry.inputPointerPosition(                pointer.row,                pointer.column,                prefix_cells,            ) orelse {                if (pointer.kind == .release and                    editor.edit_state.selection_anchor != null)                {                    editor.selectionState().finish();                    requestChildFooterFrame(app);                    return true;                }                return false;            };            const point = input_visual_layout.cursorPointAtPosition(.{                .input = editor.edit_state.input.items,                .cursor = editor.edit_state.cursor,                .terminal_cols = child_shell.layout.cols,                .pasted_blocks = editor.entities.pasted_blocks.items,                .skill_tokens = editor.entities.skill_tokens.items,            }, position.row_index, position.content_column) orelse return false;             const changed = switch (pointer.kind) {                .press => editor.selectionState().begin(point.raw_offset),                .drag => editor.selectionState().extend(point.raw_offset),                .release => blk: {                    if (editor.edit_state.selection_anchor == null) break :blk false;                    _ = editor.selectionState().extend(point.raw_offset);                    editor.selectionState().finish();                    break :blk true;                },            };            if (changed) requestChildFooterFrame(app);            return changed;        }         fn requestChildFooterFrame(app: *App) void {            app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                app,                .footer,            );        }         pub fn handleSubagentRawInput(            app: *App,            raw: input_action.RawTerminalInput,        ) !void {            const byte = raw.byte;            if (comptime @hasField(App, "model_cache") and                @hasDecl(@TypeOf(app.subagents), "childRouteId") and                @hasDecl(@TypeOf(app.subagents), "childPresentationView"))            {                if (childSkillsMenuActive(app) and                    try handleChildSkillsMenuByte(app, byte))                {                    return;                }                if (childModelMenuActive(app) and                    try handleChildModelMenuByte(app, byte))                {                    return;                }            }            if (raw.composer_shortcut) |shortcut| {                if (try routeChildComposerShortcut(app, shortcut)) return;            }            if (raw.subagent_action) |action| {                try handleSubagentAction(app, action);                return;            }            if (raw.composer_shortcut != null) return;            const failure_before = if (comptime @hasDecl(                @TypeOf(app.subagents),                "childPresentationView",            ))                if (app.subagents.childPresentationView()) |view|                    view.input_failure                else                    null            else                null;            const result = if (comptime @hasDecl(@TypeOf(app.subagents), "handleKeyWithMainApproval"))                try app.subagents.handleKeyWithMainApproval(app.alloc, byte, mainApprovalId(app))            else                try app.subagents.handleKey(app.alloc, byte);            if (comptime @hasDecl(                @TypeOf(app.subagents),                "invalidateChildConversationProjection",            )) {                const failure_after = if (app.subagents.childPresentationView()) |view|                    view.input_failure                else                    null;                if (!std.meta.eql(failure_before, failure_after)) {                    app.subagents.invalidateChildConversationProjection(app.alloc);                }            }            switch (result) {                .acknowledge => try acknowledgeAndRedraw(app),                .none => {},                .exit_app => exitApp(app),                .close_manager => try closeSubagentManager(app),                .page_changed => try refreshPageAndRedraw(app),                .child_changed => try childChangedAndRedraw(app),                .load_older_history => try loadOlderHistoryAndRedraw(app),                .refresh_newest_history => try refreshNewestHistoryAndRedraw(app),                .submit_child_message => try submitChildMessageAndRedraw(app),                .load_attach_candidates => try loadAttachCandidatesAndRedraw(app, false),                .load_more_attach_candidates => try loadAttachCandidatesAndRedraw(app, true),                .submit_manager_mutation => try submitManagerMutationAndRedraw(app),                .resolve_child_approval => try resolveChildApprovalAndRedraw(app),                .open_terminal => try openSelectedTerminal(app),                .redraw => try redraw(app),            }        }         fn handleSubagentAction(app: *App, action: subagent_input.Action) !void {            const result = if (comptime @hasDecl(@TypeOf(app.subagents), "handleActionWithMainApproval"))                try app.subagents.handleActionWithMainApproval(app.alloc, action, mainApprovalId(app))            else                try app.subagents.handleAction(app.alloc, action);            switch (result) {                .acknowledge => try acknowledgeAndRedraw(app),                .none => {},                .exit_app => exitApp(app),                .close_manager => try closeSubagentManager(app),                .page_changed => try refreshPageAndRedraw(app),                .child_changed => try childChangedAndRedraw(app),                .load_older_history => try loadOlderHistoryAndRedraw(app),                .refresh_newest_history => try refreshNewestHistoryAndRedraw(app),                .submit_child_message => try submitChildMessageAndRedraw(app),                .load_attach_candidates => try loadAttachCandidatesAndRedraw(app, false),                .load_more_attach_candidates => try loadAttachCandidatesAndRedraw(app, true),                .submit_manager_mutation => try submitManagerMutationAndRedraw(app),                .resolve_child_approval => try resolveChildApprovalAndRedraw(app),                .open_terminal => try openSelectedTerminal(app),                .redraw => try redraw(app),            }        }         fn openSelectedTerminal(app: *App) !void {            requestSelectedTerminalOpen(app);            try redraw(app);        }         fn requestSelectedTerminalOpen(app: *App) void {            if (comptime @hasDecl(@TypeOf(app.subagents), "selectedTerminalId") and                @hasDecl(App, "requestTerminalOpen"))            {                const session_id = app.subagents.selectedTerminalId() orelse return;                switch (app.requestTerminalOpen(session_id)) {                    .accepted, .occupied, .rejected => {},                }            }        }         fn exitApp(app: *App) void {            app_session_runtime.Runtime(App).requestResumeHandoff(app);            app.should_exit = true;        }         fn redraw(app: *App) !void {            if (app.subagents.isViewActive()) {                app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                    app,                    .subagent_panel,                );                app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                    app,                    .footer,                );            } else {                try shell_runtime.requestRedraw(&app.shell, &app.metrics, .replay_viewport);            }        }         fn refreshPageAndRedraw(app: *App) !void {            if (comptime @hasDecl(@TypeOf(app.subagents), "pageCursor")) {                try app_render_runtime.Runtime(App).refreshSubagentManager(app, true);            }            app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                app,                .subagent_panel,            );        }         fn acknowledgeAndRedraw(app: *App) !void {            if (comptime @hasDecl(App, "acknowledgeSubagentManagerSelection")) {                try app.acknowledgeSubagentManagerSelection();            }            app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                app,                .subagent_panel,            );        }         fn childChangedAndRedraw(app: *App) !void {            if (comptime !@hasDecl(@TypeOf(app.subagents), "childRouteId")) {                app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                    app,                    .subagent_panel,                );                return;            }            if (app.subagents.childRouteId() != null) {                try app_render_runtime.Runtime(App).refreshChildChat(                    app,                    null,                    false,                    true,                );            }            app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                app,                .subagent_panel,            );        }         fn loadOlderHistoryAndRedraw(app: *App) !void {            if (comptime !@hasDecl(@TypeOf(app.subagents), "olderHistoryCursor")) {                app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                    app,                    .subagent_panel,                );                return;            }            const cursor = app.subagents.olderHistoryCursor() orelse return;            try app_render_runtime.Runtime(App).refreshChildChat(                app,                cursor,                true,                false,            );            app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                app,                .subagent_panel,            );        }         fn refreshNewestHistoryAndRedraw(app: *App) !void {            if (comptime !@hasDecl(@TypeOf(app.subagents), "childRouteId")) {                app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                    app,                    .subagent_panel,                );                return;            }            try app_render_runtime.Runtime(App).refreshChildChat(                app,                null,                false,                true,            );            app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                app,                .subagent_panel,            );        }         fn submitChildMessageAndRedraw(app: *App) !void {            if (comptime !@hasDecl(@TypeOf(app.subagents), "prepareSubmission")) {                app_render_runtime.Runtime(App).requestSubagentSurfaceFrame(                    app,                    .subagent_panel,                );                return;            }            if (comptime @hasDecl(@TypeOf(app.subagents), "childPresentationView") and                @hasDecl(App, "slashRegistry"))            {

Lines 1–500 of 1052.