From ab714a24ce15863d442f698625f6629d07c86b5a Mon Sep 17 00:00:00 2001 From: EugeneTes Date: Thu, 8 Oct 2026 14:09:18 +0000 Subject: [PATCH] refactor(a2a-client): no runtime-internal native calls for attachments --- .../A2ATool.cs | 27 ++----------------- .../A2A/A2AToolTests.cs | 15 ----------- 2 files changed, 2 insertions(+), 40 deletions(-) diff --git a/extensions/Volcanic.AgenticCore.Extensions.A2A/A2ATool.cs b/extensions/Volcanic.AgenticCore.Extensions.A2A/A2ATool.cs index c08eada..e7c9e63 100644 --- a/extensions/Volcanic.AgenticCore.Extensions.A2A/A2ATool.cs +++ b/extensions/Volcanic.AgenticCore.Extensions.A2A/A2ATool.cs @@ -285,7 +285,7 @@ internal sealed class A2ATool(A2AClientSettings settings, string target, A2AWatc } /// - /// Reads the attachments: regular workspace files (symlinks resolved and confined), together at most + /// Reads the attachments: workspace files (symlinks resolved and confined), together at most /// , each read through one opened stream so a file growing meanwhile is /// still capped. /// @@ -297,7 +297,7 @@ internal sealed class A2ATool(A2AClientSettings settings, string target, A2AWatc { var full = ConfinedPath(workspace, path); if (Directory.Exists(full) || !File.Exists(full)) throw new ArgumentException($"{path} does not exist or is not a file."); - if (!IsRegularFile(full)) throw new ArgumentException($"{path} is not a regular file."); + // Special files (FIFOs, devices) aren't detected: a FIFO would block here until the run is aborted. byte[] bytes; await using (var stream = new FileStream(full, new FileStreamOptions { Mode = FileMode.Open, Access = FileAccess.Read, Share = FileShare.Read, Options = FileOptions.Asynchronous })) @@ -321,29 +321,6 @@ internal sealed class A2ATool(A2AClientSettings settings, string target, A2AWatc return parts; } - /// - /// False for FIFOs, sockets and devices, which could block or never end. Unix: the runtime's own stat (the managed - /// API reports every non-directory as a file); elsewhere every existing file counts as regular. - /// - private static bool IsRegularFile(string path) - { - if (OperatingSystem.IsWindows()) return true; - try - { - var status = new byte[512]; // FileStatus is ~120 bytes; Mode is the second int32 - if (SystemNativeStat(path, status) != 0) return false; - const int typeMask = 0xF000, regular = 0x8000; - return (BitConverter.ToInt32(status, 4) & typeMask) == regular; - } - catch (Exception ex) when (ex is DllNotFoundException or EntryPointNotFoundException) - { - return true; - } - } - - [System.Runtime.InteropServices.DllImport("libSystem.Native", EntryPoint = "SystemNative_Stat")] - private static extern int SystemNativeStat([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPUTF8Str)] string path, byte[] status); - /// The real path of a workspace file: inside the workspace lexically and after every symlink is resolved. internal static string ConfinedPath(string workspace, string path) { diff --git a/tests/Volcanic.AgenticCore.Extensions.Tests/A2A/A2AToolTests.cs b/tests/Volcanic.AgenticCore.Extensions.Tests/A2A/A2AToolTests.cs index e73533c..5bdafcd 100644 --- a/tests/Volcanic.AgenticCore.Extensions.Tests/A2A/A2AToolTests.cs +++ b/tests/Volcanic.AgenticCore.Extensions.Tests/A2A/A2AToolTests.cs @@ -315,21 +315,6 @@ public class A2AToolTests Assert.Empty(remote.Faux.Requests); } - [Fact] - public async Task Send_Fifo_Rejected() - { - if (OperatingSystem.IsWindows()) return; - await using var remote = await RemoteAgentServer.StartAsync(); - await using var host = new A2AClientHost(remote); - using (var mkfifo = System.Diagnostics.Process.Start("mkfifo", Path.Combine(host.Workspace, "pipe"))) - await mkfifo.WaitForExitAsync(); - var session = await host.CreateSessionAsync(); - var (text, isError) = await host.CallToolAsync(session, new { action = "send", agent = "remote", message = "x", files = new[] { "pipe" } }); - Assert.True(isError); - Assert.Contains("not a regular file", text); - Assert.Empty(remote.Faux.Requests); - } - [Fact] public async Task Cancel_RemoteDown_KeepsWatching_AndResultStillArrives() {