* Add a mobile native-chat opt-in so users pick terminal vs chat once
Mirror the notifications one-time opt-in for the native-chat default view.
After pairing, a full-screen modal (modeled on notification-opt-in) lets the
user choose whether supported agent sessions open in the terminal or in native
chat, then persists the choice to the existing orca:defaultSessionView key.
- Expose readDefaultSessionViewPreference() (tri-state; absent key = undecided)
so the gate can prompt exactly once; loadDefaultSessionView() is unchanged.
- shouldPresentSessionViewOptIn() gates the screen; the home focus effect shows
it after the notification opt-in.
- Settings -> Native chat toggle (already shipped) remains the recovery path.
* fix(mobile): preserve onboarding flow after pairing
* refine mobile session view opt-in copy
* Unify mobile onboarding prompts
* feat(native-chat): add mobile default-view toggle for native chat
Adds a per-device "Native chat" setting on mobile (Settings → Native chat)
that controls whether supported agent sessions open in the native chat view
or the raw terminal. Default stays terminal; flipping it on makes eligible
sessions render as native chat, mirroring the desktop default-view control.
- New orca:defaultSessionView preference (loadDefaultSessionView/save).
- Per-tab chat set refactored into a tri-state override map so a session can
be pinned to terminal or chat regardless of the default; the legacy array
format migrates to chat overrides on load.
- useMobileSessionViewMode resolves each tab as override ?? default and
reloads the default on focus so a Settings change applies without remount.
- Toggle/action-sheet builders take an isTabChatView predicate.
* chore(skills): refresh skill bundle manifest for v1.4.144-rc.2
The v1.4.144-rc.2 release bump left resources/skills/current-manifest.json
pointing at rc.1, so verify:skill-bundle-manifest fails on any branch that
reaches it. Regenerated (appVersion only; no skill content changed) to
unblock CI. Unrelated to the native-chat toggle in this PR.
* fix(native-chat): harden mobile view preferences
* fix(native-chat): harden default view persistence
* fix(native-chat): serialize mobile view persistence
* fix(native-chat): reconcile failed mobile view saves
* fix(native-chat): fail closed on unreadable view overrides
* fix(native-chat): honor fail-closed view races
The rpc-client has always emitted a detailed connection lifecycle log
(dials, timeouts, close codes, handshake steps, retries) via onLog, but
only the pairing screen wired it up — for long-lived host connections
everything went to console.log, invisible to users. Debugging reports
like #7824/#6928 meant asking reporters for facts the app already knew.
- connection-log-buffer: bounded (200/host) module-level ring buffer with
referentially-stable snapshots for useSyncExternalStore; survives
client swaps and provider remounts.
- client-context: wire onLog for every shared host client.
- connection-log screen: live per-host log (reuses the pairing
ConnectionLog component), host picker, and a Copy Diagnostics button
that bundles app/platform versions, endpoint (flagged if Tailscale),
state, attempt count, last-connected, and the event log into one
shareable blob.
- troubleshoot: 'View connection log' entry point.
Co-authored-by: Orca <help@stably.ai>
scheduledNotificationsByHostAndNotificationId (mobile-notifications.ts)
retained one entry per scheduled desktop notification. The key embeds
notificationId, which carries a per-completion timestamp
(buildAgentNotificationId), so every agent-task-complete inserts a new,
never-reused key. Entries are removed only when the desktop sends a
matching dismiss — which a remote mobile user (not sitting at the
desktop) frequently never receives — so the module-level map grew for
the app's whole lifetime. Small per entry, but genuinely unbounded.
Fix: bound the map to the 256 most-recent SETTLED entries (never evict
one mid-schedule). A settled entry only retains a small identifier used
for later programmatic dismissal, which is unnecessary for long-past
completions, so eviction has no user-visible effect.
Also FIFO-cap RootLayout's handledNotificationIdsRef tap-dedup Set
(RootLayout never unmounts, so it otherwise grew one id per tapped
notification forever).
Test (red->green): with the cap at 1, scheduling a second notification
evicts the first, so a later dismiss for the evicted id is a no-op while
the retained one still dismisses; without the cap the old entry survives.
## Summary
- add mobile Settings → Browser → Open terminal links
- route terminal HTTP(S)/OSC 8 link taps to either the streamed Orca browser or the phone browser
- keep existing file-path tap behavior intact
## Verification
- pnpm --dir mobile test
- pnpm --dir mobile lint
- pnpm --dir mobile exec tsc --noEmit
- git diff --check
* release: v1.4.48-rc.0 [rc-slot:2026-06-05-15]
* release: v0.0.1-rc.0 [rc-slot:2026-06-06-03]
* fix(mobile): respect Android device rotation lock instead of forcing rotation
The app rotated on Android even when the system auto-rotate lock was on.
Expo's top-level `orientation: "default"` emits `android:screenOrientation="unspecified"`,
which auto-rotates on many devices regardless of the lock, and the
`react-native-screens` `orientation: 'all'` screenOption mapped to FULL_SENSOR,
which forces rotation outright.
react-native-screens exposes no orientation value that honors the rotation lock
(even 'default' calls setRequestedOrientation(UNSPECIFIED) at runtime, overriding
the manifest). The Android value that respects the lock is "fullUser", which must
come from the manifest. So:
- Add an `android-respect-rotation-lock` Expo config plugin that sets the main
activity to `android:screenOrientation="fullUser"` (honors the lock; allows all
orientations when unlocked, matching iOS).
- Remove the runtime `orientation` screenOption so the manifest value governs.
iOS behavior is unchanged (UISupportedInterfaceOrientations still lists all four).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>