| 1 | # Session Operations and Runtime Decoupling |
| 2 | |
| 3 | This document records the compatibility contract for target-bound local session |
| 4 | operations introduced in September 2026. |
| 5 | |
| 6 | ## Routing |
| 7 | |
| 8 | Persistent session operations resolve an explicit `SessionSelector` in this |
| 9 | order: |
| 10 | |
| 11 | 1. canonical `SessionRef` (`hostId + sessionId`); |
| 12 | 2. validated `sessionPath`; |
| 13 | 3. `topicId` as a legacy/topic-only compatibility lookup. |
| 14 | |
| 15 | An invalid higher-priority selector is an error and never falls through to a |
| 16 | lower-priority field. There is intentionally no bare `sessionId` selector: |
| 17 | canonical session IDs are qualified by `hostId`, while legacy sessions may not |
| 18 | have a canonical session ID at all. A `topicId` is not a session identity and |
| 19 | is accepted only as the lowest-priority compatibility address; if it resolves |
| 20 | to multiple sessions, the caller must provide a `SessionRef` or `sessionPath`. |
| 21 | Legacy identity is the validated canonical path plus the BranchMeta/file |
| 22 | generation observed by the operation. Runtime bindings are optional |
| 23 | projections; they are not proof that a durable session exists. |
| 24 | |
| 25 | Persistent title, history, search, archive, restore, move, delete, fork, and |
| 26 | full-history copy operations can run without selecting or booting the target |
| 27 | conversation. Legacy move first adopts the source through the existing |
| 28 | migration journal; the retained legacy artifacts remain unchanged. Runtime |
| 29 | commands such as send, stop, and live model control still require an open, |
| 30 | ready binding. |
| 31 | |
| 32 | Target history APIs cover bounded pages and windows, search, message location, |
| 33 | large-content capabilities, and bounded message-field reads. Every cursor or |
| 34 | content capability remains bound to the resolved durable identity and snapshot; |
| 35 | switching tabs cannot redirect the read. |
| 36 | |
| 37 | Fork and full copy intentionally have different semantics. Fork accepts only a |
| 38 | verified completed-turn boundary. Full copy freezes the complete durable source, |
| 39 | publishes a new identity, and uses a caller operation ID plus a durable copy |
| 40 | receipt so a retry cannot create a second child. Neither operation navigates to |
| 41 | the child automatically. |
| 42 | |
| 43 | ## Title concurrency |
| 44 | |
| 45 | Canonical session titles keep the existing `session/title` event payload. |
| 46 | `Projection.TitleSequence` is reconstructed from the sequence of the latest |
| 47 | title event and is used as the title-only CAS token. Normal message appends do |
| 48 | not create title conflicts. Every explicit title save, including a same-value |
| 49 | save, appends a title event. |
| 50 | |
| 51 | Legacy BranchMeta adds one optional field: |
| 52 | |
| 53 | ```json |
| 54 | {"title_revision":"opaque random mutation token"} |
| 55 | ``` |
| 56 | |
| 57 | Old sidecars without the field remain readable. The first title snapshot |
| 58 | initializes a token under the cross-process metadata lock. Manual and AI writes |
| 59 | replace it with a new random token. Transcript and listing projection writers |
| 60 | preserve the latest title and token, while the compatibility whole-record |
| 61 | `SaveBranchMeta` API continues to honor an explicitly supplied title. |
| 62 | |
| 63 | An AI title result commits only if its title token, durable target identity, and |
| 64 | lifecycle generation still match. Cancellation is an optimization; CAS remains |
| 65 | the authority when a provider returns after cancellation. |
| 66 | |
| 67 | ### Mixed-version limit |
| 68 | |
| 69 | Writers that implement this protocol detect A→B→A and same-value manual saves. |
| 70 | An older writer that removes `title_revision` causes a new AI result to be |
| 71 | rejected. No implementation can universally detect an old writer that changes |
| 72 | the title A→B→A while deliberately preserving an unknown revision token. |
| 73 | |
| 74 | ## Auxiliary providers |
| 75 | |
| 76 | Cold AI title generation uses a provider-only handle with the target session and |
| 77 | workspace context. Config-backed models do not start extension runtimes. |
| 78 | Extension models start only the owning `plugin/<name>` package and package |
| 79 | dependencies declared through capability requirements. The handle does not |
| 80 | publish tools, MCP servers, UI actions, or prompt contributions into another |
| 81 | conversation and releases its sidecars when the operation ends. |
| 82 | |
| 83 | The provider-visible title prompt and the maximum three authored user-message |
| 84 | inputs are unchanged. |
| 85 | |
| 86 | ## RPC and events |
| 87 | |
| 88 | Mutation versions cross the Desktop RPC boundary as strings. Business errors |
| 89 | add `sessionCode`, `targetKey`, `operationId`, and `retryable` to JSON-RPC error |
| 90 | data. Unknown lower-level failures use a generic product message; local paths, |
| 91 | controller identities, lease details, provider bodies, and credentials are not |
| 92 | rendered in toasts. |
| 93 | |
| 94 | Committed changes emit target-bound hints: |
| 95 | |
| 96 | - `session_metadata_changed` |
| 97 | - `session_archived` |
| 98 | - `session_restored` |
| 99 | - `session_moved` |
| 100 | - `session_deleted` |
| 101 | |
| 102 | The existing project-tree notifications remain during compatibility. Durable |
| 103 | storage is authoritative if an incremental event is missed or cannot be |
| 104 | ordered. |
| 105 |