Threads and runs
Keep conversation identity, native lifecycle and durable history with their actual owners.
Three identities
A thread/session names the conversation. A run/turn/task names work within it. A message/part names content that may be replaced as it streams. Keep these separate; a new token is not a new run, and a reconnect is not a new thread.
Ghost's runtime identity is { threadId, revision, runId? }. Use the native thread/session/context ID. Increase revision when replacing the bound context, including A→B→A, clearing history, or replacing its command owner. Keep it unchanged for ordinary updates within that context. Use a real run ID when available.
Switch without crossing contexts
Create native hooks/stores per authenticated conversation owner. Mount a hook integration with a key derived from the thread and replacement revision when the SDK reads initial configuration once. Create a new createAgUiRuntime for each native thread/revision; do not mutate its bound client's thread ID in place.
A command captured in an old Ghost snapshot rejects after context replacement. An asynchronous upload must call its supplied context.assertCurrent() before native dispatch. The host must also reject late network results before publishing them: Ghost cannot retract a write made inside an application callback.
Preserve native workflows
| Native concept | Integration decision |
|---|---|
| LangGraph checkpoint/branch | Keep checkpoint IDs and submit(..., { forkFrom }); a generic edit is not a persisted fork. |
| Eve durable session/delivery | send acknowledges delivery acceptance; use native follow/resume for completion. |
| AG-UI thread/run + shared state | Client reconstructs events; application validates and renders client.state. |
| A2A context/task | Keep task status and artifacts; input_required / auth_required are not boolean tool approvals. |
| OpenCode session/permission | Keep session, request and tool-call IDs; permissions and questions have distinct native commands. |
| Managed Agent session/event log | Replay buffered facts in order; deduplicate IDs before applying a live tail. Preview deltas are not durable facts. |
Stop, disconnect and recover
A UI stop request is not proof that remote work stopped. Use the SDK's cancellation API when available and render its eventual outcome. A disconnect may only stop local observation. Preserve failed, cancelled and denied states; do not replace them with a successful final answer.
Persistence, authorization, event cursors, retention, title generation and multi-thread lists remain caller-owned. Rehydrate the native store first, then project it. For SSR use request-local owners and matching getServerSnapshot hydration data; no shared user store at module scope. See external stores and each runtime guide for native recovery details.