Something went wrong. Try again.
My version of https://github.com/vercel-labs/fx
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386const std = @import("std");const build_options = @import("build_options");const app_lifecycle = @import("app_lifecycle.zig");const provider_runtime = @import("provider_runtime.zig");const app_input_runtime = @import("app_input_runtime.zig");const app_permission_runtime = @import("app_permission_runtime.zig");const app_render_runtime = @import("app_render_runtime.zig");const app_runtime_setup = @import("app_runtime_setup.zig");const app_session_runtime = @import("app_session_runtime.zig");const auth_runtime = @import("../auth/auth_runtime.zig");const credentials = @import("../auth/credentials.zig");const config_runtime = @import("../config/config_runtime.zig");const model_provider = @import("../config/model_provider.zig");const host = @import("../hosts/host.zig");const debug_trace = @import("../shared/debug_trace.zig");const record_tape = @import("../workspace/record_tape.zig");const statusline_identity = @import("../workspace/statusline_identity.zig");const shared_io = @import("../shared/io.zig");const mcp_runtime = @import("../mcp/mcp_runtime.zig");const mcp_health = @import("../mcp/health.zig");const permissions = @import("../permissions/permissions.zig");const skill_contract = @import("../skills/skill_contract.zig");const skill_runtime = @import("../skills/skill_runtime.zig");const types = @import("../shared/types.zig");const worker_runtime = @import("../agent/worker_runtime.zig");const auto_upgrade = @import("../upgrade/auto_upgrade.zig");const update_target = @import("../upgrade/update_target.zig");const core_input_runtime = @import("../input/runtime.zig");const ui_render = @import("../../ui/render.zig");const ui_input = @import("../../ui/input/runtime.zig");const shell_runtime = @import("../../ui/shell_runtime.zig");const transcript_runtime = @import("../../ui/transcript/runtime.zig");
const Allocator = std.mem.Allocator;
const SessionAssemblyMcpServer = struct { name: []const u8, connection: []const u8, tools: ?usize,};
const SessionAssemblyFacts = struct { provider: []const u8, model: []const u8, effort: []const u8, system_prompt_bytes: ?usize, tool_names: []const []const u8, skill_count: usize, mcp_servers: []const SessionAssemblyMcpServer,};
const session_tool_name_preview_max = 8;const session_mcp_server_preview_max = 10;
/// Pure formatter for the full-only session assembly record. Facts in,/// bounded body text out; no I/O, no app state.fn writeSessionAssemblyBody( writer: *std.Io.Writer, facts: SessionAssemblyFacts,) !void { try writer.print("provider: {s} · model: {s} · effort: {s}\n", .{ facts.provider, facts.model, facts.effort, }); if (facts.system_prompt_bytes) |bytes| { try writer.print("system prompt: ready · {d} bytes\n", .{bytes}); } try writer.print("tools: {d} advertised", .{facts.tool_names.len}); if (facts.tool_names.len > 0) { try writer.writeAll(" ("); const shown = @min(facts.tool_names.len, session_tool_name_preview_max); for (facts.tool_names[0..shown], 0..) |name, index| { if (index > 0) try writer.writeAll(", "); try writer.writeAll(name); } if (facts.tool_names.len > shown) { try writer.print(", +{d} more", .{facts.tool_names.len - shown}); } try writer.writeAll(")"); } try writer.writeByte('\n'); try writer.print("skills: {d} in catalog\n", .{facts.skill_count}); if (facts.mcp_servers.len == 0) { try writer.writeAll("mcp: none"); return; } try writer.print("mcp: {d} server{s}: ", .{ facts.mcp_servers.len, if (facts.mcp_servers.len == 1) "" else "s", }); const shown = @min(facts.mcp_servers.len, session_mcp_server_preview_max); for (facts.mcp_servers[0..shown], 0..) |server, index| { if (index > 0) try writer.writeAll(", "); try writer.writeAll(server.name); try writer.writeAll(" ("); try writer.writeAll(server.connection); if (server.tools) |tools| try writer.print(", {d} tools", .{tools}); try writer.writeAll(")"); } if (facts.mcp_servers.len > shown) { try writer.print(", +{d} more", .{facts.mcp_servers.len - shown}); }}
pub const CapabilityProviders = struct { load_mcp_runtime: mcp_runtime.LoadRuntimeFn, skill_root_policy: skill_contract.RootPolicy, terminal_title: host.TerminalTitle,};
fn BootstrapDeps(comptime App: type) type { return struct { const BootstrapInteractiveAppFn = *const fn (app_lifecycle.BootstrapConfig) anyerror!app_lifecycle.StartupState; const ConfigureSessionPreferencesFn = *const fn ( *App, model_provider.ProviderId, []const u8, config_runtime.ModelSource, []const u8, types.ReasoningEffort, bool, bool, ?types.ReasoningEffort, ?bool, ?model_provider.ProviderId, ) anyerror!void; const InitializePersistenceFn = *const fn (*App, bool) anyerror!void; const LoadSkillsFn = *const fn ( Allocator, []const u8, skill_contract.RootPolicy, ) app_runtime_setup.LoadSkillsError!app_runtime_setup.LoadedSkills; const WelcomeMessageFn = *const fn (Allocator) anyerror![]u8; const BeginFreshPersistedSessionFn = *const fn (*App) anyerror!void; const EnableSessionStoresFn = *const fn (*App) void; bootstrap_interactive_app: BootstrapInteractiveAppFn, configure_session_preferences: ConfigureSessionPreferencesFn, initialize_persistence: InitializePersistenceFn, load_mcp_runtime: mcp_runtime.LoadRuntimeFn, load_skills: LoadSkillsFn, skill_root_policy: skill_contract.RootPolicy, welcome_message: WelcomeMessageFn, begin_fresh_persisted_session: BeginFreshPersistedSessionFn, enable_session_stores: EnableSessionStoresFn, terminal_title: host.TerminalTitle, };}
pub fn Runtime(comptime App: type) type { return struct { pub const LaunchOverrides = struct { provider: ?model_provider.ProviderId = null, model: ?[]const u8 = null, effort: ?types.ReasoningEffort = null, fast: ?bool = null, };
pub fn bootstrap( app: *App, footer_rows: u16, default_model: []const u8, default_agent_step_limit: usize, resize_handler: app_lifecycle.ResizeHandler, capability_providers: CapabilityProviders, launch_overrides: LaunchOverrides, ) !void { try bootstrapWithDeps( app, footer_rows, default_model, default_agent_step_limit, resize_handler, defaultDeps(capability_providers), launch_overrides, ); }
fn defaultDeps(capability_providers: CapabilityProviders) BootstrapDeps(App) { return .{ .bootstrap_interactive_app = bootstrapInteractiveAppDefault, .configure_session_preferences = configureSessionPreferencesDefault, .initialize_persistence = initializePersistenceDefault, .load_mcp_runtime = capability_providers.load_mcp_runtime, .load_skills = app_runtime_setup.loadSkills, .skill_root_policy = capability_providers.skill_root_policy, .welcome_message = welcomeMessageDefault, .begin_fresh_persisted_session = beginFreshPersistedSessionDefault, .enable_session_stores = enableSessionStoresDefault, .terminal_title = capability_providers.terminal_title, }; }
fn bootstrapInteractiveAppDefault(cfg: app_lifecycle.BootstrapConfig) !app_lifecycle.StartupState { return app_lifecycle.bootstrapInteractiveApp(cfg); }
fn initializePersistenceDefault( app: *App, required: bool, ) !void { try app_session_runtime.Runtime(App).initializePersistence( app, required, ); }
fn configureSessionPreferencesDefault( app: *App, provider: model_provider.ProviderId, configured_model: []const u8, model_source: config_runtime.ModelSource, selected_model: []const u8, effort: types.ReasoningEffort, fast_mode: bool, fast_mode_model_bound: bool, effort_process_override: ?types.ReasoningEffort, fast_process_override: ?bool, provider_process_override: ?model_provider.ProviderId, ) !void { try app_session_runtime.Runtime(App).configureStartupPreferences( app, provider, configured_model, model_source, selected_model, effort, fast_mode, fast_mode_model_bound, effort_process_override, fast_process_override, provider_process_override, ); }
fn welcomeMessageDefault(alloc: Allocator) ![]u8 { return ui_render.welcomeMessage(alloc); }
fn beginFreshPersistedSessionDefault(app: *App) !void { try app_session_runtime.Runtime(App).beginFreshPersistedSession(app); }
fn enableSessionStoresDefault(app: *App) void { app_session_runtime.Runtime(App).enableSessionStores(app); }
// Neutral one-line summary inline; the full detail stays behind Ctrl+O. fn writeCollapsedStartupNotice(app: *App, topic: []const u8, summary_lead: []const u8, detail: []const u8) !void { const summary = try std.fmt.allocPrint(app.alloc, "{s} (ctrl+o to view)", .{summary_lead}); defer app.alloc.free(summary); try app.writeDomainNotice(.{ .topic = topic, .tone = .neutral, .body = summary }, true); try app.writeDomainNotice(.{ .topic = topic, .tone = .neutral, .body = detail, .visibility = .full_only }, true); }
/// Writes one full-only record describing what this session assembled: /// provider/model/effort, system prompt size, advertised tools, skill /// catalog size, and MCP server states. Live-session only; resume does /// not replay it, matching other full-only startup detail. fn writeSessionAssemblyNotice(app: *App, provider_label: []const u8) !void { var mcp_servers: std.ArrayList(SessionAssemblyMcpServer) = .empty; // Names are duped: the health snapshot is released with its lease // at the end of the acquire block, before the body is rendered. defer { for (mcp_servers.items) |server| app.alloc.free(server.name); mcp_servers.deinit(app.alloc); } if (comptime @hasDecl(App, "acquireMcpRuntime")) { if (app.acquireMcpRuntime()) |lease_value| { var lease = lease_value; defer lease.deinit(); const captured_at_ms: u64 = @intCast(@max(shared_io.milliTimestamp(), 0)); var snapshot: ?mcp_health.Snapshot = lease.runtime.snapshotHealth(app.alloc, captured_at_ms) catch |err| blk: { debug_trace.logf("bootstrap", "session assembly mcp snapshot failed err={s}", .{@errorName(err)}); break :blk null; }; defer if (snapshot) |*value| value.deinit(app.alloc); if (snapshot) |*value| { for (value.servers) |server| { const name = try app.alloc.dupe(u8, server.configured_name); errdefer app.alloc.free(name); try mcp_servers.append(app.alloc, .{ .name = name, .connection = @tagName(server.connection), .tools = server.counts.tools, }); } } } } const system_prompt_bytes: ?usize = if (comptime @hasDecl(App, "promptPolicy")) app.promptPolicy().system_prompt.len else null; const tool_names: []const []const u8 = if (comptime @hasDecl(App, "toolAdvertisementSet")) app.toolAdvertisementSet().order else &.{}; const effort_label: []const u8 = if (comptime @hasField(App, "effort")) app.effort.label() else "auto"; var body: std.Io.Writer.Allocating = .init(app.alloc); defer body.deinit(); try writeSessionAssemblyBody(&body.writer, .{ .provider = provider_label, .model = provider_runtime.model(app), .effort = effort_label, .system_prompt_bytes = system_prompt_bytes, .tool_names = tool_names, .skill_count = app.skills.items.len, .mcp_servers = mcp_servers.items, }); try app.shell.appendFullDetailRecord(app.alloc, .{ .topic = "session", .tone = .neutral, .body = body.written(), .visibility = .full_only, }); }
fn bootstrapWithDeps( app: *App, footer_rows: u16, default_model: []const u8, default_agent_step_limit: usize, resize_handler: app_lifecycle.ResizeHandler, deps: BootstrapDeps(App), launch_overrides: LaunchOverrides, ) !void { errdefer app.deinit();
var startup = try deps.bootstrap_interactive_app(.{ .alloc = app.alloc, .terminal = &app.terminal, .shell = &app.shell, .metrics = &app.metrics, .terminal_title = deps.terminal_title, .footer_rows = footer_rows, .startup_min_body_rows = ui_render.welcome_message_reserved_rows, .default_model = default_model, .default_agent_step_limit = default_agent_step_limit, .secret_store = if (comptime @hasDecl(App, "secretStore")) app.secretStore() else host.unavailable_secret_store, .auth_mode = if (comptime @hasDecl(@TypeOf(app.auth), "authMode")) app.auth.authMode() else .local, .resize_handler = resize_handler, .fx_version = App.app_version, .provider_override = launch_overrides.provider, }); defer startup.deinit(app.alloc);
if (launch_overrides.model) |model| { try startup.applyLaunchModelOverride(app.alloc, model); }
app.workspace_root = startup.takeWorkspaceRoot(); if (comptime @hasDecl(App, "adoptWorkspaceAccess")) { app.adoptWorkspaceAccess(startup.takeWorkspaceAccess()); } if (startup.takeCredential()) |credential_value| { var credential = credential_value; defer credential.deinit(app.alloc); _ = app.auth.adoptCredential(app.alloc, &credential); } app.auth.recordStartupStatus( startup.stored_key_status, startup.fx_login_status, startup.credential_load_failure, startup.credential_onboarding_skipped, ); if (comptime @hasDecl(@TypeOf(app.auth), "refreshSourceInventory")) { app.auth.refreshSourceInventory(app.alloc) catch |err| { debug_trace.logf("auth", "startup source inventory refresh failed err={s}", .{@errorName(err)}); }; } else if (comptime @hasDecl(@TypeOf(app.auth), "refreshChatGptSourceInventory")) { app.auth.refreshChatGptSourceInventory(app.alloc) catch |err| { debug_trace.logf("auth", "startup source inventory refresh failed err={s}", .{@errorName(err)}); }; } const startup_auth_view = app.auth.view(); if (startup_auth_view.active_source == null and !startup_auth_view.onboarding_skipped) { app.auth.openOnboardingPicker(app.alloc); } if (comptime @hasField(App, "terminal_input_runtime") and @hasField(App, "terminal")) { // Own theme protocol bytes even under FX_THEME; probing stays gated. app.terminal_input_runtime.terminal_theme_monitor.start(); if (startup.theme_monitor_enabled) { app.terminal.enableThemeNotifications() catch |err| { debug_trace.logf("theme", "theme_notification_enable_failed err={s}", .{@errorName(err)}); }; app.terminal.requestThemeColorScheme() catch |err| { debug_trace.logf("theme", "theme_color_scheme_query_failed err={s}", .{@errorName(err)}); }; } } var prompt_history_unavailable = false; if (comptime @hasField(App, "prompt_history")) { prompt_history_unavailable = (try app.prompt_history.initialize( app.alloc, shared_io.getenv("HOME"), startup.prompt_history_enabled, startup.prompt_history_store_allowed, )) == .unavailable; } if (comptime @hasField(App, "session") and @hasDecl(@TypeOf(app.session), "initializeProfileUsage")) { _ = try app.session.initializeProfileUsage( app.alloc, shared_io.getenv("HOME"), ); }
var selected_model = startup.takeSelectedModel(); defer if (selected_model.len > 0) app.alloc.free(selected_model); if (comptime @hasField(App, "provider_selection")) { app.provider_selection.model_requests_blocked = startup.model_requests_blocked; app.provider_selection.definitions = startup.configured_providers; startup.configured_providers = .{}; app.provider_selection.adoptOwned(startup.provider, &selected_model); } else { try provider_runtime.replaceModel(app, selected_model); } const active_model = provider_runtime.model(app); // Per-launch --effort/--fast flags shape runtime state only; the // configured and stored preferences keep their pre-flag values. const persisted_effort = startup.effort; const persisted_fast_mode = startup.fast_mode; startup.applyLaunchTurnOverrides(launch_overrides.effort, launch_overrides.fast); try deps.configure_session_preferences( app, startup.provider, startup.configured_model, startup.model_source, active_model, persisted_effort, persisted_fast_mode, startup.fast_mode_model_bound, launch_overrides.effort, launch_overrides.fast, launch_overrides.provider, ); app.permission_engine.mode = startup.permission_mode; app.permission_engine.replaceRules(app.alloc, startup.takePermissionRules()); app.agent_step_limit = startup.agent_step_limit; app.worker.agent_turn_settings.max_tool_result_bytes = startup.max_tool_result_bytes; if (comptime @hasField(App, "context_limits")) app.context_limits = startup.context_limits; app.worker.agent_turn_settings.first_call_tool_choice = startup.first_call_tool_choice; app.worker.agent_turn_settings.fast_mode = startup.fast_mode; app.worker.agent_turn_settings.effort = startup.effort; app.context_enabled = startup.context_enabled; app.fast_mode = startup.fast_mode; app.input_runtime.slash_menu_categories = startup.slash_menu_categories; app.shell.collapse_tool_calls = startup.collapse_tool_calls; app.auto_upgrade_enabled = startup.auto_upgrade; app.upgrader.configure_channel(startup.update_channel); app.effort = startup.effort; if (comptime @hasField(App, "review_model")) app.review_model = startup.takeReviewModel(); app.shell.setCommandOutputRenderPolicy( app_render_runtime.Runtime(App).shellStyles(), ); app.permission_state.yolo_acknowledged = startup.yolo_acknowledged; app_permission_runtime.Runtime(App).initializeYoloWarning(app); app.statusline_context = startup.statusline_context; app.statusline_session = startup.statusline_session; if (comptime @hasField(App, "session_title_generation")) { app.session_title_generation = startup.session_title_generation; } if (comptime @hasField(App, "workspace_identity")) { app.workspace_identity.enabled = startup.statusline_workspace; } if (comptime @hasDecl(App, "setNotificationPreferences")) { app.setNotificationPreferences( startup.notification_turn_end, startup.notification_attention_required, startup.notification_max, ); } try deps.initialize_persistence( app, app.requested_resume != null, ); const profile_mcp = try deps.load_mcp_runtime( app.alloc, app.workspace_root, .{ .form = true, .url = true }, ); if (comptime @hasDecl(App, "installInitialMcpRuntime")) { app.installInitialMcpRuntime(profile_mcp); } else { app.mcp_runtime = profile_mcp; }
var loaded = try deps.load_skills( std.heap.c_allocator, app.workspace_root, deps.skill_root_policy, ); errdefer loaded.deinit(std.heap.c_allocator); skill_runtime.traceDiagnostics("interactive_startup", loaded.diagnostics); try app.skills.replaceLoaded(std.heap.c_allocator, loaded.dir, loaded.skills, loaded.diagnostics); loaded = .{};
if (app.requested_resume == null) { const welcome_message = try deps.welcome_message(app.alloc); defer app.alloc.free(welcome_message); var welcome_preview_buf: [96]u8 = undefined; const welcome_preview = debug_trace.terminalPreview(welcome_preview_buf[0..], welcome_message); debug_trace.logf( "paint", "welcome_write bytes={d} reserved_rows={d} has_version={s} has_help={s} has_feedback={s} preview=\"{s}\"", .{ welcome_message.len, ui_render.welcome_message_reserved_rows, if (std.mem.find(u8, welcome_message, " v") != null) "true" else "false", if (std.mem.find(u8, welcome_message, "Run /help") != null) "true" else "false", if (std.mem.find(u8, welcome_message, "Feedback?") != null) "true" else "false", welcome_preview, }, ); try app.writeTranscriptClassified(welcome_message, true, .welcome); if (comptime @hasDecl(App, "presentProjectMcpPrompt")) { try app.presentProjectMcpPrompt(); } // Fresh sessions only: on resume the transcript must stay // empty until the deferred session load replays history. try writeSessionAssemblyNotice(app, startup.provider.label()); } if (app.skills.diagnostics.len > 0) { var notice_writer: std.Io.Writer.Allocating = .init(app.alloc); defer notice_writer.deinit(); skill_runtime.writeDiagnosticSummary(app.alloc, ¬ice_writer.writer, app.skills.diagnostics) catch return error.OutOfMemory; const skills_body = notice_writer.toOwnedSlice() catch return error.OutOfMemory; defer app.alloc.free(skills_body); if (comptime @hasField(App, "session") and @hasDecl(@TypeOf(app.session), "claimContextNotice")) { _ = try app.session.claimContextNotice(app.alloc, skills_body); }
const skills_summary = try std.fmt.allocPrint(app.alloc, "{d} discovery issue{s}; some skills may be missing", .{ app.skills.diagnostics.len, if (app.skills.diagnostics.len == 1) "" else "s", }); defer app.alloc.free(skills_summary); try writeCollapsedStartupNotice(app, "skills", skills_summary, skills_body); } if (comptime @hasField(App, "auth")) { const auth_view = app.auth.view(); const load_error: ?anyerror = if (startup.credential_load_failure) |failure| failure.err else if (auth_view.stored_key_status == .unavailable or auth_view.fx_login_status == .unavailable) error.CredentialStorageUnavailable else null; if (auth_view.active_source == null and load_error != null) { const body = try auth_runtime.preparationFailureText(app.alloc, startup.provider, load_error.?); defer app.alloc.free(body); try app.writeDomainNotice(.{ .topic = "auth", .tone = .warning, .body = body, }, true); } } var recording = try record_tape.captureStatus(app.alloc); defer recording.deinit(app.alloc); if (recording == .active) { const recording_body = try std.fmt.allocPrint( app.alloc, "visual terminal capture: {s}\nvisible terminal content, including typed prompt text, is recorded", .{recording.active.path}, ); defer app.alloc.free(recording_body); try app.writeDomainNotice(.{ .topic = "recording", .tone = .warning, .body = recording_body, .visibility = if (recording.active.show_inline_notice) .compact_and_full else .full_only, }, true); } { var detail_writer: std.Io.Writer.Allocating = .init(app.alloc); defer detail_writer.deinit(); var reported: usize = 0; for (startup.config_diagnostics) |diagnostic| { if (!diagnostic.reportAtStartup()) continue; if (reported > 0) try detail_writer.writer.writeByte('\n'); try detail_writer.writer.print( "{s}: {s}", .{ @tagName(diagnostic.layer), @tagName(diagnostic.cause) }, ); try config_runtime.writeDiagnosticMetadata(&detail_writer.writer, diagnostic); reported += 1; } if (reported > 0) { const summary = try std.fmt.allocPrint(app.alloc, "{d} configuration issue{s}", .{ reported, if (reported == 1) "" else "s", }); defer app.alloc.free(summary); try writeCollapsedStartupNotice(app, "config", summary, detail_writer.written()); } } if (comptime @hasField(App, "prompt_history")) { if (prompt_history_unavailable) { try app.writeDomainNotice(.{ .topic = "history", .tone = .warning, .body = "durable prompt history unavailable", }, true); } else { app_input_runtime.Runtime(App).loadAndInstallPromptHistory( app, ) catch |err| { app.prompt_history.writes_available = false; debug_trace.logf( "prompt_history", "startup load failed err={s}", .{@errorName(err)}, ); const history_body = try std.fmt.allocPrint( app.alloc, "failed to load durable prompt history ({s})", .{@errorName(err)}, ); defer app.alloc.free(history_body); try app.writeDomainNotice(.{ .topic = "history", .tone = .warning, .body = history_body, }, true); }; } }
if (app.requested_resume == null) { try deps.begin_fresh_persisted_session(app); deps.enable_session_stores(app); app_session_runtime.Runtime(App).syncTerminalTitleWith(app, deps.terminal_title); }
app.shell.render_requests.request(.first_frame); } };}
const TestCapture = struct { alloc: Allocator, bootstrap_calls: usize = 0, footer_rows: u16 = 0, default_model: []const u8 = "", default_agent_step_limit: usize = 0, fx_version: []const u8 = "", configured_model: [64]u8 = undefined, configured_model_len: usize = 0, configured_model_source: config_runtime.ModelSource = .compiled_default, runtime_model: [64]u8 = undefined, runtime_model_len: usize = 0, configured_effort: types.ReasoningEffort = .auto, configured_fast_mode: bool = false, configured_fast_mode_model_bound: bool = false, effort_process_override: ?types.ReasoningEffort = null, fast_process_override: ?bool = null, provider_process_override: ?model_provider.ProviderId = null, initialize_required: bool = false, load_skills_workspace: []const u8 = "", load_skills_workspace_root_count: usize = 0, load_skills_global_root_count: usize = 0, transcript_recorded: bool = false, begin_calls: usize = 0, enable_calls: usize = 0, title: [64]u8 = undefined, title_len: usize = 0, bootstrap_error: ?anyerror = null, bootstrap_init_backing_before_error: bool = false, load_skills_error: ?app_runtime_setup.LoadSkillsError = null, emit_skill_diagnostic: bool = false, emit_config_diagnostics: bool = false, startup_with_credential: bool = true, onboarding_skipped: bool = true, early_notice_palette_initialized: bool = false, events: [16][]const u8 = undefined, event_count: usize = 0,
fn init(alloc: Allocator) TestCapture { return .{ .alloc = alloc }; }
fn titleText(self: *const TestCapture) []const u8 { return self.title[0..self.title_len]; }
fn configuredModel(self: *const TestCapture) []const u8 { return self.configured_model[0..self.configured_model_len]; }
fn runtimeModel(self: *const TestCapture) []const u8 { return self.runtime_model[0..self.runtime_model_len]; }
fn recordEvent(self: *TestCapture, event: []const u8) void { self.events[self.event_count] = event; self.event_count += 1; }
fn eventSlice(self: *const TestCapture) []const []const u8 { return self.events[0..self.event_count]; }};
var active_capture: ?*TestCapture = null;
const test_workspace_skill_roots = [_]skill_contract.RootSpec{ .{ .source = .workspace_shared, .path = "skills" },};
const test_global_skill_roots = [_]skill_contract.RootSpec{ .{ .source = .global_codex, .path = ".codex/skills" },};
const TestApp = struct { pub const app_version = "0.2.10-test";
alloc: Allocator, terminal: shell_runtime.TerminalState = .{}, shell: transcript_runtime.TranscriptRuntime = .{}, metrics: types.Metrics = .{}, workspace_root: []u8 = &.{}, auth: auth_runtime.Runtime = .{}, selected_model: std.ArrayList(u8) = .empty, permission_engine: permissions.PermissionEngine = .{}, agent_step_limit: usize = 0, worker: worker_runtime.WorkerRuntime = .{}, input_runtime: core_input_runtime.Runtime = .{}, terminal_input_runtime: ui_input.Runtime = .{}, context_enabled: bool = true, fast_mode: bool = false, auto_upgrade_enabled: bool = true, upgrader: auto_upgrade.AutoUpgrade = .{}, effort: types.ReasoningEffort = .auto, permission_state: app_permission_runtime.State = .{}, statusline_context: bool = false, statusline_session: bool = false, workspace_identity: statusline_identity.Runtime = .{}, requested_resume: ?u8 = null, mcp_runtime: ?*mcp_runtime.McpRuntime = null, skills: skill_runtime.Runtime = .{}, transcript: std.ArrayList(u8) = .empty, transcript_recorded: bool = false, begin_fresh_called: bool = false, deinit_calls: usize = 0,
fn init(alloc: Allocator) TestApp { return .{ .alloc = alloc }; }
fn deinit(self: *TestApp) void { self.deinit_calls += 1; if (self.workspace_root.len > 0) { self.alloc.free(self.workspace_root); self.workspace_root = &.{}; } self.auth.deinit(self.alloc); self.workspace_identity.deinit(self.alloc); self.selected_model.deinit(self.alloc); self.permission_engine.deinit(self.alloc); self.worker.deinit(std.heap.c_allocator); if (self.mcp_runtime) |runtime| { runtime.deinit(); self.alloc.destroy(runtime); self.mcp_runtime = null; } self.skills.deinit(std.heap.c_allocator); self.input_runtime.deinit(self.alloc); self.terminal_input_runtime.deinit(self.alloc); self.shell.deinit(self.alloc); self.transcript.deinit(self.alloc); }
fn writeTranscript(self: *TestApp, text: []const u8, record: bool) !void { self.transcript_recorded = record; try self.transcript.appendSlice(self.alloc, text); active_capture.?.recordEvent("welcome"); }
fn writeTranscriptClassified(self: *TestApp, text: []const u8, record: bool, class: transcript_runtime.RawEntryClass) !void { _ = class; try self.writeTranscript(text, record); }
fn writeDomainNotice(self: *TestApp, semantic_notice: types.SemanticNotice, record: bool) !void { const styles = self.shell.retainedTranscriptStyles(); active_capture.?.early_notice_palette_initialized = styles.notice_information_style.len > 0 and styles.system_notice_text_style.len > 0 and styles.reset_style.len > 0; const notice = if (semantic_notice.topic.len > 0) try std.fmt.allocPrint(self.alloc, "{s} {s}: {s}{s}\n", .{ types.noticeGlyph(semantic_notice.tone), semantic_notice.topic, semantic_notice.body, if (semantic_notice.visibility == .full_only) " [full-only]" else "", }) else try std.fmt.allocPrint(self.alloc, "{s} {s}{s}\n", .{ types.noticeGlyph(semantic_notice.tone), semantic_notice.body, if (semantic_notice.visibility == .full_only) " [full-only]" else "", }); defer self.alloc.free(notice); try self.writeTranscriptClassified(notice, record, .subagent_status); }};
fn testDeps() BootstrapDeps(TestApp) { return .{ .bootstrap_interactive_app = bootstrapInteractiveAppForTest, .configure_session_preferences = configureSessionPreferencesForTest, .initialize_persistence = initializePersistenceForTest, .load_mcp_runtime = loadMcpRuntimeForTest, .load_skills = loadSkillsForTest, .skill_root_policy = .{ .workspace_roots = &test_workspace_skill_roots, .managed_root_source = .global_fx, .global_roots = &test_global_skill_roots, }, .welcome_message = welcomeMessageForTest, .begin_fresh_persisted_session = beginFreshPersistedSessionForTest, .enable_session_stores = enableSessionStoresForTest, .terminal_title = .{ .set_fn = setTerminalTitleLabelForTest, .clear_fn = clearTerminalTitleForTest, }, };}
fn bootstrapInteractiveAppForTest(cfg: app_lifecycle.BootstrapConfig) !app_lifecycle.StartupState { const capture = active_capture.?; capture.bootstrap_calls += 1; capture.footer_rows = cfg.footer_rows; capture.default_model = cfg.default_model; capture.default_agent_step_limit = cfg.default_agent_step_limit; capture.fx_version = cfg.fx_version; try std.testing.expect(cfg.terminal == &active_app_for_pointer_check.?.terminal); active_app_for_pointer_check.?.shell.layout = .{ .rows = 24, .cols = 80, .content_bottom = 20, .divider_top_row = 21, .input_row = 22, .divider_bottom_row = 23, .hint_row = 24, }; if (capture.bootstrap_error) |err| { if (capture.bootstrap_init_backing_before_error) { try cfg.shell.initBacking(cfg.alloc); } return err; } return makeStartupState(capture.alloc);}
var active_app_for_pointer_check: ?*TestApp = null;
fn makeStartupState(alloc: Allocator) !app_lifecycle.StartupState { var state = app_lifecycle.StartupState{ .agent_step_limit = 19 }; state.max_tool_result_bytes = 131072; state.first_call_tool_choice = .none; state.workspace_root = try alloc.dupe(u8, "/workspace"); errdefer alloc.free(state.workspace_root); if (active_capture.?.startup_with_credential) { const credential_token = try alloc.dupe(u8, "api-key"); errdefer alloc.free(credential_token); const credential_team = try alloc.dupe(u8, "team_123"); errdefer alloc.free(credential_team); state.credential = .{ .token = credential_token, .source = .ai_gateway_api_key, .team_id = credential_team, }; } state.stored_key_status = .not_found; state.credential_onboarding_skipped = active_capture.?.onboarding_skipped; state.selected_model = try alloc.dupe(u8, "model-x"); errdefer alloc.free(state.selected_model); state.configured_model = try alloc.dupe(u8, "configured-model"); errdefer alloc.free(state.configured_model); state.model_source = .process_override; state.permission_mode = .auto; state.context_enabled = false; state.fast_mode = true; state.fast_mode_model_bound = true; state.auto_upgrade = false; state.update_channel = .dev; state.effort = types.ReasoningEffort.literal("high"); state.statusline_workspace = true; if (active_capture.?.emit_config_diagnostics) { const diagnostics = try alloc.alloc(config_runtime.ConfigDiagnostic, 2); errdefer alloc.free(diagnostics); diagnostics[0] = .{ .layer = .user, .cause = .malformed_settings }; diagnostics[1] = .{ .layer = .project, .cause = .settings_too_large }; state.config_diagnostics = diagnostics; } return state;}
fn initializePersistenceForTest( app: *TestApp, required: bool,) !void { _ = app; active_capture.?.initialize_required = required;}
fn loadMcpRuntimeForTest(_: Allocator, _: []const u8, _: @import("../mcp/elicitation.zig").Capabilities) !?*mcp_runtime.McpRuntime { active_capture.?.recordEvent("load_mcp"); return null;}
fn loadSkillsForTest( alloc: Allocator, workspace_root: []const u8, root_policy: skill_contract.RootPolicy,) app_runtime_setup.LoadSkillsError!app_runtime_setup.LoadedSkills { active_capture.?.recordEvent("load_skills"); active_capture.?.load_skills_workspace = workspace_root; active_capture.?.load_skills_workspace_root_count = root_policy.workspace_roots.len; active_capture.?.load_skills_global_root_count = root_policy.global_roots.len; if (active_capture.?.load_skills_error) |err| return err;
const dir = try alloc.dupe(u8, "/skills"); errdefer alloc.free(dir); if (!active_capture.?.emit_skill_diagnostic) { return .{ .dir = dir, .skills = &.{} }; }
const diagnostics = try alloc.alloc(skill_runtime.SkillDiagnostic, 1); errdefer alloc.free(diagnostics); diagnostics[0] = .{ .path = try alloc.dupe(u8, "/skills/hostile\npath/body-sentinel"), .source = .global_fx, .scope = .candidate, .cause = .{ .invalid_metadata = .missing_name }, }; return .{ .dir = dir, .skills = &.{}, .diagnostics = diagnostics };}
fn welcomeMessageForTest(alloc: Allocator) ![]u8 { return alloc.dupe(u8, "welcome\n");}
fn configureSessionPreferencesForTest( _: *TestApp, _: model_provider.ProviderId, configured_model: []const u8, model_source: config_runtime.ModelSource, selected_model: []const u8, effort: types.ReasoningEffort, fast_mode: bool, fast_mode_model_bound: bool, effort_process_override: ?types.ReasoningEffort, fast_process_override: ?bool, provider_process_override: ?model_provider.ProviderId,) !void { const capture = active_capture.?; capture.configured_model_len = @min( configured_model.len, capture.configured_model.len, ); @memcpy( capture.configured_model[0..capture.configured_model_len], configured_model[0..capture.configured_model_len], ); capture.configured_model_source = model_source; capture.runtime_model_len = @min( selected_model.len, capture.runtime_model.len, ); @memcpy( capture.runtime_model[0..capture.runtime_model_len], selected_model[0..capture.runtime_model_len], ); capture.configured_effort = effort; capture.configured_fast_mode = fast_mode; capture.configured_fast_mode_model_bound = fast_mode_model_bound; capture.effort_process_override = effort_process_override; capture.fast_process_override = fast_process_override; capture.provider_process_override = provider_process_override;}
fn beginFreshPersistedSessionForTest(app: *TestApp) !void { active_capture.?.begin_calls += 1; active_capture.?.recordEvent("begin_fresh"); app.begin_fresh_called = true;}
fn enableSessionStoresForTest(_: *TestApp) void { const capture = active_capture.?; capture.enable_calls += 1; capture.recordEvent("enable_stores");}
fn setTerminalTitleLabelForTest(_: ?*anyopaque, label: []const u8) void { const capture = active_capture.?; capture.title_len = @min(label.len, capture.title.len); @memcpy(capture.title[0..capture.title_len], label[0..capture.title_len]); capture.recordEvent("title");}
fn clearTerminalTitleForTest(_: ?*anyopaque) void { const capture = active_capture.?; capture.title_len = 0; capture.recordEvent("title_clear");}
fn runBootstrapForTest(app: *TestApp, capture: *TestCapture) !void { active_capture = capture; active_app_for_pointer_check = app; defer { active_capture = null; active_app_for_pointer_check = null; }
try Runtime(TestApp).bootstrapWithDeps( app, 4, "default-model", 24, resizeHandlerForTest, testDeps(), .{}, );}
fn runBootstrapWithOverridesForTest(app: *TestApp, capture: *TestCapture, overrides: Runtime(TestApp).LaunchOverrides) !void { active_capture = capture; active_app_for_pointer_check = app; defer { active_capture = null; active_app_for_pointer_check = null; }
try Runtime(TestApp).bootstrapWithDeps( app, 4, "default-model", 24, resizeHandlerForTest, testDeps(), overrides, );}
fn resizeHandlerForTest(_: std.posix.SIG) callconv(.c) void {}
test "app_bootstrap_runtime applies interactive launch flag overrides" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); var app = TestApp.init(alloc); defer app.deinit();
try runBootstrapWithOverridesForTest(&app, &capture, .{ .model = "launch-model", .effort = types.ReasoningEffort.literal("low"), .fast = true, });
try std.testing.expectEqualStrings("launch-model", capture.runtimeModel()); try std.testing.expectEqualStrings("launch-model", app.selected_model.items); try std.testing.expect(app.fast_mode); try std.testing.expect(app.effort.eql(types.ReasoningEffort.literal("low"))); // Stored preferences keep the configured values; the flags stay per-launch. try std.testing.expect(capture.configured_effort.eql(types.ReasoningEffort.literal("high"))); try std.testing.expect(!capture.configured_fast_mode); // --fast binds to the launch model so the footer indicator reflects it. try std.testing.expect(capture.configured_fast_mode_model_bound); try std.testing.expectEqualStrings("configured-model", capture.configuredModel()); // The process overrides carry the flag values so a resume re-applies them. try std.testing.expect(capture.effort_process_override.?.eql(types.ReasoningEffort.literal("low"))); try std.testing.expectEqual(@as(?bool, true), capture.fast_process_override); try std.testing.expectEqual(@as(?model_provider.ProviderId, null), capture.provider_process_override);}
test "app_bootstrap_runtime launch provider override marks the provider for resume" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); var app = TestApp.init(alloc); defer app.deinit();
try runBootstrapWithOverridesForTest(&app, &capture, .{ .provider = .grok, });
try std.testing.expectEqual( @as(?model_provider.ProviderId, .grok), capture.provider_process_override, );}
test "app_bootstrap_runtime model override drops compiled-default fast mode" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); var app = TestApp.init(alloc); defer app.deinit();
try runBootstrapWithOverridesForTest(&app, &capture, .{ .model = "other-model", });
try std.testing.expectEqualStrings("other-model", capture.runtimeModel()); try std.testing.expectEqualStrings("other-model", app.selected_model.items); try std.testing.expect(!app.fast_mode); try std.testing.expect(!capture.configured_fast_mode); try std.testing.expect(capture.configured_effort.eql(types.ReasoningEffort.literal("high"))); try std.testing.expectEqual(@as(?types.ReasoningEffort, null), capture.effort_process_override); try std.testing.expectEqual(@as(?bool, null), capture.fast_process_override);}
test "app_bootstrap_runtime transfers startup state and starts a fresh session" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); var app = TestApp.init(alloc); defer app.deinit();
try runBootstrapForTest(&app, &capture);
try std.testing.expectEqual(@as(usize, 1), capture.bootstrap_calls); try std.testing.expectEqual(@as(u16, 4), capture.footer_rows); try std.testing.expectEqualStrings("default-model", capture.default_model); try std.testing.expectEqual(@as(usize, 24), capture.default_agent_step_limit); try std.testing.expectEqualStrings(TestApp.app_version, capture.fx_version); try std.testing.expectEqualStrings( "configured-model", capture.configuredModel(), ); try std.testing.expectEqual( config_runtime.ModelSource.process_override, capture.configured_model_source, ); try std.testing.expectEqualStrings("model-x", capture.runtimeModel()); try std.testing.expectEqual( types.ReasoningEffort.literal("high"), capture.configured_effort, ); try std.testing.expect(capture.configured_fast_mode); try std.testing.expect(capture.configured_fast_mode_model_bound); try std.testing.expectEqual( update_target.Channel.dev, app.upgrader.channel(), ); try std.testing.expect(!capture.initialize_required); try std.testing.expectEqualStrings("/workspace", capture.load_skills_workspace); try std.testing.expectEqual(@as(usize, 1), capture.load_skills_workspace_root_count); try std.testing.expectEqual(@as(usize, 1), capture.load_skills_global_root_count); const events = capture.eventSlice(); try std.testing.expectEqual(@as(usize, 6), events.len); try std.testing.expectEqualStrings("load_mcp", events[0]); try std.testing.expectEqualStrings("load_skills", events[1]); try std.testing.expectEqualStrings("welcome", events[2]); try std.testing.expectEqualStrings("begin_fresh", events[3]); try std.testing.expectEqualStrings("enable_stores", events[4]); try std.testing.expectEqualStrings("title", events[5]); try std.testing.expectEqual(@as(usize, 1), capture.begin_calls); try std.testing.expectEqual(@as(usize, 1), capture.enable_calls); try std.testing.expectEqualStrings("fx v" ++ build_options.app_version ++ " | workspace", capture.titleText());
try std.testing.expectEqualStrings("/workspace", app.workspace_root); try std.testing.expectEqualStrings("api-key", app.auth.apiKey().?); try std.testing.expectEqual(types.CredentialSource.ai_gateway_api_key, app.auth.credentialSource().?); try std.testing.expectEqualStrings("team_123", app.auth.gatewayTeam().?); const auth_view = app.auth.view(); try std.testing.expectEqual(credentials.StoredKeyReadStatus.not_found, auth_view.stored_key_status); try std.testing.expect(auth_view.onboarding_skipped); try std.testing.expectEqualStrings("model-x", app.selected_model.items); try std.testing.expectEqual(types.PermissionMode.auto, app.permission_engine.mode); try std.testing.expectEqual(@as(usize, 19), app.agent_step_limit); try std.testing.expectEqual(@as(usize, 131072), app.worker.agent_turn_settings.max_tool_result_bytes); try std.testing.expectEqual(types.ToolChoice.none, app.worker.agent_turn_settings.first_call_tool_choice); try std.testing.expect(app.worker.agent_turn_settings.fast_mode); try std.testing.expectEqual(types.ReasoningEffort.literal("high"), app.worker.agent_turn_settings.effort); try std.testing.expect(!app.context_enabled); try std.testing.expect(app.fast_mode); try std.testing.expect(!app.auto_upgrade_enabled); try std.testing.expectEqual(types.ReasoningEffort.literal("high"), app.effort); try std.testing.expect(app.workspace_identity.enabled); try std.testing.expectEqualStrings("/skills", app.skills.dir); try std.testing.expectEqualStrings("welcome\n", app.transcript.items); try std.testing.expect(app.transcript_recorded); try std.testing.expect(app.shell.render_requests.hasReason(.first_frame)); try std.testing.expect(app.begin_fresh_called);}
test "app_bootstrap_runtime opens onboarding before first frame without a credential" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); capture.startup_with_credential = false; capture.onboarding_skipped = false; var app = TestApp.init(alloc); defer app.deinit();
try runBootstrapForTest(&app, &capture);
const picker = app.auth.pickerView(); try std.testing.expect(picker.active); try std.testing.expect(picker.include_skip); try std.testing.expectEqual(auth_runtime.PickerStage.root, picker.stage); try std.testing.expect(app.shell.render_requests.hasReason(.first_frame));}
test "app_bootstrap_runtime defers requested session loading until after bootstrap" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); var app = TestApp.init(alloc); app.requested_resume = 1; defer app.deinit();
try runBootstrapForTest(&app, &capture);
try std.testing.expect(capture.initialize_required); try std.testing.expectEqual(@as(usize, 0), capture.begin_calls); try std.testing.expectEqual(@as(usize, 0), capture.enable_calls); try std.testing.expectEqualStrings("", capture.titleText()); try std.testing.expect(app.shell.render_requests.hasReason(.first_frame)); try std.testing.expectEqual(@as(?u8, 1), app.requested_resume); try std.testing.expect(!app.begin_fresh_called); try std.testing.expectEqualStrings("", app.transcript.items); try std.testing.expect(!app.transcript_recorded); const events = capture.eventSlice(); try std.testing.expectEqualSlices( []const u8, &.{ "load_mcp", "load_skills" }, events, );}
test "app_bootstrap_runtime renders startup notices without a resume cache" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); capture.emit_skill_diagnostic = true; var app = TestApp.init(alloc); app.requested_resume = 1; defer app.deinit();
try runBootstrapForTest(&app, &capture);
try std.testing.expectEqualSlices( []const u8, &.{ "load_mcp", "load_skills", "welcome", "welcome", }, capture.eventSlice(), );}
test "app_bootstrap_runtime deinitializes app after bootstrap dependency failure" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); capture.bootstrap_error = error.LayoutFailed; capture.bootstrap_init_backing_before_error = true; var app = TestApp.init(alloc);
try std.testing.expectError(error.LayoutFailed, runBootstrapForTest(&app, &capture)); try std.testing.expectEqual(@as(usize, 1), app.deinit_calls);}
test "app_bootstrap_runtime propagates skill discovery allocation failure" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); capture.load_skills_error = error.OutOfMemory; var app = TestApp.init(alloc);
try std.testing.expectError(error.OutOfMemory, runBootstrapForTest(&app, &capture)); try std.testing.expectEqual(@as(usize, 1), app.deinit_calls);}
test "app_bootstrap_runtime reports a bounded skill discovery warning" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); capture.emit_skill_diagnostic = true; var app = TestApp.init(alloc); defer app.deinit();
try runBootstrapForTest(&app, &capture);
try std.testing.expectEqual(@as(usize, 1), app.skills.diagnostics.len); try std.testing.expect(capture.early_notice_palette_initialized); try std.testing.expect(std.mem.find(u8, app.transcript.items, "* skills: 1 discovery issue; some skills may be missing (ctrl+o to view)\n") != null); try std.testing.expect(std.mem.find(u8, app.transcript.items, "* skills: skill discovery warning:") != null); try std.testing.expect(std.mem.find(u8, app.transcript.items, "hostile
path/body-sentinel") != null); try std.testing.expect(std.mem.find(u8, app.transcript.items, "metadata is invalid (missing_name)") != null); try std.testing.expect(std.mem.find(u8, app.transcript.items, " [full-only]\n") != null);}
test "app_bootstrap_runtime collapses config diagnostics into one neutral summary" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); capture.emit_config_diagnostics = true; var app = TestApp.init(alloc); defer app.deinit();
try runBootstrapForTest(&app, &capture);
try std.testing.expect(std.mem.find(u8, app.transcript.items, "* config: 2 configuration issues (ctrl+o to view)\n") != null); try std.testing.expect(std.mem.find(u8, app.transcript.items, "user: malformed_settings\nproject: settings_too_large [full-only]\n") != null);}
test "writeSessionAssemblyBody renders bounded facts" { const alloc = std.testing.allocator; var body: std.Io.Writer.Allocating = .init(alloc); defer body.deinit(); const tool_names = [_][]const u8{ "read_file", "edit_file", "run_command" }; const mcp_servers = [_]SessionAssemblyMcpServer{ .{ .name = "linear", .connection = "ready", .tools = 12 }, .{ .name = "slack", .connection = "connecting", .tools = null }, }; try writeSessionAssemblyBody(&body.writer, .{ .provider = "gateway", .model = "kimi-k3", .effort = "high", .system_prompt_bytes = 12345, .tool_names = &tool_names, .skill_count = 7, .mcp_servers = &mcp_servers, }); try std.testing.expectEqualStrings( "provider: gateway · model: kimi-k3 · effort: high\n" ++ "system prompt: ready · 12345 bytes\n" ++ "tools: 3 advertised (read_file, edit_file, run_command)\n" ++ "skills: 7 in catalog\n" ++ "mcp: 2 servers: linear (ready, 12 tools), slack (connecting)", body.written(), );}
test "writeSessionAssemblyBody caps long tool and server lists" { const alloc = std.testing.allocator; var body: std.Io.Writer.Allocating = .init(alloc); defer body.deinit(); var tool_names: [12][]const u8 = undefined; for (&tool_names, 0..) |*name, index| { name.* = try std.fmt.allocPrint(alloc, "tool_{d}", .{index}); } defer for (&tool_names) |*name| alloc.free(name.*); try writeSessionAssemblyBody(&body.writer, .{ .provider = "gateway", .model = "m", .effort = "auto", .system_prompt_bytes = null, .tool_names = &tool_names, .skill_count = 0, .mcp_servers = &.{}, }); try std.testing.expect(std.mem.find(u8, body.written(), "system prompt") == null); try std.testing.expect(std.mem.find(u8, body.written(), "+4 more") != null); try std.testing.expect(std.mem.find(u8, body.written(), "mcp: none") != null);}
test "app_bootstrap_runtime records a full-only session assembly record" { const alloc = std.testing.allocator; var capture = TestCapture.init(alloc); var app = TestApp.init(alloc); defer app.deinit();
try runBootstrapForTest(&app, &capture);
// The record lives in the full-detail side list, not the transcript. try std.testing.expectEqualStrings("welcome\n", app.transcript.items); try std.testing.expectEqual(@as(usize, 1), app.shell.full_detail_records.items.len); const record = app.shell.full_detail_records.items[0].notice; try std.testing.expectEqualStrings("session", record.topic); try std.testing.expectEqual(types.NoticeTone.neutral, record.tone); try std.testing.expectEqual(types.NoticeVisibility.full_only, record.visibility); try std.testing.expect(std.mem.find(u8, record.body, "provider:") != null); try std.testing.expect(std.mem.find(u8, record.body, "model: model-x") != null); try std.testing.expect(std.mem.find(u8, record.body, "skills: 0 in catalog") != null); try std.testing.expect(std.mem.find(u8, record.body, "mcp: none") != null);}