2398 lines · 95.5 KB · Zig
RawDownload
const std = @import("std");const io_mod = @import("../shared/io.zig");const agent_steps = @import("../config/agent_steps.zig");const config_runtime = @import("../config/config_runtime.zig");const auth_runtime = @import("../auth/auth_runtime.zig");const credentials = @import("../auth/credentials.zig");const host = @import("../hosts/host.zig");const oauth_transport = @import("../auth/oauth_transport.zig");const input_appearance = @import("../config/input_appearance.zig");const model_capabilities = @import("../config/model_capabilities.zig");const debug_trace = @import("../shared/debug_trace.zig");const record_tape = @import("../workspace/record_tape.zig");const workspace_access = @import("../workspace/workspace_access.zig");const update_target = @import("../upgrade/update_target.zig");const sandbox = @import("../permissions/sandbox.zig");const notification_sound = @import("../notifications/sound.zig");const tool_result_limits = @import("../tooling/tool_result_limits.zig");const types = @import("../shared/types.zig");const ui_render = @import("../../ui/render.zig");const presentation_mode = @import("../config/presentation_mode.zig");const transcript_presentation = @import("../output/transcript_presentation.zig");const shell_runtime = @import("../../ui/shell_runtime.zig");const ui_terminal = @import("../../ui/terminal/terminal.zig");const terminal_diff = @import("../../ui/render_engine/terminal_diff.zig");const transcript_runtime = @import("../../ui/transcript/runtime.zig"); const Allocator = std.mem.Allocator;const Metrics = types.Metrics;const PermissionMode = types.PermissionMode;const CursorPosition = shell_runtime.CursorPosition;const TerminalState = shell_runtime.TerminalState;const TranscriptRuntime = transcript_runtime.TranscriptRuntime;pub const ResizeHandler = shell_runtime.ResizeHandler;pub const default_permission_mode = config_runtime.default_permission_mode; /// Compile-time terminal restoration for one async-signal-safe `write(2)` call./// Resets terminal modes and ends with a newline before the next shell prompt.const abnormal_exit_restore_prefix = "\x1b[?2026l\x1b[?1000l\x1b[?1002l\x1b[?1004l\x1b[?1006l\x1b[?1l\x1b>\x1b[?1049l\x1b[?7h\x1b[4l\x1b[?6l\x1b[0m\x1b[?25h\x1b[?2031l\x1b[?2004l";const abnormal_exit_restore = abnormal_exit_restore_prefix ++ "\x1b[<u\x1b[>4;0m\n";const tmux_abnormal_exit_restore = abnormal_exit_restore_prefix ++ "\x1b[>4;0m\n";const normal_exit_restore_prefix = ui_terminal.theme_notification_disable_sequence ++ "\x1b[?2026l\x1b[?1000l\x1b[?1002l\x1b[?1004l\x1b[?1006l\x1b[?1l\x1b>\x1b[4l\x1b[?6l\x1b[?2004l";const normal_exit_restore = normal_exit_restore_prefix ++ "\x1b[<u\x1b[>4;0m";const tmux_normal_exit_restore = normal_exit_restore_prefix ++ "\x1b[>4;0m";const alternate_screen_enter = "\x1b[?1049h";const alternate_mouse_tracking_enter = "\x1b[?1000h\x1b[?1006h";const terminal_takeover_reset = "\x1b[?2026l\x1b[?1000l\x1b[?1002l\x1b[?1004l\x1b[?1006l\x1b[?1l\x1b>\x1b[?2004l\x1b[<u\x1b[>4;0m\x1b[4l\x1b[?6l\x1b[?7h\x1b[0m\x1b[?25h"; /// Original handlers, written at bootstrap and restored at shutdown./// Signal context never mutates them.var old_sigterm_action: ?std.posix.Sigaction = null;var old_sighup_action: ?std.posix.Sigaction = null; /// Restores terminal state, installs the default disposition, and re-raises./// Must remain async-signal-safe: only `write(2)`, `sigaction(2)`, and `raise(3)`.fn abnormalExitHandlerWithRestore(comptime restore: []const u8, sig: std.posix.SIG) void {    // Signal context cannot recover from a failed async-signal-safe write.    _ = std.c.write(        std.posix.STDOUT_FILENO,        restore.ptr,        restore.len,    );     const default_action: std.posix.Sigaction = .{        .handler = .{ .handler = std.posix.SIG.DFL },        .mask = std.posix.sigemptyset(),        .flags = 0,    };    std.posix.sigaction(sig, &default_action, null);    _ = std.c.raise(sig);} fn abnormalExitHandler(sig: std.posix.SIG) callconv(.c) void {    abnormalExitHandlerWithRestore(abnormal_exit_restore, sig);} fn tmuxAbnormalExitHandler(sig: std.posix.SIG) callconv(.c) void {    abnormalExitHandlerWithRestore(tmux_abnormal_exit_restore, sig);} /// Install handlers for SIGTERM and SIGHUP so an externally-terminated/// fx restores terminal state before dying. SIGINT is not included/// because raw mode disables terminal-generated SIGINT.pub fn installAbnormalExitHandlers(tmux: ?[]const u8) void {    if (!shell_runtime.supports_resize_signal) return;     const handler: std.posix.Sigaction.handler_fn = if (tmux == null)        abnormalExitHandler    else        tmuxAbnormalExitHandler;    const act: std.posix.Sigaction = .{        .handler = .{ .handler = handler },        .mask = std.posix.sigemptyset(),        .flags = 0,    };     var old_term: std.posix.Sigaction = undefined;    std.posix.sigaction(std.posix.SIG.TERM, &act, &old_term);    old_sigterm_action = old_term;     var old_hup: std.posix.Sigaction = undefined;    std.posix.sigaction(std.posix.SIG.HUP, &act, &old_hup);    old_sighup_action = old_hup;} pub fn uninstallAbnormalExitHandlers() void {    if (!shell_runtime.supports_resize_signal) return;     if (old_sigterm_action) |old| {        std.posix.sigaction(std.posix.SIG.TERM, &old, null);        old_sigterm_action = null;    }    if (old_sighup_action) |old| {        std.posix.sigaction(std.posix.SIG.HUP, &old, null);        old_sighup_action = null;    }} pub const StartupState = struct {    workspace_root: []u8 = &.{},    workspace_access: workspace_access.WorkspaceAccess = .{},    credential: ?credentials.Credential = null,    credential_onboarding_skipped: bool = false,    stored_key_status: credentials.StoredKeyReadStatus = .not_attempted,    selected_model: []u8 = &.{},    configured_model: []u8 = &.{},    model_source: config_runtime.ModelSource = .compiled_default,    permission_mode: PermissionMode = default_permission_mode,    yolo_acknowledged: bool = false,    permission_rules: types.PermissionRuleSet = .{},    agent_step_limit: usize,    max_tool_result_bytes: usize = tool_result_limits.default_max_tool_result_bytes,    context_limits: config_runtime.context_limits.Values = .{},    context_enabled: bool = true,    fast_mode: bool = false,    input_appearance: input_appearance.InputAppearance = .default,    maxxing_mode: presentation_mode.MaxxingMode = presentation_mode.MaxxingMode.default,    slash_menu_categories: bool = true,    auto_upgrade: bool = true,    update_channel: update_target.Channel = .stable,    startup_scrollback: bool = true,    prompt_history_enabled: bool = true,    prompt_history_store_allowed: bool = true,    config_diagnostics: []config_runtime.ConfigDiagnostic = &.{},    effort: types.ReasoningEffort = .auto,    first_call_tool_choice: types.ToolChoice = .auto,    sandbox_backend: sandbox.BackendKind = .none,    statusline_sandbox: bool = false,    statusline_context: bool = false,    statusline_session: bool = false,    notification_turn_end: bool = false,    notification_attention_required: bool = false,    notification_max: bool = false,    theme_monitor_enabled: bool = false,     pub fn deinit(self: *StartupState, alloc: Allocator) void {        self.workspace_access.deinit(alloc);        if (self.workspace_root.len > 0) alloc.free(self.workspace_root);        if (self.credential) |*credential| credential.deinit(alloc);        if (self.selected_model.len > 0) alloc.free(self.selected_model);        if (self.configured_model.len > 0) alloc.free(self.configured_model);        self.permission_rules.deinit(alloc);        if (self.config_diagnostics.len > 0) {            for (self.config_diagnostics) |*diagnostic| diagnostic.deinit(alloc);            alloc.free(self.config_diagnostics);        }        self.* = .{ .agent_step_limit = self.agent_step_limit };    }     pub fn takeWorkspaceRoot(self: *StartupState) []u8 {        const value = self.workspace_root;        self.workspace_root = &.{};        return value;    }     pub fn takeWorkspaceAccess(self: *StartupState) workspace_access.WorkspaceAccess {        const value = self.workspace_access;        self.workspace_access = .{};        return value;    }     pub fn apiKey(self: *const StartupState) ?[]const u8 {        const credential = self.credential orelse return null;        if (credential.needsRefreshAt(io_mod.milliTimestamp())) return null;        return credential.token;    }     pub fn modelCatalogAccess(self: *const StartupState) credentials.CatalogAccess {        return credentials.catalogAccessAt(self.credential, io_mod.milliTimestamp());    }     pub fn gatewayTeam(self: *const StartupState) ?[]const u8 {        const credential = self.credential orelse return null;        if (credential.needsRefreshAt(io_mod.milliTimestamp())) return null;        return credential.gatewayTeam();    }     pub fn takeCredential(self: *StartupState) ?credentials.Credential {        const value = self.credential;        self.credential = null;        return value;    }     pub fn takeSelectedModel(self: *StartupState) []u8 {        const value = self.selected_model;        self.selected_model = &.{};        return value;    }     pub fn takePermissionRules(self: *StartupState) types.PermissionRuleSet {        const value = self.permission_rules;        self.permission_rules = .{};        return value;    }}; pub const StartupStatus = struct {    workspace_root: []u8,    selected_model: []const u8,    owned_selected_model: ?[]u8 = null,    auth: auth_runtime.StatusSnapshot = .{},    permission_mode: PermissionMode,    sandbox_backend: sandbox.BackendKind = .none,    agent_step_limit: usize,    update_channel: update_target.Channel = .stable,    config_diagnostics: []config_runtime.ConfigDiagnostic = &.{},     pub fn deinit(self: *StartupStatus, alloc: Allocator) void {        alloc.free(self.workspace_root);        if (self.owned_selected_model) |model| alloc.free(model);        self.auth.deinit(alloc);        if (self.config_diagnostics.len > 0) {            for (self.config_diagnostics) |*diagnostic| diagnostic.deinit(alloc);            alloc.free(self.config_diagnostics);        }        self.* = undefined;    }}; const StartupStatusModel = struct {    value: []const u8,    owned: ?[]u8 = null,}; pub const BootstrapConfig = struct {    alloc: Allocator,    terminal: *TerminalState,    shell: *TranscriptRuntime,    metrics: *Metrics,    terminal_title: host.TerminalTitle,    footer_rows: u16,    startup_min_body_rows: u16 = 0,    default_model: []const u8,    default_agent_step_limit: usize,    secret_store: host.SecretStore,    resize_handler: ResizeHandler,    fx_version: []const u8 = "",    record_requested: bool = false,}; pub fn loadStartupState(    alloc: Allocator,    transport: oauth_transport.Provider,    secret_store: host.SecretStore,    default_model: []const u8,    default_agent_step_limit: usize,) !StartupState {    const workspace_root = try io_mod.realpathAlloc(alloc, ".");    return loadStartupStateFromOwnedWorkspace(alloc, transport, secret_store, workspace_root, default_model, default_agent_step_limit, null, .refresh_if_needed);} pub fn loadStartupStateWithoutCredentials(alloc: Allocator, default_model: []const u8, default_agent_step_limit: usize) !StartupState {    const workspace_root = try io_mod.realpathAlloc(alloc, ".");    return loadStartupStateFromOwnedWorkspace(alloc, oauth_transport.unavailable_provider, host.unavailable_secret_store, workspace_root, default_model, default_agent_step_limit, null, null);} pub fn loadEmbeddedStartupState(    alloc: Allocator,    home_dir: []const u8,    workspace_root: []const u8,    default_model: []const u8,    default_agent_step_limit: usize,) !StartupState {    const owned_workspace_root = try io_mod.realpathAlloc(alloc, workspace_root);    return loadStartupStateFromOwnedWorkspace(        alloc,        oauth_transport.unavailable_provider,        host.unavailable_secret_store,        owned_workspace_root,        default_model,        default_agent_step_limit,        home_dir,        null,    );} pub fn loadCatalogStartupState(    alloc: Allocator,    secret_store: host.SecretStore,    default_model: []const u8,    default_agent_step_limit: usize,) !StartupState {    const workspace_root = try io_mod.realpathAlloc(alloc, ".");    return loadStartupStateFromOwnedWorkspace(alloc, oauth_transport.unavailable_provider, secret_store, workspace_root, default_model, default_agent_step_limit, null, .stored);} pub fn loadStartupStatus(    alloc: Allocator,    secret_store: host.SecretStore,    default_model: []const u8,    default_agent_step_limit: usize,) !StartupStatus {    const workspace_root = try io_mod.realpathAlloc(alloc, ".");    errdefer alloc.free(workspace_root);    debug_trace.configureFromEnv(alloc, workspace_root);     var detailed = try config_runtime.loadMergedSettingsDetailed(alloc, workspace_root);    defer detailed.deinit(alloc);    const settings = &detailed.settings;     const selected_model = try loadStartupStatusModel(alloc, default_model, settings.model);    errdefer if (selected_model.owned) |model| alloc.free(model);     var auth_status = try auth_runtime.loadStatusSnapshot(alloc, secret_store, settings.credential_source);    errdefer auth_status.deinit(alloc);     const result = StartupStatus{        .workspace_root = workspace_root,        .selected_model = selected_model.value,        .owned_selected_model = selected_model.owned,        .auth = auth_status,        .permission_mode = loadPermissionMode(settings.permission_mode),        .sandbox_backend = sandbox.backendFromConfig(settings.sandbox),        .agent_step_limit = loadAgentStepLimit(default_agent_step_limit, settings.max_agent_steps),        .update_channel = settings.update_channel orelse .stable,        .config_diagnostics = detailed.diagnostics,    };    auth_status.owned_team = null;    detailed.diagnostics = &.{};    return result;} pub fn applyWorkspaceLaunch(    self: *StartupState,    alloc: Allocator,    command_line_directories: []const []const u8,    saved_suppressed: bool,) !void {    try self.workspace_access.applyLaunch(        alloc,        self.workspace_root,        command_line_directories,        saved_suppressed,    );} fn loadStartupStateForWorkspace(alloc: Allocator, workspace_root: []const u8, default_model: []const u8, default_agent_step_limit: usize) !StartupState {    const owned_workspace_root = try alloc.dupe(u8, workspace_root);    return loadStartupStateFromOwnedWorkspace(alloc, oauth_transport.unavailable_provider, host.unavailable_secret_store, owned_workspace_root, default_model, default_agent_step_limit, null, null);} const CredentialLoadMode = credentials.LoadMode; fn loadStartupStateFromOwnedWorkspace(    alloc: Allocator,    transport: oauth_transport.Provider,    secret_store: host.SecretStore,    owned_workspace_root: []u8,    default_model: []const u8,    default_agent_step_limit: usize,    profile_home: ?[]const u8,    credential_mode: ?CredentialLoadMode,) !StartupState {    var state = StartupState{ .agent_step_limit = default_agent_step_limit };    errdefer state.deinit(alloc);     state.workspace_root = owned_workspace_root;    debug_trace.configureFromEnv(alloc, state.workspace_root);    var detailed = if (profile_home) |home_dir|        try config_runtime.loadMergedSettingsDetailedFromHome(alloc, home_dir, state.workspace_root)    else        try config_runtime.loadMergedSettingsDetailed(alloc, state.workspace_root);    defer detailed.deinit(alloc);    const settings = &detailed.settings;     state.workspace_access = try workspace_access.WorkspaceAccess.init(        alloc,        state.workspace_root,        detailed.additional_directory_sources orelse &.{},        &.{},        false,    );     state.configured_model = try alloc.dupe(u8, settings.model orelse default_model);    state.model_source = detailed.model_source orelse .compiled_default;    state.selected_model = try loadInitialModel(alloc, default_model, settings.model);    if (hasProcessModelOverride()) state.model_source = .process_override;    state.config_diagnostics = detailed.diagnostics;    detailed.diagnostics = &.{};    state.prompt_history_enabled = settings.prompt_history_enabled orelse true;    state.prompt_history_store_allowed = detailed.prompt_history_store_allowed;    if (credential_mode) |mode| {        const resolution = try credentials.resolvePreferring(alloc, transport, secret_store, mode, settings.credential_source);        state.credential = resolution.credential;        state.stored_key_status = resolution.stored_key_status;    }    state.permission_mode = loadPermissionMode(settings.permission_mode);    state.yolo_acknowledged = settings.yolo_acknowledged orelse false;    state.permission_rules = try types.dupePermissionRuleSet(alloc, settings.permission_rules);    state.agent_step_limit = loadAgentStepLimit(default_agent_step_limit, settings.max_agent_steps);    state.max_tool_result_bytes = tool_result_limits.resolveMaxToolResultBytes(settings.max_tool_result_bytes, tool_result_limits.default_max_tool_result_bytes);    state.context_limits = config_runtime.resolveContextLimits(settings, &.{});    state.context_enabled = settings.context orelse true;    state.fast_mode = settings.fast_mode orelse false;    state.input_appearance = initialInputAppearance(settings.input_appearance);    state.maxxing_mode = initialMaxxingMode(settings.maxxing_mode);    state.slash_menu_categories = settings.slash_menu_categories orelse true;    state.auto_upgrade = settings.auto_upgrade orelse true;    state.update_channel = settings.update_channel orelse .stable;    state.startup_scrollback = settings.startup_scrollback orelse true;    state.effort = settings.effort orelse .auto;    state.first_call_tool_choice = settings.first_call_tool_choice orelse .auto;    state.sandbox_backend = sandbox.backendFromConfig(settings.sandbox);    state.statusline_sandbox = settings.statusline_sandbox orelse false;    state.statusline_context = settings.statusline_context orelse false;    state.statusline_session = settings.statusline_session orelse false;    const sound_override = soundEnvOverride();    const sound_on_override: ?bool = if (sound_override) |level| level != .off else null;    const max_override: ?bool = if (sound_override) |level| level == .max else null;    state.notification_turn_end = sound_on_override orelse settings.notification_turn_end orelse notification_sound.default_enabled;    state.notification_attention_required = sound_on_override orelse settings.notification_attention_required orelse notification_sound.default_enabled;    state.notification_max = max_override orelse settings.notification_max orelse false;     return state;} pub fn bootstrapInteractiveApp(cfg: BootstrapConfig) !StartupState {    try cfg.terminal.ensureInteractive();    try cfg.terminal.captureOriginalTermios();     cfg.shell.sync_updates_enabled = shell_runtime.detectSyncUpdatesEnabled(cfg.alloc);    cfg.shell.history_reset_uses_ris = shell_runtime.detectHistoryResetUsesRis(cfg.alloc);    cfg.shell.layout = minimalLayout();    try cfg.shell.initBacking(cfg.alloc);     var state = try loadCatalogStartupState(        cfg.alloc,        cfg.secret_store,        cfg.default_model,        cfg.default_agent_step_limit,    );    errdefer state.deinit(cfg.alloc);     state.credential_onboarding_skipped = credentialOnboardingDisabled();     errdefer shutdownInteractiveShell(        cfg.terminal,        cfg.shell,        cfg.metrics,        cfg.terminal_title,    );     try cfg.terminal.enableRawMode();    cfg.terminal.installResizeSignal(cfg.resize_handler);    installAbnormalExitHandlers(io_mod.getenv("TMUX"));    cfg.shell.layout = try cfg.terminal.queryLayout(cfg.footer_rows);     record_tape.configureFromEnv(        cfg.alloc,        state.workspace_root,        cfg.shell.layout.cols,        cfg.shell.layout.rows,        cfg.fx_version,        cfg.record_requested,    ) catch |err| {        debug_trace.logf("record", "startup recording failed err={s}", .{@errorName(err)});        return error.RecordingStartFailed;    };     // The shadow VT is load-bearing: frame commit diffs the target    // surface against it and records the accepted terminal state.    try cfg.shell.enableShadowVt(cfg.alloc);     ui_render.setTruecolorSupport(ui_render.truecolorSupportedForValues(        io_mod.getenv("COLORTERM"),        io_mod.getenv("TERM_PROGRAM"),    ));    const theme = ui_render.detectTheme(cfg.alloc, cfg.terminal);    ui_render.initTheme(theme.light, theme.rgb);    state.theme_monitor_enabled = ui_render.explicitThemeOverride() == null;     const cursor = cfg.terminal.queryCursorPosition() catch blk: {        break :blk CursorPosition{            .row = cfg.shell.layout.content_bottom,            .col = 1,        };    };    const start_row = blk: {        var row = cursor.row;        if (cursor.col > 1 and row < cfg.shell.layout.rows) row += 1;        break :blk row;

Lines 1–500 of 2398.