9272 lines · 357.4 KB · Zig
RawDownload
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.