An ordinary `xmd run --default-agent devin` starts exactly `devin acp`, which
is the command ACPX recognizes and answers as Windsurf. That agent's sessions
are declared invocation-scoped before any adapter is prepared or any
availability probe spawns anything, because Devin's ACP surface publishes no
acceptance event and no provider-native conversation identity, so nothing about
one of its sessions can be written down and continued later.
The provider owns one private in-memory ACPX store for that lifetime and a
separate unbound runtime built on it, so no load or save crosses between
lifetimes. A fresh Devin <Session> is inert; the first subscribed Prompt probes,
ensures in persistent mode without first-turn acceptance, and keeps that handle
until teardown, so a second Prompt continues the same conversation while a
different Session runs beside it. Nothing it reports becomes durable identity.
Lifetime is part of a provider-issued Session's compatibility, compared before
the resolved command and independently of it: two agent names can resolve to one
command and be declared with opposite lifetimes, so consuming a Session through
a disagreeing lifetime is refused before any further store access, route,
ensure, turn or retention.
Native launch, a workflow attachment including a partial continuation, and Plan
authorship on both its surfaces refuse Devin before it is contacted. Every other
agent keeps its existing registry, materialization, retention, ownership and
native behavior.
Why
xmd run --default-agent devindid not work, and could not be made to workwithout deciding something first. ACPX's registry has no Devin entry, so the
name fell through to the bare command
devin— Devin's interactive CLI, whichspeaks no protocol. The command that does is
devin acp.Behind that is the real question. Every agent this provider serves is durable:
its conversation outlives the invocation, and a later run continues the one its
record names. Devin's ACP surface publishes no acceptance event and no
provider-native conversation identity — established by the sanitized ACP traces
linked from #740 — so nothing about one of its sessions can be written down and
continued later. Making it durable anyway would
have meant weakening #648's materialization contract for Codex and Claude too.
Closes#740.
What changes
Before, with
devinonPATH:After, the same document keeps one live Devin conversation for the length of the
run:
The second Prompt reaches the conversation the first started. A different
<Session>gets its own live conversation and may run beside it. When the runends, nothing about either is retained — the next invocation starts fresh.
Four surfaces refuse Devin instead, each before it is contacted:
<Session.Launch>unsupported-capability— there is no durable identity for a native UI to resumexmd workflowxmd plan--sessiondoes not change it<Plan>in an ordinary run<Plan session>does not change itEvery other agent is unchanged: same registry answer, same materialization, same
retention, same ownership, same native behavior.
How it works
The lifetime is a host decision, not an inference.
AcpxProviderDependenciesgains
sessionLifetime(agentName): Result<AcpxSessionLifetime>, answering"durable"or"invocation". The provider asks it with the resolved agent namebefore it prepares an adapter, probes availability, reads a store, places a
session or starts any child; an absent dependency is
Ok("durable"), which iswhat every existing host and agent already was. An
Errrefuses the operationthere — which is how the workflow profile gets its refusal ahead of any contact.
Nothing infers a lifetime from a command string, adapter metadata, a record, a
title, or anything an agent answered.
For an invocation-scoped session the provider owns one private in-memory ACPX
store, keyed as ACPX's own file store is and reachable from nothing else, plus a
separate unbound runtime built on it — because a runtime carries one store, so
sharing one would share every record either lifetime wrote. Placement, the
ensure, later record reads, the permission-routing refresh and teardown all use
the store and runtime the session was placed with.
A fresh Devin
<Session>is inert. The first subscribed<Prompt>probesavailability — the probe moves here precisely so that native launch, workflow
and Plan can refuse before Devin is spawned — then ensures in
persistentmodewithout
materialization: "first-turn-acceptance", waits on no acceptanceDevin does not publish, and keeps that handle until provider teardown.
Review guide
Start with:
AcpxProviderDependencies.sessionLifetimeinpackages/acp/src/provider.ts, and the §Session lifetime section inspecs/acp-client-spec.md.Then review:
packages/cli/src/agent-stack.ts— thedevin acpoverlay and the two hostdeclarations, which is where the product decision lives.
packages/acp/src/provider.ts—createInvocationStore,storeFor,lifetime-keyed
unbound, and thelifetime === "invocation"branches inpromptStream,resolveSessionandlaunch.packages/cli/src/{workflow-agent,authorship-profile}.ts— the two hostrefusals.
Look carefully at:
ManagedStategaining"live-invocation", andplacementStateOf(), which iswhat keeps
pending/establishedmeaning exactly what they meant before.ensureThrough()refusing an invocation-scoped bound build rather thanfalling back — a build binding is retained history, and this session has none.
all-settled ledger closes it.
What must stay true
routing every difference through one
lifetimevalue carried on the preparedand managed entry; checked by the whole pre-existing Tier AP/SM/WAP suite and
by AI5, which watches a mixed run and asserts Codex still materializes through
first-turn acceptance and still writes to the durable store.
storeFor()and thelifetime-keyed runtime map; checked by AI4 (a watching durable store that
records every key it is asked about, and is asked none) and AI5.
sessionLifetimefirst inresolveAgent()and at the top oflaunch();checked by AI6, WAL17, C14, PC27 and PC28, each asserting the doctor, ensure,
turn and native-process counters at zero.
Two agent names can resolve to one command and be declared with opposite
lifetimes, because ACPX resolves an unknown name to the name itself and a
lifetime is never inferred from a command. Enforced by comparing the
consuming agent's declared lifetime against the Session's retained one before
the resolved command, as an independent third guard; checked by AI8 and AI9
in both directions with every store, route, ensure, turn and establishment
observation snapshotted and unchanged, by DV3 through the production
<Agent name="devin acp">/<Prompt agent="devin">shape, and by AI10,which crosses the alias under one declared lifetime and shows the Session
placed through one name and consumed through the other still reaches one
ensure and one live handle.
Devin checkpoint namespace and by skipping route publication and
sessions.established(); checked by AI7, which supplies an ACPX record id, abackend session id and
cognition.ai/userMessageIdand asserts none of themis written anywhere.
How to verify it
Every new case was verified by breaking the implementation and watching it fail,
not only by watching it pass:
lifetimeForis forced to answerdurable.cancellation reaches the halted turn only, and that a failing close does not
prevent the remaining handles from being attempted.
sessionLifetimeis removed. Itcovers a first use and a partial continuation whose next live operation is
the Prompt, journaling nothing new.
invocationScopedAgentis neutralized.they pin needs no test seam, since a document naming the raw
devin acpforits
<Session>anddevinfor its<Prompt>reaches it today.placed it. That is the opposite defect, and the pair is what shows the guard
refuses disagreement about the lifetime rather than the alias itself.
devinresolves to the bare command, and again whenstoreForalways answers with the durable store.packages/cli/tests/devin-agent.test.tsis the black-box half: it puts a realACP agent named
devinon a child'sPATH, refuses any argv other thanacp,and runs the production CLI against it. That is the only way to reach the
command string and ACPX's own Windsurf compatibility shim — it asserts the
Windsurf
clientInfo, thecognition.ai/requestDiagnosticscapability, the{}diagnostics reply, a quietly ignored vendor notification, two Promptsreaching one live child, and a second invocation reaching a fresh one. No
credential, no network, no model turn.
Scope
Included
devin→devin acpon thexmd runandxmd planstacks.architecture.mdand the five specifications the boundary touches.Intentionally unchanged
artifact portability, conversation forking, native Devin launch and model
selection. None is buildable on what Devin's ACP surface publishes today.
packages/acp/vendor/acpx— the Devin shim already exists there; this adds nopatch and no dependency merely to name a command.
agent-ACP capability to be designed later, so no Devin-specific discovery or
certification document ships here; the sanitized traces linked from Add Devin Agent sessions to
xmd run#740remain the discovery evidence.
packages/acp/src/checkpoint.ts—cognition.ai/userMessageIdstaysunrecognized beside the explicit Codex and Claude namespaces.
prerequisite history rather than squashed.
New abstractions
AcpxSessionLifetimeandsessionLifetimeexist because three hosts need togive three different answers about one agent —
xmd runserves it,xmd workflowcannot, and Plan authorship cannot — and a provider that guessedfrom the command would be inferring the one thing that must be declared.
ManagedState's"live-invocation"exists sopendingandestablishedkeep their durable meanings; reusing
establishedwould have made a sessionwith no durable identity report that it had one.
Risks and limitations
be. Every assertion is about this host's decisions and the wire the vendored
ACPX shim speaks; none depends on a Devin account. That Devin itself holds a
conversation across two turns and honours ACP cancellation is unproven, and
proving it waits on a provider-neutral agent-ACP certification capability that
does not exist yet.
storeread would silently cross lifetimes. The mitigationis that
lifetimeis carried on the prepared and managed entry and every readgoes through
storeFor(); AI4 and AI5 are what would catch a regression.packages/cli/tests/devin-agent.test.tsdoes not run under Node or Bun: itspawns the Deno source entrypoint and a Deno-module ACP agent, an argument
vector only the Deno CLI understands. Recorded in
scripts/runtime-test-exclusions.ts; the provider decisions it fences areproved portably by Tier AI.
documents those commits added are deleted again on top, so they cancel out and
appear in no file in the net diff — read the commits, not just the diff, to see
that history.
git diff d4192c9a..HEADis the implementation plus thatdeletion.
d820e8f8, which includes ✂️ Makexmd planproduce approved XMD source only (#724) #749 ("Makexmd planproduce approved XMD source only"). That removed
xmd plan --runand rewroteplan-cli.test.ts, so this PR's Plan-refusal case was rewritten against thenew surface as PS11, and the Plan acceptance row is PS13 in both
specifications. Reviewers who saw the earlier SHAs should re-read those two
places.
Scope confirmation