DEV Community

Hector Flores
Hector Flores

Posted on • Originally published at htek.dev

I Taught My AI Agent to Restart Itself

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: 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, 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.

// 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 }
);
Enter fullscreen mode Exit fullscreen mode

The Windows Process Tree Trap

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.

# 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
Enter fullscreen mode Exit fullscreen mode

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

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 agentsread_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, 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 (which enables cross-session communication), 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 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):

New-Item -ItemType Directory -Path .github\extensions\self-restart -Force
# Copy extension.mjs and restart-copilot.ps1 into that directory
Enter fullscreen mode Exit fullscreen mode

Or install user-level for all projects:

New-Item -ItemType Directory -Path "$HOME\.copilot\extensions\self-restart" -Force
# Copy both files there
Enter fullscreen mode Exit fullscreen mode

The full source, architecture docs, and the safe-restart skill template are on GitHub: htekdev/copilot-self-restart.

The Bottom Line

The Copilot CLI extension 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.

Top comments (0)