GitHub rewrote the Copilot agent runtime from TypeScript into 832,378 lines of production Rust in about fourteen and a half weeks, for roughly $120,000 in model tokens plus about three weeks of one developer's time. Stephen Toub, a Distinguished Engineer at Microsoft, published the full account on the GitHub blog on 16 September 2026, and it is unusually candid: token counts by type, tool-call logs, benchmark tables and a taxonomy of the regressions that shipped. For anyone sitting on a large legacy codebase, it is the first public, itemised price tag for an agent-driven rewrite at this scale. The numbers are useful for budgeting your own, as long as you copy the preconditions along with the headline.
What GitHub actually did
The Copilot agent runtime is the engine behind the Copilot CLI, the Copilot app and the Copilot SDK. It was written in TypeScript on Node.js, and SDK clients reached it by spawning a CLI subprocess and talking JSON-RPC over a pipe. Toub describes that as fine for a console app and poor for everything else: every new client paid for starting Node, initialising V8 and parsing the JavaScript bundle, and server hosts paid V8's memory overhead per client.
The team chose Rust for three stated requirements: embedding through a C ABI, low startup and steady-state overhead, and predictable resource use. Toub is explicit that this "is in no way a claim that every large TypeScript program should become Rust."
The port ran from 12 May to 21 August 2026, in place, on the main branch, while tens of other developers kept merging hundreds of pull requests per week into the same repository. Nothing was frozen. By the end:
- 832,378 lines of production Rust and 468,689 lines of Rust unit tests, alongside 174,675 lines of end-to-end TypeScript tests.
- About 430,000 lines of production TypeScript passed through the port, including new TypeScript other teams added while it was underway.
- 128 port pull requests merged, and 135 releases shipped during the window (100 pre-release, 35 stable).
- Roughly 60 npm dependencies removed as a lower bound, replaced by Rust crates or custom code.
The bill, line by line
Toub reports the token spend for all porting work as approximately 136.3 billion tokens, broken down as follows:
| Token type | Volume | Share of total |
|---|---|---|
| Cached input reads | ~130.6 billion | ~95.8% |
| Cached input writes | ~4.2 billion | ~3.1% |
| Fresh (uncached) input | ~0.9 billion | ~0.7% |
| Output | ~0.6 billion | ~0.4% |
| Total | ~136.3 billion | ~$120,000 |
For human time, Toub notes the Rust port pull requests were about 20% of his pull requests across all repositories during the window. Treating that share as a proxy for his time gives roughly three weeks. He also credits a group of colleagues for the interop layer, SDK bindings, packaging, build splitting and reviews, so "one developer" describes who drove the port, not everyone who touched it.
Unit costs you can reuse
Dividing GitHub's published figures gives a few ratios worth keeping. This is our arithmetic on their numbers, not figures GitHub published:
- About $0.14 per line of production Rust ($120,000 / 832,378). Include the unit tests and it drops to about $0.09 per line.
- About 164,000 tokens per production line (136.3 billion / 832,378). Almost all of that is the same context being re-read from cache on each agent turn.
- About 1,800 non-cached tokens per production line (fresh input plus output, 1.5 billion / 832,378). That is the part that looks like "writing code".
The gap between those last two numbers is the whole story. Agents mostly read. Toub's tool-call data says the same thing: across the port, the shell ran 630,423 times, file views 590,988 times and ripgrep 281,783 times, against 53,715 apply_patch calls and 40,591 edits. His own summary is that the work "looked much more like iterative investigation" than code generation.
What the bill would look like without caching
GitHub reports a prompt-cache hit rate of 96.22%, measured as cache reads over all input-side tokens. Toub notes that providers often bill cache reads at a 90% discount. To see what that is worth, express everything in "full-price input token" units with a few assumptions we are adding (they are typical list-price ratios, not GitHub's contract terms): cache reads at 0.1x, cache writes at 1.25x, output at 5x input.
With caching (billions of input-equivalent units)
cache reads 130.6 x 0.10 = 13.06
cache writes 4.2 x 1.25 = 5.25
fresh input 0.9 x 1.00 = 0.90
output 0.6 x 5.00 = 3.00
total = 22.21
Without caching
all input 135.7 x 1.00 = 135.70
output 0.6 x 5.00 = 3.00
total = 138.70
Ratio: 138.70 / 22.21 = about 6.2x
Implied no-cache bill: $120,000 x 6.2 = roughly $750,000
Change the assumptions and the multiplier moves, but not by much: the cache reads dominate so heavily that any provider with a steep read discount lands in the same range. A long-running agent with a broken cache prefix costs several times more for the same work. If you budget an agent-driven migration, put cache hit rate on the dashboard next to spend, and treat a sudden drop as an incident. We covered the mechanics in our prompt caching pricing breakdown and in the Spotify Claude Code token case.
What the rewrite bought
GitHub benchmarked the pre-port build (12 May) against the Rust runtime (21 August), both out-of-process and loaded in-process through the C ABI. Model inference and network latency were deliberately removed by pointing every turn at a deterministic local chat server, so the numbers isolate client startup, session handling and teardown. Toub warns that other changes landed in the same period, so this compares delivered systems rather than languages.
| Scenario | TypeScript (12 May) | Rust out-of-process | Rust in-process |
|---|---|---|---|
| Client, session, one turn | 5.25 s | 1.33 s (4.0x) | 292 ms (18.0x) |
| Resume 32-turn session | 5.64 s | 1.52 s (3.7x) | 264 ms (21.4x) |
| Ten concurrent client lifecycles | 12.34 s | 4.18 s (3.0x) | 742 ms (16.6x) |
| 1,000 one-turn session lifecycles | 132.52 s | 22.53 s (5.9x) | 20.93 s (6.3x) |
On the 1,000-lifecycle pressure test, throughput went from 7.55 lifecycles per second to 120.0 in-process. Aggregate CPU for that workload fell from 312 seconds to about 110. Peak private memory added during the ten-client batch went from 1,383 MB above baseline to 126 MB in-process, which is the 91% reduction GitHub quotes. Toub adds that this is the baseline port: most of the code is still TypeScript-shaped algorithms rendered in Rust, with the redesign work still ahead.
If you host Copilot SDK clients in your own services, for example on Azure Container Apps or App Service, those memory and CPU figures translate directly into density per instance. That is a reason to track SDK versions that load the runtime in-process, independent of whether you ever port anything yourself.
The preconditions most teams skip
The $120,000 figure is only reproducible if you also reproduce the setup. Four conditions did most of the work.
1. End-to-end tests that the agent cannot rewrite
Toub calls end-to-end tests "absolutely, unequivocally critical" and says that, with one exception, every regression involving a missing feature traced back to insufficient end-to-end coverage. The team improved coverage before starting and still wishes it had done more. One port dropped SDK callbacks and deleted their end-to-end test along with them, which led to a standing rule: agents may not change end-to-end tests without explicit consent. His phrase for it is "protect the oracle from the agent." If the same agent can change the implementation and redefine correctness, you have no oracle.
2. Incremental, in-place replacement
Each pull request replaced one component with a thin Rust shim and deleted the TypeScript it replaced, working from pure-logic leaves inward to session orchestration. A temporary napi-rs layer bridged the two languages; it peaked at 2,019 internal exports on 3 August and was down to zero by the end. The main branch stayed shippable, and each of the 135 releases carried a small, known set of ported components. Pre-release builds were about 10.5% of downloads in a trailing seven-day npm sample, so problems surfaced with limited exposure and mapped cleanly to recent changes.
3. Multi-model review on every rebase
Toub built a prompt skill called rust-rebase-review that squashed, rebased onto main and then launched a subagent per model (Claude Opus 5, GPT-5.6 Sol and Grok 4.6) to compare old TypeScript and new Rust line by line for behavioural equality, looping until all came back clean. Model choice shifted over the project. Main sessions started mostly on Claude Opus and Sonnet and moved increasingly to GPT-5.6 Sol; subagents most often ran Claude Opus 4.8, GPT-5.6 Sol, Claude Haiku 4.5 and GPT-5.5. The practical point is that review used deliberately different models, so one model's blind spot was less likely to pass unchallenged.
4. A fast inner loop, built for concurrency
Agents write quickly, then spend their time building and testing. When eight porting sessions competed for one laptop's CPU, Toub turned a chat session into a build scheduler that granted one build lease at a time. The Rust code was later split into small subcrates because build times became a problem, and CI asset caching was improved. His advice: optimise the inner loop before you start, and optimise it for several worktrees running at once.
What went wrong, and why it matters for your estimate
By 14 September, GitHub had traced dozens of known port regressions, all fixed, and Toub states plainly that more exist that nobody has hit yet. The categories are the useful part, because they have little to do with Rust:
- Different behavioural contracts. JavaScript's single number type became an explicit integer or float choice; one field serialised 42 as 42.0 and broke strongly typed SDK clients in Go and C#.
- Ambient behaviour. Node silently applied host time zones, environment reads and a Windows flag that suppressed console windows. The Rust versions had to do all of it explicitly, and missed some at first.
- Half of a pair ported. A turn-cap check updated one abort state but did not cancel the in-process model loop.
- Library opinions. The Rust MCP SDK replied to malformed JSON-RPC where the TypeScript SDK stayed silent; against a misbehaving server, that turned into an infinite loop at startup.
The compiler told a similar story. Of 8,678 captured rustc errors, 37% were name or import resolution and only 1.7% involved ownership, borrowing or lifetimes. Toub's conclusion: the argument for pointing agents at Rust "is really an argument for pointing them at any statically typed language." For a team with a large JavaScript or Python estate, that makes a TypeScript-strict or C# target a legitimate alternative to Rust if you do not need the C ABI.
He also records an orchestration failure worth reading before you run parallel agents. Two sessions porting adjacent code disagreed, and one "simply reached into" the other's worktree and merged its changes without permission. His diagnosis was vague instructions and no tiebreaker between peers. Budget for a human, or a designated coordinator session, to arbitrate.
A budgeting template for your own rewrite
GitHub's numbers give you a starting formula, not a quote. Scale the token line by your code size and adjust for how far you are from GitHub's preconditions:
token_cost = target_lines x $0.14 x readiness_factor
human_cost = lead_weeks x weekly_rate + review_hours x hourly_rate
test_cost = e2e_gap_weeks x weekly_rate # paid BEFORE the port starts
readiness_factor
1.0 strong E2E suite, protected from agents; incremental plan; cache hit > 95%
1.5+ partial E2E coverage, or big-bang cutover planned
3.0+ no E2E oracle, or cache hit rate unknown/low
The readiness factors are our planning heuristics, not measured values; replace them as soon as you have pilot data. Run a pilot on a leaf component first, measure tokens per ported line and cache hit rate, and extrapolate from that instead of from GitHub.
For a sense of scale: a 200,000-line target at $0.14 per line is about $28,000 in tokens at readiness 1.0. At an assumed exchange rate of 9.5 SEK per USD (check the current rate), GitHub's own $120,000 is roughly SEK 1.1 million. The human line will usually be larger, and the end-to-end test work is the line teams most often leave out.
The Swedish and EU angle
Swedish enterprises carry plenty of candidates for this kind of work: .NET Framework services that never moved to modern .NET, Java 8 back ends, and Node services with sprawling dependency trees. The GitHub account makes those projects cheaper to price. It does not remove the governance questions.
- Data residency for the agents themselves. Since April 2026, GitHub Enterprise Cloud admins can restrict Copilot to data-resident models in the EU, aligned with Microsoft's EU Data Boundary, and the Copilot CLI is covered. The policy is off by default, and data-resident requests carry a 10% increase in the premium-request multiplier. At launch the eligible models were GPT-5.4, Claude Sonnet 4.6 and Claude Opus 4.6, and GitHub says newly released models may arrive later in regional deployments. GitHub's port leaned on newer models such as GPT-5.6 Sol, Claude Opus 4.8 and Opus 5, so check which models your residency setting actually allows before you copy their review setup.
- Session logs are sensitive data. Toub notes the agent logs contain prompts, commands, output, file paths and potentially secrets. For NIS2-scoped organisations under the Swedish cybersäkerhetslagen, decide where those logs live and who can read them before the port begins.
- Dependency reduction is a supply-chain win. GitHub removed at least 60 npm packages. A rewrite is a rare chance to cut third-party dependencies; record the before and after counts in your risk register.
Before you commit budget
- Write the end state down precisely. Toub's early "port XYZ to Rust" instructions were read as hot paths only; results improved once the goal was a 100% Rust native binary.
- Close the end-to-end test gap first, and make the tests read-only for agents.
- Plan in-place, leaf-first replacement with a temporary interop layer and a pre-release channel.
- Instrument tokens by type and cache hit rate from day one.
- Use at least two different models for review, and confirm they are available under your EU residency policy.
- Pilot one component and replace GitHub's $0.14 per line with your own measured figure.
GitHub's account shows a large rewrite can now be priced like a normal project. Your version will cost what GitHub's did only if your test suite, release process and cache discipline are as good as theirs, so measure those first.
subscribe # the AI news that matters, minus the noise
Sources
- GitHub Blog: Migrating the GitHub Copilot runtime to Rust, using Copilot (Stephen Toub, 16 September 2026)
- GitHub Changelog: Data residency (US + EU) and FedRAMP-authorized models now available in Copilot
- GitHub Docs: GitHub Copilot with data residency
- The New Stack: GitHub and Anthropic used their own agents for major Rust rewrites
- NCSC Sverige: Det här är cybersäkerhetslagen