diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkillScript.cs b/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkillScript.cs index c0abc73252..db4ed334df 100644 --- a/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkillScript.cs +++ b/dotnet/src/Microsoft.Agents.AI/Skills/Programmatic/AgentInlineSkillScript.cs @@ -92,18 +92,42 @@ private static AIFunctionArguments ConvertToFunctionArguments(JsonElement? argum return []; } - if (arguments.Value.ValueKind != JsonValueKind.Object) + JsonDocument? parsedStringArguments = null; + var argumentElement = arguments.Value; + + if (argumentElement.ValueKind == JsonValueKind.String) { - throw new InvalidOperationException( - $"Inline skill scripts expect arguments as a JSON object but received a JSON element of kind '{arguments.Value.ValueKind}'."); + string? rawArguments = argumentElement.GetString(); + try + { + parsedStringArguments = JsonDocument.Parse(rawArguments ?? string.Empty); + argumentElement = parsedStringArguments.RootElement; + } + catch (JsonException ex) + { + throw new InvalidOperationException("Inline skill scripts received arguments as a JSON string, but the string did not contain valid JSON.", ex); + } } - var dict = new Dictionary(); - foreach (var property in arguments.Value.EnumerateObject()) + if (argumentElement.ValueKind != JsonValueKind.Object) { - dict[property.Name] = property.Value; + throw new InvalidOperationException( + $"Inline skill scripts expect arguments as a JSON object but received a JSON element of kind '{argumentElement.ValueKind}'."); } - return new AIFunctionArguments(dict); + try + { + var dict = new Dictionary(); + foreach (var property in argumentElement.EnumerateObject()) + { + dict[property.Name] = parsedStringArguments is null ? property.Value : property.Value.Clone(); + } + + return new AIFunctionArguments(dict); + } + finally + { + parsedStringArguments?.Dispose(); + } } } diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentInlineSkillScriptTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentInlineSkillScriptTests.cs index 85521ba4a9..dfd1110251 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentInlineSkillScriptTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentInlineSkillScriptTests.cs @@ -43,6 +43,37 @@ public async Task RunAsync_WithParameters_PassesArgumentsAsync() Assert.Equal(10, int.Parse(result?.ToString()!)); } + [Fact] + public async Task RunAsync_WithJsonStringObjectArguments_PassesArgumentsAsync() + { + // Arrange + var script = new AgentInlineSkillScript("add", (int a, int b) => a + b); + var skill = new AgentInlineSkill("calc-skill", "Calc.", "Instructions."); + using var argsDoc = JsonDocument.Parse("\"{\\\"a\\\":3,\\\"b\\\":7}\""); + var args = argsDoc.RootElement; + + // Act + var result = await script.RunAsync(skill, args, null, CancellationToken.None); + + // Assert + Assert.Equal(10, int.Parse(result?.ToString()!)); + } + + [Fact] + public async Task RunAsync_WithInvalidJsonStringArguments_ThrowsInvalidOperationExceptionAsync() + { + // Arrange + var script = new AgentInlineSkillScript("noop", () => "ok"); + var skill = new AgentInlineSkill("test-skill", "Test.", "Instructions."); + using var argsDoc = JsonDocument.Parse("\"not json\""); + var args = argsDoc.RootElement; + + // Act & Assert + var ex = await Assert.ThrowsAsync( + () => script.RunAsync(skill, args, null, CancellationToken.None)); + Assert.Contains("did not contain valid JSON", ex.Message); + } + [Fact] public void ParametersSchema_NoParameters_ReturnsSchema() {