# Inline conversation and history navigation via agent entries ## Context APP-4313 makes inline conversation surfaces use `AgentConversationsModel` entries instead of local-only `ConversationNavigationData`. The old source, `ConversationNavigationData`, is local-conversation-centric. It is built from open terminal views, cleared/live local conversations, and local metadata in `app/src/ai/conversation_navigation/mod.rs (18-348)`. It has pane/window navigation fields, but no ambient task identity. The new source, `AgentConversationsModel`, exposes one normalized projection for local conversations and ambient runs: - `get_entries` merges task rows, loaded conversation rows, and historical metadata rows in `app/src/ai/agent_conversations_model.rs (1032-1081)`. - When a task shadows a local conversation, the task keeps row ownership. - `AgentConversationEntryId` identifies either an `AmbientRun` or `Conversation` in `app/src/ai/agent_conversations_model/entry.rs (29-38)`. The same model owns open-action policy. `resolve_open_action` re-reads current state at accept time, then prefers: - already-open ambient sessions - already-open local conversations - joinable ambient sessions - restorable local conversations - transcript viewer fallback See `app/src/ai/agent_conversations_model.rs (1117-1239)`. `ActiveAgentViewsModel` tracks open/focused local conversations and ambient sessions through `ConversationOrTaskId` in `app/src/ai/active_agent_views_model.rs (25-66)`. Its focused/open terminal view lookup APIs feed inline menu labels and suffixes in `app/src/ai/active_agent_views_model.rs (297-515)`. Before this change, inline conversation menu rows carried `ConversationNavigationData`, and inline history rows carried `AIConversationId`. That excluded task-backed cloud agent rows and duplicated navigation rules in input handlers. ## Proposed changes ### Shared row identity Use `AgentConversationEntryId` as the row action payload. `AcceptConversation` now stores `item_id`. The message bar maps that id to `ConversationOrTaskId` to choose “go to conversation” vs “continue in this pane” in `app/src/terminal/input/conversations/mod.rs (29-72)`. Inline history mirrors that shape. `AcceptHistoryItem`, `MenuItem`, row identity restoration, and accepted events now preserve `AgentConversationEntryId` in `app/src/terminal/input/inline_history/data_source.rs (33-219)` and `app/src/terminal/input/inline_history/view.rs (34-87)`. ### Inline conversation menu `ConversationMenuDataSource` now starts from `AgentConversationsModel::get_entries(...)`, then keeps only rows where `resolve_open_action(..., ActivePane, ...)` returns an action. This hides unopenable rows and centralizes navigation policy in `app/src/terminal/input/conversations/data_source.rs (32-50)`. Search behavior stays the same: - empty query shows up to 50 recent entries - non-empty query fuzzy-matches title - Current Directory compares `entry.display.working_directory` - the currently active local conversation is hidden See `app/src/terminal/input/conversations/data_source.rs (57-156)`. Rendering uses entry display data: - status icon from `entry.display.status` - timestamp from `entry.display.last_updated` - title/accessibility text from `entry.display.title` See `app/src/terminal/input/conversations/search_item.rs (24-196)`. Open-state rendering asks `ActiveAgentViewsModel::get_terminal_view_id_for_entry(...)` for the open terminal view, using task id first and local conversation id as fallback. It compares that terminal view with `get_focused_terminal_view_id(...)` to decide whether to show “open in different pane” in `app/src/terminal/input/conversations/search_item.rs (99-113)` and `app/src/ai/active_agent_views_model.rs (297-515)`. ## Testing and validation Preserve: - inline conversation menu hides the currently active local conversation - Current Directory filter still works Verify: - task-backed `AgentConversationEntryId::AmbientRun` survives inline-history interleaving - accepting a task-backed row opens or focuses the ambient session when possible - accepting a local conversation row still restores/navigates into the active pane - labels and suffixes are correct for open local conversations, open ambient sessions, and task-backed rows with local ids Manual checks: - Open inline conversation menu, search a local conversation, accept it, confirm it opens in active pane. - Open inline history, search a cloud/ambient run, accept it, confirm it focuses or opens through the shared resolver. - Toggle Current Directory filter and confirm only matching `entry.display.working_directory` rows appear. ## Risks and mitigations ### Stale action payloads Risk: rows carry a stale `WorkspaceAction` or old `ConversationNavigationData`. Mitigation: rows carry only `AgentConversationEntryId`; actions resolve at accept time. ### Hidden task-backed rows Risk: menus only show local conversations. Mitigation: data sources start from `AgentConversationsModel::get_entries(...)` and only drop rows without an open action. ### Local/task alias mismatch Risk: a task-backed row with a local conversation id renders wrong open/focused state. Mitigation: rendering uses the same task-first/local-fallback terminal view lookup shape as open-action resolution. ### Over-broad history rows Risk: display-only metadata creates unopenable history rows. Mitigation: both menu data sources filter through `resolve_open_action`.