Now I have a complete picture. Here's the analysis:
Data Structure Saved to SQLite
The session table in SQLite now has two new columns (lines 30–31 of session.sql.ts):
agent TEXT -- plain text, the agent name (e.g. "explore", "build")
model TEXT (JSON) -- JSON object: { providerID: string, modelID: string }
These sit alongside the existing session-level columns. The full SessionTable schema is essentially a flattened, column-per-field representation of the Session.Info type. It is not a 1:1 structural clone of the old JSON blob — it's a normalized decomposition:
| JSON blob field |
SQL column(s) |
Storage mode |
id |
id TEXT PK |
plain text |
title |
title TEXT |
plain text |
parentID |
parent_id TEXT |
plain text |
summary.additions |
summary_additions INTEGER |
scalar |
summary.diffs |
summary_diffs TEXT (JSON) |
JSON blob |
permission |
permission TEXT (JSON) |
JSON blob |
time.created |
time_created INTEGER |
epoch ms |
agent (new) |
agent TEXT |
plain text |
model (new) |
model TEXT (JSON) |
{ providerID, modelID } |
So yes — the session state that was previously in the filesystem JSON blob is now in SQLite columns, but it's not a blind JSON dump. Simple scalars (strings, numbers) get their own typed columns; complex nested objects (summary_diffs, revert, permission, model) are stored as TEXT with mode: "json" (Drizzle auto-serializes/deserializes). The fromRow() / toRow() functions in session/index.ts handle the bidirectional mapping.
The agent and model fields capture the session's identity at creation time — specifically, which agent type and which provider+model were assigned to this session. This is persisted so it survives across:
- Session reverts (the key motivating problem from the test comments)
- Process restarts
- Re-reads from the database
The Resolution Chain
The critical change is in createUserMessage (line 850–854 of prompt.ts):
const session = await Session.get(input.sessionID)
const agent = await Agent.get(input.agent ?? session.agent ?? (await Agent.defaultAgent()))
const model = input.model ?? session.model ?? agent.model ?? (await lastModel(input.sessionID))
Before: input.agent → defaultAgent() and input.model → agent.model → lastModel()
After: input.agent → session.agent → defaultAgent() and input.model → session.model → agent.model → lastModel()
The session's stored agent/model now acts as a sticky override — higher priority than the agent's default model, but lower priority than an explicit per-prompt override.
Plans for the Remote Share Store
Looking at share-next.ts, the remote sync mechanism sends SDK-typed objects to the web server at opncd.ai (or an enterprise URL). The Data union type (lines 103–123) defines what gets synced:
type Data =
| { type: "session", data: SDK.Session }
| { type: "message", data: SDK.Message }
| { type: "part", data: SDK.Part }
| { type: "session_diff", data: SDK.FileDiff[] }
| { type: "model", data: SDK.Model[] }
Since Session.Info now includes agent and model, and the Session.Event.Updated bus event fires with the full info object (which is typed as SDK.Session), the new agent and model fields will automatically flow to the remote share server whenever a session is created or updated — no changes to share-next.ts are needed.
The share system works as a debounced event queue (1-second batching window per session). When a session is updated (including the new Session.update() for agent/model changes), it:
- Publishes
Session.Event.Updated on the bus
share-next.ts catches it and queues a { type: "session", data: info } payload
- After 1 second, it POSTs the batch to
/api/share/{shareId}/sync
The fullSync function (line 179) also sends the complete session state when a share is first created, which will include the new fields.
In summary: The remote store receives the same SDK.Session shape that the local SQL stores — the agent/model fields propagate automatically through the existing event-driven sync pipeline. The web server will receive session.agent and session.model as part of the session payload, enabling the share viewer to display which agent and model were used for a given session.