Fix SQLite recovery and preserve provider conversation context
Persist exact provider history and archived tool results independently of terminal scrollback, and retain them when active run checkpoints are cleared. Keep conversation and session saving independent of automatic reopening. Deduplicate only committed session snapshots, retry transient SQLite write contention, and enqueue conversation updates before writer shutdown. Recover healthy startup data independently and protect unread records by disabling writes with a persistent warning after an incomplete restore. Retain local transcript pane conversation links across restarts. Add regression coverage for SQLite failures, context round trips, legacy fallbacks, and transcript snapshots.
This commit is contained in:
@@ -1086,8 +1086,19 @@ pub struct LocalAgentGroup {
|
||||
pub notified: bool,
|
||||
}
|
||||
|
||||
/// Exact provider history, independent of terminal scrollback and UI task projections.
|
||||
/// `None` on AgentConversationData denotes a legacy row requiring reconstruction;
|
||||
/// an explicitly empty history must stay empty after restoration.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ProviderConversationContext {
|
||||
pub messages: Vec<galaxy_agent_core::ConversationMessage>,
|
||||
pub tool_result_archive: Vec<galaxy_agent_core::ConversationMessage>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct AgentConversationData {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider_context: Option<ProviderConversationContext>,
|
||||
/// Display-only tool intent, scoped to this conversation and keyed by call ID.
|
||||
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
|
||||
pub tool_titles: HashMap<String, String>,
|
||||
|
||||
@@ -137,6 +137,7 @@ fn is_restorable_accepts_empty_and_single_task_conversations() {
|
||||
fn agent_conversation_data_roundtrips_last_event_sequence() {
|
||||
let data = AgentConversationData {
|
||||
agent_backend: Default::default(),
|
||||
provider_context: None,
|
||||
active_provider_run_json: None,
|
||||
local_agent_groups: Vec::new(),
|
||||
tool_titles: Default::default(),
|
||||
@@ -242,6 +243,7 @@ fn agent_conversation_data_omits_default_provider_backend() {
|
||||
fn agent_conversation_data_roundtrips_remote_child_marker() {
|
||||
let data = AgentConversationData {
|
||||
agent_backend: Default::default(),
|
||||
provider_context: None,
|
||||
active_provider_run_json: None,
|
||||
local_agent_groups: Vec::new(),
|
||||
tool_titles: Default::default(),
|
||||
@@ -272,6 +274,7 @@ fn agent_conversation_data_roundtrips_remote_child_marker() {
|
||||
fn agent_conversation_data_roundtrips_optimistic_root_marker() {
|
||||
let data = AgentConversationData {
|
||||
agent_backend: Default::default(),
|
||||
provider_context: None,
|
||||
active_provider_run_json: None,
|
||||
local_agent_groups: Vec::new(),
|
||||
tool_titles: Default::default(),
|
||||
@@ -314,6 +317,7 @@ fn agent_conversation_data_deserializes_legacy_payload_without_last_event_sequen
|
||||
fn agent_conversation_data_skips_serializing_none_last_event_sequence() {
|
||||
let data = AgentConversationData {
|
||||
agent_backend: Default::default(),
|
||||
provider_context: None,
|
||||
active_provider_run_json: None,
|
||||
local_agent_groups: Vec::new(),
|
||||
tool_titles: Default::default(),
|
||||
@@ -346,6 +350,7 @@ fn agent_conversation_data_skips_serializing_none_last_event_sequence() {
|
||||
fn agent_conversation_data_roundtrips_pinned() {
|
||||
let data = AgentConversationData {
|
||||
agent_backend: Default::default(),
|
||||
provider_context: None,
|
||||
active_provider_run_json: None,
|
||||
local_agent_groups: Vec::new(),
|
||||
tool_titles: Default::default(),
|
||||
@@ -376,6 +381,7 @@ fn agent_conversation_data_roundtrips_pinned() {
|
||||
fn agent_conversation_data_skips_serializing_unpinned() {
|
||||
let data = AgentConversationData {
|
||||
agent_backend: Default::default(),
|
||||
provider_context: None,
|
||||
active_provider_run_json: None,
|
||||
local_agent_groups: Vec::new(),
|
||||
tool_titles: Default::default(),
|
||||
|
||||
Reference in New Issue
Block a user