4.3 KiB
Increment 1: entry schema and merge builder
Context
This increment adds the normalized projection API behind existing UI code. AgentConversationsModel already stores tasks and conversations separately in app/src/ai/agent_conversations_model.rs (844-1901), and ConversationOrTask currently provides display helpers over borrowed raw data in app/src/ai/agent_conversations_model.rs (399-813). The migration should add the new API without removing or migrating those call sites yet.
The builder must understand metadata-only conversations. AIConversationMetadata can represent local persisted conversations or cloud-only metadata in app/src/ai/blocklist/history_model.rs (49-157). merge_cloud_conversation_metadata creates a local AIConversationId for new cloud-only metadata and records the server token in server_token_to_conversation_id in app/src/ai/blocklist/history_model/conversation_loader.rs (334-453).
Proposed changes
Add a module near agent_conversations_model.rs, for example app/src/ai/agent_conversations_model/entry.rs if splitting the file is practical, or keep the first pass in agent_conversations_model.rs to reduce churn. Define:
AgentConversationEntryAgentConversationEntryIdAgentConversationIdentityAgentConversationDisplayDataAgentConversationProvenanceAgentConversationBackingDataAgentConversationCapabilitiesAdd an internal builder that consumes current model state andAppContext. The builder should:
- build a map from server token to local conversation id from history metadata and loaded conversations;
- build a map from run id to local conversation id using
conversation_id_for_agent_id; - create one entry for each
AmbientAgentTask, keyed byAgentConversationEntryId::AmbientRun(task.run_id()); - attach a matching local conversation id by run id first, then server token;
- attach server token from the task, matching conversation metadata, or loaded conversation;
- create
Conversationentries for metadata/local conversations not already attached to an ambient run. The display-data derivation should reuse existingConversationOrTaskbehavior where possible during this increment, but the new type should not exposeConversationOrTaskpublicly. It is acceptable for the builder to use temporary private helpers to avoid duplicating all display logic in the first PR. Capabilities should be conservative in this increment.can_openshould mean the entry has at least one of: open ambient run id, local conversation id, or server token.can_share,can_delete, andcan_fork_locallycan mirror current behavior but should remain data booleans, not actions. Add a new read API:
pub fn get_entries(
&self,
filters: &AgentManagementFilters,
app: &AppContext,
) -> Vec<AgentConversationEntry>
Returning a Vec is acceptable for the first pass because entries are owned snapshots and list sizes are small. Keep the existing get_tasks_and_conversations API for current UI.
Testing and validation
Add unit tests in app/src/ai/agent_conversations_model_tests.rs for builder identity and dedupe behavior:
- task-only entry uses
AmbientRunid andAmbientRunprovenance; - local-only entry uses
Conversationid andLocalInteractiveprovenance; - cloud metadata-only entry uses
Conversationid andCloudSyncedConversationprovenance withhas_cloud_data = trueandhas_loaded_conversation = false; - task plus local conversation matched by run id produces one
AmbientRunentry with both ids attached; - task plus local conversation matched by server token produces one
AmbientRunentry with both ids attached; - unrelated task and conversation produce two entries;
- child-agent cloud metadata skipped today by metadata merge remains skipped from entries. Run the focused test module after implementation. This increment should not change rendered UI, so existing conversation list and management view behavior should remain unchanged.
Risks and mitigations
Duplicates from incomplete indexing
Build token and run-id indices before constructing entries. Keep the “ambient run owns row identity” rule centralized in one builder function.
Too much display duplication
It is fine to temporarily delegate to private helpers based on ConversationOrTask as long as the public API is normalized. Increment 4 should remove the old helper path.