const std = @import("std");const builtin = @import("builtin");const command_policy = @import("command_policy.zig");const file_mutation_contract = @import("file_mutation_contract.zig");const text_utils = @import("../shared/text_utils.zig");const tool_args = @import("tool_args.zig");const tool_dispatch = @import("tool_dispatch.zig");const types = @import("../shared/types.zig");const test_builtin_tools = if (builtin.is_test) @import("../../builtins/tools.zig")else struct {}; const Allocator = std.mem.Allocator;const ToolCall = types.ToolCall;const max_run_command_activity_bytes = 120;const max_run_command_activity_source_bytes = max_run_command_activity_bytes * max_run_command_activity_bytes;pub const max_auto_permission_reason_presentation_bytes: usize = 160; pub const ToolActionInput = struct { tool_registry: tool_dispatch.Registry, call: ToolCall, workspace_root: []const u8 = "", file_display_path: ?[]const u8 = null, is_available_dynamic_mcp_tool: bool = false,}; pub const RunCommandActivity = struct { detail: []const u8, compatibility_tool: ?*const tool_dispatch.Tool,}; pub fn isProviderSearchAlias(name: []const u8) bool { return std.mem.eql(u8, name, "perplexity_search") or std.mem.eql(u8, name, "parallel_search");} fn projectRunCommandActivitySource( command: []const u8, workspace_root_input: []const u8, storage: *[max_run_command_activity_bytes + 1]u8,) []const u8 { var workspace_root_end = workspace_root_input.len; while (workspace_root_end > 1 and workspace_root_input[workspace_root_end - 1] == '/') { workspace_root_end -= 1; } const workspace_root = workspace_root_input[0..workspace_root_end]; const can_abbreviate_workspace = workspace_root.len > 1 and workspace_root[0] == '/'; const source_limit = @min(command.len, max_run_command_activity_source_bytes); var source_index: usize = 0; var projected_len: usize = 0; var line_boundary_pending = false; while (source_index < source_limit) { const byte = command[source_index]; if (byte == '\r' or byte == '\n') { line_boundary_pending = true; source_index += 1; continue; } if (line_boundary_pending and (byte == ' ' or byte == '\t')) { source_index += 1; continue; } if (line_boundary_pending) { line_boundary_pending = false; if (projected_len > 0) { storage[projected_len] = ' '; projected_len += 1; if (projected_len == storage.len) break; } } if (can_abbreviate_workspace and workspace_root.len <= source_limit - source_index and workspaceRootMatchesAt(command, workspace_root, source_index)) { storage[projected_len] = '.'; projected_len += 1; if (projected_len == storage.len) break; source_index += workspace_root.len; continue; } storage[projected_len] = byte; projected_len += 1; if (projected_len == storage.len) break; source_index += 1; } return storage[0..projected_len];} fn workspaceRootMatchesAt(command: []const u8, workspace_root: []const u8, index: usize) bool { if (!std.mem.startsWith(u8, command[index..], workspace_root)) return false; if (index > 0 and isPathTokenByte(command[index - 1])) return false; const next_index = index + workspace_root.len; if (next_index == command.len) return true; return command[next_index] == '/' or !isPathTokenByte(command[next_index]);} fn isPathTokenByte(byte: u8) bool { return std.ascii.isAlphanumeric(byte) or switch (byte) { '/', '-', '.', '_', '~' => true, else => false, };} /// The caller owns the returned allocation and must free it with `alloc`.pub fn formatRunCommandPermissionLabel( alloc: Allocator, command: []const u8,) ![]const u8 { var scratch_state = std.heap.ArenaAllocator.init(alloc); defer scratch_state.deinit(); const scratch = scratch_state.allocator(); const encoded = try text_utils.encodeTerminalSafe( scratch, command, max_run_command_activity_bytes, ); const suffix = try commandApprovalLabelSuffix(scratch, "terminal", command); return std.fmt.allocPrint(alloc, "terminal.exec {s}{s}", .{ encoded.bytes, suffix });} /// The caller owns the returned allocation and must free it with `alloc`.pub fn formatSandboxWideningPermissionLabel( alloc: Allocator, reactive: bool, command: []const u8,) ![]const u8 { var scratch_state = std.heap.ArenaAllocator.init(alloc); defer scratch_state.deinit(); const encoded = try text_utils.encodeTerminalSafe( scratch_state.allocator(), command, max_run_command_activity_bytes, ); return std.fmt.allocPrint(alloc, "{s}broader file access: {s}", .{ if (reactive) "retry with " else "", encoded.bytes, });} pub fn isAdvertisedDynamicMcpName(registry: tool_dispatch.Registry, name: []const u8, advertised: []const []const u8) bool { if (registry.lookup(name) != null) return false; for (advertised) |advertised_name| { if (std.mem.eql(u8, advertised_name, name)) return true; } return false;} /// The caller owns `detail` and must free it with `alloc`.pub fn formatRunCommandActivity( alloc: Allocator, registry: tool_dispatch.Registry, workspace_root: []const u8, call: ToolCall,) !?RunCommandActivity { var scratch_state = std.heap.ArenaAllocator.init(std.heap.c_allocator); defer scratch_state.deinit(); const scratch = scratch_state.allocator(); const args = tool_args.parseToolArgsObject(scratch, call.arguments_json) catch return null; if (!isCapturedCommandCall(registry, call, args)) return null; const command = tool_args.optionalStringArg(args, "command") orelse return null; var projected_storage: [max_run_command_activity_bytes + 1]u8 = undefined; const projected = projectRunCommandActivitySource(command, workspace_root, &projected_storage); const encoded = try text_utils.encodeTerminalSafe(scratch, projected, max_run_command_activity_bytes); return .{ .detail = try alloc.dupe(u8, encoded.bytes), .compatibility_tool = if (try tool_dispatch.matchRunCommandCompatibility(registry, command)) |matched| matched.tool else null, };} /// The caller owns the returned allocation and must free it with `alloc`.pub fn formatPlainAction(alloc: Allocator, input: ToolActionInput) ![]const u8 { const call = input.call; if (file_mutation_contract.isToolName(call.name)) { const spec = input.tool_registry.lookup(call.name) orelse return std.fmt.allocPrint(alloc, "Working: {s}", .{call.name}); return std.fmt.allocPrint( alloc, "{s} {s}", .{ spec.action_label, input.file_display_path orelse spec.label_arg_default }, ); } if (try formatRunCommandActivity(alloc, input.tool_registry, input.workspace_root, call)) |activity| { defer alloc.free(activity.detail); const action_label = if (activity.compatibility_tool) |tool| tool.action_label else "Running"; return std.fmt.allocPrint(alloc, "{s} {s}", .{ action_label, activity.detail }); } var scratch_state = std.heap.ArenaAllocator.init(alloc); defer scratch_state.deinit(); const scratch = scratch_state.allocator(); const spec = input.tool_registry.lookup(call.name) orelse { if (isProviderSearchAlias(call.name)) { const args = tool_args.parseToolArgsObject(scratch, call.arguments_json) catch { return std.fmt.allocPrint(alloc, "Searching web", .{}); }; return std.fmt.allocPrint(alloc, "Searching {s}", .{try formatWebSearchActionDetail(scratch, args)}); } if (input.is_available_dynamic_mcp_tool) return std.fmt.allocPrint(alloc, "MCP: {s}", .{call.name}); return std.fmt.allocPrint(alloc, "Working: {s}", .{call.name}); }; const args = tool_args.parseToolArgsObject(scratch, call.arguments_json) catch { return std.fmt.allocPrint(alloc, "Working: {s}", .{call.name}); }; const presentation = tool_dispatch.presentationForArgs(spec.*, args); if (spec.executor_kind == .web_search) { return std.fmt.allocPrint(alloc, "{s} {s}", .{ presentation.action_label, try formatWebSearchActionDetail(scratch, args) }); } if (try copyRenameLabel(scratch, call.name, args)) |value| { return std.fmt.allocPrint(alloc, "{s} {s}", .{ presentation.action_label, value }); } const value = tool_dispatch.presentationLabelValue(presentation, args) orelse presentation.label_arg_default; return std.fmt.allocPrint(alloc, "{s} {s}", .{ presentation.action_label, value });} /// The caller owns the returned allocation and must free it with `alloc`.pub fn formatPermissionLabel(alloc: Allocator, registry: tool_dispatch.Registry, call: ToolCall) ![]const u8 { var scratch_state = std.heap.ArenaAllocator.init(alloc); defer scratch_state.deinit(); const scratch = scratch_state.allocator(); if (try runCommandCompatibilitySource(scratch, registry, call)) |source| { return std.fmt.allocPrint(alloc, "{s} {s}", .{ source.tool.name, source.command }); } const args = tool_args.parseToolArgsObject(scratch, call.arguments_json) catch { return try alloc.dupe(u8, call.name); }; if (isCapturedCommandCall(registry, call, args)) { const command = tool_args.optionalStringArg(args, "command") orelse return try alloc.dupe(u8, call.name); return formatRunCommandPermissionLabel(alloc, command); } const spec = registry.lookup(call.name) orelse return try alloc.dupe(u8, call.name); if (file_mutation_contract.isToolName(call.name)) { return std.fmt.allocPrint( alloc, "{s} {s}", .{ call.name, spec.label_arg_default }, ); } if (try copyRenameLabel(scratch, call.name, args)) |value| { return std.fmt.allocPrint(alloc, "{s} {s}", .{ call.name, value }); } const value = tool_dispatch.toolLabelValue(spec.*, args) orelse return try alloc.dupe(u8, call.name); if (spec.label_arg_kind == .command) { const suffix = try commandApprovalLabelSuffix(scratch, call.name, value); if (tool_args.optionalStringArg(args, "cwd")) |cwd| { return std.fmt.allocPrint(alloc, "{s} {s} @ {s}{s}", .{ call.name, value, cwd, suffix }); } return std.fmt.allocPrint(alloc, "{s} {s}{s}", .{ call.name, value, suffix }); } return std.fmt.allocPrint(alloc, "{s} {s}", .{ call.name, value });} /// The caller owns the returned allocation and must free it with `alloc`.pub fn formatWebSearchActionDetail(alloc: Allocator, args: std.json.ObjectMap) ![]const u8 { var out: std.Io.Writer.Allocating = .init(alloc); errdefer out.deinit(); var label_buf: [160]u8 = undefined; const query = tool_args.optionalStringArg(args, "query") orelse "web"; try out.writer.writeAll(text_utils.clippedLabel(&label_buf, query, 120)); try appendWebSearchDomains(&out.writer, "allowed", args.get("allowed_domains")); try appendWebSearchDomains(&out.writer, "blocked", args.get("blocked_domains")); return try out.toOwnedSlice();} /// The caller owns the returned allocation and must free it with `alloc`.pub fn formatWebSearchProgressPlain(alloc: Allocator, progress: types.WebSearchProgress) ![]u8 { var query_buf: [160]u8 = undefined; return switch (progress) { .query_started => |query| std.fmt.allocPrint( alloc, "Searching {s}", .{text_utils.clippedLabel(&query_buf, query, 120)}, ), .results_received => |entry| std.fmt.allocPrint( alloc, "Found {d} result{s} for {s}", .{ entry.result_count, if (entry.result_count == 1) "" else "s", text_utils.clippedLabel(&query_buf, entry.query, 120) }, ), };} /// The caller owns the returned allocation and must free it with `alloc`.pub fn formatWebFetchProgressPlain(alloc: Allocator, progress: types.WebFetchProgress) ![]u8 { var url_buf: [types.WebFetchCompletion.max_url_len]u8 = undefined; return switch (progress) { .fetching => |url| std.fmt.allocPrint(alloc, "Fetching {s}", .{text_utils.clippedLabel(&url_buf, url, 96)}), .converting => |url| std.fmt.allocPrint(alloc, "Converting {s}", .{text_utils.clippedLabel(&url_buf, url, 96)}), };} fn appendWebSearchDomains(writer: *std.Io.Writer, label: []const u8, value: ?std.json.Value) !void { const array = value orelse return; if (array != .array or array.array.items.len == 0) return; try writer.print(" | {s}: ", .{label}); var domain_buf: [64]u8 = undefined; const shown = @min(array.array.items.len, 3); for (array.array.items[0..shown], 0..) |item, index| { if (index > 0) try writer.writeAll(", "); if (item != .string) { try writer.writeAll("?"); continue; } try writer.writeAll(text_utils.clippedLabel(&domain_buf, item.string, 48)); } if (array.array.items.len > shown) try writer.print(" +{d}", .{array.array.items.len - shown});} fn commandApprovalLabelSuffix(alloc: Allocator, tool_name: []const u8, command: []const u8) ![]const u8 { if (!std.mem.eql(u8, tool_name, "run_command") and !std.mem.eql(u8, tool_name, "terminal")) return ""; const risk = command_policy.command_risk_note_for(command); const safer = command_policy.command_safer_alternative_for(command); if (risk == null and safer == null) return ""; const risk_text = if (risk) |note| stripNotePrefix(note) else null; if (risk_text) |note| { if (safer) |alternative| { return std.fmt.allocPrint(alloc, " (risk: {s}; {s})", .{ note, alternative }); } return std.fmt.allocPrint(alloc, " (risk: {s})", .{note}); } if (safer) |alternative| { return std.fmt.allocPrint(alloc, " ({s})", .{alternative}); } return "";} fn stripNotePrefix(note: []const u8) []const u8 { const prefix = "note: "; if (std.mem.startsWith(u8, note, prefix)) return note[prefix.len..]; return note;} const RunCommandCompatibilitySource = struct { tool: *const tool_dispatch.Tool, command: []const u8,}; fn runCommandCompatibilitySource( alloc: Allocator, registry: tool_dispatch.Registry, call: ToolCall,) !?RunCommandCompatibilitySource { const args = tool_args.parseToolArgsObject(alloc, call.arguments_json) catch return null; if (!isCapturedCommandCall(registry, call, args)) return null; const command = tool_args.optionalStringArg(args, "command") orelse return null; const matched = (try tool_dispatch.matchRunCommandCompatibility(registry, command)) orelse return null; return .{ .tool = matched.tool, .command = command };} fn isCapturedCommandCall( registry: tool_dispatch.Registry, call: ToolCall, args: std.json.ObjectMap,) bool { // Historical sessions retain their original tool name. Presentation may // interpret those records, but execution remains unavailable because the // registry no longer contains a `run_command` tool. if (std.mem.eql(u8, call.name, "run_command")) return true; const tool = registry.lookup(call.name) orelse return false; if (tool.executor_kind == .run_command) return true; const expected = tool.captured_command_action orelse return false; const action = tool_args.optionalStringArg(args, "action") orelse return false; return std.mem.eql(u8, action, expected);} fn copyRenameLabel(alloc: Allocator, tool_name: []const u8, args: std.json.ObjectMap) !?[]const u8 { if (std.mem.eql(u8, tool_name, "copy_file")) { const source = tool_args.optionalStringArg(args, "source") orelse return null; const destination = tool_args.optionalStringArg(args, "destination") orelse return null; return try std.fmt.allocPrint(alloc, "{s} -> {s}", .{ source, destination }); } if (std.mem.eql(u8, tool_name, "rename_file")) { const old_path = tool_args.optionalStringArg(args, "old_path") orelse return null; const new_path = tool_args.optionalStringArg(args, "new_path") orelse return null; return try std.fmt.allocPrint(alloc, "{s} -> {s}", .{ old_path, new_path }); } return null;} fn matchesTestSkillInstall(command: []const u8) bool { return std.mem.startsWith(u8, command, "npx skills add ");} fn executeTestSkillInstall( ctx: tool_dispatch.DispatchContext, _: []const u8,) tool_dispatch.DispatchError!tool_dispatch.ToolResult { return .{ .success = try ctx.allocator.dupe(u8, "installed") };} const test_install_skill = blk: { var tool = test_builtin_tools.skill; tool.name = "install_skill"; tool.gateway_schema.name = "install_skill"; tool.executor_kind = .install_skill; tool.activity_kind = .write; tool.requires_approval = true; tool.action_label = "Installing skill"; tool.completed_action_label = "Installed skill"; tool.label_arg_kind = .source; tool.label_arg_default = "skill"; tool.run_command_compatibility = .{ .matches = matchesTestSkillInstall, .execute = executeTestSkillInstall, }; break :blk tool;}; const test_web_search = blk: { var tool = test_builtin_tools.read_file; tool.name = "web_search"; tool.gateway_schema.name = "web_search"; tool.executor_kind = .web_search; tool.action_label = "Searching"; tool.completed_action_label = "Searched"; tool.label_arg_kind = .query; tool.label_arg_default = "web"; break :blk tool;}; const test_tools = [_]tool_dispatch.Tool{ test_builtin_tools.read_file, test_builtin_tools.write_file, test_builtin_tools.edit_file, test_builtin_tools.rename_file, test_builtin_tools.copy_file, test_web_search, test_builtin_tools.terminal, test_builtin_tools.memory, test_builtin_tools.skill, test_install_skill, test_builtin_tools.ask_user_question,};const test_tool_registry = tool_dispatch.Registry{ .tools = test_tools[0..] };const custom_presentation_tool = blk: { var tool = test_builtin_tools.memory; tool.name = "custom_presentation"; tool.action_label = "Inspecting"; tool.label_arg_kind = .name; tool.label_arg_default = "custom fallback"; break :blk tool;};const custom_presentation_registry = tool_dispatch.Registry{ .tools = &.{custom_presentation_tool} }; test "tool presentation formats dynamic MCP availability distinctly" { const alloc = std.testing.allocator; const call: ToolCall = .{ .id = "dynamic", .name = "mcp_lookup", .arguments_json = "{}", }; const available = try formatPlainAction(alloc, .{ .tool_registry = test_tool_registry, .call = call, .is_available_dynamic_mcp_tool = true, }); defer alloc.free(available); try std.testing.expectEqualStrings("MCP: mcp_lookup", available); const unavailable = try formatPlainAction(alloc, .{ .tool_registry = test_tool_registry, .call = call }); defer alloc.free(unavailable); try std.testing.expectEqualStrings("Working: mcp_lookup", unavailable);} test "tool presentation reads labels from the supplied registry" { const alloc = std.testing.allocator; const label = try formatPlainAction(alloc, .{ .tool_registry = custom_presentation_registry, .call = .{ .id = "custom", .name = "custom_presentation", .arguments_json = "{\"name\":\"registry metadata\"}", }, }); defer alloc.free(label); try std.testing.expectEqualStrings("Inspecting registry metadata", label);} test "tool presentation formats plain web progress variants" { const alloc = std.testing.allocator; const cases = [_]struct { text: []const u8, progress: union(enum) { search: types.WebSearchProgress, fetch: types.WebFetchProgress,Lines 1–500 of 800. Load more
const std = @import("std"); const builtin = @import("builtin"); const command_policy = @import("command_policy.zig"); const file_mutation_contract = @import("file_mutation_contract.zig"); const text_utils = @import("../shared/text_utils.zig"); const tool_args = @import("tool_args.zig"); const tool_dispatch = @import("tool_dispatch.zig"); const types = @import("../shared/types.zig"); const test_builtin_tools = if (builtin.is_test) @import("../../builtins/tools.zig") else struct {}; const Allocator = std.mem.Allocator; const ToolCall = types.ToolCall; const max_run_command_activity_bytes = 120; const max_run_command_activity_source_bytes = max_run_command_activity_bytes * max_run_command_activity_bytes; pub const max_auto_permission_reason_presentation_bytes: usize = 160; pub const ToolActionInput = struct { tool_registry: tool_dispatch.Registry, call: ToolCall, workspace_root: []const u8 = "", file_display_path: ?[]const u8 = null, is_available_dynamic_mcp_tool: bool = false, }; pub const RunCommandActivity = struct { detail: []const u8, compatibility_tool: ?*const tool_dispatch.Tool, }; pub fn isProviderSearchAlias(name: []const u8) bool { return std.mem.eql(u8, name, "perplexity_search") or std.mem.eql(u8, name, "parallel_search"); } fn projectRunCommandActivitySource( command: []const u8, workspace_root_input: []const u8, storage: *[max_run_command_activity_bytes + 1]u8, ) []const u8 { var workspace_root_end = workspace_root_input.len; while (workspace_root_end > 1 and workspace_root_input[workspace_root_end - 1] == '/') { workspace_root_end -= 1; } const workspace_root = workspace_root_input[0..workspace_root_end]; const can_abbreviate_workspace = workspace_root.len > 1 and workspace_root[0] == '/'; const source_limit = @min(command.len, max_run_command_activity_source_bytes); var source_index: usize = 0; var projected_len: usize = 0; var line_boundary_pending = false; while (source_index < source_limit) { const byte = command[source_index]; if (byte == '\r' or byte == '\n') { line_boundary_pending = true; source_index += 1; continue; } if (line_boundary_pending and (byte == ' ' or byte == '\t')) { source_index += 1; continue; } if (line_boundary_pending) { line_boundary_pending = false; if (projected_len > 0) { storage[projected_len] = ' '; projected_len += 1; if (projected_len == storage.len) break; } } if (can_abbreviate_workspace and workspace_root.len <= source_limit - source_index and workspaceRootMatchesAt(command, workspace_root, source_index)) { storage[projected_len] = '.'; projected_len += 1; if (projected_len == storage.len) break; source_index += workspace_root.len; continue; } storage[projected_len] = byte; projected_len += 1; if (projected_len == storage.len) break; source_index += 1; } return storage[0..projected_len]; } fn workspaceRootMatchesAt(command: []const u8, workspace_root: []const u8, index: usize) bool { if (!std.mem.startsWith(u8, command[index..], workspace_root)) return false; if (index > 0 and isPathTokenByte(command[index - 1])) return false; const next_index = index + workspace_root.len; if (next_index == command.len) return true; return command[next_index] == '/' or !isPathTokenByte(command[next_index]); } fn isPathTokenByte(byte: u8) bool { return std.ascii.isAlphanumeric(byte) or switch (byte) { '/', '-', '.', '_', '~' => true, else => false, }; } /// The caller owns the returned allocation and must free it with `alloc`. pub fn formatRunCommandPermissionLabel( alloc: Allocator, command: []const u8, ) ![]const u8 { var scratch_state = std.heap.ArenaAllocator.init(alloc); defer scratch_state.deinit(); const scratch = scratch_state.allocator(); const encoded = try text_utils.encodeTerminalSafe( scratch, command, max_run_command_activity_bytes, ); const suffix = try commandApprovalLabelSuffix(scratch, "terminal", command); return std.fmt.allocPrint(alloc, "terminal.exec {s}{s}", .{ encoded.bytes, suffix }); } /// The caller owns the returned allocation and must free it with `alloc`. pub fn formatSandboxWideningPermissionLabel( alloc: Allocator, reactive: bool, command: []const u8, ) ![]const u8 { var scratch_state = std.heap.ArenaAllocator.init(alloc); defer scratch_state.deinit(); const encoded = try text_utils.encodeTerminalSafe( scratch_state.allocator(), command, max_run_command_activity_bytes, ); return std.fmt.allocPrint(alloc, "{s}broader file access: {s}", .{ if (reactive) "retry with " else "", encoded.bytes, }); } pub fn isAdvertisedDynamicMcpName(registry: tool_dispatch.Registry, name: []const u8, advertised: []const []const u8) bool { if (registry.lookup(name) != null) return false; for (advertised) |advertised_name| { if (std.mem.eql(u8, advertised_name, name)) return true; } return false; } /// The caller owns `detail` and must free it with `alloc`. pub fn formatRunCommandActivity( alloc: Allocator, registry: tool_dispatch.Registry, workspace_root: []const u8, call: ToolCall, ) !?RunCommandActivity { var scratch_state = std.heap.ArenaAllocator.init(std.heap.c_allocator); defer scratch_state.deinit(); const scratch = scratch_state.allocator(); const args = tool_args.parseToolArgsObject(scratch, call.arguments_json) catch return null; if (!isCapturedCommandCall(registry, call, args)) return null; const command = tool_args.optionalStringArg(args, "command") orelse return null; var projected_storage: [max_run_command_activity_bytes + 1]u8 = undefined; const projected = projectRunCommandActivitySource(command, workspace_root, &projected_storage); const encoded = try text_utils.encodeTerminalSafe(scratch, projected, max_run_command_activity_bytes); return .{ .detail = try alloc.dupe(u8, encoded.bytes), .compatibility_tool = if (try tool_dispatch.matchRunCommandCompatibility(registry, command)) |matched| matched.tool else null, }; } /// The caller owns the returned allocation and must free it with `alloc`. pub fn formatPlainAction(alloc: Allocator, input: ToolActionInput) ![]const u8 { const call = input.call; if (file_mutation_contract.isToolName(call.name)) { const spec = input.tool_registry.lookup(call.name) orelse return std.fmt.allocPrint(alloc, "Working: {s}", .{call.name}); return std.fmt.allocPrint( alloc, "{s} {s}", .{ spec.action_label, input.file_display_path orelse spec.label_arg_default }, ); } if (try formatRunCommandActivity(alloc, input.tool_registry, input.workspace_root, call)) |activity| { defer alloc.free(activity.detail); const action_label = if (activity.compatibility_tool) |tool| tool.action_label else "Running"; return std.fmt.allocPrint(alloc, "{s} {s}", .{ action_label, activity.detail }); } var scratch_state = std.heap.ArenaAllocator.init(alloc); defer scratch_state.deinit(); const scratch = scratch_state.allocator(); const spec = input.tool_registry.lookup(call.name) orelse { if (isProviderSearchAlias(call.name)) { const args = tool_args.parseToolArgsObject(scratch, call.arguments_json) catch { return std.fmt.allocPrint(alloc, "Searching web", .{}); }; return std.fmt.allocPrint(alloc, "Searching {s}", .{try formatWebSearchActionDetail(scratch, args)}); } if (input.is_available_dynamic_mcp_tool) return std.fmt.allocPrint(alloc, "MCP: {s}", .{call.name}); return std.fmt.allocPrint(alloc, "Working: {s}", .{call.name}); }; const args = tool_args.parseToolArgsObject(scratch, call.arguments_json) catch { return std.fmt.allocPrint(alloc, "Working: {s}", .{call.name}); }; const presentation = tool_dispatch.presentationForArgs(spec.*, args); if (spec.executor_kind == .web_search) { return std.fmt.allocPrint(alloc, "{s} {s}", .{ presentation.action_label, try formatWebSearchActionDetail(scratch, args) }); } if (try copyRenameLabel(scratch, call.name, args)) |value| { return std.fmt.allocPrint(alloc, "{s} {s}", .{ presentation.action_label, value }); } const value = tool_dispatch.presentationLabelValue(presentation, args) orelse presentation.label_arg_default; return std.fmt.allocPrint(alloc, "{s} {s}", .{ presentation.action_label, value }); } /// The caller owns the returned allocation and must free it with `alloc`. pub fn formatPermissionLabel(alloc: Allocator, registry: tool_dispatch.Registry, call: ToolCall) ![]const u8 { var scratch_state = std.heap.ArenaAllocator.init(alloc); defer scratch_state.deinit(); const scratch = scratch_state.allocator(); if (try runCommandCompatibilitySource(scratch, registry, call)) |source| { return std.fmt.allocPrint(alloc, "{s} {s}", .{ source.tool.name, source.command }); } const args = tool_args.parseToolArgsObject(scratch, call.arguments_json) catch { return try alloc.dupe(u8, call.name); }; if (isCapturedCommandCall(registry, call, args)) { const command = tool_args.optionalStringArg(args, "command") orelse return try alloc.dupe(u8, call.name); return formatRunCommandPermissionLabel(alloc, command); } const spec = registry.lookup(call.name) orelse return try alloc.dupe(u8, call.name); if (file_mutation_contract.isToolName(call.name)) { return std.fmt.allocPrint( alloc, "{s} {s}", .{ call.name, spec.label_arg_default }, ); } if (try copyRenameLabel(scratch, call.name, args)) |value| { return std.fmt.allocPrint(alloc, "{s} {s}", .{ call.name, value }); } const value = tool_dispatch.toolLabelValue(spec.*, args) orelse return try alloc.dupe(u8, call.name); if (spec.label_arg_kind == .command) { const suffix = try commandApprovalLabelSuffix(scratch, call.name, value); if (tool_args.optionalStringArg(args, "cwd")) |cwd| { return std.fmt.allocPrint(alloc, "{s} {s} @ {s}{s}", .{ call.name, value, cwd, suffix }); } return std.fmt.allocPrint(alloc, "{s} {s}{s}", .{ call.name, value, suffix }); } return std.fmt.allocPrint(alloc, "{s} {s}", .{ call.name, value }); } /// The caller owns the returned allocation and must free it with `alloc`. pub fn formatWebSearchActionDetail(alloc: Allocator, args: std.json.ObjectMap) ![]const u8 { var out: std.Io.Writer.Allocating = .init(alloc); errdefer out.deinit(); var label_buf: [160]u8 = undefined; const query = tool_args.optionalStringArg(args, "query") orelse "web"; try out.writer.writeAll(text_utils.clippedLabel(&label_buf, query, 120)); try appendWebSearchDomains(&out.writer, "allowed", args.get("allowed_domains")); try appendWebSearchDomains(&out.writer, "blocked", args.get("blocked_domains")); return try out.toOwnedSlice(); } /// The caller owns the returned allocation and must free it with `alloc`. pub fn formatWebSearchProgressPlain(alloc: Allocator, progress: types.WebSearchProgress) ![]u8 { var query_buf: [160]u8 = undefined; return switch (progress) { .query_started => |query| std.fmt.allocPrint( alloc, "Searching {s}", .{text_utils.clippedLabel(&query_buf, query, 120)}, ), .results_received => |entry| std.fmt.allocPrint( alloc, "Found {d} result{s} for {s}", .{ entry.result_count, if (entry.result_count == 1) "" else "s", text_utils.clippedLabel(&query_buf, entry.query, 120) }, ), }; } /// The caller owns the returned allocation and must free it with `alloc`. pub fn formatWebFetchProgressPlain(alloc: Allocator, progress: types.WebFetchProgress) ![]u8 { var url_buf: [types.WebFetchCompletion.max_url_len]u8 = undefined; return switch (progress) { .fetching => |url| std.fmt.allocPrint(alloc, "Fetching {s}", .{text_utils.clippedLabel(&url_buf, url, 96)}), .converting => |url| std.fmt.allocPrint(alloc, "Converting {s}", .{text_utils.clippedLabel(&url_buf, url, 96)}), }; } fn appendWebSearchDomains(writer: *std.Io.Writer, label: []const u8, value: ?std.json.Value) !void { const array = value orelse return; if (array != .array or array.array.items.len == 0) return; try writer.print(" | {s}: ", .{label}); var domain_buf: [64]u8 = undefined; const shown = @min(array.array.items.len, 3); for (array.array.items[0..shown], 0..) |item, index| { if (index > 0) try writer.writeAll(", "); if (item != .string) { try writer.writeAll("?"); continue; } try writer.writeAll(text_utils.clippedLabel(&domain_buf, item.string, 48)); } if (array.array.items.len > shown) try writer.print(" +{d}", .{array.array.items.len - shown}); } fn commandApprovalLabelSuffix(alloc: Allocator, tool_name: []const u8, command: []const u8) ![]const u8 { if (!std.mem.eql(u8, tool_name, "run_command") and !std.mem.eql(u8, tool_name, "terminal")) return ""; const risk = command_policy.command_risk_note_for(command); const safer = command_policy.command_safer_alternative_for(command); if (risk == null and safer == null) return ""; const risk_text = if (risk) |note| stripNotePrefix(note) else null; if (risk_text) |note| { if (safer) |alternative| { return std.fmt.allocPrint(alloc, " (risk: {s}; {s})", .{ note, alternative }); } return std.fmt.allocPrint(alloc, " (risk: {s})", .{note}); } if (safer) |alternative| { return std.fmt.allocPrint(alloc, " ({s})", .{alternative}); } return ""; } fn stripNotePrefix(note: []const u8) []const u8 { const prefix = "note: "; if (std.mem.startsWith(u8, note, prefix)) return note[prefix.len..]; return note; } const RunCommandCompatibilitySource = struct { tool: *const tool_dispatch.Tool, command: []const u8, }; fn runCommandCompatibilitySource( alloc: Allocator, registry: tool_dispatch.Registry, call: ToolCall, ) !?RunCommandCompatibilitySource { const args = tool_args.parseToolArgsObject(alloc, call.arguments_json) catch return null; if (!isCapturedCommandCall(registry, call, args)) return null; const command = tool_args.optionalStringArg(args, "command") orelse return null; const matched = (try tool_dispatch.matchRunCommandCompatibility(registry, command)) orelse return null; return .{ .tool = matched.tool, .command = command }; } fn isCapturedCommandCall( registry: tool_dispatch.Registry, call: ToolCall, args: std.json.ObjectMap, ) bool { // Historical sessions retain their original tool name. Presentation may // interpret those records, but execution remains unavailable because the // registry no longer contains a `run_command` tool. if (std.mem.eql(u8, call.name, "run_command")) return true; const tool = registry.lookup(call.name) orelse return false; if (tool.executor_kind == .run_command) return true; const expected = tool.captured_command_action orelse return false; const action = tool_args.optionalStringArg(args, "action") orelse return false; return std.mem.eql(u8, action, expected); } fn copyRenameLabel(alloc: Allocator, tool_name: []const u8, args: std.json.ObjectMap) !?[]const u8 { if (std.mem.eql(u8, tool_name, "copy_file")) { const source = tool_args.optionalStringArg(args, "source") orelse return null; const destination = tool_args.optionalStringArg(args, "destination") orelse return null; return try std.fmt.allocPrint(alloc, "{s} -> {s}", .{ source, destination }); } if (std.mem.eql(u8, tool_name, "rename_file")) { const old_path = tool_args.optionalStringArg(args, "old_path") orelse return null; const new_path = tool_args.optionalStringArg(args, "new_path") orelse return null; return try std.fmt.allocPrint(alloc, "{s} -> {s}", .{ old_path, new_path }); } return null; } fn matchesTestSkillInstall(command: []const u8) bool { return std.mem.startsWith(u8, command, "npx skills add "); } fn executeTestSkillInstall( ctx: tool_dispatch.DispatchContext, _: []const u8, ) tool_dispatch.DispatchError!tool_dispatch.ToolResult { return .{ .success = try ctx.allocator.dupe(u8, "installed") }; } const test_install_skill = blk: { var tool = test_builtin_tools.skill; tool.name = "install_skill"; tool.gateway_schema.name = "install_skill"; tool.executor_kind = .install_skill; tool.activity_kind = .write; tool.requires_approval = true; tool.action_label = "Installing skill"; tool.completed_action_label = "Installed skill"; tool.label_arg_kind = .source; tool.label_arg_default = "skill"; tool.run_command_compatibility = .{ .matches = matchesTestSkillInstall, .execute = executeTestSkillInstall, }; break :blk tool; }; const test_web_search = blk: { var tool = test_builtin_tools.read_file; tool.name = "web_search"; tool.gateway_schema.name = "web_search"; tool.executor_kind = .web_search; tool.action_label = "Searching"; tool.completed_action_label = "Searched"; tool.label_arg_kind = .query; tool.label_arg_default = "web"; break :blk tool; }; const test_tools = [_]tool_dispatch.Tool{ test_builtin_tools.read_file, test_builtin_tools.write_file, test_builtin_tools.edit_file, test_builtin_tools.rename_file, test_builtin_tools.copy_file, test_web_search, test_builtin_tools.terminal, test_builtin_tools.memory, test_builtin_tools.skill, test_install_skill, test_builtin_tools.ask_user_question, }; const test_tool_registry = tool_dispatch.Registry{ .tools = test_tools[0..] }; const custom_presentation_tool = blk: { var tool = test_builtin_tools.memory; tool.name = "custom_presentation"; tool.action_label = "Inspecting"; tool.label_arg_kind = .name; tool.label_arg_default = "custom fallback"; break :blk tool; }; const custom_presentation_registry = tool_dispatch.Registry{ .tools = &.{custom_presentation_tool} }; test "tool presentation formats dynamic MCP availability distinctly" { const alloc = std.testing.allocator; const call: ToolCall = .{ .id = "dynamic", .name = "mcp_lookup", .arguments_json = "{}", }; const available = try formatPlainAction(alloc, .{ .tool_registry = test_tool_registry, .call = call, .is_available_dynamic_mcp_tool = true, }); defer alloc.free(available); try std.testing.expectEqualStrings("MCP: mcp_lookup", available); const unavailable = try formatPlainAction(alloc, .{ .tool_registry = test_tool_registry, .call = call }); defer alloc.free(unavailable); try std.testing.expectEqualStrings("Working: mcp_lookup", unavailable); } test "tool presentation reads labels from the supplied registry" { const alloc = std.testing.allocator; const label = try formatPlainAction(alloc, .{ .tool_registry = custom_presentation_registry, .call = .{ .id = "custom", .name = "custom_presentation", .arguments_json = "{\"name\":\"registry metadata\"}", }, }); defer alloc.free(label); try std.testing.expectEqualStrings("Inspecting registry metadata", label); } test "tool presentation formats plain web progress variants" { const alloc = std.testing.allocator; const cases = [_]struct { text: []const u8, progress: union(enum) { search: types.WebSearchProgress, fetch: types.WebFetchProgress,