Commit 9e98f513 authored by shrabbit's avatar shrabbit
Browse files

feat(RequestTools): 更新工具描述为英文,并在 ReadWatchableConsole 中检测进程退出并返回退出提示

parent 8c226054
Loading
Loading
Loading
Loading
+25 −10
Original line number Diff line number Diff line
@@ -36,10 +36,10 @@ public class RequestTools(ProcessMap pm)
        return sw.Elapsed.TotalSeconds.ToString("f3");
    }

    [McpServerTool, Description("创建一个可观察的控制台:在独立窗口中由 McpBase.Request.exe 启动指定的交互式命令行程序(如 cmd/pwsh/bash),并将其输出流通过命名管道回传,供 Agent 通过 ReadWatchableConsole 观察。返回观察 ID(8 位十六进制)。注意:由于未使用 PTY,部分行编辑/TUI 程序可能表现受限;用户在 Request 窗口中输入命令、查看输出,Agent 同步观察同一输出流。")]
    [McpServerTool, Description("Launch a watchable interactive console in a separate window. The specified interactive command-line program (e.g. cmd, pwsh, bash) is hosted by McpBase.Request.exe so a human can interact with it in its own window, while its stdout/stderr are streamed back to the agent over a named pipe. Returns a watch ID (8-char hex) to pass to ReadWatchableConsole. The human types input and sees output in the host window; the agent observes the same output stream by polling ReadWatchableConsole. No PTY is used, so line-editing/TUI programs may be limited. The watch stays alive until the observed program exits. Use this when the agent needs a human to drive an interactive shell while the agent observes the output.")]
    public string CreateWatchableConsole(
        [Description("可执行程序路径,支持后跟参数。如果位于环境变量(PATH)中,则仅需文件名即可。仅支持交互式命令行界面(如 bash, cmd, pwsh...),不支持普通命令行与 GUI 程序。")] string fileName,
        [Description("在 Request 窗口中显示的提示文本,支持 Spectre.Console 标记语法。")] string? message = "")
        [Description("Executable to launch, optionally followed by arguments. A bare filename works if it is on PATH. Only interactive CLI shells (cmd, pwsh, bash, ...) are supported; do not pass GUI or non-interactive programs.")] string fileName,
        [Description("Optional banner text shown at the top of the host window. Supports Spectre.Console markup (e.g. [yellow]warning[/]); double [ and ] to print them literally.")] string? message = "")
    {
        // 服务端侧创建命名管道(方向 In:接收 Request 转发的子程序输出)。
        var pipeName = $"mcpbase.watch.{Guid.NewGuid():N}";
@@ -93,7 +93,7 @@ public class RequestTools(ProcessMap pm)
        if (process is null)
        {
            try { pipe.Dispose(); } catch { }
            throw new InvalidOperationException("无法启动 McpBase.Request.exe");
            throw new InvalidOperationException("Failed to start McpBase.Request.exe.");
        }

        var id = pm.Add(new WatchableConsole
@@ -107,20 +107,35 @@ public class RequestTools(ProcessMap pm)
        return id;
    }

    [McpServerTool, Description("读取指定可观察控制台的输出流内容(自上次读取以来累积的快照)。")]
    [McpServerTool, Description("Read output captured from a watchable console created by CreateWatchableConsole. Returns the text accumulated since the last read (when consume=true, the default). Poll periodically while the process runs: an empty string means the process is still running but has produced no new output. When the observed program has exited, the returned string ends with an exit notice and every subsequent call keeps reporting that it has exited (no further output will arrive). Recommended flow: call CreateWatchableConsole, then poll ReadWatchableConsole until the exit notice appears.")]
    public string ReadWatchableConsole(
        [Description("CreateWatchableConsole 返回的观察 ID。")] string id,
        [Description("是否在读取后清空已读缓冲(默认 true)。设为 false 可重复读取历史输出。")] bool consume = true)
        [Description("Watch ID returned by CreateWatchableConsole.")] string id,
        [Description("If true (default), the returned output is cleared from the buffer after reading. Set false to re-read the full history without consuming it; note that with consume=false the exit notice is still appended when the process has exited.")] bool consume = true)
    {
        if (!pm.TryGetValue(id, out var wc))
            return $"错误:找不到观察 ID '{id}'。";
            return $"Error: watch id '{id}' not found.";

        // Request.exe 按设计在子程序退出后退出,故以其 HasExited 作为「被观察程序已退出」的判定。
        var exited = false;
        try { exited = wc.RequestProcess.HasExited; }
        catch { exited = true; } // 进程对象不可用(已被回收)视为已退出。

        string snapshot;
        lock (wc.Buffer)
        {
            var snapshot = wc.Buffer.ToString();
            snapshot = wc.Buffer.ToString();
            if (consume && snapshot.Length > 0)
                wc.Buffer.Clear();
            return snapshot;
        }

        const string exitNotice = "[WatchableConsole] The observed process has exited; no further output will be produced.";

        if (!exited)
            return snapshot;

        // 已退出:返回剩余快照(若有)并追加退出提示,确保 Agent 能明确感知终止。
        return snapshot.Length > 0
            ? snapshot + "\n" + exitNotice
            : exitNotice;
    }
}