---
title: "I Taught My AI Agent to Restart Itself"
description: "A Copilot CLI extension that lets agents programmatically kill and respawn their own session — and the Windows process tree trap I had to solve to make it work."
date: 2026-05-05
tags: ["GitHub Copilot", "Copilot CLI", "AI Agents", "Automation", "Case Study", "Open Source"]
canonical: https://htek.dev/articles/copilot-cli-self-restart-extension
---
## The Moment Your Agent Outgrows Its Own Runtime

Here's a scenario that will sound familiar if you're building autonomous agents with [GitHub Copilot CLI](https://docs.github.com/en/copilot/github-copilot-in-the-cli): your orchestrator agent creates a brand-new custom agent — writes the `.github/agents/budget-review.agent.md` file, commits it, and then tries to delegate work to it via the `task` tool. Except... it can't. The new agent doesn't exist yet, at least not in the running session's registry.

The `task` tool's `agent_type` list is frozen at session start. Your new agent won't be discoverable until a fresh session begins. And there's no built-in way to restart from within the session.

So you close the terminal. Reopen it. Resume. It works now. But if your agent platform does this ten times a day — creating specialized agents on the fly based on family needs, work context, or content pipelines — that manual restart becomes the single biggest bottleneck in your entire autonomous workflow.

I solved this with [copilot-self-restart](https://github.com/htekdev/copilot-self-restart), an extension that gives any agent the ability to kill its own runtime and spawn a fresh session — with full conversation resume support.

## What the Extension Does

The extension registers a single tool called `restart_session` that orchestrates a controlled shutdown and respawn sequence:

1. **Writes a temporary PowerShell script** containing the kill-wait-relaunch logic
2. **Spawns a fully independent PowerShell window** using `execSync → Start-Process`
3. **That new window kills the current Copilot runtime** via PID
4. **Waits for cleanup** (file handle release, graceful shutdown)
5. **Launches a fresh Copilot CLI session** with `--resume` to preserve conversation context

The result: the agent says "restarting," the current terminal dies, and a new window spawns with a fully resumed session — same conversation, fresh registry. New agents, new extensions, clean context — all without human intervention.

```javascript
// The core pattern in extension.mjs
execSync(
  `pwsh -NoProfile -Command "Start-Process -FilePath pwsh ` +
  `-ArgumentList @('-NoProfile','-NoExit','-File','${tmpScript}') ` +
  `-WorkingDirectory '${cwd}'"`,
  { cwd, timeout: 5000, windowsHide: false }
);
```

![5-step restart sequence: write temp script, spawn independent window, kill runtime, wait for cleanup, launch fresh session with resume](/images/articles/copilot-cli-self-restart-extension/restart-sequence.webp)
*The complete restart sequence — from fire-and-forget execSync call to a fresh Copilot session with --resume*

## The Windows Process Tree Trap

![Comparison of three approaches to creating independent processes on Windows — spawn+detached fails, windowsHide fails, execSync→Start-Process succeeds](/images/articles/copilot-cli-self-restart-extension/windows-process-trap.webp)
*The Windows Process Tree Trap: two dead-end Node.js approaches and the one PowerShell pattern that actually works*

This extension took me two days to build. The code is ~100 lines. The problem was discovering *why* the obvious approach doesn't work on Windows.

**Attempt 1: `child_process.spawn()` with `detached: true`**

This is what every Node.js tutorial suggests for creating independent child processes. On Linux, it works beautifully. On Windows, it creates a **headless process** that cannot spawn visible child windows. The spawned process inherits the console's window station but can't create new independent ones.

What this means in practice: the "new" Copilot CLI session launches invisibly. You can't see it. You can't interact with it. It's running headless in some orphaned process tree. Useless.

**Attempt 2: `spawn` + `CREATE_NEW_CONSOLE`**

Node.js does expose a `windowsHide: false` option and various `stdio` configurations. None of them actually create a visible, interactive terminal window that outlives the parent process. The child is still bound to the parent's window station.

**The Working Pattern: `execSync → Start-Process`**

The solution is to delegate window creation entirely to PowerShell's native `Start-Process` cmdlet. By calling `execSync` with a `pwsh -Command "Start-Process ..."` invocation, you create a **fully independent visible window** that outlives the calling Node.js process — a new PowerShell terminal with no parent-child relationship to the original session.

```powershell
# The temp script that runs in the new window:
Stop-Process -Id 1234 -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 3
& 'restart-copilot.ps1' -Folder 'C:\Repos\myproject' -SessionId 'abc-123'
Remove-Item -LiteralPath 'tempscript.ps1' -Force
```

The new window kills the old runtime (which also kills the extension that created it), waits for cleanup, and launches a fresh session. The temporary script self-destructs after execution. Clean.

## Architecture: Two Files, Zero Dependencies

The extension is deliberately minimal:

**`extension.mjs`** — Joins the Copilot CLI session via `@github/copilot-sdk`, registers the `restart_session` tool, and handles the spawn logic. Uses `process.ppid` to identify the runtime's PID (the extension's parent process). Writes a temp `.ps1` script to avoid PowerShell quoting hell, then fires-and-forgets via `execSync`.

**`restart-copilot.ps1`** — Resolves the working directory, ensures the folder is trusted in `~/.copilot/config.json` (so the CLI doesn't prompt for trust approval), and launches the new session with `--add-dir`, `--yolo`, `--autopilot`, and optionally `--resume=<sessionId>`.

The `restart_session` tool accepts two parameters:

| Parameter | Default | Purpose |
|-----------|---------|---------|
| `reason` | — | Logged for debugging ("New agent created: budget-review") |
| `new_session` | `false` | If true, skips `--resume` for a clean slate |

## Why Not Just `process.exit()`?

I got this question immediately. Calling `process.exit()` from an extension kills the *extension process*, not the Copilot runtime. The runtime detects the extension died and either restarts it or continues without it — neither results in a session restart. You need to kill the runtime's actual PID, which is `process.ppid` from the extension's perspective.

## The Safe Restart Workflow

![7-step safe restart pre-flight workflow organized in three phases: guard checks, preparation steps, and execution](/images/articles/copilot-cli-self-restart-extension/safe-restart-workflow.webp)
*Safe Restart Workflow — 7 pre-flight steps ensuring no in-flight agent work is lost before killing the runtime*

Raw restart is dangerous. If background agents are running tasks — writing files, making API calls, mid-computation — killing the runtime means their work is lost with no recovery. So this extension ships with a companion **safe-restart skill** that wraps the restart in pre-flight checks:

1. **`list_agents()`** — verify no background agents are `running`
2. **Wait for active agents** — `read_agent(id, wait=true)` blocks until completion
3. **Close idle agents** — graceful shutdown via `write_agent()` + wait
4. **Save all work** — commit pending changes, update memory files
5. **Notify the user** — "Restarting to discover new agent: budget-review"
6. **`restart_session(reason="...", new_session=true)`**
7. **Post-restart verification** — confirm the new agent appears in `task` tool, run a smoke test delegation

Note: the safe-restart workflow uses `new_session=true` because after agent creation you want a clean registry refresh. Normal restarts (recovering from bloated context) use `new_session=false` to resume the existing conversation.

This workflow is codified as a reusable skill — a procedural guide that any agent can invoke when it needs to safely restart.

## Why This Matters: Self-Modifying Agent Platforms

This extension isn't really about restarting a terminal. It's about **agent lifecycle management** — the ability for an autonomous system to modify its own runtime topology without human intervention.

In my [home assistant platform](/articles/copilot-home-assistant-ai-runs-my-household), agents create other agents dynamically. A realtor-team agent might spin up a credit-coach agent. A content pipeline might create a specialized editor agent for a new content format. These agents need to be discoverable *immediately* — not after I manually restart a terminal.

Combined with the [agent mesh](https://github.com/htekdev/agent-mesh) (which enables [cross-session communication](/articles/agent-mesh-cross-session-communication-copilot-cli)), this creates an infrastructure layer where agents can:

- **Create new agents** → self-restart to discover them
- **Communicate across sessions** → via the mesh's SQLite IPC
- **Modify their own extensions** → restart to load new tools
- **Recover from bloated context** → resume with a clean window

This is what [self-healing infrastructure](/articles/self-healing-infrastructure-with-agentic-ai) looks like at the agent platform level. The system doesn't just detect problems — it restructures itself to solve them.

## Getting Started

Install per-project (recommended):

```powershell
New-Item -ItemType Directory -Path .github\extensions\self-restart -Force
# Copy extension.mjs and restart-copilot.ps1 into that directory
```

Or install user-level for all projects:

```powershell
New-Item -ItemType Directory -Path "$HOME\.copilot\extensions\self-restart" -Force
# Copy both files there
```

The full source, architecture docs, and the safe-restart skill template are on GitHub: **[htekdev/copilot-self-restart](https://github.com/htekdev/copilot-self-restart)**.

## The Bottom Line

The [Copilot CLI extension SDK](https://www.npmjs.com/package/@github/copilot-sdk) is deceptively powerful. With one file and zero external dependencies, you can give agents the ability to restart their own runtime — something that sounds trivial until you hit the Windows process tree trap and realize why nobody else has shipped this.

If you're building agent platforms where agents create other agents, this is table-stakes infrastructure. The alternative is manual restarts, which defeats the entire point of autonomous operation. Your agents should be able to evolve their own capabilities without waiting for a human to close a terminal.

The pattern — `execSync → Start-Process → kill parent → relaunch` — is weird. It's counterintuitive. And it's the only thing that works on Windows for creating visible, interactive, independent process trees from Node.js. Sometimes the best engineering is just finding the one path through a maze of platform limitations.
