Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"files": {
"desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift": 1926,
"desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift": 1952,
"desktop/macos/Desktop/Sources/MainWindow/Pages/AppsPage.swift": 3643,
"desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift": 4543,
"desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift": 4582,
"desktop/macos/Desktop/Sources/MainWindow/Pages/MemoriesPage.swift": 3298,
"desktop/macos/Desktop/Sources/MainWindow/Pages/TasksPage.swift": 5518,
"desktop/macos/Desktop/Sources/MainWindow/SidebarView.swift": 1570
},
"raise_justifications": {
"desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift": "Keeps all chat tool groups compact and collapsed while streamed steps increment, with an explicit expansion policy, fixed collapsed height, and regression coverage.",
"desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift": "Metadata-row reveal now honors keyboard focus (shared FocusState + testable ChatBubbleMetadataReveal) so Full Keyboard Access never lands on invisible actions.",
"desktop/macos/Desktop/Sources/MainWindow/Pages/AppsPage.swift": "Rebase reconciliation applies the pinned swift-format to the main-branch app-detail safety fixes; runtime behavior is unchanged.",
"desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift": "Home redesign: chat-as-home surface plus the 2nd-brain knows-list hub replace the wordmark/ribbon/WMN-card layout; a component split is tracked follow-up.",
"desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift": "Home redesign bug fixes: hub-safe collapse-catcher predicate, Continue-in-Omi chat landing consume, and a lifecycle chain split for the type-checker; component split remains tracked follow-up.",
"desktop/macos/Desktop/Sources/MainWindow/Pages/MemoriesPage.swift": "Bridge memory search now awaits the active initial or pagination projection before refreshing, preventing an immediate post-navigation marker search from using stale lifecycle capability state.",
"desktop/macos/Desktop/Sources/MainWindow/Pages/TasksPage.swift": "The Load-more-tasks action reads displayTasks.last at execution time via loadMoreTapped() instead of force-unwrapping (an emptied list no longer crashes); merged with main binding first-load/loading/error/retry state to the selected To Do or Done view.",
"desktop/macos/Desktop/Sources/MainWindow/SidebarView.swift": "Strict concurrency imports annotate legacy AppKit image usage without changing runtime behavior."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
"desktop/macos/Desktop/Sources/CloudConnectorFormAutomation.swift": 1678,
"desktop/macos/Desktop/Sources/DesktopAutomationBridge.swift": 4238,
"desktop/macos/Desktop/Sources/MemoryExportService.swift": 1578,
"desktop/macos/Desktop/Sources/OmiApp.swift": 1638
"desktop/macos/Desktop/Sources/OmiApp.swift": 1646
},
"raise_justifications": {
"desktop/macos/Desktop/Sources/AuthService.swift": "Apple first-auth name capture signals observers (authNameDidUpdate) and persists the name to Firebase so it survives reinstalls.",
"desktop/macos/Desktop/Sources/DesktopAutomationBridge.swift": "Reach-error card gains a debug bridge action so the actionable failure surface is verifiable in-process.",
"desktop/macos/Desktop/Sources/MemoryExportService.swift": "Pinned swift-format normalizes line layout without changing this source's runtime behavior.",
"desktop/macos/Desktop/Sources/OmiApp.swift": "shared openMainAppWindow helper for menu bar + Open Omi shortcut"
"desktop/macos/Desktop/Sources/OmiApp.swift": "openMainAppChat() gives the floating bar Continue-in-Omi affordances a chat-landing entry next to openMainAppWindow()."
},
"threshold": 1500
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AppKit

Check warning on line 1 in desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarView.swift

View workflow job for this annotation

GitHub Actions / Hygiene

Large changed file

desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarView.swift is 2853 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarView.swift

View workflow job for this annotation

GitHub Actions / Desktop Swift Static Contracts

Large changed file

desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarView.swift is 2853 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarView.swift

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarView.swift is 2853 lines; consider splitting files over 800 lines.
import Combine
@preconcurrency import MarkdownUI
import OmiSupport
Expand Down Expand Up @@ -1691,7 +1691,7 @@
onEscape: onEscape,
onOpenMainApp: {
(window as? FloatingControlBarWindow)?.closeAIConversation()
(NSApp.delegate as? AppDelegate)?.openMainAppWindow()
(NSApp.delegate as? AppDelegate)?.openMainAppChat()
},
onRate: onRate,
onShareLink: onShareLink,
Expand Down Expand Up @@ -2295,7 +2295,7 @@
HStack(spacing: OmiSpacing.xs) {
Button {
onEscape()
(NSApp.delegate as? AppDelegate)?.openMainAppWindow()
(NSApp.delegate as? AppDelegate)?.openMainAppChat()
} label: {
HStack(spacing: OmiSpacing.xs) {
Text("Continue in Omi")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AppKit

Check warning on line 1 in desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift

View workflow job for this annotation

GitHub Actions / Hygiene

Large changed file

desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift is 1952 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift

View workflow job for this annotation

GitHub Actions / Desktop Swift Static Contracts

Large changed file

desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift is 1952 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift is 1952 lines; consider splitting files over 800 lines.
@preconcurrency import MarkdownUI
import OmiTheme
import SwiftUI
Expand All @@ -25,6 +25,10 @@
@State private var showRatingFeedback = false
@State private var showInfoPopover = false
@State private var lastSubmittedRating: Int?
// Shared across every metadata control: true while any of them holds
// keyboard focus, so Tab / Full Keyboard Access never lands on an
// invisible button.
@FocusState private var isMetadataControlFocused: Bool

init(
message: ChatMessage, app: OmiApp?, onRate: @escaping (Int?) -> Void,
Expand Down Expand Up @@ -329,10 +333,18 @@
}
}
// Quiet timeline: actions and timestamps only surface while the reader
// is on the message (or mid-interaction with them).
.opacity(isRowHovering || showRatingFeedback || showCopied || showInfoPopover ? 1 : 0)
// is on the message — by pointer hover or keyboard focus — or
// mid-interaction with them.
.opacity(
ChatBubbleMetadataReveal.isVisible(
hovering: isRowHovering,
controlFocused: isMetadataControlFocused,
transientFeedback: showRatingFeedback || showCopied || showInfoPopover
) ? 1 : 0
)
.omiAnimation(.easeInOut(duration: 0.12), value: isTimestampHovering)
.omiAnimation(.easeInOut(duration: 0.15), value: isRowHovering)
.omiAnimation(.easeInOut(duration: 0.15), value: isMetadataControlFocused)
}

@ViewBuilder
Expand All @@ -351,6 +363,7 @@
.foregroundColor(message.rating == 1 ? OmiColors.accent : OmiColors.textTertiary)
}
.buttonStyle(.plain)
.focused($isMetadataControlFocused)
.help("Helpful response")

// Thumbs down
Expand All @@ -366,6 +379,7 @@
.foregroundColor(message.rating == -1 ? .red : OmiColors.textTertiary)
}
.buttonStyle(.plain)
.focused($isMetadataControlFocused)
.help("Not helpful")

if showRatingFeedback {
Expand Down Expand Up @@ -408,6 +422,7 @@
.foregroundColor(showCopied ? .green : OmiColors.textTertiary)
}
.buttonStyle(.plain)
.focused($isMetadataControlFocused)
.help("Copy message")
}

Expand All @@ -422,6 +437,7 @@
.foregroundColor(showInfoPopover ? OmiColors.textPrimary : OmiColors.textTertiary)
}
.buttonStyle(.plain)
.focused($isMetadataControlFocused)
.help("View response context")
.popover(isPresented: $showInfoPopover, arrowEdge: .bottom) {
if let metadata = message.metadata {
Expand All @@ -431,6 +447,16 @@
}
}

/// Visibility rule for the quiet timeline's per-message metadata row
/// (rating / copy / info / timestamp). Keyboard parity is part of the
/// contract: focus on any metadata control must reveal the row, otherwise
/// Tab / Full Keyboard Access ends up on an invisible button.
enum ChatBubbleMetadataReveal {
static func isVisible(hovering: Bool, controlFocused: Bool, transientFeedback: Bool) -> Bool {
hovering || controlFocused || transientFeedback
}
}

struct BackgroundAgentSummary: Equatable {
let agentID: UUID?
let prompt: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,13 @@ enum HomeKnowsListComposer {
rows.append(HomeKnowsRow(kind: .task(id: task.id), text: task.text))
}

// Question rows are identified by their text, so a repeated suggestion
// would collide as a ForEach ID — keep only the first occurrence.
var seenQuestions = Set<String>()
let cleanQuestions =
questions
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty }
.filter { !$0.isEmpty && seenQuestions.insert($0).inserted }

// Insights fill the middle; one slot stays reserved for a question so the
// list always ends with something the user can ask.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AppKit

Check warning on line 1 in desktop/macos/Desktop/Sources/MainWindow/DesktopHomeView.swift

View workflow job for this annotation

GitHub Actions / Hygiene

Large changed file

desktop/macos/Desktop/Sources/MainWindow/DesktopHomeView.swift is 1259 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Sources/MainWindow/DesktopHomeView.swift

View workflow job for this annotation

GitHub Actions / Desktop Swift Static Contracts

Large changed file

desktop/macos/Desktop/Sources/MainWindow/DesktopHomeView.swift is 1259 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Sources/MainWindow/DesktopHomeView.swift

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

desktop/macos/Desktop/Sources/MainWindow/DesktopHomeView.swift is 1259 lines; consider splitting files over 800 lines.
import OmiTheme
import SwiftUI

Expand Down Expand Up @@ -486,6 +486,11 @@
notification in
handleAutomationNavigation(notification)
}
// "Continue in Omi" from the floating bar: switch to the Home tab; the
// dashboard consumes the pending request and opens the chat panel.
.onReceive(NotificationCenter.default.publisher(for: .openMainChatRequested)) { _ in
selectedIndex = SidebarNavItem.dashboard.rawValue
}
}

private func enforceMainWindowMinimumSize() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Foundation

/// One-shot "open the main chat" request raised by surfaces outside the main
/// window (the floating bar's "Continue in Omi" affordances). Revealing the
/// window alone is not enough: the main window may be resting on any tab, so
/// the conversation the user asked to continue would be nowhere in sight.
///
/// Flow: the raiser calls `request()` (which also posts
/// `.openMainChatRequested`); `DesktopHomeView` switches to the Home tab on
/// the notification, and `DashboardPage` consumes the pending request when it
/// is (or becomes) visible and opens the chat panel.
@MainActor
final class MainChatNavigationRequestStore {
static let shared = MainChatNavigationRequestStore()

private(set) var isPending = false

func request() {
isPending = true
NotificationCenter.default.post(name: .openMainChatRequested, object: nil)
}

/// Returns whether a request was pending, and clears it.
func consume() -> Bool {
defer { isPending = false }
return isPending
}
}

extension Notification.Name {
static let openMainChatRequested = Notification.Name("openMainChatRequested")
}
55 changes: 47 additions & 8 deletions desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AppKit

Check warning on line 1 in desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift

View workflow job for this annotation

GitHub Actions / Hygiene

Large changed file

desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift is 4582 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift

View workflow job for this annotation

GitHub Actions / Desktop Swift Static Contracts

Large changed file

desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift is 4582 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift is 4582 lines; consider splitting files over 800 lines.
import Combine
import OmiTheme
import SwiftUI
Expand Down Expand Up @@ -461,14 +461,21 @@
}
}

// Split in two (`applyHomeLifecycle` → `applyHomeStageObservers`) so each
// modifier chain stays within the type-checker's budget.
private func applyHomeLifecycle<Content: View>(to content: Content) -> some View {
applyHomeStageObservers(to: applyHomeLifecycleCore(to: content))
}

private func applyHomeLifecycleCore<Content: View>(to content: Content) -> some View {
content
.onAppear {
if PostOnboardingPromptSuggestions.shouldShowPopup && !postOnboardingSuggestions.isEmpty {
NotificationCenter.default.post(name: .showTryAskingPopup, object: nil)
}
syncCaptureState()
autoOpenChatForExistingHistoryIfNeeded()
consumePendingMainChatOpenRequest()
reportHomeAutomationMode()
intelligenceStore.setRecommendationActionHandler { recommendation in
await openRecommendation(recommendation)
Expand Down Expand Up @@ -516,6 +523,15 @@
.onReceive(NotificationCenter.default.publisher(for: .screenCaptureKitBroken)) { _ in
syncCaptureState()
}
}

private func applyHomeStageObservers<Content: View>(to content: Content) -> some View {
content
// "Continue in Omi" while the dashboard is already mounted; the
// not-yet-mounted case is covered by the consume in onAppear.
.onReceive(NotificationCenter.default.publisher(for: .openMainChatRequested)) { _ in
consumePendingMainChatOpenRequest()
}
// Chat history is the home surface: as soon as the (async) history
// load shows prior messages, land on the chat panel, not the greeting.
.onChange(of: chatProvider.messages.count) { _, _ in
Expand Down Expand Up @@ -657,10 +673,12 @@
HomeCanvasBackground()

// Clicking anywhere outside the chat / connect panel collapses
// back to the hub (panels and the ask bar consume their own
// clicks above this catcher). When chat history exists, chat IS the
// resting Home surface, so no catcher is mounted over it.
if homeMode != homeRestingMode {
// back to the resting surface (panels and the ask bar consume their
// own clicks above this catcher). When chat history exists, chat IS
// the resting Home surface, so no catcher is mounted over it — and
// the hub is never an overlay, so no catcher is ever mounted over
// the hub either (a stray click must not throw the user into chat).
if HomeStageMode.collapseCatcherActive(mode: homeMode, resting: homeRestingMode) {
Color.black.opacity(0.001)
.ignoresSafeArea()
.contentShape(Rectangle())
Expand Down Expand Up @@ -703,8 +721,10 @@
// Esc collapses the connect tray (and, with no chat history, the
// inline chat) back to the resting surface — but only while no modal
// overlay owns the key. Chat with history is Home itself and cannot
// be escaped.
if homeMode != homeRestingMode && !isHomeModalPresented {
// be escaped; the hub is likewise never escaped *into* a panel.
if HomeStageMode.collapseCatcherActive(mode: homeMode, resting: homeRestingMode)
&& !isHomeModalPresented
{
OverlayModalEscapeCatcher {
collapseHomeStagePanel()
}
Expand Down Expand Up @@ -1151,7 +1171,9 @@

/// Chat with history is the default Home surface: whenever prior messages
/// exist, the hub greeting yields to the chat panel. Runs once per page
/// visit so an explicit Esc back to the hub is respected afterwards.
/// visit so the automation bridge's `home_close_panel` hub jump is not
/// immediately overridden by the next `messages.count` change. (There is no
/// user-facing path back to the hub once history exists — chat is Home.)
private func autoOpenChatForExistingHistoryIfNeeded() {
guard !didAutoOpenChatForHistory else { return }
guard !useLegacyHomeDesign, homeMode == .hub, !chatProvider.messages.isEmpty else { return }
Expand All @@ -1160,6 +1182,14 @@
reportHomeAutomationMode()
}

/// Floating-bar "Continue in Omi": land directly on the chat panel instead
/// of whatever surface Home was resting on.
private func consumePendingMainChatOpenRequest() {
guard MainChatNavigationRequestStore.shared.consume() else { return }
guard !useLegacyHomeDesign else { return }
openHomeChat()
}

private func openHomeChat(focusInput: Bool = true) {
guard homeMode != .chat else { return }
OmiMotion.withGated(Self.homeStageAnimation) {
Expand Down Expand Up @@ -2123,11 +2153,20 @@
case quiet
}

private enum HomeStageMode: Equatable {
enum HomeStageMode: Equatable {
case hub
case chat
case connect

/// Whether the user-facing collapse catchers (click-outside + Esc) mount.
/// Only a panel that can collapse to a *different* resting surface gets a
/// catcher. The hub is the base surface, never an overlay: mounting a
/// catcher over hub-with-history would invert the gesture and make a stray
/// click or Esc *open* the chat.
static func collapseCatcherActive(mode: HomeStageMode, resting: HomeStageMode) -> Bool {
mode != resting && mode != .hub
}

var automationLabel: String {
switch self {
case .hub: return "hub"
Expand Down
8 changes: 8 additions & 0 deletions desktop/macos/Desktop/Sources/OmiApp.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import FirebaseAuth

Check warning on line 1 in desktop/macos/Desktop/Sources/OmiApp.swift

View workflow job for this annotation

GitHub Actions / Hygiene

Large changed file

desktop/macos/Desktop/Sources/OmiApp.swift is 1646 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Sources/OmiApp.swift

View workflow job for this annotation

GitHub Actions / Desktop Swift Static Contracts

Large changed file

desktop/macos/Desktop/Sources/OmiApp.swift is 1646 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Sources/OmiApp.swift

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

desktop/macos/Desktop/Sources/OmiApp.swift is 1646 lines; consider splitting files over 800 lines.
import FirebaseCore
import OmiSupport
import OmiTheme
Expand Down Expand Up @@ -263,7 +263,7 @@
isExporting: ViewExporter.shouldExport())
}

func applicationDidFinishLaunching(_ notification: Notification) {

Check warning on line 266 in desktop/macos/Desktop/Sources/OmiApp.swift

View workflow job for this annotation

GitHub Actions / Hygiene

Long function

func applicationDidFinishLaunching(_ notification: Notification) is 347 lines; consider extracting focused helpers over 150 lines.

Check warning on line 266 in desktop/macos/Desktop/Sources/OmiApp.swift

View workflow job for this annotation

GitHub Actions / Desktop Swift Static Contracts

Long function

func applicationDidFinishLaunching(_ notification: Notification) is 347 lines; consider extracting focused helpers over 150 lines.

Check warning on line 266 in desktop/macos/Desktop/Sources/OmiApp.swift

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Long function

func applicationDidFinishLaunching(_ notification: Notification) is 347 lines; consider extracting focused helpers over 150 lines.
if ViewExporter.shouldExport() {
ViewExporter.run()
return
Expand Down Expand Up @@ -1037,6 +1037,14 @@
openMainAppWindow()
}

/// "Continue in Omi": bring the main window forward *and* land on the chat
/// timeline, wherever the window was last resting. The pending request
/// survives window creation, so a freshly created window also lands on chat.
@MainActor func openMainAppChat() {
MainChatNavigationRequestStore.shared.request()
openMainAppWindow()
}

/// Bring the main Omi window to the front, creating it if needed. Shared by
/// the menu-bar "Open Omi" item, the global Open Omi (formerly Ask Omi)
/// shortcut, and the floating bar's "Continue in Omi" affordance.
Expand Down
23 changes: 12 additions & 11 deletions desktop/macos/Desktop/Tests/AgentPillLifecycleTests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AppKit

Check warning on line 1 in desktop/macos/Desktop/Tests/AgentPillLifecycleTests.swift

View workflow job for this annotation

GitHub Actions / Hygiene

Large changed file

desktop/macos/Desktop/Tests/AgentPillLifecycleTests.swift is 1970 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Tests/AgentPillLifecycleTests.swift

View workflow job for this annotation

GitHub Actions / Desktop Swift Static Contracts

Large changed file

desktop/macos/Desktop/Tests/AgentPillLifecycleTests.swift is 1970 lines; consider splitting files over 800 lines.

Check warning on line 1 in desktop/macos/Desktop/Tests/AgentPillLifecycleTests.swift

View workflow job for this annotation

GitHub Actions / PR Metadata Preflight

Large changed file

desktop/macos/Desktop/Tests/AgentPillLifecycleTests.swift is 1970 lines; consider splitting files over 800 lines.
import VoiceTurnDomain
import XCTest

Expand Down Expand Up @@ -71,9 +71,9 @@
}

func testMainChatSpawnReceiptProjectsTheExistingFloatingPill() throws {
let pillSource = try agentPillSource()
let providerSource = try chatProviderSource()
let viewSource = try floatingControlBarViewSource()
let pillSource = try agentPillSource()

XCTAssertTrue(providerSource.contains("AgentPillsManager.shared.upsertSpawnedPill("))
XCTAssertTrue(providerSource.contains("producingJournalSurface: mainChatSurfaceReference()"))
Expand Down Expand Up @@ -373,9 +373,9 @@
XCTAssertFalse(windowSource.contains("resolveDelegationAndDispatch"))
XCTAssertTrue(windowSource.contains("await dispatchChatQuery("))
XCTAssertFalse(source.contains("AgentPillFollowUpRoutingPolicy"))
// The bar has no typed pill composer since typing moved to the app's chat:
// pill steering routes to the main app instead of parsing wording locally.
XCTAssertTrue(source.contains("(NSApp.delegate as? AppDelegate)?.openMainAppWindow()"))
// The bar's typed follow-up composer was retired (#10181): its "Continue
// in Omi" affordance routes to the main chat instead of spawning.
XCTAssertTrue(source.contains("openMainAppChat()"))
}

func testSubagentChatRendersMarkdownAndLargeBackHitTarget() throws {
Expand Down Expand Up @@ -835,13 +835,14 @@
}

func testSubagentFollowUpsOnlyContinueTheCanonicalSession() throws {
// omi-test-quality: source-inspection -- static contract: the bar's typed composer is gone; follow-ups continue only through the manager bound to the pill's canonical session.
// omi-test-quality: source-inspection -- static contract: the bar's typed composer is gone; the pill's only affordance opens the main chat.
let viewSource = try floatingControlBarViewSource()
let pillSource = try agentPillSource()

XCTAssertFalse(viewSource.contains("AgentPillFollowUpRoutingPolicy"))
XCTAssertTrue(pillSource.contains("guard pill.canonicalSessionId == sessionId else { return }"))
XCTAssertTrue(pillSource.contains("DesktopCoordinatorService.shared.continueAgent("))
// Typed steering from the pill was retired (#10181): the composer's only
// affordance opens the main chat, so no second send path can exist.
XCTAssertFalse(viewSource.contains("manager.continueAgent(from:"))
XCTAssertTrue(viewSource.contains("openMainAppChat()"))
}

func testSpawnAgentToolCallOpensSubagentChat() throws {
Expand Down Expand Up @@ -926,10 +927,10 @@
let inputSource = String(viewSource[inputRange.lowerBound..<inputEnd.lowerBound])

XCTAssertTrue(inputSource.contains(".beginVisibleMainQuery(message, fromVoice: false, animated: true)"))
// Archiving the previous exchange moved into the window alongside sizing;
// the view must not archive on its own.
// Archiving moved with the retired typed follow-up (#10181): the window's
// query paths own it now; the view must not archive on its own.
XCTAssertTrue(windowSource.contains("state.archiveCurrentExchange(using:"))
XCTAssertFalse(viewSource.contains("archiveCurrentExchange"))
XCTAssertTrue(windowSource.contains("state.archiveCurrentExchange(using: self.historyChatProvider)"))
XCTAssertTrue(viewSource.contains(".beginVisibleMainQuery(message, fromVoice: false, animated: true)"))
XCTAssertFalse(inputSource.contains("state.showingAIResponse = true"))
XCTAssertFalse(viewSource.contains("state.conversationSurface == .mainResponse || state.showingAIResponse"))
Expand Down
Loading
Loading