The code was never the expensive part of leaving.
When a managed agent runtime closes to new customers, the announcement usually promises that existing workloads keep running — and that promise is what makes teams wait, because the real deadline is not the shutdown date, it is the day the frozen model catalog stops carrying a model you need. Meanwhile the loop you assume is the hard part ports in a sprint, and the conversation state you never thought about is the migration. Extract state first; it is the only asset that grows while you deliberate.
The exit price was set at adoption, and it is still readable today.
Nothing about a migration is decided during the migration. It was decided when someone chose whether the loop lived in your repository or in the vendor's configuration format, and whether the conversation store was a database you own or a service you call. Read your own answers before planning anything, because they determine whether this is a two-week project or a two-quarter one.
- Where the loop is expressed. A loop in a framework you chose is a redeploy. A loop in the vendor's declarative format is a rewrite — and the rewrite is small, which is why teams under-plan the rest.
- Whether the primitives outlive the loop product. If memory, gateway and identity are separately callable services, a frozen loop product costs you the loop only. If they were reachable only through it, the product's end-of-life is your migration date. This is the distinction managed agent runtimes turns on.
- Whether anyone has ever exported the conversation store. Not "is there an export API" — has a human run it, on production volume, and looked at what came out. The answer is usually no, and the gap between the documented capability and the working one is where migrations slip.
If you are reading this before adopting a runtime rather than while leaving one, the entire page reduces to two sentences. Write the loop against a framework, and prove the state export works on day one while the volume is small enough that failure is cheap. Both are nearly free at adoption and neither can be retrofitted.
Find the real deadline, because nobody will send it to you.
Maintenance mode is a generous-sounding status: existing workloads continue, nothing breaks, no date is announced. That framing hides the deadline rather than removing it. A service in maintenance mode stops adding features and freezes its model catalog at the maintenance date, which means your expiry is whichever of these arrives first:
- Model expiry. The day a model you need is one the frozen catalog will never carry. For most teams this is the binding constraint and it arrives within a year, long before any shutdown.
- Compliance expiry. The day a control you are required to have — a new residency region, a retention capability, an audit export — lands as a feature that will never ship to a frozen product.
- Dependency expiry. The day a client SDK, a runtime version, or an auth mechanism the frozen service depends on goes end-of-life somewhere else.
- Announced shutdown. The one everyone plans around, and usually the last to arrive.
Write down which of the four is closest and put a date on it. A migration with no date does not get staffed, and "they said existing workloads keep running" is how a two-week extraction becomes an emergency eighteen months later.
Inventory what the runtime holds that you do not.
Four asset classes live inside the vendor boundary, and they fail differently. Enumerate each with an owner and a size before writing any code.
- Conversation state — threads, sessions, extracted memories. Grows daily, has vendor-specific structure, and is the only asset whose migration cost rises while you plan. Measure it now: how many threads, how many events, how many bytes, and how fast each is growing.
- Identity bindings — the credentials the runtime brokers on the agent's behalf, and every downstream scope granted to them. Bounded work, but it touches other teams' systems and therefore other teams' calendars.
- Trace history — the record of what the agent did. Often the asset nobody assigns an owner to, and often the one with a legal retention obligation that does not care that you changed vendors.
- Tool and gateway configuration — the least painful of the four if the gateway speaks a standard protocol, and genuinely painful if it does not.
The common miss is trace history. Teams migrate state and identity, cut over, decommission — and then discover that the evaluation set they were about to build lived in the traces, or that an auditor wants runs from a quarter that now exists only in a deprecated console. Decide explicitly whether trace history comes with you, is archived cold, or is abandoned. All three are defensible; the accident is not.
Extract state first — the instinct to port code first is wrong.
Every team starts by rewriting the loop, because it is the part engineers enjoy and the part that demos. It is also the part whose cost is fixed and small. State is the opposite: its cost is a function of elapsed time, and every week spent on the loop makes it larger. Invert the order.
# Wrong: cost grows while you work on the fixed-cost part. port_loop() # 2 weeks, fixed rebind_identity() # 1 week, fixed export_state() # grew for 3 weeks while you did the above # Right: freeze the growing asset first, then do the fixed-cost work. prove_export() # on a sample, day one — this is the go/no-go start_dual_write() # new state lands in BOTH stores from here on port_loop() # now the backlog is bounded, not growing backfill_state() # one-time, against a fixed set
Proving the export on a sample is the decision point, not a task. If the export loses the structure that made the memories useful — the timestamps, the attribution to a session, whatever the vendor's extraction layer inferred — you have learned that the state does not survive the move, and the honest plan is to start the new deployment cold and run the old one until its sessions age out. That is a legitimate outcome, and finding it in week one is much better than finding it in month four.
Dual-write is what converts a moving target into a fixed one. From the moment new state lands in both stores, the backfill is a bounded job against a set that stops growing, and every subsequent decision gets easier.
Cut over on session boundaries, never mid-conversation.
An agent migration is not a stateless deploy, so the usual rollout shape needs one modification: the unit you shift is the session, and a session in flight must finish where it started. A conversation whose first ten turns have one memory model and whose next ten have another is a bug that presents as the agent getting worse, and it is nearly impossible to diagnose from a trace that spans two systems.
- Route by session, pinned for its lifetime. New sessions go to the new runtime; existing ones drain on the old one.
- Shift a task class at a time, starting with the one whose failures are cheapest and whose state is thinnest — not with the flagship workflow.
- Compare on outcomes, not on latency. The failure mode of a state migration is subtly worse answers, which no infrastructure metric shows. Run the same task class through both and diff results.
- Keep the old runtime reachable read-only until the trace-history question from STEP 3 has an answer that someone has signed.
The identity re-binding usually sets the calendar here, because it touches systems other teams own. Start those conversations at the same time as the export proof, not after the loop is ported — they are the long pole and they do not compress.
When staying is the right answer.
Not every frozen runtime needs to be evacuated. A deployment on a stable task class, whose quality does not depend on models newer than the freeze, whose compliance posture is already satisfied, and whose state is small enough to abandon, can reasonably run on a maintenance-mode service until an announced shutdown. The cost of staying is capped and known; the cost of migrating is a quarter of engineering time that could go elsewhere.
What is not defensible is staying by default. The decision to remain should be written down with the four expiry dates from STEP 2 next to it, reviewed when any of them moves, and owned by a name. The failure mode is not choosing to stay — it is never choosing at all, and discovering the deadline through an incident rather than a plan.
Do three things this week regardless of whether you are migrating. Measure the conversation store — threads, events, bytes, growth rate — so the migration has a number instead of a shrug. Run the export on a thousand sessions and look at what comes back. And put a date on the nearest of the four expiries. Those three cost a day and they convert an open-ended risk into a scheduled decision; everything else on this page is downstream of having done them.
Related: model deprecation & migration for the same problem one layer down, durable state & resumability for what the runtime was solving, and build vs buy vs orchestrate for the decision that precedes all of this.