const std = @import("std");const builtin = @import("builtin");const agent_stream_provider = @import("../agent/stream_provider.zig");const auth_runtime = @import("../auth/auth_runtime.zig");const oauth_transport = @import("../auth/oauth_transport.zig");const command_contract = @import("../execution/command_contract.zig");const command_environment = @import("../execution/command_environment.zig");const background_process_provider = @import( "../execution/background_process_provider.zig",);const debug_trace = @import("../shared/debug_trace.zig");const diagnostics = @import("../workspace/diagnostics.zig");const image_attachments = @import("../images/image_attachments.zig");const io_mod = @import("../shared/io.zig");const tool_contracts = @import("../agent/runtime/tool_contracts.zig");const vision_executor = @import("../agent/runtime/vision_executor.zig");const background_runtime = @import("../background/background_runtime.zig");const background_launch_identity = @import("../background/background_launch_identity.zig");const process_supervisor = @import("../background/process_supervisor.zig");const change_tracker = @import("../workspace/change_tracker.zig");const diff_mod = @import("../output/diff.zig");const file_mutation = @import("file_mutation.zig");const file_mutation_contract = @import("file_mutation_contract.zig");const hooks = @import("../hooks/hooks.zig");const permission_auto_classifier = @import("../permissions/auto_classifier.zig");const glob_pattern = @import("../workspace/glob_pattern.zig");const permission_prompter = @import("../permissions/permission_prompter.zig");const permission_request = @import("../permissions/permission_request.zig");const command_admission = @import("../permissions/command_admission.zig");const pathing = @import("../workspace/pathing.zig");const devbox_executor = @import("../execution/devbox_executor.zig");const execution_router = @import("../execution/router.zig");const sandbox = @import("../permissions/sandbox.zig");const skill_runtime = @import("../skills/skill_runtime.zig");const subagent_authority = @import("../subagent/authority.zig");const subagent_communication_store = @import("../subagent/communication_store.zig");const subagent_control_store = @import("../subagent/control_store.zig");const subagent_create_store = @import("../subagent/create_store.zig");const subagent_domain = @import("../subagent/domain.zig");const subagent_tool_host = @import("../subagent/tool_host.zig");const subagent_tool_provider = @import("../subagent/tool_provider.zig");const subagent_tool_result = @import("../subagent/tool_result.zig");const session_runtime = @import("../session/session.zig");const session_permission_state = @import("../permissions/session_permission_state.zig");const session_codec_mod = @import("../session/session_codec.zig");const task_helpers = @import("../tasks/task_helpers.zig");const session_child_store = @import("../session/session_child_store.zig");const command_replay_store = @import("../session/command_replay_store.zig");const session_store = @import("../session/session_store.zig");const text_utils = @import("../shared/text_utils.zig");const model_capabilities = @import("../config/model_capabilities.zig");const mcp_access_policy = @import("../mcp/access_policy.zig");const tool_admission = @import("tool_admission.zig");const tool_args = @import("tool_args.zig");const command_result_mapping = @import("command_result_mapping.zig");const tool_dispatch = @import("tool_dispatch.zig");const tool_specs = @import("tool_specs.zig");const tool_result_errors = @import("tool_result_errors.zig");const tool_result_limits = @import("tool_result_limits.zig");const file_mutation_execution = @import("file_mutation_execution.zig");const tool_mcp_registry = @import("tool_mcp_registry.zig");const tool_mcp_runtime = @import("tool_mcp_runtime.zig");const tool_mcp_feature_dispatch = @import("tool_mcp_feature_dispatch.zig");const tool_presentation = @import("tool_presentation.zig");const web_fetch_runtime = @import("web_fetch_runtime.zig");const web_search_contract = @import("web_search_contract.zig");const web_fetch_artifacts = @import("../session/web_fetch_artifacts.zig");const types = @import("../shared/types.zig");const worker_runtime = @import("../agent/worker_runtime.zig");const context_contract = @import("../workspace/context_contract.zig");const test_builtin_tools = if (builtin.is_test) @import("../../builtins/tools.zig")else struct {};const test_builtin_gateway = if (builtin.is_test) @import("../../builtins/gateway.zig")else struct {};const test_browser_workspace_tools = if (builtin.is_test) @import("../../builtins/browser_workspace_tools.zig")else struct {};const js_host_workspace = @import("../hosts/js_host_workspace.zig"); const agent_test_support = if (builtin.is_test) @import("../agent/runtime/tests/support.zig")else struct {}; const Allocator = std.mem.Allocator;const ToolCall = types.ToolCall;const ChatMessage = types.ChatMessage; const PermissionGrant = types.PermissionGrant;const PermissionMode = types.PermissionMode;const ToolPermissionDecision = types.ToolPermissionDecision;const subagent_tool_name = "subagent";const ToolExecutionResult = tool_contracts.ToolExecutionResult;const BackgroundRuntime = background_runtime.BackgroundRuntime;const SessionRuntime = session_runtime.SessionRuntime;const WorkerRuntime = worker_runtime.WorkerRuntime;const max_file_mutation_success_bytes: usize = 8 * 1024; const helpers = struct { const requiredStringArg = tool_args.requiredStringArg; const parseToolArgsObject = tool_args.parseToolArgsObject;}; const optionalIntArg = tool_args.optionalIntArg;const parseToolArgsObject = helpers.parseToolArgsObject;const context_limits = @import("../config/context_limits.zig");const workspace_access = @import("../workspace/workspace_access.zig");const host_capabilities = @import("../hosts/host.zig");const terminal_client_runtime = @import("../terminal/client.zig"); test { _ = tool_admission; _ = command_result_mapping; _ = @import("../session/command_replay_store.zig"); _ = file_mutation_execution; _ = tool_presentation;} pub const Context = struct { workspace_root: []const u8, access_scope: ?workspace_access.AccessScope = null, 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 = tool_result_limits.default_max_tool_result_bytes, api_key: []const u8, agent_stream_provider: agent_stream_provider.Provider = agent_stream_provider.unavailable_provider, gateway_team: ?[]const u8 = null, credential_source: ?types.CredentialSource = null, oauth_transport: oauth_transport.Provider = oauth_transport.unavailable_provider, model: []const u8, permission_review_turn: ?permission_auto_classifier.ReviewTurnContext = null, root_user_intent_context: []const u8 = "", root_user_messages: []const []const u8 = &.{}, root_user_evidence_complete: bool = false, current_turn_messages: []const ChatMessage = &.{}, gateway_retry_count: usize, gateway_chat_url: []const u8, gateway_models_path: []const u8 = "/v1/models", agent_step_limit: usize, fast_mode: bool = false, effort: types.ReasoningEffort = .auto, first_call_tool_choice: types.ToolChoice = .auto, tool_registry: tool_dispatch.Registry = .{}, subagent_host: ?*subagent_tool_host.Runtime = null, subagent_caller_id: ?[]const u8 = null, permission_mode: PermissionMode, permission_grants: []const PermissionGrant, session_grants: []const PermissionGrant = &.{}, permission_rules: types.PermissionRuleSet, permission_state_override: ?*const session_permission_state.State = null, worker: *WorkerRuntime, /// Sole prompt capability admission consults. When null, admission never /// prompts: it resolves by rule, automatic review, or fail-closed denial /// (e.g. ACP hosts prompt over JSON-RPC by setting this). permission_prompter: ?permission_prompter.Prompter = null, cancel_flag: ?*std.atomic.Value(bool) = null, background: *BackgroundRuntime, session: *SessionRuntime, session_allocator: Allocator = std.heap.c_allocator, skills_dir: []const u8 = "", context_limits: context_limits.Values = .{}, context_registry: context_contract.Registry, context_enabled: bool = true, output_chunk_lifecycle_id: ?types.ToolLifecycleId = null, output_chunk_ctx: *anyopaque, on_output_chunk: command_contract.CommandOutputCallback, background_url_ctx: *anyopaque, on_background_url_ready: *const fn (*anyopaque, u64, []const u8) void, command_artifact_dir: ?[]const u8 = null, tool_result_dir: ?[]const u8 = null, session_child_capability: ?*session_child_store.SessionChildCapability = null, terminal_client: ?*terminal_client_runtime.Runtime = null, command_timeout_ms: ?usize = null, command_timeout_started_ms: ?i64 = null, command_replay_capture: ?*command_replay_store.Capture = null, command_replay_unavailable: bool = false, sandbox_backend: sandbox.BackendKind = .none, devbox_provider: ?devbox_executor.Provider = null, tracker: ?*change_tracker.ChangeTracker = null, mcp_ctx: ?*anyopaque = null, mcp_has_tool: ?tool_mcp_runtime.HasToolFn = null, mcp_validate_tool: ?tool_mcp_runtime.ValidateToolFn = null, mcp_call_tool: ?tool_mcp_runtime.CallToolFn = null, mcp_search_tools: ?tool_mcp_runtime.SearchToolsFn = null, mcp_tool_schema: ?tool_mcp_runtime.ToolSchemaFn = null, mcp_call_feature: ?tool_mcp_runtime.FeatureCallFn = null, mcp_access: tool_mcp_runtime.Access = .unrestricted, mcp_input_responder: ?tool_mcp_runtime.InputResponder = null, mcp_progress_ctx: ?*anyopaque = null, on_mcp_progress: ?*const fn (*anyopaque, types.ToolLifecycleId, []const u8) void = null, advertised_dynamic_tool_names: []const []const u8 = &.{}, permission_reviewer_provider: ?permission_auto_classifier.Provider = null, auto_classifier: permission_auto_classifier.Classifier = permission_auto_classifier.Classifier.disabled(), web_fetch_runtime: ?*web_fetch_runtime.Runtime = null, web_fetch_artifact_store: ?*web_fetch_artifacts.Store = null, web_fetch_artifact_error: ?anyerror = null, web_search_runtime_ready: bool = false, web_search_backend: ?tool_dispatch.WebSearchBackend = null, web_search_progress_ctx: ?*anyopaque = null, on_web_search_progress: ?tool_dispatch.WebSearchProgressFn = null, web_fetch_progress_ctx: ?*anyopaque = null, on_web_fetch_progress: ?tool_dispatch.WebFetchProgressFn = null, workspace_executor: ?js_host_workspace.Executor = null, host_sandbox_default: tool_admission.HostSandboxDefault = .none, model_capability_resolver: ?model_capabilities.Resolver = null, /// False when running outside an interactive TUI (e.g. ACP). Tools /// that require a live user (like `ask_user_question`) short-circuit /// in that case. interactive: bool = true, lifecycle_view: hooks.RuntimeView = hooks.RuntimeView.empty(), lifecycle_scope: hooks.Scope = .{ .kind = .interactive, .workspace_root = "", }, /// Projects only the borrowed capabilities consumed by admission. pub fn admissionInput(self: Context) tool_admission.Input { var input: tool_admission.Input = .{ .workspace_root = self.workspace_root, .access_scope = self.access_scope, .permission_review_turn = self.permission_review_turn, .permission_grants = self.permission_grants, .permission_rules = self.permission_rules, .session_permission_state = self.permission_state_override, .session_permission_state_provider = .{ .context = @ptrCast(self.session), .snapshot_fn = snapshotSessionPermissionState, }, .tool_registry = self.tool_registry, .worker = self.worker, .permission_prompter = self.permission_prompter, .background = self.background, .sandbox_backend = self.sandbox_backend, .advertised_dynamic_tool_names = self.advertised_dynamic_tool_names, .mcp_runtime = mcpRuntimeCapabilities(self), .context_limits = self.context_limits, .auto_classifier = self.admissionAutoClassifier(), .host_sandbox_default = self.host_sandbox_default, }; if (self.permission_state_override != null) { input.session_permission_state_provider = null; } return input; } pub fn admissionInputWithLiveAuthority( self: Context, authority: ?tool_contracts.LiveToolAuthority, ) tool_admission.Input { var input = self.admissionInput(); if (authority) |live| { input.permission_grants = live.grants; input.permission_rules = live.rules; input.session_permission_state = live.permission_state; if (live.permission_state != null) { input.session_permission_state_provider = null; } input.sandbox_backend = live.sandbox_backend; input.advertised_dynamic_tool_names = live.integrations; } return input; } fn admissionAutoClassifier(self: Context) permission_auto_classifier.Classifier { if (self.auto_classifier.enabled()) return self.auto_classifier; const provider = self.permission_reviewer_provider orelse return permission_auto_classifier.Classifier.disabled(); return permission_auto_classifier.Classifier.withProvider(provider, .{ .credential = self.api_key, .tenant = self.gateway_team, .endpoint = self.gateway_chat_url, .cancel_flag = self.cancel_flag, .usage = &self.session.usage, .usage_allocator = self.session_allocator, }); }}; fn snapshotSessionPermissionState( raw_session: *anyopaque, alloc: Allocator,) !session_permission_state.State { const session: *SessionRuntime = @ptrCast(@alignCast(raw_session)); return session.snapshotPermissionState(alloc);} fn registeredToolSpec(ctx: Context, name: []const u8) ?*const tool_specs.ToolSpec { return ctx.tool_registry.lookup(name);} pub fn validateToolCall(ctx: Context, arena: Allocator, call: ToolCall) !tool_contracts.ToolCallValidationResult { const spec = registeredToolSpec(ctx, call.name) orelse { return switch (try tool_mcp_runtime.validateAdvertisedDynamicTool(.{ .is_registered_tool = false, .advertised_dynamic_tool_names = ctx.advertised_dynamic_tool_names, .runtime = mcpRuntimeCapabilities(ctx), }, arena, call.name, call.arguments_json)) { .valid => .valid, .invalid => |reason| .{ .failure = reason }, .not_available => .not_registered, }; }; switch (spec.executor_kind) { // Preserve execution-time argument failures for MCP control tool calls. .mcp_search_tools, .mcp_select_tool, .mcp_features => return .valid, // File mutation arguments are decoded once by shared permission preflight. .write_file, .edit_file => return .valid, else => {}, } var dispatch_ctx = typedDispatchContext(ctx, arena); dispatch_ctx.captured_command_host = spec.captured_command_host; return switch (try tool_dispatch.validateRegisteredToolCall(dispatch_ctx, ctx.tool_registry, call)) { .not_registered => .not_registered, .valid => .valid, .failure => |reason| .{ .failure = reason }, };} pub fn checkToolAvailability(ctx: Context, arena: Allocator, call: ToolCall) !?[]const u8 { return tool_dispatch.localToolAvailabilityFailureForCall( typedDispatchContext(ctx, arena), ctx.tool_registry, call, );} pub fn executeToolCallAuthorized( ctx: Context, request: tool_contracts.ToolExecutionRequest,) !ToolExecutionResult { var replay_continuation_transferred = request.command_replay_capture == null; defer if (!replay_continuation_transferred) { request.command_replay_capture.?.abort(request.result_allocator); }; var execution_ctx = ctx; if (request.live_authority) |authority| { if (!containsName(authority.tools, request.call.name) and !containsName(authority.integrations, request.call.name)) { return error.LiveToolAuthorityUnavailable; } execution_ctx.permission_mode = authority.permission_mode; execution_ctx.permission_grants = authority.grants; execution_ctx.session_grants = authority.grants; execution_ctx.permission_rules = authority.rules; execution_ctx.sandbox_backend = authority.sandbox_backend; execution_ctx.advertised_dynamic_tool_names = authority.integrations; execution_ctx.mcp_access = rebindMcpAuthorityGeneration( execution_ctx.mcp_access, authority.generation, ); } else { execution_ctx.session_grants = request.session_grants; execution_ctx.advertised_dynamic_tool_names = request.advertised_dynamic_tool_names; } execution_ctx.max_tool_result_bytes = request.max_tool_result_bytes; execution_ctx.current_turn_messages = request.current_turn_messages; execution_ctx.output_chunk_lifecycle_id = request.lifecycle_id; execution_ctx.command_timeout_started_ms = request.command_timeout_started_ms; execution_ctx.command_replay_capture = request.command_replay_capture; execution_ctx.command_replay_unavailable = request.command_replay_unavailable; const started_at_ms = io_mod.milliTimestamp(); const uses_file_mutation_contract = if (registeredToolSpec( execution_ctx, request.call.name, )) |spec| spec.take_file_mutation_input_fn != null else false; const result = (if (comptime builtin.os.tag == .wasi) executeWorkspaceToolCallInner( execution_ctx, request.result_allocator, request.call, request.authority, request.classification_complete, ) else if (uses_file_mutation_contract) file_mutation_execution.execute(.{ .call_allocator = request.call_allocator, .result_allocator = request.result_allocator, .call = request.call, .authorization = switch (request.authority) { .file_mutation => |value| value, else => null, }, .maybe_cancel_flag = execution_ctx.cancel_flag, .lifecycle_id = request.lifecycle_id, }) else executeToolCallInner( execution_ctx, request.result_allocator, request.call, request.authority, request.classification_complete, request.authorized_image_catalog, )) catch |err| { if (request.isSandboxWideningRetryCancellationError(err)) return err; diagnostics.recordToolCallResult(.{ .name = request.call.name, .arguments_json = request.call.arguments_json, .model_output = "", .ok = false, .started_at_ms = started_at_ms, }); return if (err == error.CancelledBeforeExecution) error.Cancelled else err; }; if (!request.resultAwaitsSandboxWidening(result)) { const ok = switch (result.status) { .success => true, else => false, }; diagnostics.recordToolCallResult(.{ .name = request.call.name, .arguments_json = request.call.arguments_json, .model_output = result.model_output, .ok = ok, .started_at_ms = started_at_ms, }); } if (request.command_replay_capture) |continued| { replay_continuation_transferred = result.command_replay_capture == continued or (result.sandbox_scope_required != null and result.sandbox_scope_required.?.command_replay_capture == continued); } return result;} fn rebindMcpAuthorityGeneration( access: tool_mcp_runtime.Access, authority_generation: u64,) tool_mcp_runtime.Access { return switch (access) { .scoped => |scope| .{ .scoped = .{ .captured = scope.captured, .admission_authority_generation = scope.admission_authority_generation, .live = scope.live, .action_authority_generation = authority_generation, } }, else => access, };} fn containsName(values: []const []const u8, needle: []const u8) bool { for (values) |value| { if (std.mem.eql(u8, value, needle)) return true; } return false;} pub fn executeToolCall( ctx: Context, arena: Allocator, call: ToolCall,) !ToolExecutionResult { return executeToolCallAuthorized(ctx, .{ .call_allocator = arena, .result_allocator = arena, .call = call, .authority = .ordinary, .current_turn_messages = ctx.current_turn_messages, .session_grants = ctx.session_grants, .advertised_dynamic_tool_names = ctx.advertised_dynamic_tool_names, .max_tool_result_bytes = ctx.max_tool_result_bytes, });} const ToolDispatchPrelude = union(enum) { completed: ToolExecutionResult, registered_static, registered_dynamic: tool_dispatch.Tool,}; fn executeToolCallInner( ctx: Context, arena: Allocator, call: ToolCall, authority: command_admission.ToolExecutionAuthority, classification_complete: bool, authorized_image_catalog: []const types.ImageAttachment,) !ToolExecutionResult { return switch (try resolveToolDispatchPrelude( ctx, arena, call, classification_complete,Lines 1–500 of 9272. Load more
const std = @import("std"); const builtin = @import("builtin"); const agent_stream_provider = @import("../agent/stream_provider.zig"); const auth_runtime = @import("../auth/auth_runtime.zig"); const oauth_transport = @import("../auth/oauth_transport.zig"); const command_contract = @import("../execution/command_contract.zig"); const command_environment = @import("../execution/command_environment.zig"); const background_process_provider = @import( "../execution/background_process_provider.zig", ); const debug_trace = @import("../shared/debug_trace.zig"); const diagnostics = @import("../workspace/diagnostics.zig"); const image_attachments = @import("../images/image_attachments.zig"); const io_mod = @import("../shared/io.zig"); const tool_contracts = @import("../agent/runtime/tool_contracts.zig"); const vision_executor = @import("../agent/runtime/vision_executor.zig"); const background_runtime = @import("../background/background_runtime.zig"); const background_launch_identity = @import("../background/background_launch_identity.zig"); const process_supervisor = @import("../background/process_supervisor.zig"); const change_tracker = @import("../workspace/change_tracker.zig"); const diff_mod = @import("../output/diff.zig"); const file_mutation = @import("file_mutation.zig"); const file_mutation_contract = @import("file_mutation_contract.zig"); const hooks = @import("../hooks/hooks.zig"); const permission_auto_classifier = @import("../permissions/auto_classifier.zig"); const glob_pattern = @import("../workspace/glob_pattern.zig"); const permission_prompter = @import("../permissions/permission_prompter.zig"); const permission_request = @import("../permissions/permission_request.zig"); const command_admission = @import("../permissions/command_admission.zig"); const pathing = @import("../workspace/pathing.zig"); const devbox_executor = @import("../execution/devbox_executor.zig"); const execution_router = @import("../execution/router.zig"); const sandbox = @import("../permissions/sandbox.zig"); const skill_runtime = @import("../skills/skill_runtime.zig"); const subagent_authority = @import("../subagent/authority.zig"); const subagent_communication_store = @import("../subagent/communication_store.zig"); const subagent_control_store = @import("../subagent/control_store.zig"); const subagent_create_store = @import("../subagent/create_store.zig"); const subagent_domain = @import("../subagent/domain.zig"); const subagent_tool_host = @import("../subagent/tool_host.zig"); const subagent_tool_provider = @import("../subagent/tool_provider.zig"); const subagent_tool_result = @import("../subagent/tool_result.zig"); const session_runtime = @import("../session/session.zig"); const session_permission_state = @import("../permissions/session_permission_state.zig"); const session_codec_mod = @import("../session/session_codec.zig"); const task_helpers = @import("../tasks/task_helpers.zig"); const session_child_store = @import("../session/session_child_store.zig"); const command_replay_store = @import("../session/command_replay_store.zig"); const session_store = @import("../session/session_store.zig"); const text_utils = @import("../shared/text_utils.zig"); const model_capabilities = @import("../config/model_capabilities.zig"); const mcp_access_policy = @import("../mcp/access_policy.zig"); const tool_admission = @import("tool_admission.zig"); const tool_args = @import("tool_args.zig"); const command_result_mapping = @import("command_result_mapping.zig"); const tool_dispatch = @import("tool_dispatch.zig"); const tool_specs = @import("tool_specs.zig"); const tool_result_errors = @import("tool_result_errors.zig"); const tool_result_limits = @import("tool_result_limits.zig"); const file_mutation_execution = @import("file_mutation_execution.zig"); const tool_mcp_registry = @import("tool_mcp_registry.zig"); const tool_mcp_runtime = @import("tool_mcp_runtime.zig"); const tool_mcp_feature_dispatch = @import("tool_mcp_feature_dispatch.zig"); const tool_presentation = @import("tool_presentation.zig"); const web_fetch_runtime = @import("web_fetch_runtime.zig"); const web_search_contract = @import("web_search_contract.zig"); const web_fetch_artifacts = @import("../session/web_fetch_artifacts.zig"); const types = @import("../shared/types.zig"); const worker_runtime = @import("../agent/worker_runtime.zig"); const context_contract = @import("../workspace/context_contract.zig"); const test_builtin_tools = if (builtin.is_test) @import("../../builtins/tools.zig") else struct {}; const test_builtin_gateway = if (builtin.is_test) @import("../../builtins/gateway.zig") else struct {}; const test_browser_workspace_tools = if (builtin.is_test) @import("../../builtins/browser_workspace_tools.zig") else struct {}; const js_host_workspace = @import("../hosts/js_host_workspace.zig"); const agent_test_support = if (builtin.is_test) @import("../agent/runtime/tests/support.zig") else struct {}; const Allocator = std.mem.Allocator; const ToolCall = types.ToolCall; const ChatMessage = types.ChatMessage; const PermissionGrant = types.PermissionGrant; const PermissionMode = types.PermissionMode; const ToolPermissionDecision = types.ToolPermissionDecision; const subagent_tool_name = "subagent"; const ToolExecutionResult = tool_contracts.ToolExecutionResult; const BackgroundRuntime = background_runtime.BackgroundRuntime; const SessionRuntime = session_runtime.SessionRuntime; const WorkerRuntime = worker_runtime.WorkerRuntime; const max_file_mutation_success_bytes: usize = 8 * 1024; const helpers = struct { const requiredStringArg = tool_args.requiredStringArg; const parseToolArgsObject = tool_args.parseToolArgsObject; }; const optionalIntArg = tool_args.optionalIntArg; const parseToolArgsObject = helpers.parseToolArgsObject; const context_limits = @import("../config/context_limits.zig"); const workspace_access = @import("../workspace/workspace_access.zig"); const host_capabilities = @import("../hosts/host.zig"); const terminal_client_runtime = @import("../terminal/client.zig"); test { _ = tool_admission; _ = command_result_mapping; _ = @import("../session/command_replay_store.zig"); _ = file_mutation_execution; _ = tool_presentation; } pub const Context = struct { workspace_root: []const u8, access_scope: ?workspace_access.AccessScope = null, 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 = tool_result_limits.default_max_tool_result_bytes, api_key: []const u8, agent_stream_provider: agent_stream_provider.Provider = agent_stream_provider.unavailable_provider, gateway_team: ?[]const u8 = null, credential_source: ?types.CredentialSource = null, oauth_transport: oauth_transport.Provider = oauth_transport.unavailable_provider, model: []const u8, permission_review_turn: ?permission_auto_classifier.ReviewTurnContext = null, root_user_intent_context: []const u8 = "", root_user_messages: []const []const u8 = &.{}, root_user_evidence_complete: bool = false, current_turn_messages: []const ChatMessage = &.{}, gateway_retry_count: usize, gateway_chat_url: []const u8, gateway_models_path: []const u8 = "/v1/models", agent_step_limit: usize, fast_mode: bool = false, effort: types.ReasoningEffort = .auto, first_call_tool_choice: types.ToolChoice = .auto, tool_registry: tool_dispatch.Registry = .{}, subagent_host: ?*subagent_tool_host.Runtime = null, subagent_caller_id: ?[]const u8 = null, permission_mode: PermissionMode, permission_grants: []const PermissionGrant, session_grants: []const PermissionGrant = &.{}, permission_rules: types.PermissionRuleSet, permission_state_override: ?*const session_permission_state.State = null, worker: *WorkerRuntime, /// Sole prompt capability admission consults. When null, admission never /// prompts: it resolves by rule, automatic review, or fail-closed denial /// (e.g. ACP hosts prompt over JSON-RPC by setting this). permission_prompter: ?permission_prompter.Prompter = null, cancel_flag: ?*std.atomic.Value(bool) = null, background: *BackgroundRuntime, session: *SessionRuntime, session_allocator: Allocator = std.heap.c_allocator, skills_dir: []const u8 = "", context_limits: context_limits.Values = .{}, context_registry: context_contract.Registry, context_enabled: bool = true, output_chunk_lifecycle_id: ?types.ToolLifecycleId = null, output_chunk_ctx: *anyopaque, on_output_chunk: command_contract.CommandOutputCallback, background_url_ctx: *anyopaque, on_background_url_ready: *const fn (*anyopaque, u64, []const u8) void, command_artifact_dir: ?[]const u8 = null, tool_result_dir: ?[]const u8 = null, session_child_capability: ?*session_child_store.SessionChildCapability = null, terminal_client: ?*terminal_client_runtime.Runtime = null, command_timeout_ms: ?usize = null, command_timeout_started_ms: ?i64 = null, command_replay_capture: ?*command_replay_store.Capture = null, command_replay_unavailable: bool = false, sandbox_backend: sandbox.BackendKind = .none, devbox_provider: ?devbox_executor.Provider = null, tracker: ?*change_tracker.ChangeTracker = null, mcp_ctx: ?*anyopaque = null, mcp_has_tool: ?tool_mcp_runtime.HasToolFn = null, mcp_validate_tool: ?tool_mcp_runtime.ValidateToolFn = null, mcp_call_tool: ?tool_mcp_runtime.CallToolFn = null, mcp_search_tools: ?tool_mcp_runtime.SearchToolsFn = null, mcp_tool_schema: ?tool_mcp_runtime.ToolSchemaFn = null, mcp_call_feature: ?tool_mcp_runtime.FeatureCallFn = null, mcp_access: tool_mcp_runtime.Access = .unrestricted, mcp_input_responder: ?tool_mcp_runtime.InputResponder = null, mcp_progress_ctx: ?*anyopaque = null, on_mcp_progress: ?*const fn (*anyopaque, types.ToolLifecycleId, []const u8) void = null, advertised_dynamic_tool_names: []const []const u8 = &.{}, permission_reviewer_provider: ?permission_auto_classifier.Provider = null, auto_classifier: permission_auto_classifier.Classifier = permission_auto_classifier.Classifier.disabled(), web_fetch_runtime: ?*web_fetch_runtime.Runtime = null, web_fetch_artifact_store: ?*web_fetch_artifacts.Store = null, web_fetch_artifact_error: ?anyerror = null, web_search_runtime_ready: bool = false, web_search_backend: ?tool_dispatch.WebSearchBackend = null, web_search_progress_ctx: ?*anyopaque = null, on_web_search_progress: ?tool_dispatch.WebSearchProgressFn = null, web_fetch_progress_ctx: ?*anyopaque = null, on_web_fetch_progress: ?tool_dispatch.WebFetchProgressFn = null, workspace_executor: ?js_host_workspace.Executor = null, host_sandbox_default: tool_admission.HostSandboxDefault = .none, model_capability_resolver: ?model_capabilities.Resolver = null, /// False when running outside an interactive TUI (e.g. ACP). Tools /// that require a live user (like `ask_user_question`) short-circuit /// in that case. interactive: bool = true, lifecycle_view: hooks.RuntimeView = hooks.RuntimeView.empty(), lifecycle_scope: hooks.Scope = .{ .kind = .interactive, .workspace_root = "", }, /// Projects only the borrowed capabilities consumed by admission. pub fn admissionInput(self: Context) tool_admission.Input { var input: tool_admission.Input = .{ .workspace_root = self.workspace_root, .access_scope = self.access_scope, .permission_review_turn = self.permission_review_turn, .permission_grants = self.permission_grants, .permission_rules = self.permission_rules, .session_permission_state = self.permission_state_override, .session_permission_state_provider = .{ .context = @ptrCast(self.session), .snapshot_fn = snapshotSessionPermissionState, }, .tool_registry = self.tool_registry, .worker = self.worker, .permission_prompter = self.permission_prompter, .background = self.background, .sandbox_backend = self.sandbox_backend, .advertised_dynamic_tool_names = self.advertised_dynamic_tool_names, .mcp_runtime = mcpRuntimeCapabilities(self), .context_limits = self.context_limits, .auto_classifier = self.admissionAutoClassifier(), .host_sandbox_default = self.host_sandbox_default, }; if (self.permission_state_override != null) { input.session_permission_state_provider = null; } return input; } pub fn admissionInputWithLiveAuthority( self: Context, authority: ?tool_contracts.LiveToolAuthority, ) tool_admission.Input { var input = self.admissionInput(); if (authority) |live| { input.permission_grants = live.grants; input.permission_rules = live.rules; input.session_permission_state = live.permission_state; if (live.permission_state != null) { input.session_permission_state_provider = null; } input.sandbox_backend = live.sandbox_backend; input.advertised_dynamic_tool_names = live.integrations; } return input; } fn admissionAutoClassifier(self: Context) permission_auto_classifier.Classifier { if (self.auto_classifier.enabled()) return self.auto_classifier; const provider = self.permission_reviewer_provider orelse return permission_auto_classifier.Classifier.disabled(); return permission_auto_classifier.Classifier.withProvider(provider, .{ .credential = self.api_key, .tenant = self.gateway_team, .endpoint = self.gateway_chat_url, .cancel_flag = self.cancel_flag, .usage = &self.session.usage, .usage_allocator = self.session_allocator, }); } }; fn snapshotSessionPermissionState( raw_session: *anyopaque, alloc: Allocator, ) !session_permission_state.State { const session: *SessionRuntime = @ptrCast(@alignCast(raw_session)); return session.snapshotPermissionState(alloc); } fn registeredToolSpec(ctx: Context, name: []const u8) ?*const tool_specs.ToolSpec { return ctx.tool_registry.lookup(name); } pub fn validateToolCall(ctx: Context, arena: Allocator, call: ToolCall) !tool_contracts.ToolCallValidationResult { const spec = registeredToolSpec(ctx, call.name) orelse { return switch (try tool_mcp_runtime.validateAdvertisedDynamicTool(.{ .is_registered_tool = false, .advertised_dynamic_tool_names = ctx.advertised_dynamic_tool_names, .runtime = mcpRuntimeCapabilities(ctx), }, arena, call.name, call.arguments_json)) { .valid => .valid, .invalid => |reason| .{ .failure = reason }, .not_available => .not_registered, }; }; switch (spec.executor_kind) { // Preserve execution-time argument failures for MCP control tool calls. .mcp_search_tools, .mcp_select_tool, .mcp_features => return .valid, // File mutation arguments are decoded once by shared permission preflight. .write_file, .edit_file => return .valid, else => {}, } var dispatch_ctx = typedDispatchContext(ctx, arena); dispatch_ctx.captured_command_host = spec.captured_command_host; return switch (try tool_dispatch.validateRegisteredToolCall(dispatch_ctx, ctx.tool_registry, call)) { .not_registered => .not_registered, .valid => .valid, .failure => |reason| .{ .failure = reason }, }; } pub fn checkToolAvailability(ctx: Context, arena: Allocator, call: ToolCall) !?[]const u8 { return tool_dispatch.localToolAvailabilityFailureForCall( typedDispatchContext(ctx, arena), ctx.tool_registry, call, ); } pub fn executeToolCallAuthorized( ctx: Context, request: tool_contracts.ToolExecutionRequest, ) !ToolExecutionResult { var replay_continuation_transferred = request.command_replay_capture == null; defer if (!replay_continuation_transferred) { request.command_replay_capture.?.abort(request.result_allocator); }; var execution_ctx = ctx; if (request.live_authority) |authority| { if (!containsName(authority.tools, request.call.name) and !containsName(authority.integrations, request.call.name)) { return error.LiveToolAuthorityUnavailable; } execution_ctx.permission_mode = authority.permission_mode; execution_ctx.permission_grants = authority.grants; execution_ctx.session_grants = authority.grants; execution_ctx.permission_rules = authority.rules; execution_ctx.sandbox_backend = authority.sandbox_backend; execution_ctx.advertised_dynamic_tool_names = authority.integrations; execution_ctx.mcp_access = rebindMcpAuthorityGeneration( execution_ctx.mcp_access, authority.generation, ); } else { execution_ctx.session_grants = request.session_grants; execution_ctx.advertised_dynamic_tool_names = request.advertised_dynamic_tool_names; } execution_ctx.max_tool_result_bytes = request.max_tool_result_bytes; execution_ctx.current_turn_messages = request.current_turn_messages; execution_ctx.output_chunk_lifecycle_id = request.lifecycle_id; execution_ctx.command_timeout_started_ms = request.command_timeout_started_ms; execution_ctx.command_replay_capture = request.command_replay_capture; execution_ctx.command_replay_unavailable = request.command_replay_unavailable; const started_at_ms = io_mod.milliTimestamp(); const uses_file_mutation_contract = if (registeredToolSpec( execution_ctx, request.call.name, )) |spec| spec.take_file_mutation_input_fn != null else false; const result = (if (comptime builtin.os.tag == .wasi) executeWorkspaceToolCallInner( execution_ctx, request.result_allocator, request.call, request.authority, request.classification_complete, ) else if (uses_file_mutation_contract) file_mutation_execution.execute(.{ .call_allocator = request.call_allocator, .result_allocator = request.result_allocator, .call = request.call, .authorization = switch (request.authority) { .file_mutation => |value| value, else => null, }, .maybe_cancel_flag = execution_ctx.cancel_flag, .lifecycle_id = request.lifecycle_id, }) else executeToolCallInner( execution_ctx, request.result_allocator, request.call, request.authority, request.classification_complete, request.authorized_image_catalog, )) catch |err| { if (request.isSandboxWideningRetryCancellationError(err)) return err; diagnostics.recordToolCallResult(.{ .name = request.call.name, .arguments_json = request.call.arguments_json, .model_output = "", .ok = false, .started_at_ms = started_at_ms, }); return if (err == error.CancelledBeforeExecution) error.Cancelled else err; }; if (!request.resultAwaitsSandboxWidening(result)) { const ok = switch (result.status) { .success => true, else => false, }; diagnostics.recordToolCallResult(.{ .name = request.call.name, .arguments_json = request.call.arguments_json, .model_output = result.model_output, .ok = ok, .started_at_ms = started_at_ms, }); } if (request.command_replay_capture) |continued| { replay_continuation_transferred = result.command_replay_capture == continued or (result.sandbox_scope_required != null and result.sandbox_scope_required.?.command_replay_capture == continued); } return result; } fn rebindMcpAuthorityGeneration( access: tool_mcp_runtime.Access, authority_generation: u64, ) tool_mcp_runtime.Access { return switch (access) { .scoped => |scope| .{ .scoped = .{ .captured = scope.captured, .admission_authority_generation = scope.admission_authority_generation, .live = scope.live, .action_authority_generation = authority_generation, } }, else => access, }; } fn containsName(values: []const []const u8, needle: []const u8) bool { for (values) |value| { if (std.mem.eql(u8, value, needle)) return true; } return false; } pub fn executeToolCall( ctx: Context, arena: Allocator, call: ToolCall, ) !ToolExecutionResult { return executeToolCallAuthorized(ctx, .{ .call_allocator = arena, .result_allocator = arena, .call = call, .authority = .ordinary, .current_turn_messages = ctx.current_turn_messages, .session_grants = ctx.session_grants, .advertised_dynamic_tool_names = ctx.advertised_dynamic_tool_names, .max_tool_result_bytes = ctx.max_tool_result_bytes, }); } const ToolDispatchPrelude = union(enum) { completed: ToolExecutionResult, registered_static, registered_dynamic: tool_dispatch.Tool, }; fn executeToolCallInner( ctx: Context, arena: Allocator, call: ToolCall, authority: command_admission.ToolExecutionAuthority, classification_complete: bool, authorized_image_catalog: []const types.ImageAttachment, ) !ToolExecutionResult { return switch (try resolveToolDispatchPrelude( ctx, arena, call, classification_complete,