Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

CopilotClient.stop() leaks the CLI server's child process tree on Windows (orphaned node/copilot.exe per session)

未关闭
#1,804 2 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

  • #1829 来自 @syf2211 —— 已关闭,未合并
  • #2073 来自 @rinceyuan —— 已关闭,未合并

评估

难度
4/5
预计耗时
3-5 天
新手友好度
52/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
冷清
技术栈
python

调研方向

检查 client.py,尤其是 CopilotClient.stop() 和 StdioRuntimeConnection 的进程创建,然后跟踪后代进程,复现 issue 中的 Windows create_session()/stop() 循环。比较 Windows 与 POSIX 上的生命周期行为,并考虑所述的 Job Object 或 process-group 方案。当停止一个已生成的客户端后不再留下孤立的 node.exe 或 copilot.exe 后代进程时,即表示完成。

由索引模型根据 Issue 内容生成。

描述

bug
Summary

On Windows, CopilotClient.stop() does not terminate the CLI server's child process tree — only the top-level launcher process. Every create_session() / stop() cycle therefore orphans a full copilot process tree (node.exe + the copilot.exe broker/worker/webview helpers) that survives until its own idle timeout. In a long-running app that creates one client/session per unit of work (e.g. a batch/eval harness), this is a deterministic ~1 leaked process tree per session, which accumulates and eventually OOMs the host.

Environment
  • github-copilot-sdk (Python) 1.0.0b7 (also visible by inspection in current client.py)
  • Copilot CLI runtime 1.0.66-0
  • Windows 11, Python 3.11 (miniconda)
  • Transport: StdioRuntimeConnection, use_logged_in_user=True
Root cause

CopilotClient.stop() only calls terminate() / kill() on its own launcher Popen (self._process):

# client.py (stop)
if self._process and not self._is_external_server:
    self._process.terminate()
    try:
        self._process.wait(timeout=5)
    except subprocess.TimeoutExpired:
        self._process.kill()
    self._process = None

On Windows, terminating the launcher does not cascade to its descendants. The launcher (copilot.cmd → cmd.exe) spawns node.exe, which spawns the real copilot.exe server + helper processes; these are left orphaned and alive after stop().

Reproduction
import asyncio, psutil
from copilot import CopilotClient, StdioRuntimeConnection

CLI = r"C:\Users\<you>\AppData\Roaming\npm\copilot.cmd"

def n_copilot():
    return sum(p.info["name"] == "copilot.exe"
               for p in psutil.process_iter(["name"]))

async def main():
    for i in range(5):
        client = CopilotClient(
            connection=StdioRuntimeConnection(CLI, []),
            use_logged_in_user=True,
        )
        session = await client.create_session()
        await session.send("hello")
        # ...consume events until SessionIdle...
        await session.disconnect()
        await client.stop()
        print(f"iter {i}: copilot.exe alive = {n_copilot()}")

asyncio.run(main())

Observed (Windows): copilot.exe alive grows 1, 2, 3, 4, 5 — one orphaned tree per iteration, none reaped by stop().

Expected

After await client.stop() (for a client that spawned the server), the entire CLI server process tree should be terminated, leaving no orphaned node.exe / copilot.exe processes.

Workaround

Capture the launcher PID after create_session() and kill the whole tree explicitly on teardown — e.g. psutil.Process(pid).children(recursive=True) + kill all (enumerate before killing the parent), or taskkill /F /T /PID on Windows / os.killpg on POSIX.

Suggested fix

Have the SDK own the process-tree lifecycle so stop() reaps descendants:

  • Windows: assign the launcher to a Job Object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, so the OS atomically kills the tree when the job/handle closes.
  • POSIX: spawn with start_new_session=True and os.killpg(os.getpgid(pid), SIGKILL) on stop.
  • Or a psutil-based recursive child kill inside stop().

This is likely related to the CLI-side reports of orphaned processes (e.g. github/copilot-cli#1368, #2279) but is reproducible purely through the SDK's own create_session()/stop() lifecycle.

主要语言
TypeScript
星标
10.5k
派生
1.5k
平均合并
1 天 7 小时
30 天内合并 PR
64

环境准备

在 Codespaces 中打开

在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

github/copilot-sdk 的其他 Issue

查看 github/copilot-sdk 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。