{"repo": "alexfrankfurt/agentdesktopuiharness", "files": {"Services/DeepSeekService.cs": {"b467613d9e5f5bf4cceb90778cc3eec3bc0e0701": {"t": 1785746203.929747, "n": 15385, "h": "4bf46fb550513568", "c": "using System.Net.Http.Headers;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\n\nnamespace ihsbmodern.Services;\n\npublic class ToolCall\n{\n [JsonPropertyName(\"id\")] public string Id { get; set; } = \"\";\n [JsonPropertyName(\"type\")] public string Type { get; set; } = \"function\";\n [JsonPropertyName(\"function\")] public ToolCallFunction Function { get; set; } = new();\n}\n\npublic class ToolCallFunction\n{\n [JsonPropertyName(\"name\")] public string Name { get; set; } = \"\";\n [JsonPropertyName(\"arguments\")] public string Arguments { get; set; } = \"{}\";\n}\n\npublic class ChatResult\n{\n public string Content { get; set; } = \"\";\n public List ToolCalls { get; set; } = new();\n public bool HasToolCalls => ToolCalls.Count > 0;\n public List<(string toolName, string summary)> ToolActivity { get; set; } = new();\n}\n\npublic class DeepSeekService\n{\n private const string BaseUrl = \"https://api.deepseek.com/v1/chat/completions\";\n private readonly HttpClient _httpClient = new();\n\n public string? ApiKey { get; set; }\n public string Model { get; set; } = \"deepseek-chat\";\n public string ReasoningEffort { get; set; } = \"medium\";\n\n public bool IsConfigured => !string.IsNullOrWhiteSpace(ApiKey);\n\n private static JsonElement GetToolsJson() => JsonSerializer.Deserialize(@\"[\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"list_files\"\",\n \"\"description\"\": \"\"List files and directories at a given path relative to the working directory. Use '.' for the root.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative path from working directory. Use '.' for root.\"\" }\n },\n \"\"required\"\": [\"\"path\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"read_file\"\",\n \"\"description\"\": \"\"Read the full contents of a file at the given path relative to the working directory.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative path to the file from working directory.\"\" }\n },\n \"\"required\"\": [\"\"path\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"edit_file\"\",\n \"\"description\"\": \"\"Edit a file by replacing exact old text with new text. The old_text must match exactly including whitespace.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative path to the file.\"\" },\n \"\"old_text\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Exact text to find and replace.\"\" },\n \"\"new_text\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Replacement text.\"\" }\n },\n \"\"required\"\": [\"\"path\"\", \"\"old_text\"\", \"\"new_text\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"create_file\"\",\n \"\"description\"\": \"\"Create or overwrite a file with the given content. Parent directories are created automatically.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative path for the new file.\"\" },\n \"\"content\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"File content to write.\"\" }\n },\n \"\"required\"\": [\"\"path\"\", \"\"content\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"grep\"\",\n \"\"description\"\": \"\"Search file contents using a regex pattern. Returns matching lines with file paths and line numbers. Searches recursively from the given path.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"pattern\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Regex pattern to search for.\"\" },\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative directory or file path to search in. Use '.' for entire working directory.\"\" },\n \"\"glob\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Optional file glob filter, e.g. '*.cs' or '*.py'. Defaults to all files.\"\" }\n },\n \"\"required\"\": [\"\"pattern\"\", \"\"path\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"run_command\"\",\n \"\"description\"\": \"\"Run a PowerShell command and wait for its output. Use for build, test, git, install, lint, or any command where you need stdout/stderr. Returns after the command finishes.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"command\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"The PowerShell command to execute.\"\" }\n },\n \"\"required\"\": [\"\"command\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"launch_process\"\",\n \"\"description\"\": \"\"Start a background process that keeps running after the call returns. Use for dev servers, watchers, or any long-running command. Does NOT capture output.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"command\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"The PowerShell command to run in the background.\"\" }\n },\n \"\"required\"\": [\"\"command\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"open_file\"\",\n \"\"description\"\": \"\"Open a file or URL in the user's default application (browser, editor, etc.). Use this to show files to the user, NOT run_command.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative file path or URL to open.\"\" }\n },\n \"\"required\"\": [\"\"path\"\"]\n }\n }\n }\n ]\");\n\n public async Task SendMessageAsync(\n IEnumerable<(string role, string content)> messages,\n Func? toolExecutor = null,\n CancellationToken ct = default)\n {\n if (!IsConfigured)\n throw new InvalidOperationException(\"API key is not configured.\");\n\n var conversationMessages = new List();\n foreach (var m in messages)\n conversationMessages.Add(new { role = m.role, content = m.content });\n\n return await SendWithToolsAsync(conversationMessages, toolExecutor, ct);\n }\n\n private async Task SendWithToolsAsync(\n List conversationMessages,\n Func? toolExecutor,\n CancellationToken ct,\n int depth = 0)\n {\n if (depth > 10)\n return new ChatResult { Content = \"Error: Too many tool call iterations.\" };\n\n var useTools = toolExecutor != null;\n var toolsElement = useTools ? GetToolsJson() : (JsonElement?)null;\n var messageArray = conversationMessages.ToArray();\n\n Dictionary payload;\n if (Model == \"deepseek-reasoner\")\n {\n payload = new Dictionary\n {\n [\"model\"] = Model,\n [\"messages\"] = messageArray,\n [\"stream\"] = false,\n [\"reasoning_effort\"] = ReasoningEffort\n };\n }\n else\n {\n payload = new Dictionary\n {\n [\"model\"] = Model,\n [\"messages\"] = messageArray,\n [\"stream\"] = false\n };\n }\n\n if (toolsElement.HasValue)\n payload[\"tools\"] = toolsElement.Value;\n\n var json = JsonSerializer.Serialize(payload);\n using var request = new HttpRequestMessage(HttpMethod.Post, BaseUrl);\n request.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", ApiKey);\n request.Content = new StringContent(json, Encoding.UTF8, \"application/json\");\n\n using var response = await _httpClient.SendAsync(request, ct);\n var body = await response.Content.ReadAsStringAsync(ct);\n\n if (!response.IsSuccessStatusCode)\n throw new Exception($\"API error {(int)response.StatusCode}: {body}\");\n\n using var doc = JsonDocument.Parse(body);\n var choice = doc.RootElement.GetProperty(\"choices\")[0].GetProperty(\"message\");\n\n var result = new ChatResult();\n var content = choice.GetProperty(\"content\").GetString() ?? \"\";\n\n if (Model == \"deepseek-reasoner\" && choice.TryGetProperty(\"reasoning_content\", out var reasoning))\n {\n var reasoningText = reasoning.GetString();\n if (!string.IsNullOrWhiteSpace(reasoningText))\n content = $\"💭 *Reasoning:*\\n{reasoningText}\\n\\n---\\n\\n*Answer:*\\n{content}\";\n }\n\n result.Content = content;\n\n if (choice.TryGetProperty(\"tool_calls\", out var toolCallsElement) && toolCallsElement.ValueKind == JsonValueKind.Array)\n {\n foreach (var tc in toolCallsElement.EnumerateArray())\n {\n result.ToolCalls.Add(new ToolCall\n {\n Id = tc.GetProperty(\"id\").GetString() ?? \"\",\n Type = \"function\",\n Function = new ToolCallFunction\n {\n Name = tc.GetProperty(\"function\").GetProperty(\"name\").GetString() ?? \"\",\n Arguments = tc.GetProperty(\"function\").GetProperty(\"arguments\").GetString() ?? \"{}\"\n }\n });\n }\n }\n\n if (!result.HasToolCalls || toolExecutor == null)\n return result;\n\n conversationMessages.Add(new\n {\n role = \"assistant\",\n content = (string?)null,\n tool_calls = result.ToolCalls.Select(tc => new\n {\n id = tc.Id,\n type = \"function\",\n function = new { name = tc.Function.Name, arguments = tc.Function.Arguments }\n }).ToArray()\n });\n\n foreach (var tc in result.ToolCalls)\n {\n string toolResult;\n try\n {\n var args = JsonSerializer.Deserialize>(tc.Function.Arguments);\n toolResult = tc.Function.Name switch\n {\n \"list_files\" => toolExecutor(\"list_files\", args![\"path\"].GetString() ?? \".\"),\n \"read_file\" => toolExecutor(\"read_file\", args![\"path\"].GetString() ?? \"\"),\n \"edit_file\" => toolExecutor(\"edit_file\", JsonSerializer.Serialize(new\n {\n path = args![\"path\"].GetString(),\n old_text = args[\"old_text\"].GetString(),\n new_text = args[\"new_text\"].GetString()\n })),\n \"create_file\" => toolExecutor(\"create_file\", JsonSerializer.Serialize(new\n {\n path = args![\"path\"].GetString(),\n content = args[\"content\"].GetString()\n })),\n \"grep\" => toolExecutor(\"grep\", JsonSerializer.Serialize(new\n {\n pattern = args![\"pattern\"].GetString(),\n path = args[\"path\"].GetString(),\n glob = args.TryGetValue(\"glob\", out var gv) ? gv.GetString() : null\n })),\n \"run_command\" => toolExecutor(\"run_command\", JsonSerializer.Serialize(new\n {\n command = args![\"command\"].GetString()\n })),\n \"launch_process\" => toolExecutor(\"launch_process\", JsonSerializer.Serialize(new\n {\n command = args![\"command\"].GetString()\n })),\n \"open_file\" => toolExecutor(\"open_file\", JsonSerializer.Serialize(new\n {\n path = args![\"path\"].GetString()\n })),\n _ => $\"Unknown tool: {tc.Function.Name}\"\n };\n }\n catch (Exception ex)\n {\n toolResult = $\"Error executing {tc.Function.Name}: {ex.Message}\";\n }\n\n var summary = tc.Function.Name switch\n {\n \"list_files\" => $\"list_files({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"path\"].GetString()})\",\n \"read_file\" => $\"read_file({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"path\"].GetString()})\",\n \"edit_file\" => $\"edit_file({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"path\"].GetString()})\",\n \"create_file\" => $\"create_file({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"path\"].GetString()})\",\n \"grep\" => $\"grep({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"pattern\"].GetString()})\",\n \"run_command\" => $\"run_command({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"command\"].GetString()})\",\n \"launch_process\" => $\"launch_process({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"command\"].GetString()})\",\n \"open_file\" => $\"open_file({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"path\"].GetString()})\",\n _ => tc.Function.Name\n };\n result.ToolActivity.Add((tc.Function.Name, summary));\n\n conversationMessages.Add(new\n {\n role = \"tool\",\n tool_call_id = tc.Id,\n content = toolResult\n });\n }\n\n var followUp = await SendWithToolsAsync(conversationMessages, toolExecutor, ct, depth + 1);\n result.Content = followUp.Content;\n result.ToolCalls = followUp.ToolCalls;\n result.ToolActivity.AddRange(followUp.ToolActivity);\n return result;\n }\n}\n"}, "6d12e0dc2346b186482e319e2e6523379b7ac51b": {"t": 1785760577.6900692, "n": 15385, "h": "4bf46fb550513568", "c": "using System.Net.Http.Headers;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\n\nnamespace ihsbmodern.Services;\n\npublic class ToolCall\n{\n [JsonPropertyName(\"id\")] public string Id { get; set; } = \"\";\n [JsonPropertyName(\"type\")] public string Type { get; set; } = \"function\";\n [JsonPropertyName(\"function\")] public ToolCallFunction Function { get; set; } = new();\n}\n\npublic class ToolCallFunction\n{\n [JsonPropertyName(\"name\")] public string Name { get; set; } = \"\";\n [JsonPropertyName(\"arguments\")] public string Arguments { get; set; } = \"{}\";\n}\n\npublic class ChatResult\n{\n public string Content { get; set; } = \"\";\n public List ToolCalls { get; set; } = new();\n public bool HasToolCalls => ToolCalls.Count > 0;\n public List<(string toolName, string summary)> ToolActivity { get; set; } = new();\n}\n\npublic class DeepSeekService\n{\n private const string BaseUrl = \"https://api.deepseek.com/v1/chat/completions\";\n private readonly HttpClient _httpClient = new();\n\n public string? ApiKey { get; set; }\n public string Model { get; set; } = \"deepseek-chat\";\n public string ReasoningEffort { get; set; } = \"medium\";\n\n public bool IsConfigured => !string.IsNullOrWhiteSpace(ApiKey);\n\n private static JsonElement GetToolsJson() => JsonSerializer.Deserialize(@\"[\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"list_files\"\",\n \"\"description\"\": \"\"List files and directories at a given path relative to the working directory. Use '.' for the root.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative path from working directory. Use '.' for root.\"\" }\n },\n \"\"required\"\": [\"\"path\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"read_file\"\",\n \"\"description\"\": \"\"Read the full contents of a file at the given path relative to the working directory.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative path to the file from working directory.\"\" }\n },\n \"\"required\"\": [\"\"path\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"edit_file\"\",\n \"\"description\"\": \"\"Edit a file by replacing exact old text with new text. The old_text must match exactly including whitespace.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative path to the file.\"\" },\n \"\"old_text\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Exact text to find and replace.\"\" },\n \"\"new_text\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Replacement text.\"\" }\n },\n \"\"required\"\": [\"\"path\"\", \"\"old_text\"\", \"\"new_text\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"create_file\"\",\n \"\"description\"\": \"\"Create or overwrite a file with the given content. Parent directories are created automatically.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative path for the new file.\"\" },\n \"\"content\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"File content to write.\"\" }\n },\n \"\"required\"\": [\"\"path\"\", \"\"content\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"grep\"\",\n \"\"description\"\": \"\"Search file contents using a regex pattern. Returns matching lines with file paths and line numbers. Searches recursively from the given path.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"pattern\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Regex pattern to search for.\"\" },\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative directory or file path to search in. Use '.' for entire working directory.\"\" },\n \"\"glob\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Optional file glob filter, e.g. '*.cs' or '*.py'. Defaults to all files.\"\" }\n },\n \"\"required\"\": [\"\"pattern\"\", \"\"path\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"run_command\"\",\n \"\"description\"\": \"\"Run a PowerShell command and wait for its output. Use for build, test, git, install, lint, or any command where you need stdout/stderr. Returns after the command finishes.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"command\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"The PowerShell command to execute.\"\" }\n },\n \"\"required\"\": [\"\"command\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"launch_process\"\",\n \"\"description\"\": \"\"Start a background process that keeps running after the call returns. Use for dev servers, watchers, or any long-running command. Does NOT capture output.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"command\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"The PowerShell command to run in the background.\"\" }\n },\n \"\"required\"\": [\"\"command\"\"]\n }\n }\n },\n {\n \"\"type\"\": \"\"function\"\",\n \"\"function\"\": {\n \"\"name\"\": \"\"open_file\"\",\n \"\"description\"\": \"\"Open a file or URL in the user's default application (browser, editor, etc.). Use this to show files to the user, NOT run_command.\"\",\n \"\"parameters\"\": {\n \"\"type\"\": \"\"object\"\",\n \"\"properties\"\": {\n \"\"path\"\": { \"\"type\"\": \"\"string\"\", \"\"description\"\": \"\"Relative file path or URL to open.\"\" }\n },\n \"\"required\"\": [\"\"path\"\"]\n }\n }\n }\n ]\");\n\n public async Task SendMessageAsync(\n IEnumerable<(string role, string content)> messages,\n Func? toolExecutor = null,\n CancellationToken ct = default)\n {\n if (!IsConfigured)\n throw new InvalidOperationException(\"API key is not configured.\");\n\n var conversationMessages = new List();\n foreach (var m in messages)\n conversationMessages.Add(new { role = m.role, content = m.content });\n\n return await SendWithToolsAsync(conversationMessages, toolExecutor, ct);\n }\n\n private async Task SendWithToolsAsync(\n List conversationMessages,\n Func? toolExecutor,\n CancellationToken ct,\n int depth = 0)\n {\n if (depth > 10)\n return new ChatResult { Content = \"Error: Too many tool call iterations.\" };\n\n var useTools = toolExecutor != null;\n var toolsElement = useTools ? GetToolsJson() : (JsonElement?)null;\n var messageArray = conversationMessages.ToArray();\n\n Dictionary payload;\n if (Model == \"deepseek-reasoner\")\n {\n payload = new Dictionary\n {\n [\"model\"] = Model,\n [\"messages\"] = messageArray,\n [\"stream\"] = false,\n [\"reasoning_effort\"] = ReasoningEffort\n };\n }\n else\n {\n payload = new Dictionary\n {\n [\"model\"] = Model,\n [\"messages\"] = messageArray,\n [\"stream\"] = false\n };\n }\n\n if (toolsElement.HasValue)\n payload[\"tools\"] = toolsElement.Value;\n\n var json = JsonSerializer.Serialize(payload);\n using var request = new HttpRequestMessage(HttpMethod.Post, BaseUrl);\n request.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", ApiKey);\n request.Content = new StringContent(json, Encoding.UTF8, \"application/json\");\n\n using var response = await _httpClient.SendAsync(request, ct);\n var body = await response.Content.ReadAsStringAsync(ct);\n\n if (!response.IsSuccessStatusCode)\n throw new Exception($\"API error {(int)response.StatusCode}: {body}\");\n\n using var doc = JsonDocument.Parse(body);\n var choice = doc.RootElement.GetProperty(\"choices\")[0].GetProperty(\"message\");\n\n var result = new ChatResult();\n var content = choice.GetProperty(\"content\").GetString() ?? \"\";\n\n if (Model == \"deepseek-reasoner\" && choice.TryGetProperty(\"reasoning_content\", out var reasoning))\n {\n var reasoningText = reasoning.GetString();\n if (!string.IsNullOrWhiteSpace(reasoningText))\n content = $\"💭 *Reasoning:*\\n{reasoningText}\\n\\n---\\n\\n*Answer:*\\n{content}\";\n }\n\n result.Content = content;\n\n if (choice.TryGetProperty(\"tool_calls\", out var toolCallsElement) && toolCallsElement.ValueKind == JsonValueKind.Array)\n {\n foreach (var tc in toolCallsElement.EnumerateArray())\n {\n result.ToolCalls.Add(new ToolCall\n {\n Id = tc.GetProperty(\"id\").GetString() ?? \"\",\n Type = \"function\",\n Function = new ToolCallFunction\n {\n Name = tc.GetProperty(\"function\").GetProperty(\"name\").GetString() ?? \"\",\n Arguments = tc.GetProperty(\"function\").GetProperty(\"arguments\").GetString() ?? \"{}\"\n }\n });\n }\n }\n\n if (!result.HasToolCalls || toolExecutor == null)\n return result;\n\n conversationMessages.Add(new\n {\n role = \"assistant\",\n content = (string?)null,\n tool_calls = result.ToolCalls.Select(tc => new\n {\n id = tc.Id,\n type = \"function\",\n function = new { name = tc.Function.Name, arguments = tc.Function.Arguments }\n }).ToArray()\n });\n\n foreach (var tc in result.ToolCalls)\n {\n string toolResult;\n try\n {\n var args = JsonSerializer.Deserialize>(tc.Function.Arguments);\n toolResult = tc.Function.Name switch\n {\n \"list_files\" => toolExecutor(\"list_files\", args![\"path\"].GetString() ?? \".\"),\n \"read_file\" => toolExecutor(\"read_file\", args![\"path\"].GetString() ?? \"\"),\n \"edit_file\" => toolExecutor(\"edit_file\", JsonSerializer.Serialize(new\n {\n path = args![\"path\"].GetString(),\n old_text = args[\"old_text\"].GetString(),\n new_text = args[\"new_text\"].GetString()\n })),\n \"create_file\" => toolExecutor(\"create_file\", JsonSerializer.Serialize(new\n {\n path = args![\"path\"].GetString(),\n content = args[\"content\"].GetString()\n })),\n \"grep\" => toolExecutor(\"grep\", JsonSerializer.Serialize(new\n {\n pattern = args![\"pattern\"].GetString(),\n path = args[\"path\"].GetString(),\n glob = args.TryGetValue(\"glob\", out var gv) ? gv.GetString() : null\n })),\n \"run_command\" => toolExecutor(\"run_command\", JsonSerializer.Serialize(new\n {\n command = args![\"command\"].GetString()\n })),\n \"launch_process\" => toolExecutor(\"launch_process\", JsonSerializer.Serialize(new\n {\n command = args![\"command\"].GetString()\n })),\n \"open_file\" => toolExecutor(\"open_file\", JsonSerializer.Serialize(new\n {\n path = args![\"path\"].GetString()\n })),\n _ => $\"Unknown tool: {tc.Function.Name}\"\n };\n }\n catch (Exception ex)\n {\n toolResult = $\"Error executing {tc.Function.Name}: {ex.Message}\";\n }\n\n var summary = tc.Function.Name switch\n {\n \"list_files\" => $\"list_files({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"path\"].GetString()})\",\n \"read_file\" => $\"read_file({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"path\"].GetString()})\",\n \"edit_file\" => $\"edit_file({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"path\"].GetString()})\",\n \"create_file\" => $\"create_file({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"path\"].GetString()})\",\n \"grep\" => $\"grep({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"pattern\"].GetString()})\",\n \"run_command\" => $\"run_command({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"command\"].GetString()})\",\n \"launch_process\" => $\"launch_process({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"command\"].GetString()})\",\n \"open_file\" => $\"open_file({JsonSerializer.Deserialize>(tc.Function.Arguments)![\"path\"].GetString()})\",\n _ => tc.Function.Name\n };\n result.ToolActivity.Add((tc.Function.Name, summary));\n\n conversationMessages.Add(new\n {\n role = \"tool\",\n tool_call_id = tc.Id,\n content = toolResult\n });\n }\n\n var followUp = await SendWithToolsAsync(conversationMessages, toolExecutor, ct, depth + 1);\n result.Content = followUp.Content;\n result.ToolCalls = followUp.ToolCalls;\n result.ToolActivity.AddRange(followUp.ToolActivity);\n return result;\n }\n}\n"}}}}