Dashboard › opencode › Distillation
e7d2cf30-26e0-4621-a7af-8c3a0b26db28["lore_tm_v1_IcP0OZ6-wauL6Sq9lyQtIbr6qriLAlFPJq8jJBX1bkY","lore_tm_v1_WZnVq3esKuwZyXZXlKEMFtRbXbjXG7THy8fiK9Ts29M","lore_tm_v1_zvO4P73Rhpl2fJ5hxZn5dPcd_GJ43YTavoiBQY-UMMA","lore_tm_v1_JKCiOCu-Kt1uKuHgXBglitazPiVkctCuoO-kdgwhfFE","lore_tm_v1_3K8BU9SfNlwxH35sJrnXWoAvjVqgZa7RomYappYq2tc","lore_tm_v1__Jxtd8vJoXEWoWOKwLEG7ejmziOrmo5hZpNEdDMU0Pk","lore_tm_v1_SXPf2Ui3XMLc0eyeQqRxuGvNucZCBnao0cTyFcQZa_4"]
Date: Sep 15, 2026
packages/core/src/database/migration/20260914170650_session-recovery.ts has ID 20260914170650_session-recovery; it adds session_input.wake_pending as integer DEFAULT false NOT NULL.20260914170650_session-recovery creates session_execution with columns id, session_id, owner_id, nullable assistant_message_id, phase, time_created, expires_at, and nullable recovery_error; id is the primary key, session_id references session(id) with ON DELETE CASCADE, and phase is constrained to 'ready', 'safe', 'unknown', or 'continue'.20260914170650_session-recovery creates session_recovery with columns session_id, execution_id, nullable assistant_message_id, continuation_message_id, phase, wake_pending integer DEFAULT true NOT NULL, nullable recovery_error, and time_created; execution_id is the primary key. Foreign keys cascade from session_id to session(id), assistant_message_id to session_message(id), and continuation_message_id to session_input(id).20260914170650_session-recovery creates indexes: unique session_execution_session_idx on session_execution(session_id); session_execution_owner_idx on session_execution(owner_id); session_input_wake_pending_promoted_time_created_id_idx on session_input(wake_pending, promoted_seq, time_created, id); and unique session_recovery_continuation_message_idx on session_recovery(continuation_message_id).packages/core/test/database-migration.test.ts:166-219 test "adds final recovery ownership tables to a populated predecessor" constructs existing session, session_message, and session_input tables with rows ses_existing, msg_existing, and msg_input, then applies only sessionRecoveryMigration; all 3 preexisting rows remain intact.session_execution.id is required; session_input.wake_pending defaults to "false"; session_recovery.execution_id is required and has pk: 1; session_recovery.assistant_message_id and recovery_error are nullable; and session_recovery.phase is required. Inserting session_execution.phase = 'invalid' fails.packages/core/test/database-migration.test.ts:221-230 test "rejects a non-empty database without a session table" creates only unrelated(id text PRIMARY KEY) and expects DatabaseMigration.apply(db) to throw "Database is not empty and has no session table".packages/core/test/database-migration.test.ts:233-250 test "backfills existing Context Epoch rows to the build agent" applies contextEpochAgentMigration to an existing session_context_epoch row for ses_existing and verifies agent: "build".packages/schema/src/session-event.ts:207-217 defines SessionEvent.Step.Recovered with event type "session.next.step.recovered" and fields from Base plus executionID: Schema.String, optional assistantMessageID: SessionMessage.ID, and phase: Schema.Literals(["ready", "unknown", "continue"]); notably, recovery events do not expose the execution-table-only "safe" phase.SessionEvent.Step.Recovered used in packages/core/src/session/recovery.ts:105 and projected in packages/core/src/session/projector.ts:392; handling for "session.next.step.recovered" appears in packages/core/src/session/message-updater.ts:251.packages/core/src/session/message-updater.ts:251-297 handles "session.next.step.recovered" as follows: phase "continue" performs no message update; phase "unknown" uses "Provider Step outcome unknown after server restart"; other recoverable phases use "Session execution interrupted by server restart".assistantMessageID, message-updater.ts appends a synthetic message with ID message(event.data.executionID), type: "synthetic", the recovery error text, and creation time event.data.timestamp.assistantMessageID, message-updater.ts marks the owned assistant completed at the event timestamp with finish = "error" and the recovery error, then appends an assistant text part with ID recovery:${event.data.executionID} containing that error message."continue" recovery of an existing assistant, every pending or running tool is completed at the event timestamp and changed to SessionMessage.ToolStateError with message "Tool execution outcome unknown after server restart"; string tool input becomes {}, while running tools retain structured and content, and pending tools receive empty structured and content.packages/core/src/session/message-updater.ts:231-249 marks the assistant finish = "error", stores the interruption error, and converts pending/running tools to errors with message "Tool execution interrupted".