const std = @import("std");const std_builtin = @import("builtin");const command_admission = @import("../permissions/command_admission.zig");const agent_runtime = @import("../agent/agent_runtime.zig");const app_lifecycle = @import("../app/app_lifecycle.zig");const app_runtime_setup = @import("../app/app_runtime_setup.zig");const auth_runtime = @import("../auth/auth_runtime.zig");const credentials = @import("../auth/credentials.zig");const oauth_transport = @import("../auth/oauth_transport.zig");const background_runtime = @import("../background/background_runtime.zig");const terminal_client_runtime = @import("../terminal/client.zig");const background_store = @import("../background/background_store.zig");const process_supervisor = @import("../background/process_supervisor.zig");const context_contract = @import("../workspace/context_contract.zig");const devbox_executor = @import("../execution/devbox_executor.zig");const gateway_provider = @import("../gateway/gateway_provider.zig");const background_process_provider = @import( "../execution/background_process_provider.zig",);const host = @import("../hosts/host.zig");const pathing = @import("../workspace/pathing.zig");const workspace_access = @import("../workspace/workspace_access.zig");const debug_trace = @import("../shared/debug_trace.zig");const diff_mod = @import("../output/diff.zig");const file_mutation = @import("../tooling/file_mutation.zig");const gateway_error_format = @import("../shared/gateway_error_format.zig");const image_attachments = @import("../images/image_attachments.zig");const hooks = @import("../hooks/hooks.zig");const notification_sound = @import("../notifications/sound.zig");const io_mod = @import("../shared/io.zig");const config_runtime = @import("../config/config_runtime.zig");const model_capabilities = @import("../config/model_capabilities.zig");const mcp_elicitation_interaction = @import("../mcp/elicitation_interaction.zig");const mcp_elicitation = @import("../mcp/elicitation.zig");const mcp_access_policy = @import("../mcp/access_policy.zig");const mcp_model_catalog = @import("../mcp/model_catalog.zig");const mcp_runtime = @import("../mcp/mcp_runtime.zig");const mode_registry = @import("../modes/mode_registry.zig");const permission_auto_classifier = @import("../permissions/auto_classifier.zig");const prompt_policy = @import("../config/prompt_policy.zig");const auto_classifier_context = @import("../permissions/auto_classifier_context.zig");const permission_gate = @import("../permissions/permission_gate.zig");const permission_request = @import("../permissions/permission_request.zig");const permissions = @import("../permissions/permissions.zig");const sandbox = @import("../permissions/sandbox.zig");const session_runtime = @import("../session/session.zig");const session_codec = @import("../session/session_codec.zig");const session_usage = @import("../session/session_usage.zig");const usage_report = @import("../session/usage_report.zig");const session_store = @import("../session/session_store.zig");const skill_contract = @import("../skills/skill_contract.zig");const skill_runtime = @import("../skills/skill_runtime.zig");const subagent_agent_adapter = @import("../subagent/agent_adapter.zig");const subagent_authority = @import("../subagent/authority.zig");const subagent_domain = @import("../subagent/domain.zig");const subagent_execution = @import("../subagent/execution.zig");const subagent_manager = @import("../subagent/manager.zig");const subagent_resume_admission = @import("../subagent/resume_admission.zig");const parent_delivery_projector = @import("../subagent/parent_delivery_projector.zig");const subagent_tool_host = @import("../subagent/tool_host.zig");const text_utils = @import("../shared/text_utils.zig");const test_builtin_gateway = if (std_builtin.is_test) @import("../../builtins/gateway.zig")else struct {};const test_builtin_tools = @import("../../builtins/tools.zig");const tool_advertisement = @import("../tooling/tool_advertisement.zig");const tool_admission = @import("../tooling/tool_admission.zig");const tool_args = @import("../tooling/tool_args.zig");const tool_dispatch = @import("../tooling/tool_dispatch.zig");const command_output_content = @import("../tooling/command_output_content.zig");const tool_mcp_runtime = @import("../tooling/tool_mcp_runtime.zig");const tool_presentation = @import("../tooling/tool_presentation.zig");const tool_result_errors = @import("../tooling/tool_result_errors.zig");const tool_runtime = @import("../tooling/tool_runtime.zig");const tool_set_contract = @import("../tooling/tool_set.zig");const tool_specs = @import("../tooling/tool_specs.zig");const skill_invocation = @import("../skills/skill_invocation.zig");const web_fetch_runtime = @import("../tooling/web_fetch_runtime.zig");const web_search_runtime = @import("../tooling/web_search_runtime.zig");const types = @import("../shared/types.zig");const worker_runtime = @import("../agent/worker_runtime.zig");const ask_presentation = @import("../../ui/ask_presentation.zig");const url_opener = @import("../hosts/url_opener.zig"); const Allocator = std.mem.Allocator;const BackgroundRuntime = background_runtime.BackgroundRuntime;const ChatMessage = types.ChatMessage;const HistoryTurn = types.HistoryTurn;const ImageAttachment = types.ImageAttachment;const PermissionGrant = types.PermissionGrant;const PermissionMode = types.PermissionMode;const SessionRuntime = session_runtime.SessionRuntime;const ToolCall = types.ToolCall;const ToolExecutionResult = agent_runtime.ToolExecutionResult;const ToolPermissionDecision = types.ToolPermissionDecision;const WorkerEvent = worker_runtime.WorkerEvent;const WorkerRuntime = worker_runtime.WorkerRuntime;const McpHasToolFn = tool_mcp_runtime.HasToolFn; const supports_headless_interrupt = switch (std_builtin.os.tag) { .windows, .wasi, .freestanding => false, else => true,}; const HeadlessInterruptInstallError = error{HeadlessInterruptBusy};const headless_interrupt_exit_code: u8 = 130;const headless_termination_exit_code: u8 = 143; const headless_interrupt = if (supports_headless_interrupt) struct { var coordinator_mutex: std.Io.Mutex = .init; var coordinator_active = false; var cancel_requested = std.atomic.Value(bool).init(false); var requested_signal = std.atomic.Value(u8).init(0); var test_after_reset_before_install: if (std_builtin.is_test) ?*const fn () void else void = if (std_builtin.is_test) null else {}; var test_after_restore: if (std_builtin.is_test) ?*const fn () void else void = if (std_builtin.is_test) null else {}; fn handle(signal: std.posix.SIG) callconv(.c) void { _ = requested_signal.cmpxchgStrong( 0, @intCast(@intFromEnum(signal)), .seq_cst, .seq_cst, ); cancel_requested.store(true, .seq_cst); } fn exitCode() u8 { return switch (@as(std.posix.SIG, @enumFromInt(requested_signal.load(.seq_cst)))) { std.posix.SIG.TERM => headless_termination_exit_code, else => headless_interrupt_exit_code, }; } const Scope = struct { old_sigint_action: std.posix.Sigaction = undefined, old_sigterm_action: std.posix.Sigaction = undefined, installed: bool = false, fn install(enabled: bool) HeadlessInterruptInstallError!Scope { if (!enabled) return .{}; coordinator_mutex.lockUncancelable(io_mod.getIo()); defer coordinator_mutex.unlock(io_mod.getIo()); if (coordinator_active) return error.HeadlessInterruptBusy; const action: std.posix.Sigaction = .{ .handler = .{ .handler = handle }, .mask = std.posix.sigemptyset(), .flags = 0, }; var scope = Scope{}; requested_signal.store(0, .seq_cst); cancel_requested.store(false, .seq_cst); if (std_builtin.is_test) { if (test_after_reset_before_install) |hook| hook(); } std.posix.sigaction(std.posix.SIG.INT, &action, &scope.old_sigint_action); std.posix.sigaction(std.posix.SIG.TERM, &action, &scope.old_sigterm_action); coordinator_active = true; scope.installed = true; return scope; } fn restore(self: *Scope, redeliver: bool) void { if (!self.installed) return; coordinator_mutex.lockUncancelable(io_mod.getIo()); defer coordinator_mutex.unlock(io_mod.getIo()); std.debug.assert(coordinator_active); std.posix.sigaction(std.posix.SIG.INT, &self.old_sigint_action, null); std.posix.sigaction(std.posix.SIG.TERM, &self.old_sigterm_action, null); if (std_builtin.is_test) { if (test_after_restore) |hook| hook(); } if (redeliver and cancel_requested.load(.seq_cst)) { const signal: std.posix.SIG = @enumFromInt(requested_signal.load(.seq_cst)); _ = std.c.raise(signal); } coordinator_active = false; self.installed = false; } fn deinit(self: *Scope) void { self.restore(false); } fn restoreAndRedeliver(self: *Scope) void { self.restore(true); } fn requested(self: *const Scope) bool { return self.installed and cancel_requested.load(.seq_cst); } };} else struct { fn exitCode() u8 { return headless_interrupt_exit_code; } const Scope = struct { fn install(_: bool) HeadlessInterruptInstallError!Scope { return .{}; } fn deinit(_: *Scope) void {} fn restoreAndRedeliver(_: *Scope) void {} fn requested(_: *const Scope) bool { return false; } };}; pub const Config = struct { command_usage: []const u8, default_model: []const u8, default_agent_step_limit: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, gateway_models_path: []const u8, gateway_provider: gateway_provider.Provider, background_process_provider: background_process_provider.Provider = background_process_provider.unavailable_provider, secret_store: host.SecretStore, prompt_policy: prompt_policy.Policy, skill_root_policy: skill_contract.RootPolicy, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, max_tool_result_bytes: usize, max_history_turns: usize, mode_registry: mode_registry.Registry, load_mcp_runtime: mcp_runtime.LoadRuntimeFn, devbox_provider: ?devbox_executor.Provider = null, permission_reviewer_provider: ?permission_auto_classifier.Provider = null, context_limit_overrides: []const config_runtime.context_limits.Override = &.{}, additional_directories: []const []const u8 = &.{}, saved_directories_suppressed: bool = false,}; fn runAskChild( raw: ?*anyopaque, turn: *subagent_execution.TurnContext, message: subagent_domain.QueuedMessage, admission: subagent_domain.AdmissionSnapshot, cancel: *std.atomic.Value(bool),) subagent_execution.ServiceError!subagent_execution.RunOutcome { const ctx: *AskContext = @ptrCast(@alignCast(raw.?)); var child_projection = ctx.cfg.mode_registry.buildGatewayToolProjection( ctx.alloc, ctx.deps.tool_set, ctx.mode_id, .{ .permission_mode = admission.permission_mode, .permission_rules = admission.rules, .mcp_runtime = ctx.mcp, .subagent_available = true, }, ) catch return error.OutOfMemory; defer child_projection.deinit(ctx.alloc); return subagent_agent_adapter.run(.{ .host = ctx.subagent_host orelse return error.ProviderFailed, .tool_context = ctx.toolContext(), .system_prompt = ctx.cfg.prompt_policy.system_prompt, .model_prompt_overlay = ctx.cfg.prompt_policy.modelPromptOverlay(admission.model), .skills_prompt_section = ctx.subagent_skills_prompt, .explicit_skills_prompt_section = ctx.subagent_explicit_skills_prompt, .gateway_tools_json = child_projection.tools_json, .custom_tool_guidance = child_projection.custom_guidance, .context_registry = ctx.deps.context_registry, .context_enabled = ctx.context_enabled, .project_context = ctx.modelVisibleProjectContext(), .lifecycle_view = ctx.lifecycle_view, }, turn, message, admission, cancel);} pub const PromptRunResult = struct { exit_code: u8, assistant_output: []u8, interrupted: bool = false, model: []u8 = &.{}, session_id: []u8 = &.{}, tool_calls: []ToolCallRecord = &.{}, step_count: usize = 0, error_code: ?[]const u8 = null, auth_failure: ?auth_runtime.FailureSnapshot = null, recovery: ?types.RouteRecoveryStatus = null, recovery_durable: bool = false, pub fn deinit(self: PromptRunResult, alloc: Allocator) void { alloc.free(self.assistant_output); if (self.model.len > 0) alloc.free(self.model); if (self.session_id.len > 0) alloc.free(self.session_id); freeToolCallRecords(alloc, self.tool_calls); }}; /// Resume selector parsed from fx ask --resume.const ResumeTarget = session_store.ResumeTarget; const AskOptions = struct { prompt: []u8, resume_target: ?ResumeTarget = null, permission_override: ?PermissionMode = null, image_paths: std.ArrayList([]u8) = .empty, images: std.ArrayList(ImageAttachment) = .empty, system_prompt_override: ?[]u8 = null, json_output: bool = false, timeout_ms: ?usize = null, quiet: bool = false, verbose: bool = false, no_save: bool = false, no_color: bool = false, continue_recovery: bool = false, fn deinit(self: *AskOptions, alloc: Allocator) void { alloc.free(self.prompt); for (self.image_paths.items) |p| alloc.free(p); self.image_paths.deinit(alloc); for (self.images.items) |image| types.freeImageAttachment(alloc, image); self.images.deinit(alloc); if (self.system_prompt_override) |s| alloc.free(s); }}; const ToolCallRecord = struct { name: []u8, status: []u8, command_result_json: ?[]u8 = null, ask_question_text: ?[]u8 = null, web_search_completion: ?types.WebSearchCompletion = null, web_fetch_completion: ?types.WebFetchCompletion = null,}; const PendingToolProgress = struct { call_id: []u8, tool_name: []u8, text: ?[]u8 = null, visible: bool = false, fn deinit(self: PendingToolProgress, alloc: Allocator) void { alloc.free(self.call_id); alloc.free(self.tool_name); if (self.text) |text| alloc.free(text); }}; fn freeToolCallRecord(alloc: Allocator, record: ToolCallRecord) void { alloc.free(record.name); alloc.free(record.status); if (record.command_result_json) |json| alloc.free(json); if (record.ask_question_text) |text| alloc.free(text);} fn freeToolCallRecords(alloc: Allocator, records: []ToolCallRecord) void { for (records) |record| freeToolCallRecord(alloc, record); if (records.len > 0) alloc.free(records);} const WriteFn = *const fn (?*anyopaque, []const u8) anyerror!void;const PermissionApprovalPromptResult = enum { approve, deny, unavailable,};const NotifyAttentionFn = *const fn (?*anyopaque) void;const PermissionApprovalPromptFn = *const fn (?*anyopaque, ?*anyopaque, WriteFn, []const u8, ?*anyopaque, NotifyAttentionFn) anyerror!PermissionApprovalPromptResult;const IsTtyFn = *const fn (?*anyopaque) bool;const LoadStartupStateFn = *const fn (Allocator, oauth_transport.Provider, host.SecretStore, []const u8, usize) anyerror!app_lifecycle.StartupState;const InitializeSessionStoresFn = *const fn (*AskContext) anyerror!void;const LoadSkillsFn = *const fn ( Allocator, []const u8, skill_contract.RootPolicy,) app_runtime_setup.LoadSkillsError!app_runtime_setup.LoadedSkills;const ProcessQueuedPromptFn = *const fn (*const agent_runtime.AgentRuntimeDeps, ?agent_runtime.SemanticPresentationSink, agent_runtime.LifecycleContext, agent_runtime.Config, worker_runtime.QueuedPrompt) anyerror!void;const DiscardPristineSessionFn = *const fn (?*anyopaque, *AskContext, *session_store.LoadedWritableSession) session_store.PristineDiscardDisposition;const PersistYoloAcknowledgmentFn = *const fn (Allocator) config_runtime.CommitAttempt; const RunDeps = struct { stdin_ctx: ?*anyopaque = null, stdout_ctx: ?*anyopaque = null, stderr_ctx: ?*anyopaque = null, permission_approval_prompt_ctx: ?*anyopaque = null, write_stdout: WriteFn = writeRealStdout, write_stderr: WriteFn = writeRealStderr, permission_approval_prompt: PermissionApprovalPromptFn = promptRealPermissionApproval, stdin_is_tty: IsTtyFn = realStdinIsTty, stdout_is_tty: IsTtyFn = realStdoutIsTty, stderr_is_tty: IsTtyFn = realStderrIsTty, load_startup_state: LoadStartupStateFn = loadStartupStateDefault, initialize_session_stores: InitializeSessionStoresFn = initializeSessionStoresDefault, load_skills: LoadSkillsFn = app_runtime_setup.loadSkills, context_registry: context_contract.Registry, tool_set: tool_set_contract.ToolSet, load_mcp_runtime: mcp_runtime.LoadRuntimeFn, process_queued_prompt: ProcessQueuedPromptFn = processQueuedPromptDefault, persist_yolo_acknowledgment: PersistYoloAcknowledgmentFn = persistYoloAcknowledgmentDefault, discard_pristine_session_ctx: ?*anyopaque = null, discard_pristine_session: DiscardPristineSessionFn = discardPristineSessionDefault, install_headless_interrupt: bool = false, start_subagent_background_recovery: bool = true, stdin_source: StdinSource = .real,}; const OutputMode = enum { quiet, json, raw, terminal, terminal_no_color, fn isTerminal(self: OutputMode) bool { return self == .terminal or self == .terminal_no_color; } fn capturesJson(self: OutputMode) bool { return self == .json; } fn permitsPermissionPrompt(self: OutputMode) bool { return self == .raw or self.isTerminal(); }}; const RunOptions = struct { output_mode: OutputMode, images: []const ImageAttachment = &.{}, command_timeout_ms: ?usize = null, save_session: bool = true, resume_target: ?ResumeTarget = null, color_enabled: bool = true, continue_recovery: bool = false, deps: RunDeps,}; const StdinSource = union(enum) { real, tty, bytes: []const u8, read_error,}; const stdin_prompt_resource_byte_limit: usize = 8 * 1024 * 1024; const AskContext = struct { alloc: Allocator, cfg: Config, deps: RunDeps, workspace_root: []const u8, workspace_access: workspace_access.WorkspaceAccess = .{}, api_key: []const u8 = "", gateway_team: ?[]const u8 = null, credential_source: ?types.CredentialSource = null, model_catalog_access: credentials.CatalogAccess = .{ .public_only = .no_credential }, model: []const u8 = "", agent_step_limit: usize = 0, max_tool_result_bytes: usize = 64 * 1024, context_limits: config_runtime.context_limits.Values = .{}, fast_mode: bool = false, effort: types.ReasoningEffort = .auto, first_call_tool_choice: types.ToolChoice = .auto, permission_mode: PermissionMode = .ask, permission_rules: types.PermissionRuleSet = .{}, mode_id: []const u8, auto_classifier: permission_auto_classifier.Classifier = permission_auto_classifier.Classifier.disabled(), sandbox_backend: sandbox.BackendKind = .none, worker: WorkerRuntime = .{}, use_process_interrupt_flag: bool = false, background: BackgroundRuntime = .{}, terminal_client: terminal_client_runtime.Runtime = .{}, subagent_host: ?*subagent_tool_host.Runtime = null, subagent_skills_prompt: []u8 = &.{}, subagent_explicit_skills_prompt: []u8 = &.{}, store: ?session_store.Store = null, writable: ?session_store.LoadedWritableSession = null, session_write_mutex: std.Io.Mutex = .init, requested_resume: ?ResumeTarget = null, seed_model: []const u8 = "", command_timeout_ms: ?usize = null, session: SessionRuntime, skills_dir: []u8 = &.{}, context_snapshot: context_contract.GatheredContextSnapshot = .{}, context_enabled: bool = true, mcp: ?*mcp_runtime.McpRuntime = null, mcp_elicitation_capabilities: @import("../mcp/elicitation.zig").Capabilities = .{}, failed: bool = false, typed_error_code: ?[]const u8 = null, auth_failure: ?auth_runtime.FailureSnapshot = null, output_mode: OutputMode = .raw, presenter: ?*ask_presentation.Runtime = null, pending_tool_progress: std.ArrayList(PendingToolProgress) = .empty, deferred_tool_progress: std.ArrayList([]u8) = .empty, pending_tool_progress_mutex: std.Io.Mutex = .init,Lines 1–500 of 9053. Load more
const std = @import("std"); const std_builtin = @import("builtin"); const command_admission = @import("../permissions/command_admission.zig"); const agent_runtime = @import("../agent/agent_runtime.zig"); const app_lifecycle = @import("../app/app_lifecycle.zig"); const app_runtime_setup = @import("../app/app_runtime_setup.zig"); const auth_runtime = @import("../auth/auth_runtime.zig"); const credentials = @import("../auth/credentials.zig"); const oauth_transport = @import("../auth/oauth_transport.zig"); const background_runtime = @import("../background/background_runtime.zig"); const terminal_client_runtime = @import("../terminal/client.zig"); const background_store = @import("../background/background_store.zig"); const process_supervisor = @import("../background/process_supervisor.zig"); const context_contract = @import("../workspace/context_contract.zig"); const devbox_executor = @import("../execution/devbox_executor.zig"); const gateway_provider = @import("../gateway/gateway_provider.zig"); const background_process_provider = @import( "../execution/background_process_provider.zig", ); const host = @import("../hosts/host.zig"); const pathing = @import("../workspace/pathing.zig"); const workspace_access = @import("../workspace/workspace_access.zig"); const debug_trace = @import("../shared/debug_trace.zig"); const diff_mod = @import("../output/diff.zig"); const file_mutation = @import("../tooling/file_mutation.zig"); const gateway_error_format = @import("../shared/gateway_error_format.zig"); const image_attachments = @import("../images/image_attachments.zig"); const hooks = @import("../hooks/hooks.zig"); const notification_sound = @import("../notifications/sound.zig"); const io_mod = @import("../shared/io.zig"); const config_runtime = @import("../config/config_runtime.zig"); const model_capabilities = @import("../config/model_capabilities.zig"); const mcp_elicitation_interaction = @import("../mcp/elicitation_interaction.zig"); const mcp_elicitation = @import("../mcp/elicitation.zig"); const mcp_access_policy = @import("../mcp/access_policy.zig"); const mcp_model_catalog = @import("../mcp/model_catalog.zig"); const mcp_runtime = @import("../mcp/mcp_runtime.zig"); const mode_registry = @import("../modes/mode_registry.zig"); const permission_auto_classifier = @import("../permissions/auto_classifier.zig"); const prompt_policy = @import("../config/prompt_policy.zig"); const auto_classifier_context = @import("../permissions/auto_classifier_context.zig"); const permission_gate = @import("../permissions/permission_gate.zig"); const permission_request = @import("../permissions/permission_request.zig"); const permissions = @import("../permissions/permissions.zig"); const sandbox = @import("../permissions/sandbox.zig"); const session_runtime = @import("../session/session.zig"); const session_codec = @import("../session/session_codec.zig"); const session_usage = @import("../session/session_usage.zig"); const usage_report = @import("../session/usage_report.zig"); const session_store = @import("../session/session_store.zig"); const skill_contract = @import("../skills/skill_contract.zig"); const skill_runtime = @import("../skills/skill_runtime.zig"); const subagent_agent_adapter = @import("../subagent/agent_adapter.zig"); const subagent_authority = @import("../subagent/authority.zig"); const subagent_domain = @import("../subagent/domain.zig"); const subagent_execution = @import("../subagent/execution.zig"); const subagent_manager = @import("../subagent/manager.zig"); const subagent_resume_admission = @import("../subagent/resume_admission.zig"); const parent_delivery_projector = @import("../subagent/parent_delivery_projector.zig"); const subagent_tool_host = @import("../subagent/tool_host.zig"); const text_utils = @import("../shared/text_utils.zig"); const test_builtin_gateway = if (std_builtin.is_test) @import("../../builtins/gateway.zig") else struct {}; const test_builtin_tools = @import("../../builtins/tools.zig"); const tool_advertisement = @import("../tooling/tool_advertisement.zig"); const tool_admission = @import("../tooling/tool_admission.zig"); const tool_args = @import("../tooling/tool_args.zig"); const tool_dispatch = @import("../tooling/tool_dispatch.zig"); const command_output_content = @import("../tooling/command_output_content.zig"); const tool_mcp_runtime = @import("../tooling/tool_mcp_runtime.zig"); const tool_presentation = @import("../tooling/tool_presentation.zig"); const tool_result_errors = @import("../tooling/tool_result_errors.zig"); const tool_runtime = @import("../tooling/tool_runtime.zig"); const tool_set_contract = @import("../tooling/tool_set.zig"); const tool_specs = @import("../tooling/tool_specs.zig"); const skill_invocation = @import("../skills/skill_invocation.zig"); const web_fetch_runtime = @import("../tooling/web_fetch_runtime.zig"); const web_search_runtime = @import("../tooling/web_search_runtime.zig"); const types = @import("../shared/types.zig"); const worker_runtime = @import("../agent/worker_runtime.zig"); const ask_presentation = @import("../../ui/ask_presentation.zig"); const url_opener = @import("../hosts/url_opener.zig"); const Allocator = std.mem.Allocator; const BackgroundRuntime = background_runtime.BackgroundRuntime; const ChatMessage = types.ChatMessage; const HistoryTurn = types.HistoryTurn; const ImageAttachment = types.ImageAttachment; const PermissionGrant = types.PermissionGrant; const PermissionMode = types.PermissionMode; const SessionRuntime = session_runtime.SessionRuntime; const ToolCall = types.ToolCall; const ToolExecutionResult = agent_runtime.ToolExecutionResult; const ToolPermissionDecision = types.ToolPermissionDecision; const WorkerEvent = worker_runtime.WorkerEvent; const WorkerRuntime = worker_runtime.WorkerRuntime; const McpHasToolFn = tool_mcp_runtime.HasToolFn; const supports_headless_interrupt = switch (std_builtin.os.tag) { .windows, .wasi, .freestanding => false, else => true, }; const HeadlessInterruptInstallError = error{HeadlessInterruptBusy}; const headless_interrupt_exit_code: u8 = 130; const headless_termination_exit_code: u8 = 143; const headless_interrupt = if (supports_headless_interrupt) struct { var coordinator_mutex: std.Io.Mutex = .init; var coordinator_active = false; var cancel_requested = std.atomic.Value(bool).init(false); var requested_signal = std.atomic.Value(u8).init(0); var test_after_reset_before_install: if (std_builtin.is_test) ?*const fn () void else void = if (std_builtin.is_test) null else {}; var test_after_restore: if (std_builtin.is_test) ?*const fn () void else void = if (std_builtin.is_test) null else {}; fn handle(signal: std.posix.SIG) callconv(.c) void { _ = requested_signal.cmpxchgStrong( 0, @intCast(@intFromEnum(signal)), .seq_cst, .seq_cst, ); cancel_requested.store(true, .seq_cst); } fn exitCode() u8 { return switch (@as(std.posix.SIG, @enumFromInt(requested_signal.load(.seq_cst)))) { std.posix.SIG.TERM => headless_termination_exit_code, else => headless_interrupt_exit_code, }; } const Scope = struct { old_sigint_action: std.posix.Sigaction = undefined, old_sigterm_action: std.posix.Sigaction = undefined, installed: bool = false, fn install(enabled: bool) HeadlessInterruptInstallError!Scope { if (!enabled) return .{}; coordinator_mutex.lockUncancelable(io_mod.getIo()); defer coordinator_mutex.unlock(io_mod.getIo()); if (coordinator_active) return error.HeadlessInterruptBusy; const action: std.posix.Sigaction = .{ .handler = .{ .handler = handle }, .mask = std.posix.sigemptyset(), .flags = 0, }; var scope = Scope{}; requested_signal.store(0, .seq_cst); cancel_requested.store(false, .seq_cst); if (std_builtin.is_test) { if (test_after_reset_before_install) |hook| hook(); } std.posix.sigaction(std.posix.SIG.INT, &action, &scope.old_sigint_action); std.posix.sigaction(std.posix.SIG.TERM, &action, &scope.old_sigterm_action); coordinator_active = true; scope.installed = true; return scope; } fn restore(self: *Scope, redeliver: bool) void { if (!self.installed) return; coordinator_mutex.lockUncancelable(io_mod.getIo()); defer coordinator_mutex.unlock(io_mod.getIo()); std.debug.assert(coordinator_active); std.posix.sigaction(std.posix.SIG.INT, &self.old_sigint_action, null); std.posix.sigaction(std.posix.SIG.TERM, &self.old_sigterm_action, null); if (std_builtin.is_test) { if (test_after_restore) |hook| hook(); } if (redeliver and cancel_requested.load(.seq_cst)) { const signal: std.posix.SIG = @enumFromInt(requested_signal.load(.seq_cst)); _ = std.c.raise(signal); } coordinator_active = false; self.installed = false; } fn deinit(self: *Scope) void { self.restore(false); } fn restoreAndRedeliver(self: *Scope) void { self.restore(true); } fn requested(self: *const Scope) bool { return self.installed and cancel_requested.load(.seq_cst); } }; } else struct { fn exitCode() u8 { return headless_interrupt_exit_code; } const Scope = struct { fn install(_: bool) HeadlessInterruptInstallError!Scope { return .{}; } fn deinit(_: *Scope) void {} fn restoreAndRedeliver(_: *Scope) void {} fn requested(_: *const Scope) bool { return false; } }; }; pub const Config = struct { command_usage: []const u8, default_model: []const u8, default_agent_step_limit: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, gateway_models_path: []const u8, gateway_provider: gateway_provider.Provider, background_process_provider: background_process_provider.Provider = background_process_provider.unavailable_provider, secret_store: host.SecretStore, prompt_policy: prompt_policy.Policy, skill_root_policy: skill_contract.RootPolicy, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, max_tool_result_bytes: usize, max_history_turns: usize, mode_registry: mode_registry.Registry, load_mcp_runtime: mcp_runtime.LoadRuntimeFn, devbox_provider: ?devbox_executor.Provider = null, permission_reviewer_provider: ?permission_auto_classifier.Provider = null, context_limit_overrides: []const config_runtime.context_limits.Override = &.{}, additional_directories: []const []const u8 = &.{}, saved_directories_suppressed: bool = false, }; fn runAskChild( raw: ?*anyopaque, turn: *subagent_execution.TurnContext, message: subagent_domain.QueuedMessage, admission: subagent_domain.AdmissionSnapshot, cancel: *std.atomic.Value(bool), ) subagent_execution.ServiceError!subagent_execution.RunOutcome { const ctx: *AskContext = @ptrCast(@alignCast(raw.?)); var child_projection = ctx.cfg.mode_registry.buildGatewayToolProjection( ctx.alloc, ctx.deps.tool_set, ctx.mode_id, .{ .permission_mode = admission.permission_mode, .permission_rules = admission.rules, .mcp_runtime = ctx.mcp, .subagent_available = true, }, ) catch return error.OutOfMemory; defer child_projection.deinit(ctx.alloc); return subagent_agent_adapter.run(.{ .host = ctx.subagent_host orelse return error.ProviderFailed, .tool_context = ctx.toolContext(), .system_prompt = ctx.cfg.prompt_policy.system_prompt, .model_prompt_overlay = ctx.cfg.prompt_policy.modelPromptOverlay(admission.model), .skills_prompt_section = ctx.subagent_skills_prompt, .explicit_skills_prompt_section = ctx.subagent_explicit_skills_prompt, .gateway_tools_json = child_projection.tools_json, .custom_tool_guidance = child_projection.custom_guidance, .context_registry = ctx.deps.context_registry, .context_enabled = ctx.context_enabled, .project_context = ctx.modelVisibleProjectContext(), .lifecycle_view = ctx.lifecycle_view, }, turn, message, admission, cancel); } pub const PromptRunResult = struct { exit_code: u8, assistant_output: []u8, interrupted: bool = false, model: []u8 = &.{}, session_id: []u8 = &.{}, tool_calls: []ToolCallRecord = &.{}, step_count: usize = 0, error_code: ?[]const u8 = null, auth_failure: ?auth_runtime.FailureSnapshot = null, recovery: ?types.RouteRecoveryStatus = null, recovery_durable: bool = false, pub fn deinit(self: PromptRunResult, alloc: Allocator) void { alloc.free(self.assistant_output); if (self.model.len > 0) alloc.free(self.model); if (self.session_id.len > 0) alloc.free(self.session_id); freeToolCallRecords(alloc, self.tool_calls); } }; /// Resume selector parsed from fx ask --resume. const ResumeTarget = session_store.ResumeTarget; const AskOptions = struct { prompt: []u8, resume_target: ?ResumeTarget = null, permission_override: ?PermissionMode = null, image_paths: std.ArrayList([]u8) = .empty, images: std.ArrayList(ImageAttachment) = .empty, system_prompt_override: ?[]u8 = null, json_output: bool = false, timeout_ms: ?usize = null, quiet: bool = false, verbose: bool = false, no_save: bool = false, no_color: bool = false, continue_recovery: bool = false, fn deinit(self: *AskOptions, alloc: Allocator) void { alloc.free(self.prompt); for (self.image_paths.items) |p| alloc.free(p); self.image_paths.deinit(alloc); for (self.images.items) |image| types.freeImageAttachment(alloc, image); self.images.deinit(alloc); if (self.system_prompt_override) |s| alloc.free(s); } }; const ToolCallRecord = struct { name: []u8, status: []u8, command_result_json: ?[]u8 = null, ask_question_text: ?[]u8 = null, web_search_completion: ?types.WebSearchCompletion = null, web_fetch_completion: ?types.WebFetchCompletion = null, }; const PendingToolProgress = struct { call_id: []u8, tool_name: []u8, text: ?[]u8 = null, visible: bool = false, fn deinit(self: PendingToolProgress, alloc: Allocator) void { alloc.free(self.call_id); alloc.free(self.tool_name); if (self.text) |text| alloc.free(text); } }; fn freeToolCallRecord(alloc: Allocator, record: ToolCallRecord) void { alloc.free(record.name); alloc.free(record.status); if (record.command_result_json) |json| alloc.free(json); if (record.ask_question_text) |text| alloc.free(text); } fn freeToolCallRecords(alloc: Allocator, records: []ToolCallRecord) void { for (records) |record| freeToolCallRecord(alloc, record); if (records.len > 0) alloc.free(records); } const WriteFn = *const fn (?*anyopaque, []const u8) anyerror!void; const PermissionApprovalPromptResult = enum { approve, deny, unavailable, }; const NotifyAttentionFn = *const fn (?*anyopaque) void; const PermissionApprovalPromptFn = *const fn (?*anyopaque, ?*anyopaque, WriteFn, []const u8, ?*anyopaque, NotifyAttentionFn) anyerror!PermissionApprovalPromptResult; const IsTtyFn = *const fn (?*anyopaque) bool; const LoadStartupStateFn = *const fn (Allocator, oauth_transport.Provider, host.SecretStore, []const u8, usize) anyerror!app_lifecycle.StartupState; const InitializeSessionStoresFn = *const fn (*AskContext) anyerror!void; const LoadSkillsFn = *const fn ( Allocator, []const u8, skill_contract.RootPolicy, ) app_runtime_setup.LoadSkillsError!app_runtime_setup.LoadedSkills; const ProcessQueuedPromptFn = *const fn (*const agent_runtime.AgentRuntimeDeps, ?agent_runtime.SemanticPresentationSink, agent_runtime.LifecycleContext, agent_runtime.Config, worker_runtime.QueuedPrompt) anyerror!void; const DiscardPristineSessionFn = *const fn (?*anyopaque, *AskContext, *session_store.LoadedWritableSession) session_store.PristineDiscardDisposition; const PersistYoloAcknowledgmentFn = *const fn (Allocator) config_runtime.CommitAttempt; const RunDeps = struct { stdin_ctx: ?*anyopaque = null, stdout_ctx: ?*anyopaque = null, stderr_ctx: ?*anyopaque = null, permission_approval_prompt_ctx: ?*anyopaque = null, write_stdout: WriteFn = writeRealStdout, write_stderr: WriteFn = writeRealStderr, permission_approval_prompt: PermissionApprovalPromptFn = promptRealPermissionApproval, stdin_is_tty: IsTtyFn = realStdinIsTty, stdout_is_tty: IsTtyFn = realStdoutIsTty, stderr_is_tty: IsTtyFn = realStderrIsTty, load_startup_state: LoadStartupStateFn = loadStartupStateDefault, initialize_session_stores: InitializeSessionStoresFn = initializeSessionStoresDefault, load_skills: LoadSkillsFn = app_runtime_setup.loadSkills, context_registry: context_contract.Registry, tool_set: tool_set_contract.ToolSet, load_mcp_runtime: mcp_runtime.LoadRuntimeFn, process_queued_prompt: ProcessQueuedPromptFn = processQueuedPromptDefault, persist_yolo_acknowledgment: PersistYoloAcknowledgmentFn = persistYoloAcknowledgmentDefault, discard_pristine_session_ctx: ?*anyopaque = null, discard_pristine_session: DiscardPristineSessionFn = discardPristineSessionDefault, install_headless_interrupt: bool = false, start_subagent_background_recovery: bool = true, stdin_source: StdinSource = .real, }; const OutputMode = enum { quiet, json, raw, terminal, terminal_no_color, fn isTerminal(self: OutputMode) bool { return self == .terminal or self == .terminal_no_color; } fn capturesJson(self: OutputMode) bool { return self == .json; } fn permitsPermissionPrompt(self: OutputMode) bool { return self == .raw or self.isTerminal(); } }; const RunOptions = struct { output_mode: OutputMode, images: []const ImageAttachment = &.{}, command_timeout_ms: ?usize = null, save_session: bool = true, resume_target: ?ResumeTarget = null, color_enabled: bool = true, continue_recovery: bool = false, deps: RunDeps, }; const StdinSource = union(enum) { real, tty, bytes: []const u8, read_error, }; const stdin_prompt_resource_byte_limit: usize = 8 * 1024 * 1024; const AskContext = struct { alloc: Allocator, cfg: Config, deps: RunDeps, workspace_root: []const u8, workspace_access: workspace_access.WorkspaceAccess = .{}, api_key: []const u8 = "", gateway_team: ?[]const u8 = null, credential_source: ?types.CredentialSource = null, model_catalog_access: credentials.CatalogAccess = .{ .public_only = .no_credential }, model: []const u8 = "", agent_step_limit: usize = 0, max_tool_result_bytes: usize = 64 * 1024, context_limits: config_runtime.context_limits.Values = .{}, fast_mode: bool = false, effort: types.ReasoningEffort = .auto, first_call_tool_choice: types.ToolChoice = .auto, permission_mode: PermissionMode = .ask, permission_rules: types.PermissionRuleSet = .{}, mode_id: []const u8, auto_classifier: permission_auto_classifier.Classifier = permission_auto_classifier.Classifier.disabled(), sandbox_backend: sandbox.BackendKind = .none, worker: WorkerRuntime = .{}, use_process_interrupt_flag: bool = false, background: BackgroundRuntime = .{}, terminal_client: terminal_client_runtime.Runtime = .{}, subagent_host: ?*subagent_tool_host.Runtime = null, subagent_skills_prompt: []u8 = &.{}, subagent_explicit_skills_prompt: []u8 = &.{}, store: ?session_store.Store = null, writable: ?session_store.LoadedWritableSession = null, session_write_mutex: std.Io.Mutex = .init, requested_resume: ?ResumeTarget = null, seed_model: []const u8 = "", command_timeout_ms: ?usize = null, session: SessionRuntime, skills_dir: []u8 = &.{}, context_snapshot: context_contract.GatheredContextSnapshot = .{}, context_enabled: bool = true, mcp: ?*mcp_runtime.McpRuntime = null, mcp_elicitation_capabilities: @import("../mcp/elicitation.zig").Capabilities = .{}, failed: bool = false, typed_error_code: ?[]const u8 = null, auth_failure: ?auth_runtime.FailureSnapshot = null, output_mode: OutputMode = .raw, presenter: ?*ask_presentation.Runtime = null, pending_tool_progress: std.ArrayList(PendingToolProgress) = .empty, deferred_tool_progress: std.ArrayList([]u8) = .empty, pending_tool_progress_mutex: std.Io.Mutex = .init,