fix(chat): keep a forked conversation on the session it forked to
A fork re-points the open row at S2 and inserts a sibling row holding the pre-fork session S1. The panel learned S2 from the fork response at once, but resolved the id it reconnects with as `detail?.summary.external_id ?? runtimeExternalId` — and `detail` still said S1 until its refetch landed. So the next reconnect asked for S1, which the new sibling now owned, and the tab silently re-homed onto the pre-fork history with the [Fork] row abandoned. Forking again forked S1 a second time; the conversation table records the chain, each row created at one fork's timestamp and itself forked at the next. Resolve from the runtime store instead. It is fed by BOTH sources — the DB value is written into it whenever detail changes, and the live session id when SessionStarted fires — so it is always the more recently established of the two, with detail left as the cold-open fallback. The visible symptom was the composer's model and effort changing after a fork: the tab had landed on a session it never established, so the selectors were whatever that session defaulted to. Carry the parent's selectors across the fork as well, since `session/resume` answers with the agent's defaults and nothing else re-applies them: read the mode and config values off the session being left and replay them through the same preference path a reconnect uses, a no-op per option when the value already matches.
X
xintaofei committed
039ee6908faa989c6dc9d6f1f8dc122325924c49
Parent: 8b179c8