Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Expand Up @@ -36,13 +36,14 @@ public enum MistralCookieImporter {

public static func importSession(
browserDetection: BrowserDetection,
preferredBrowsers: [Browser] = [.chrome],
preferredBrowsers: [Browser]? = nil,
logger: ((String) -> Void)? = nil) throws -> SessionInfo
{
let log: (String) -> Void = { msg in logger?("[mistral-cookie] \(msg)") }
let installedBrowsers = preferredBrowsers.isEmpty
? mistralCookieImportOrder.cookieImportCandidates(using: browserDetection)
: preferredBrowsers.cookieImportCandidates(using: browserDetection)
let order = preferredBrowsers ?? mistralCookieImportOrder
let installedBrowsers = order.isEmpty
? Browser.defaultImportOrder.cookieImportCandidates(using: browserDetection)
: order.cookieImportCandidates(using: browserDetection)

for browserSource in installedBrowsers {
do {
Expand Down Expand Up @@ -73,7 +74,7 @@ public enum MistralCookieImporter {

public static func hasSession(
browserDetection: BrowserDetection,
preferredBrowsers: [Browser] = [.chrome],
preferredBrowsers: [Browser]? = nil,
logger: ((String) -> Void)? = nil) -> Bool
{
do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public enum MistralProviderDescriptor {
defaultEnabled: false,
isPrimaryProvider: false,
usesAccountFallback: false,
browserCookieOrder: ProviderBrowserCookieDefaults.defaultImportOrder,
browserCookieOrder: ProviderBrowserCookieDefaults.mistralCookieImportOrder,
dashboardURL: "https://admin.mistral.ai/organization/usage",
statusPageURL: nil,
statusLinkURL: "https://status.mistral.ai"),
Expand Down
11 changes: 11 additions & 0 deletions Sources/CodexBarCore/Providers/Providers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,15 @@ public enum ProviderBrowserCookieDefaults {
nil
#endif
}

/// Mistral Auto: Safari first (no Keychain prompt), Chrome next, then Firefox so users
/// signed in via Firefox or Firefox Developer Edition are detected without Manual mode.
/// Other Chromium forks stay on Manual import to avoid scanning the full default order.
public static var mistralCookieImportOrder: BrowserCookieImportOrder? {
#if os(macOS)
[.safari, .chrome, .firefox]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip invalid earlier browser sessions before retrying

When a user has an old or unauthorized Mistral ory_session_* cookie in Safari (or Chrome) and a valid session in a later browser, this new Safari → Chrome → Firefox order can make auto-import fail instead of falling through. MistralCookieImporter returns the first browser with any ory_session_*, and the invalidCredentials retry in MistralWebFetchStrategy clears the cache but calls resolveCookieHeader(false), which re-imports with this same order and picks the same bad source again, so Chrome/Firefox are never tried after the 401/403.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Updated to Chrome → Firefox → Safari in 525aabd so Chrome stays first — no regression from the original Chrome-only behavior. Firefox is still covered; Safari moved to last for Full Disk Access users.

The deeper issue (importSession returning the first browser with any ory_session_* cookie, without trying the next browser on 401/403) is a pre-existing design limitation shared by MiMo and other providers. Happy to address it in a follow-up if maintainers want a multi-session retry approach like ManusCookieImporter.importSessions.

#else
nil
#endif
}
}
11 changes: 11 additions & 0 deletions Tests/CodexBarTests/BrowserCookieOrderLabelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,16 @@ struct BrowserCookieOrderStatusStringTests {
#expect(ProviderDefaults.metadata[.copilot]?.browserCookieOrder == [.chrome])
#expect(ProviderBrowserCookieDefaults.copilotCookieImportOrder == [.chrome])
}

@Test
func `mistral cookie import order supports safari chrome and firefox`() {
let order = ProviderDefaults.metadata[.mistral]?.browserCookieOrder ?? Browser.defaultImportOrder
#expect(order == ProviderBrowserCookieDefaults.mistralCookieImportOrder)
#expect(order == [.safari, .chrome, .firefox])
#expect(order.first == .safari)
#expect(order.contains(.firefox))
#expect(!order.contains(.edge))
#expect(!order.contains(.arc))
}
#endif
}
1 change: 1 addition & 0 deletions docs/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ headers, source selection, provider ordering, and token accounts are stored in `

## Mistral
- Session cookie (`ory_session_*`) from browser auto-import or manual `Cookie:` header.
- Cookie import order: Safari → Chrome → Firefox. Users signed in via Firefox or Firefox Developer Edition are detected automatically; other Chromium forks use Manual mode.
- CSRF token (`csrftoken` cookie) sent as `X-CSRFTOKEN` for billing and Vibe usage requests.
- Domains: `admin.mistral.ai` for API billing and credit balance, and `console.mistral.ai` for optional Vibe subscription usage. Console requests forward only `csrftoken` and `ory_session_*`; all other admin cookies stay origin-bound.
- Reads monthly usage and pricing from the billing usage endpoint, plus credit balance from the billing credits endpoint, using the Mistral web session.
Expand Down