Replace the loop in place
Agents make a rewrite cheap
enough to attempt. They do
not make a big-bang cutover
safe. The Copilot runtime
port worked because the
harness stayed one product
the whole time. Main kept
shipping. Each slice died in
public, behind a shim, under
the tests that already
existed. A parallel Rust
tree would have drifted the
moment the rest of the team
kept merging TypeScript.
Two copies of session state
is not a fallback. It is a
second source of truth you
will not keep in sync.
The other failure mode is
architectural, and agents
will happily pour more code
into it. The SDK sat on the
CLI. Every language host
spawned Node to get an agent
loop. That is a containment
story that looks like
interop. Fast to ship. Then
every session pays a second
runtime, a process boundary,
and a crash domain that is
not yours. If the loop is
the product, embed it. FFI
into the host. Do not RPC
into a CLI you already
maintain. Toub’s team chose
Rust for a C ABI, low
startup, and predictable
memory — not because
TypeScript is illegal. Your
target language follows the
embed constraint. Copy that
test, not the logo on the
crate.
Order the work from the
leaves inward. Pure helpers
teach the translation,
the FFI, and the review
habit. Tools and clients
come next. The coupled
session orchestrator comes
last, when the pieces it
calls already live in the
new world. Reviewer stop:
the PR is small enough to
revert, tests that already
existed still pass, and
nothing important now
requires a second process.
Then ship. “Finish the
rewrite on a branch” is how
an affordable port becomes
a year of merge pain with
nicer types.
Checklist
-
Keep one main. Every port
PR must leave it
shippable.
-
Replace a slice with a
shim and delete the old
code in the same change.
-
Gate on the tests you
already run, not a new
suite that only the branch
knows.
-
Port leaves first. Session
orchestration last.
-
Embed the loop in-process.
Do not spawn your own CLI
so an SDK can call it.
-
Reviewer stop: revert is
cheap, tests are old,
no extra runtime. Then
ship.
This morning: pick one
agent path that shells out
to itself. Make the next
change a shim over an
in-process call. If you
cannot land it without a
long-lived branch, the slice
is too big.