AgentEnginePort and nothing else.
Quick Start
1
RegistryDeps requires a persistence
persistence is required. It is passed only into the in-process engine’s factory.2
enginesFor passes persistence to the in-process engine only
The remote engine deliberately does not receive it.
3
Build engines from the session
AppDeps.engines is a factory (persistence) => EngineChoice[]. The composition root builds it from the session, so an engine cannot exist without the store it writes through.4
Run a turn
run returns an AsyncIterable, so for await gives free backpressure and one cancellation path.5
Answer an approval
Who Persists
Two engines, two owners of the write.
The remote engine deliberately does not take persistence: the server it connects to owns the write and is the only thing that can report authoritative indices for its own store.
A picker omits an engine whose prerequisites are absent rather than offering it and then failing.
createInProcess is optional — when it is not supplied, only the remote engine is offered.Where a Completed Turn Is Written
Each engine owns its own write, and reportsend.userIndex from the store it wrote to.
The in-process engine records the turn and reports the indices it actually wrote, which is what makes
end.userIndex real:
What a Recorded Turn Returns
session.record(prompt, answer) writes the user message and the answer together, then returns the indices into the persisted array.
A
null return means the save failed — the turn is on screen but not on disk.
The Port
AgentEnginePort is the entire agent-framework coupling.
The Three Engines
Three implementations pass the same conformance suite, which is what makes “swappable” a fact.Conformance
Passingengines/src/conformance.ts is the definition of implementing the seam.
approvals: false must never emit an approval_request. Every unsupported scenario is printed on each run, so a contract that quietly shrinks is visible rather than silently green.
praisonai-ts declares 5 of 11 events unsupported because upstream Agent.streamEvents() emits only text / finish / error. See src/praisonai-mobile/docs/gaps.md.When To Pick Which Engine
Common Patterns
An engine whose prerequisites are absent is omitted, not offered and then failed.selectEngine names the available engines when an id is unknown, so a missing engine is an honest message rather than a crash.
Best Practices
Never bypass the factory
Never bypass the factory
Obtaining the engine list requires the persistence argument. Do not construct engines directly — the type makes the wiring impossible to forget.
Treat persistence as required, not optional
Treat persistence as required, not optional
RegistryDeps.persistence has no default. The in-process engine’s end.userIndex is only real because it records through this store — without it, the turn is on screen and not on disk.Do not persist remote-http into the local session
Do not persist remote-http into the local session
The remote server owns its own store and reports its own indices. Writing a second copy locally reintroduces divergence between screen position and disk position.
Do not switch chats from the request
Do not switch chats from the request
The session already knows which conversation is open. Taking direction from the run request would let an in-flight turn write into whichever chat the user has since navigated to.
Read null as 'not on disk'
Read null as 'not on disk'
When
record returns null the write failed. Null travels to the UI as “do not offer Fork or Delete”, because those affordances would address a message that does not exist. Index 0 is valid, so a falsy check is a trap.Read capabilities before rendering
Read capabilities before rendering
capabilities is a property, not a method, so the UI decides what to render before the first token arrives.Never fake an unsupported scenario
Never fake an unsupported scenario
Declaring a gap in the
unsupported map is honest; faking it hides a defect the conformance suite exists to catch.Return false, never a lie
Return false, never a lie
decide and cancel return false for an unknown id — reporting success for an id the engine never issued is a lie the UI cannot detect.Related
Architecture
Boot order and the engines factory shape.
Overview
Native navigation and the retained chat screen.
Capabilities & Gaps
What each engine can and cannot report.
Shell & Adapters
The keyboard snapshot and the pinch-zoom guard.
Protocol
The 11 events every engine speaks.
Capabilities & Gaps
What each engine emits today.

