mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 08:03:22 +00:00
2d4eab2c592adb0fd6396d6312394dddc6be6bc2
211
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8e17e75a3d |
fix(mobile): harden terminal height refit (follow-up to #8647) (#8707)
* fix(mobile): harden terminal height refit (follow-up to #8647) Addresses review feedback on #8647: - Defer height refits while the keyboard is visible and coalesce every skipped layout change into one correction after the keyboard closes, via a pure reducer. Prevents an over-fit that settles with the keyboard up from surviving (on iOS the edge-to-edge keyboard doesn't change the frame height on close, so there was no later event to re-trigger it). - Drive height layout callbacks imperatively (notifyTerminalFrameHeight) instead of setState, so height-only layout bursts no longer re-render SessionScreen. - Cache the updateViewport capability (method_not_found -> unsupported): old desktops now get one unsupported probe then legacy resubscribe, instead of one probe per refit. Reconnect resets the cache so an upgraded desktop is re-detected. No server schema or subscription-protocol changes; desktop-first stays compatible. Tests: 703 mobile terminal/session pass; tsc, oxlint, formatting clean. * fix(mobile): re-check keyboard when a deferred height refit fires Close a race in the keyboard-deferral: a height refit deferred at keyboard-close arms a 150ms debounce timer, and if the keyboard reopens inside that window the timer still fired and reflowed the PTY mid-keystroke. The timer callback now re-consults the reducer (new `refit-committed` event) when the armed refit is height-originated: if the keyboard is visible again it re-defers (pending) instead of reflowing, and runs on the next keyboard close. Scoped via a height-originated flag so width/rotation and the forced reconnect/foreground re-asserts stay unguarded and always run. Tests: reducer coverage for the reopen-during-debounce re-defer + a wiring assertion; 705 mobile terminal/session pass; tsc, oxlint clean. |
||
|
|
e04ca97dc2 |
fix(mobile): re-fit terminal PTY when the frame height settles (#8647)
* fix(mobile): re-fit terminal PTY when the frame height settles A freshly-created agent terminal fits its PTY to rows = floor(frameHeight / cellHeight) before the accessory/live-input dock has laid out, so the frame is briefly too tall and the PTY gets too many rows. Claude/Codex pin their input box to the bottom of the grid, so those extra bottom rows — the input box and status lines — render behind the dock and you can't see what you're typing. Leaving and re-entering the workspace worked around it by re-measuring against the settled layout. The refit hook previously re-fit only on width changes and deliberately ignored height-only changes, so the over-fit was never corrected. Track the measured frame height and re-fit on its change too, mirroring the width path. Safe because Expo SDK 55's edge-to-edge IME overlays instead of resizing, so the frame height doesn't change on keyboard toggle and the PTY is never reflowed while typing; the refit's row-count guard makes sub-row jitter a no-op. * fix(mobile): guard height refit against IME resize; test the decision Address review on #8647: - Extract shouldRefitOnFrameHeightChange (pure) and gate the height refit on keyboard-visible, so an IME that resizes the window (Android adjustResize) can never reflow the PTY while typing — no longer relies on the edge-to-edge no-resize assumption alone. - Add a behavioral test for the decision helper (height transition, same-value no-op, keyboard-open skip) instead of only source-string assertions. - Trim the added comments to 1-2 lines per AGENTS.md. |
||
|
|
8e0977d295 |
fix(mobile): resync worktree list + idempotent notification replay on reconnect (#8498 #8129)
Take-over of #8605 (issue #8591). Ships #8498 (worktree resync + pull-to-refresh + cache write-through) and #8129 (idempotent notification replay on reconnect). Fixes the original PR's field mismatch (seq vs notificationSeq) and adds the missing notifications.getMissedSince mobile RPC allowlist entry. #6784 and #4500 held back to avoid conflicting with the relay work (#8536). Co-authored-by: Brandon Bennett (@branben). |
||
|
|
e53a0a0158 |
fix(mobile): handle base_not_on_remote in PR-create block message (#8676)
#8651 added 'base_not_on_remote' to HostedReviewCreationBlockedReason but did not add a matching case to getMobilePrCreateBlockMessage, leaving the switch non-exhaustive so the function can fall through and implicitly return undefined against its string | null type. That fails `tsc` on main (verify does not run on direct pushes, so it surfaced on PR merge commits). Add the missing case with actionable copy consistent with the desktop create-time message. |
||
|
|
e8831108ad |
fix(mobile): host Create Workspace drawers in one Modal so dropdowns open (#8642)
* fix(mobile): host Create Workspace drawers in one Modal so dropdowns open
The Create Workspace form and each of its pickers (repository, agent,
source, trust) rendered their own native Modal. Opening a dropdown
dismissed the form's Modal and presented the picker's Modal in the same
beat, but iOS cannot present one native modal while another is being
dismissed, so the incoming picker was dropped and the form collapsed —
the repository and agent dropdowns did nothing. On slower real devices
the race is lost every time; on the simulator it was intermittent.
Host the whole flow in a single persistent native Modal via a new
BottomDrawerModalHost + React context. When a BottomDrawer detects it is
inside the host it renders its sheet without its own Modal, so switching
from the form to a picker is an in-window view swap instead of a native
modal present/dismiss — no race. Every BottomDrawer outside this flow is
unchanged (the context defaults to off), and the picker wrappers need no
changes because the context propagates through them.
* fix(mobile): route native back through the setup-trust guard
The BottomDrawerModalHost onRequestClose sent every non-form view through
transitionDrawer('form'). For the setup-trust prompt that bypassed
closeSetupTrust(), whose guard keeps the prompt up while a trust decision
or workspace creation is in flight and which clears setupTrustPrompt.
Route drawerView === 'trust' back presses through closeSetupTrust() so
the guard and cleanup run.
|
||
|
|
c408a3d852 |
feat(mobile): show usage reset countdown on accounts screen (#7954)
* feat(mobile): show usage reset countdown on accounts screen
Surface the rate-limit reset time ("5h resets in 3h 54m · 7d resets in
6d 7h") under the usage bars on the mobile accounts screen, matching the
desktop status-bar tooltip copy. The resetsAt timestamps already arrive
in the accounts.subscribe snapshot; this only adds the presentation.
Claude-Session: https://claude.ai/code/session_01FvjvCsc9QoyQALqvxkvDqQ
* docs(mobile): JSDoc for new usage reset selectors
Claude-Session: https://claude.ai/code/session_01FvjvCsc9QoyQALqvxkvDqQ
* refactor(mobile): per-bar reset countdown instead of combined line
Drop the redundant "5h/7d" prefixes — each countdown now renders under
its own bar ("Resets in 3h 54m"), matching the desktop tooltip copy
exactly.
Claude-Session: https://claude.ai/code/session_01FvjvCsc9QoyQALqvxkvDqQ
* Extract shared reset-countdown formatter for desktop and mobile
- Move duration/countdown formatting out of tooltip.tsx into
src/shared/rate-limit-reset-format.ts so mobile's account-usage-state
can reuse it instead of a duplicated copy (with tests).
- Re-export formatResetCountdown from tooltip.tsx to avoid touching
existing import paths.
- Resend the pairing deep link once more in start-emulator.mjs since
the first can arrive before the Expo app's JS router is ready.
---------
Co-authored-by: kaynan <kaynan.camargo@terceiro-sky.com.br>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
|
||
|
|
53a09afbef |
feat(mobile): match desktop's Smart workspace source picker exactly (#7985)
* feat(mobile): start a workspace from a branch, issue/PR, or Linear ticket Unify mobile workspace creation with desktop. The "+" Create Workspace modal now has a primary "Start from" field that opens a tabbed search drawer (Branch · GitHub · GitLab · Linear), letting a user start a workspace from an existing/new git branch, a GitHub issue/PR, a GitLab issue/MR, or a Linear ticket — in addition to the default blank workspace. No new backend is required: the search RPCs (github.listWorkItems, gitlab.listWorkItems, linear.searchIssues/listIssues, repo.searchRefs) and the worktree.create linked-item params were already used by the mobile Tasks screen. This surfaces them in the create flow, reusing the existing pure modules (buildTaskWorkspaceCreateParams, shouldResolveHostedReviewStartPoint, filterAvailableTaskProviders). Details: - New pure modules: workspace-source-selection, use-workspace-source-search, source-workspace-create, worktree-create-retry, blank-workspace-create (the blank/retry path extracted from the modal for reuse + line budget). - New UI: WorkspaceSourcePickerDrawer (+ row) and SetupHookTrustDrawer (extracted from the modal). - Older paired desktops (missing the mobile.tasks.v1 capability) degrade to Branch + Blank only; GitLab/Linear tabs appear only when available. - GitHub/GitLab sources pin their repo; switching repos resets the source. PR/MR sources resolve their base branch at create time; SSH repos gate search until connected (Linear search is repo/SSH-independent). * fix(mobile): hydrate settings/trust before availability probes settle Review fixes for #7985: setTrustedOrcaHooks/setRuntimeSettings no longer wait on status.get/preflight.check/linear.status (a first-open preflight.check can take seconds, widening the spurious setup-trust re-prompt window). Also adds param-parity tests for createBlankWorkspace and a GitLab MR base-resolve test. * feat(mobile): match desktop's Smart source picker exactly Rework the mobile create-workspace source picker to be a faithful port of desktop's Smart picker instead of the earlier divergent "Start from" drawer. The mobile field is now the workspace-name input AND the source search, with the exact desktop tabs — Smart · GitHub · Linear · GitLab · Branch · Name. "Smart" fans out across GitHub + GitLab + Linear + branches, prepends a "Use '<name>'" row, and resolves pasted URLs / #123 / STA-42 to exact items (with a cross-repo switch prompt). Selecting a source shows a pill and moves the editable name into Advanced. The invented "Blank workspace" concept is removed — the neutral state is just a typed/empty name (blank submit still yields a creature name). DRY: the pure desktop logic (smart-workspace-source-results, -command-value, github-links, gitlab-links, work-item-link-query-bounds, github-work-item-identity) moves to src/shared/new-workspace/ with re-export shims at the old renderer paths, so both renderer and mobile share one implementation. composer-branch-selection and workspace-name were already shared and are reused directly. Two read-only lookup RPCs are allowlisted for mobile so pasted GitLab URLs and cross-repo GitHub URLs resolve to exact items (github.workItemByOwnerRepo, gitlab.workItemByPath). New mobile modules are split for max-lines: use-mobile-composer-source (selection state + desktop-parity handlers, PR/MR base resolve), use-smart-workspace-source + smart-source-fan-out/-search-requests/-paste-intent (RPC orchestration), composer-linked-work-item / work-item-lookup-text / mobile-smart-source-modes (pure logic), and SmartWorkspaceSourceField/Drawer/Row + SmartWorkspaceAdvancedFields. Replaces WorkspaceSourcePickerDrawer/Row, workspace-source-selection, use-workspace-source-search, and MobileWorkspaceNameInput. Reviewed by three adversarial agents + re-reviewed after fixes: GitHub search now returns issues AND PRs (not issues-only), Linear defaults to assigned, create-branch preserves slashy names, cross-repo PR base resolves against the item's own repo, displayName is suppressed for user-edited names, and the smart-mode GitHub fan-out respects availability. tsc/oxlint/max-lines-ratchet clean; 1328 mobile tests pass. * fix(mobile): keep smart source drawer fully visible * refactor: share workspace creation behavior across clients * fix: address workspace creation review findings --------- Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local> |
||
|
|
371d71a262 |
fix(agents): bundle agent icons instead of loading them from Google's favicon service (#8451) (#8474)
* fix(agents): bundle agent icons instead of loading them from Google's favicon service (#8451) Agents without a hand-authored SVG glyph loaded their icon live from Google's favicon service (www.google.com/s2/favicons). That service is unreachable in some regions (e.g. mainland China) and offline, so ~23 agent icons rendered as broken images on the agent settings page, the terminal title bar, and the status bar. Bundle each favicon as a build-time asset under resources/agent-icons/ and render it via a new agent id -> URL map (agent-favicon-assets.ts). The remote favicon service now only serves as a last-resort fallback for any future agent that lacks a bundled icon. Follows the same pattern as #7373, which bundled the OpenCode mark. * fix(agents): bundle mobile agent icons too; drop dead omp faviconDomain (#8451) Mobile had the same offline/region bug: MobileAgentIcon rendered every non-glyph agent from Google's favicon service. It actually affected more agents than desktop, since mobile lacks hand-authored glyphs for Copilot, OpenCode, Kilocode, Droid, and OpenClaude — all fell through to the favicon path. Bundle the 28 favicon-path icons under mobile/assets/agent-icons/ and render them via a Metro static require() map (mobile-agent-icon-assets.ts). A node-env invariant test asserts every favicon-path agent ships a bundled PNG and is wired into the map. Also remove omp's vestigial faviconDomain from the desktop catalog — omp renders the hand-authored OmpIcon glyph, so the favicon fallback was never reachable. * refactor(agents): share one set of bundled agent icons between desktop and mobile Desktop and mobile each shipped their own copy of the favicon PNGs (23 + 28, with 23 byte-identical duplicates). Consolidate them into a single source of truth at src/shared/agent-icons/, reachable by both bundlers: - Desktop (Vite) imports them via `?url`. - Mobile (Metro) requires them; Metro already watches src/shared via metro.config.js sharedRoot, so no config change is needed. The two per-platform maps stay separate because the import syntax differs (`?url` string vs `require()` asset ref), but they now point at the same files. Verified with a real `expo export`: Metro bundles all 28 shared icons from src/shared/agent-icons. |
||
|
|
fb15d647c6 | refactor: remove state-only React effects (#8437) | ||
|
|
7fe6742b8b |
fix(pr-comments): let users mark comment authors as bots for the Humans/Bots filter (#7598)
* fix(pr-comments): let users mark comment authors as bots for the Humans/Bots filter Some review bots post from regular user accounts that defeat both provider bot metadata and login heuristics, so their comments were misclassified as human. Adds a persisted prBotAuthorOverrides setting with a "Mark author as bot" comment action, applied consistently across desktop and mobile. Fixes #7597 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(pr-comments): address review feedback on bot-author overrides - Cap sanitized prBotAuthorOverrides at 500 entries so malformed payloads can't bloat GlobalSettings or slow comment classification - Reuse the shared normalizePRCommentAuthorLogin in isBotPRComment on desktop and mobile instead of duplicating the normalization inline - Pass botAuthorOverrides from CommentRow to CommentMoreMenu instead of re-subscribing per menu instance - Re-fetch mobile bot-author overrides alongside each PR refetch so they don't stay a stale one-shot snapshot for the whole session Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(pr-comments): harden bot author override sync * fix(pr-comments): bound and recover override updates * fix(pr-comments): merge overrides from canonical settings * fix(pr-comments): make bot override updates atomic * fix(pr-comments): surface rejected bot overrides * fix(i18n): translate bot override warning * fix(i18n): translate bot author actions --------- Co-authored-by: Dzmitry Bachko <dbachko@users.noreply.github.com> Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local> |
||
|
|
92ea918b63 |
fix(mobile): don't orphan pairing tokens or leak rejections on host remove (#8317) (#8354)
Prod-release-scan P1+P2 from v1.4.137-rc.1 mobile host-remove. P1: Host remove could orphan a SecureStore pairing token with no Settings retry when BOTH the durable pending-queue write failed AND the native delete rejected/stalled. recordCleanupIntent swallowed the queue-write failure, so the only recovery handle for the failed keychain delete was silently lost. Now scheduleHostCredentialCleanup keeps a session-scoped in-memory fallback handle when the durable write fails, so Settings still surfaces the pending cleanup and offers a retry; confirmNativeCleanup clears the fallback if the native delete later lands. removeHost stays non-blocking on the keychain (freeze fix intact). P2 (updateLastConnected): the fire-and-forget `void updateLastConnected(...)` call site threw on unreadable storage, producing an unhandled rejection. updateLastConnected now swallows unreadable-storage failures internally since it's a best-effort timestamp. P2 (soft-read): loadPendingHostCredentialCleanup now reports storageUnreadable instead of pretending the queue is empty, and Settings surfaces a "couldn't check cleanup status — retry to be safe" affordance rather than hiding the section when the durable queue can't be read. Tests: dual-fault fallback + no-clobber, storageUnreadable reporting, fallback self-heal on late delete success, and updateLastConnected non-throw. |
||
|
|
7d9f6cb205 |
Remove host on mobile freeze the app (#8317)
* Add host removal lifecycle safeguards and credential cleanup retry UI - Sequence host removal so metadata commits before the client socket closes, avoiding a stranded host when storage fails, and add a cancellable open-registry to stop races between host-client opens and closes/unmounts. - Queue AsyncStorage host-list mutations (rename/removal/lastConnected) to prevent concurrent writers from clobbering each other's changes. - Track keychain credential cleanups that fail or time out as durable pending intents, surfaced with a manual retry affordance in Settings. * Fix host removal error handling to reopen confirm dialog and alert user Previously a failed host removal silently closed the confirm dialog, leaving the host listed with no feedback and no easy retry path. Now the confirm modal reopens and an alert surfaces the failure so the user can retry. * test: reconcile settings tests with universal right-click paste and promoted worktree symlinks Merging main surfaced two semantic conflicts against this branch's tests: - #8322 exposed right-click paste on every platform, so the settings navigation metadata now indexes it even when only the terminal host is Windows. Update the stale assertion accordingly. - #8318 promoted APFS worktree shared paths by dropping the experimentalWorktreeSymlinks gate, so WorktreeSymlinksSection now always mounts inside RepositoryPane and reads window.api.fs. Stub a minimal renderer fs bridge in the pane test, matching the AppearancePane pattern. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
8ced4b9e4b |
Fix stale terminal panes after backgrounding by retrying deferred foreground recovery (#8198)
* Fix stale terminal panes after backgrounding by retrying foreground reco - Foreground recovery was skipping the replay when resume landed mid-reconnect (socket typically dies after 60-80s backgrounded), leaving WKWebView panes blank until a manual tab switch. Recovery now returns a 'deferred' outcome and the session screen retries it once connState flips back to connected. - Fix a related race where a newly created tab's web-ready subscribe could be skipped if a lagging session-tab snapshot reset activeHandleRef before the subscribe fired; track the intended active handle separately. * Fix stale pending terminal handle outliving a failed create Clear pendingActiveTerminalHandleRef when terminal creation returns no handle, since web-ready subscribe logic gates on this ref being active and would otherwise see a stale value. |
||
|
|
fd6805a299 |
Fix mobile terminal query reply authority (#8227)
* Fix mobile terminal query reply authority * fix(terminal): harden mobile query reply handoffs * fix(terminal): exclude passive mobile query responders * fix(terminal): gate mobile query replies on host capability Older hosts strip terminal.send's inputKind (zod drops unknown keys), so a forwarded xterm reply would land as ordinary floor-taking shell input. Hosts now advertise terminal.query-reply-input.v1 via status.get and mobile drops replies unless the host advertises it (pre-fix behavior). Also documents the bounded desktop-to-mobile handoff double-reply residual. Co-authored-by: Orca <help@stably.ai> * fix(terminal): advance snapshot seq across recovery snapshots The pending-overflow recovery loop trims buffered output against recovery.seq while query replay and boundary strips kept using the initial snapshot seq. Unreachable under today's control flow (no await separates the initial-overflow consume from the loop), but the stale seq would silently drop covered query replies if that ordering ever changes. Track the seq that actually covered the buffered chunks. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
20ebe858af |
fix(mobile): bump Android versionCode to 7 for 0.0.27 (#8249)
* fix(mobile): bump Android versionCode to 7 for 0.0.27 Android 0.0.26 shipped with versionCode 6. Align Android on marketing version 0.0.27 with a higher versionCode so side-loaded upgrades install. * test(mobile): expect direct cmd syntax for live Windows resume Resume commands are typed into the host terminal; when that shell is already cmd, wrap with cmd /d /s /c is wrong. Align the mobile unit test with shared buildAiVaultResumeShellCommand behavior. |
||
|
|
2e48495273 |
Prevent mobile screen locking during voice dictation. (#7746)
* Prevent mobile screen locking during voice dictation Integrate expo-keep-awake to prevent the mobile device from locking or sleeping while a voice dictation session is active. - Modularize useMobileDictation logic into separate helper files for keep-awake, audio chunking, session state, and desktop startup. - Acquire keep-awake lock only after successfully establishing a desktop session to avoid locking on stale start attempts. - Release the keep-awake lock on all completion, cancellation, error, and unmount paths. - Add source invariant unit tests to verify keep-awake ownership and strict cleanup ordering. * serialize keep-awake operations and avoid stale dictation start races - Implement a global execution queue and tag tracking for keep-awake operations to prevent concurrent races and stale deactivations. - Track failed native deactivations and retry them when a replacement hook owner mounts or starts a new dictation session. - Ensure stale or canceled desktop dictation starts do not reset the UI state or propagate outdated start/keep-awake failures. - Reuse the audio chunk queue wiring in useMobileDictation to avoid allocating new closure objects on the high-frequency microphone path. - Add comprehensive unit tests for the keep-awake and desktop start hooks. * Commit native recording during dictation session startup Commit native recording in the same continuation as the final session stale check. This prevents a queued cancellation from resurrecting the microphone recording after cleanup has already run. If microphone initialization fails or throws, acquired resources (like keep-awake locks and the remote desktop session) are properly rolled back. * Make keep-awake acquisition best-effort with a bounded startup timeout - Recording start no longer blocks (or fails) on keep-awake acquisition: a hung or failing native call is capped at a short budget and logged instead of delaying or aborting dictation. - Add native-call timeouts, orphan-tag tracking, and reacquire/drain logic in mobile-dictation-keep-awake.ts so Activity recreation on Android and stale tags no longer wedge the keep-awake queue. - Add useMobileDictationForegroundKeepAwake to refresh the wake tag on Android foreground and retry failed refreshes/deactivations. - Hold the wake tag through chunk drain and the finish RPC so a screen lock can't suspend the app before the transcript arrives, and keep cleanup running even if native recording shutdown throws. - Loosen expo-keep-awake to a caret range to unblock the patch pulling in these native fixes. * Fix cancellation races in mobile dictation keep-awake handling - Run wake-lock release and dictation cancel concurrently on stale starts so a hung acquisition no longer delays the native cancel - Guard foreground reacquire retries with a run token so a stale retry chain can't deactivate a wake lock reacquired by a newer AppState transition * Update source invariant test for concurrent stale-start cleanup Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
026516f323 |
the-sandbox-flag-is-2 (#8236)
* Fix mobile source control drawer overflow and branch-compare state loss - Render BottomDrawer in a native Modal so it covers the full viewport even when mounted inside a ScrollView. - Move the conflict/Abort row onto its own line so it never overflows the branch card, and enlarge the Abort hit target. - Show the committed-on-branch footer even when the changed-files SectionList has no sections, since RN skips ListFooterComponent for empty sections. - Stop branch-compare state from collapsing to idle/error on transient base-ref resolution failures when a ready result should be preserved. * Add mobile source control drawer reload screenshot Attaches an evidence screenshot for the mobile source control drawer overflow / branch-compare state loss fix. * Remove stray temp screenshot file Accidentally committed debug artifact from mobile source control drawer work; not needed in the repo. |
||
|
|
4f7fe78921 |
perf(mobile): stop polling live worktree names (#8051)
* perf(mobile): replace worktree name polling with events * fix(worktrees): push rename invalidation to remote clients worktrees:updateMeta deliberately skips the renderer notifier (PR #209), but paired mobile clients no longer poll for titles, so a manual rename would never reach them. Emit the remote-only worktreesChanged client event (with resolved-cache invalidation), gated on displayName so per-click isUnread writes stay event-free. Co-authored-by: Orca <help@stably.ai> * test(worktrees): add missing runtimeStub type member for typecheck Co-authored-by: Orca <help@stably.ai> * fix(worktrees): derive rename event repoId with the shared non-throwing parser getRepoIdFromWorktreeId matches the mobile client's event filter exactly and cannot throw after the meta write already persisted. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
5e100914d3 |
fix(mobile): recover terminal WebView/WebGL/viewport/theme state after iOS resume (#8196)
* fix(mobile): recover terminal state after iOS resume * Refactor terminal record merge to extract snapshot-reconciliation helper Split the inline merge logic in mergeTerminalRecordsByCurrentOrder into a named mergeTerminalSnapshotWithKnownRecord function for clarity, preserving the existing behavior of keeping the last known theme when a snapshot omits it. |
||
|
|
5a8078e755 |
Improve search box on mobile (#8187)
* Redesign mobile search field as a shared, raised component - Extract MobileSearchField from duplicated Search icon + TextInput + clear button markup in worktree list and tasks screens into a reusable component - Give the field a raised bgRaised shell with focus/disabled states so it reads as a tappable control instead of blending into panel chrome - Fix delayed autoFocus via InteractionManager + timeout so the keyboard reliably appears after the search bar opens - Preserve per-screen clear behavior (preset/query fallback for GitHub, project-view filter) via configurable showClear/onClear props * Simplify GitHub project search state checks and fix stuck clear button - Extract `isGithubProjectSearch` to dedupe repeated `provider === 'github' && githubMode === 'project'` checks - Fix showClear so an explicit empty applied override doesn't leave the clear button visible forever |
||
|
|
60c26af8b8 |
fix(linear): preserve mixed-version RPC filtering compatibility (#8192)
* fix(linear): guard mixed-version RPC filtering * fix(linear): surface filter capability failures correctly Prevent capability checks from pinning to rejected compatibility cache entries, and rethrow typed attribute-filter unsupported errors from the Linear store so TaskPage can show an upgrade message instead of an empty filtered list. * fix(runtime): refresh cached capability verdicts * test(linear): mock isLinearIssueAttributeFilterUnsupportedError Prevents the invalidation slice test from failing after the runtime client gained this export, which was otherwise undefined in the mock. * Fix cold-cache capability probes firing duplicate status.get calls Coalesce concurrent status.get requests for the same environment by publishing the in-flight probe to the compatibility cache before awaiting it, so parallel capability checks share one RPC call. On failure, drop the cache entry immediately since this probe always re-fetches and must not leave a stale cached verdict. |
||
|
|
b9ccc0d17f |
Flip usage bars from percent-remaining to percent-used (#8167)
Aligns status bar, tooltip, popover mocks, and mobile usage bars with the Claude/Codex harness convention (consumption meters) so a fresh account reads empty/green and a depleted one reads full/red, instead of the inverted "left" framing that misread as "full = exhausted". |
||
|
|
8f6e44ed53 |
Show agent session history on mobile (#6786)
* Show agent session history on mobile Bring the desktop "Agent Session History" panel to Orca Mobile as a per-worktree screen: browse past agent transcript sessions across the host with scope tabs (Workspace/Project/All), search, grouping, session cards, and tap-to-read message previews. The transcript scan previously ran only over Electron IPC, so mobile could not reach it. Expose it over the runtime RPC protocol mobile already speaks (aiVault.listSessions) so the scan runs on whichever host owns the transcripts — correct for local and SSH/remote hosts. Both the desktop IPC handler and the new RPC method share one cache, so opening the desktop panel and the mobile screen never double-scan. The pure filter/group/display logic is lifted into /shared (the renderer re-exports it) so the standalone mobile package can reuse it. Mobile narrows scoped tabs client-side by cwd path-prefix because the host scan treats scope paths as a widening union. Resume-from-mobile is intentionally a follow-up. * Fix mobile agent history list rendering and RPC authorization - Authorize aiVault.listSessions in the mobile RPC allowlist so the mobile client's call is not rejected before dispatch (without this the screen could never load sessions at runtime). - Name each SectionList section's rows `data` (the field React Native reads) instead of `cards`, fixing a type error and silent empty-section rendering. * Address review feedback on agent session history - Match quoted repo:/path: search operator values so labels and paths with spaces match (e.g. path:"/Users/ada/My Project"). - Hold a scoped tab in loading until the worktree list resolves instead of firing an unscoped fetch that briefly shows unrelated host history; proceed once loaded even if the worktree is absent (no stuck spinner). - Clear cached host capabilities on disconnect/host-switch and failed status.get so a capability-gated action can't linger for a host that doesn't support it. - Cover the real OrcaRuntimeService codex-home forwarding path and the quoted-operator parser with tests. * Hide redundant mobile current worktree badges Co-authored-by: Orca <help@stably.ai> * Resume agent sessions from mobile history (#6969) Co-authored-by: Orca <help@stably.ai> * Adapt merged seams to main's lint and reply-sender hardening Co-authored-by: Orca <help@stably.ai> * Cap mobile project-scope paths to the aiVault RPC bound Co-authored-by: Orca <help@stably.ai> * Share the aiVault scopePaths bound between the RPC schema and mobile Co-authored-by: Orca <help@stably.ai> * Guard shared AI Vault inflight cleanup against concurrent key replacement The extracted cache module's .finally() cleared inflight tracking unconditionally, dropping the if (inflightKey === key) guard its sibling outer cache kept: an older scan resolving after a different-key scan replaced the tracking would null the newer scan's dedup slot, so a re-request started a duplicate transcript rescan. Mirrors the sibling guard; the regression test flushes a macrotask so a reverted guard fails fast on the call count instead of hanging. Co-authored-by: Orca <help@stably.ai> * Harden aiVault.listSessions contract and gate mobile header entry on capability - Clamp scopePaths (64) instead of rejecting, cap limit at 2000, and make executionHostId optional so mobile can omit it; restamp per caller. - Retain successful mobile terminal-create mutation ids for 60s so resume retries dedupe after transient socket drops. - Gate the session-header Agent History action on the aiVault.v1 capability (mirrors the host-list action) so old hosts never show a dead-end entry. - Fix stale contract comments (scopePaths clamp semantics; filters move includes quoted repo:/path: operator parsing). * Add subagent field to session test fixtures after #7423 merge AiVaultSession.subagent became required on main; the five fixtures added on this branch predate it. Top-level scanned sessions carry null. --------- Co-authored-by: Orca <help@stably.ai> Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local> |
||
|
|
3a3e33f14b |
The --sandbox flag (or terminal sandboxing) in Google Antigravity is a (#8017)
Here is a summary of how the sandbox behaves on your macOS system: ### ⚙️ How it Works When `--sandbox` is enabled (either via the launch flag or the `enableTerminalSandbox` setting in your `settings.json`), terminal commands run inside a lightweight containment boundary: - **macOS Native Isolation**: It utilizes macOS's native `sandbox-exec` utility to restrict system calls, network sockets, and directory access. - **Secure File Boundaries**: File system writes are locked down to designated safe zones (such as your designated workspace or scratch directory). Access to critical system paths, private user data, and external network resources is restricted. --- ### 🛡️ Active Permissions for this Session In this current session, the permission model is configured as follows: | Action / Resource | Permission Status | Details / Paths | | :--- | :--- | :--- | | **Command Execution** | ✅ **Allowed** | Terminal command execution is enabled. | | **File Reads (Allowed)** | ✅ **Allowed** | `/scratch`, `/browser_recordings`, `/html_artifacts`, `/knowledge`, `/worktrees`, `/skills`, `/builtin` | | **File Writes (Allowed)**| ✅ **Allowed** | `/scratch`, `/browser_recordings`, `/html_artifacts`, `/knowledge`, `/worktrees` | | **Sensitive Files** | ⚠️ **Ask** | `.env`, `.npmrc`, `.vscode`, `.git-credentials`, etc. | | **Root/App Settings** | 🚫 **Denied** | Direct modifications to `/config` and main `.gemini` configurations | --- ### 🔧 Configuration and Management * **Persistent Settings**: To enable sandboxing by default for all future sessions, configure the `enableTerminalSandbox` setting in your `~/.gemini/antigravity-cli/settings.json`: ```json { "enableTerminalSandbox": true } ``` * **Dynamic Adjustments**: Within an active CLI (`agy`) session, you can run the `/permissions` slash command to view or modify your autonomy and sandboxing levels on the fly. > [!NOTE] > Running in sandbox mode provides an excellent balance of autonomy and security, allowing me to execute build commands, run test scripts, and manage project files safely without risk to your primary host environment. Please let me know if you would like me to set up a new project workspace or run any specific tasks within this session! |
||
|
|
44d5ed0439 |
1.4.131 rc2 release prep (#8020)
* Support WSL Codex settings promotion and harden config write-back - Enable settings promotion for WSL runtimes using per-distro baselines. - Create parent directories if missing to prevent promotion ENOENTs. - Keep restrictive permissions (0600) and follow symlinks on promote. - Respect CRLF line endings when inserting keys into CRLF config files. - Skip redundant baseline file writes when settings are unchanged. - Include the release scan report for the 1.4.131-rc2 prep. * Refactor sleeping agent wake flow and fetch rate limits via backend - Background-mount only targeted terminal tabs during passive wake to prevent spawning unnecessary PTYs for unvisited tabs. - Latch edge-triggered wake requests that arrive mid-hibernation and track active claims to prevent double-resuming a provider session. - Query the ChatGPT wham usage backend API directly with fetch for rate limits, avoiding launching Codex or WSL login shells. - Asynchronously probe and serialize WSL auth files with timeouts to prevent synchronous I/O from stalling Electron's main process. - Fix config promotion edge cases such as missing parent directories, dangling symlinks, and atomic write permission widening. * Support WSL dotfile-symlink write-back and lengthen redeem timeout - Preserve symlinked Codex config on WSL by writing through the existing file instead of atomic-rename, since \\wsl$ symlink metadata isn't reliably detected and rename would clobber the link. - Tighten new ~/.codex directory creation to 0700 (holds auth.json). - Give explicit reset-credit redemption a 30s backend timeout instead of the 10s background-poll default, since it's user-triggered. - Read sleeping-agent session state from the worktree's actual execution-host partition instead of always the local one, so the headless-wake check works correctly for SSH-hosted worktrees. - Isolate serve-sim watcher tests from the real $TMPDIR/serve-sim state file to avoid leaking unrelated events. |
||
|
|
3a7eb8f312 |
fix(mobile): drop smart-dash write-back recovery that kills iOS dictation (#8008)
The longer-hyphen recovery path (#5222) reconstructed runs by writing a value that differed from the native field text. After #7933 stores raw field text and normalizes only on send/PTY, that recovery is unreachable and any write-back would reintroduce dictation kill. Map each smart dash to exactly "--" with a single-arg normalizer. |
||
|
|
43f639ddda |
Consolidate mobile source control into a single tabbed hub (#7923)
* Consolidate mobile source control into a single tabbed hub Unify the changes list, pull request details, and commit history into a single multi-segment panel. This improves navigation and state sharing across different lenses of a worktree's source control. - Add a segmented control to switch between Changes, PR, and History - Introduce a persistent branch status card with an integrated PR chip - Redirect standalone PR and history routes to the new unified hub - Extract reusable UI and logic for the history list and PR summary * Keep mobile source control tabs mounted to preserve view state * Keep PR and History segments mounted (using display: 'none' when hidden) to preserve fetch, scroll, and expand states during tab switches. * Decouple the History list from blocking on Git status loading. * Support deep linking directly into the history tab of the main panel instead of using a standalone route. * Enable retrying failed loads by reviving the transport loop if parked. * Fix PR chip accessibility label and comment check. * Optimize and integrate mobile PR view within source control hub - Lazy-load heavy PR comments and descriptions (Phase 2) only when the PR tab is active, using fast metadata (Phase 1) for the branch chip. - Unmount the PR body when inactive to avoid unnecessary comment tree re-renders and preserve WebView resources during commit text editing. - Implement soft-refresh on HEAD advancement to keep the ready UI visible while re-fetching checks post-commit. - Display the "Aborting..." label only when a merge or rebase abort is actively in flight. - Memoize the git history list and skip branch identity RPCs when gating the dock icon. * Improve mobile git views and concurrent rendering safety - Pass the `origin` parameter through history and PR redirect routes. - Move source control panel ref updates to `useEffect` to prevent side effects during concurrent renders. - Resolve commit file changes to empty if disconnected to avoid a stuck loading spinner. - Standardize PR sidebar header button styling and accessibility labels. * Resolve PR repo probe without active branch to avoid forever spinner Previously, checking if a repository is a GitHub remote required an active branch. In a detached HEAD or mid-rebase state (where the branch is null), the probe never resolved, leaving the PR panel on a forever spinner. Decouple the repository probe from the branch presence so the panel can correctly display the "Current branch unavailable" state. Also, hide the PR status chip when no branch is active to avoid a spinner on the chip. |
||
|
|
9c111fd7aa |
mobile: per-host connection log screen with copy-diagnostics (#7984)
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> |
||
|
|
73f1bbfc94 |
mobile: recover from wedged Tailscale tunnels and say 'check Tailscale' when it's the likely culprit (#7980)
A wedged Tailscale tunnel (known iOS failure mode) produces no AppState or network-type transition, so no revival nudge ever fires and the reconnect loop parked permanently at its give-up cap — users had to toggle Tailscale off/on just to force a transition (#7824). - rpc-client: past the give-up cap, drop to a 90s trickle dial instead of parking so the session self-heals once the tunnel recovers. - host screen: nudge the shared client on focus so opening the host retries immediately instead of waiting out a backoff/trickle timer. - connection-health: warning/unreachable verdicts on 100.64/10 or *.ts.net endpoints now carry a 'check Tailscale' hint, shown on the home host list and the in-session status line after ~3 failed attempts. - troubleshoot: 'Cannot reach <tailnet-ip>' now says to check Tailscale, adds a dedicated Tailscale section, and stops telling Tailscale users to disable their VPN (that advice killed their only route to the host); sections extracted to troubleshoot-common-issues.tsx to stay under the max-lines cap. Co-authored-by: Orca <help@stably.ai> |
||
|
|
6fb3036464 | Fix iOS native keyboard dictation in mobile terminal inputs (#7933) | ||
|
|
7f3e851696 |
Add external link button to open PR in mobile browser (#7879)
Introduce an external link action in the header of the Mobile PR View Panel. This provides a persistent and easily accessible shortcut to open the current pull request's canonical URL in the system browser. |
||
|
|
47389990a5 |
fix(mobile): surface disconnected source-control states + keep the mobile WS fallback port stable (STA-1511) (#7855)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
60037d60ab |
feat(mobile): add explicit keyboard dismiss control to terminal command dock (#5917)
* feat(mobile): add explicit keyboard dismiss control to terminal command dock Add a fixed Hide control at the left of the terminal command dock accessory bar whenever the software keyboard is open (keyboardHeight > 0). Tapping it clears any pending live-input focus timer, blurs the live and buffered command inputs, and dismisses the keyboard without sending bytes, switching input mode, or clearing typed text. The dismiss behavior lives in a dedicated, unit-tested terminal-keyboard-dismiss module rather than the customizable accessory-key path, so the escape hatch cannot be hidden by user shortcut customization. Available on every platform where the IME covers the app (iOS and Android). * review: harden keyboard dismiss control per adversarial review - document the load-bearing clear-before-blur order in dismissTerminalKeyboard - cover the both-handles-missing case in unit tests (5/5) - move the #5106 first-tap comment onto the accessory ScrollView and add a why-comment for the fixed Hide control - add accessibilityRole=button and hitSlop to the Hide control for a larger, semantically-correct touch target * fix(mobile): harden hide button visibility and scroll layout * refactor(mobile): use stacked keyboard+chevron glyph for dismiss control Replace the icon+'Hide' text with the iOS-native dismiss glyph (keyboard with a chevron-down beneath it). Narrower in the accessory row, removes the icon/word redundancy, and reads as distinct from the >> input-mode toggle. Accessibility label/hint/role unchanged. * fix(mobile): align keyboard dismiss accessory height * test(mobile): align vitest transform with Vite 8 --------- Co-authored-by: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com> Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com> |
||
|
|
54c4959830 |
perf(mobile): cap the scheduled-notification map + tap-dedup set (#7646)
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. |
||
|
|
9a3bf1293c |
Mobile file tree follow-ups: reconnect no-blank + old-desktop files.list fallback (#7599)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
99ae2dc90f |
Show full workspace file tree on mobile (#7289)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
e33b2006f4 |
Remove stale max-lines lint disables from files under the limit (#7548)
110 files carried an eslint/oxlint-disable max-lines directive but are already under the default max-lines budget (300 .ts / 400 .tsx / 600 .mjs / 800 test), so the suppression is dead. Removing it restores real max-lines coverage on these files with zero behavior change. Each removed directive had max-lines as its only rule; verified via a full oxlint run (0 max-lines violations, 0 new errors). Diff is pure deletions (200 lines, 0 additions) — no code touched. Co-authored-by: Orca <help@stably.ai> |
||
|
|
ce687221d3 |
lint(unicorn): enable prefer-number-properties, prefer-array-find, prefer-array-index-of (#7516)
Enable three unicorn rules — one correctness, two performance — and fix every existing violation repo-wide so the rules pass as errors. prefer-number-properties (76 sites) - parseInt/parseFloat/NaN -> Number.* : safe aliases (autofixed). - isNaN -> Number.isNaN (12 sites, hand-converted): global isNaN coerces its argument, Number.isNaN does not. Verified every call site already passes a number (Number.parseInt results, number-typed fields, Date.getTime()), so the conversion is behavior-preserving today and guards against a future non-numeric argument silently coercing. prefer-array-find (26 sites) - .filter(pred)[0] -> .find(pred); .filter(pred).at(-1) / .pop() -> .findLast(pred). Drops the intermediate array and short-circuits. prefer-array-index-of (5 sites) - .findIndex(x => x === v) -> .indexOf(v). Verified: typecheck (node/cli/web) clean, 53 affected suites pass (1679 tests), oxlint clean repo-wide. mobile/ uses findLast safely (already ships ES2023 .toReversed()); config scripts and e2e helpers run on Node 24. |
||
|
|
430d4b9482 |
Show all worktrees across all hosts on mobile (#7500)
* Show all worktrees across all hosts on mobile Avoid honoring desktop's host-filtering settings since mobile lacks the UI to manage or unhide them. This prevents worktrees from being silently hidden under certain host scopes. Additionally, this removes worktree filtering based on repo metadata, which previously caused worktrees to vanish when same-named repos on different hosts collapsed to a single ID. * fix(daemon): preserve promisify.custom type through wrapChildProcessApi The windows-hidden-console-children test (from #7499, admin-merged with a failing verify) failed tsgo: promisify(wrapped) resolved to its zero-arg overload because the wrapper erased its argument to a bare variadic function and the fake never statically carried promisify.custom. Preserve the wrapped type via a generic overload (accurate: the wrapper copies the call signature and symbols verbatim) and build the fake as a real CustomPromisify, so promisify routes through the custom overload as it does in production. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
12e60054b0 | fix(mobile): dismiss keyboard when opening repo/agent pickers in Create Workspace (#7365) | ||
|
|
e7d1fa4a21 |
Disable auto-merge for unstable GitHub PRs (#7415)
Prevent enabling auto-merge when a PR is in an UNSTABLE merge state. GitHub auto-merge mutations reject UNSTABLE PRs directly instead of allowing them to wait, so we should suppress the option. |
||
|
|
95caf2ee9d |
fix(mobile): match diff review line height to desktop diff density (#7276)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
d9e0b9e759 |
fix(mobile): avoid SF Mono fallback on iOS terminal (#6761)
* fix(mobile): avoid SF Mono fallback on iOS terminal * test(mobile): cover touch iPadOS terminal font fallback * refactor(mobile): share terminal font fallback tail across platforms Dedup the identical fallback chain that the iOS/non-iOS branches each repeated so the two platforms can only differ in the lead family and cannot silently drift. Make the regression tests behavioral: assert the resolved chain always terminates in the generic monospace (the real iOS bug) and that both platforms share an identical tail. Co-authored-by: Orca <help@stably.ai> * test(mobile): anchor font-block extraction on font markers only The VM-slice end boundary was an unrelated text-scale comment; re-anchor it on the terminalFontFamily declaration so edits below the font block cannot break the extraction. Co-authored-by: Orca <help@stably.ai> * chore(mobile): bump terminal-webview-html max-lines ratchet to match file size The iOS-safe font selection block adds a few code lines to terminal-webview-html.ts, pushing it to 1784. Bump the grandfathered per-file ratchet to match, consistent with prior ratchet bumps. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai> |
||
|
|
e8c2b79a93 |
Fix mobile live input keyboard and Korean IME (#7273)
* feat: 모바일 터미널 한글 미러 스텝 순수 모델 추가 * feat: 미러 델타 순서 보장용 send 체인 추가 * fix: 모바일 터미널 한글 입력을 미러 모델로 전환 * fix: 탭 상태 지연 중 한글 조합 상태 소실 방지 * fix: 미러 가드와 send 체인 리뷰 지적사항 반영 탭 상태 지연으로 활성 탭 타입이 일시적으로 null이 될 때 runMirrorStep의 stale-handle 가드가 조합 중 음절을 버리지 않도록 pending-clear 효과와 동일한 null 허용 패턴 적용. 테스트 하네스가 ref와 prop을 동일 소스에서 파생하도록 결합해 실제 경로의 lag 프레임을 검증. queueTerminalLiveMirrorSend의 previousSend await를 catch로 보호. * refactor(mobile): drop dead queueTerminalLivePendingFlush orphaned by the mirror model The mirror model migrated all live-input sends to queueTerminalLiveMirrorSend, leaving queueTerminalLivePendingFlush referenced only by its own tests. Remove the dead function and its three tests. Co-authored-by: Orca <help@stably.ai> * fix(mobile): expose live terminal keyboard target Co-authored-by: Orca <help@stably.ai> * fix(mobile): refocus live keyboard after dismissal Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: realitsyourman <wongil@demodev.io> Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai> |
||
|
|
25896f2cdb |
fix(mobile): keep Android release versionCode committed (#7271)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
f4790e9fac |
Fix blank mobile terminal on Android devices with outdated WebViews or blocked CDN (#7186)
* fix(mobile): bundle terminal engine and show load errors instead of a blank pane The mobile terminal WebView loaded xterm.js from cdn.jsdelivr.net at runtime; old WebViews (< Chrome 85) fail to parse the modern bundle and blocked-CDN networks fail to fetch it, and the resulting error was silently dropped, leaving the pane permanently blank (#7030). Bundle the engine into the app via exact-pinned npm deps + a postinstall esbuild step (chrome74 target, guarded WeakRef/structuredClone/ replaceChildren shims) emitting a gitignored generated module, inline it into the terminal document, and surface fatal engine failures as a visible overlay with diagnostics and a Reload wired into the existing resubscribe path. Non-fatal errors log without covering a live terminal. Co-authored-by: Orca <help@stably.ai> * fix(mobile): add a native watchdog so a dead terminal document can't stay silently blank CodeRabbit round: if the webview document dies before the glue can post anything (or the RN message bridge never comes up), no error message and no native handler fires. Arm a 15s foreground-gated watchdog per document generation that paints the fatal overlay when web-ready never arrives; first fatal diagnostics win over later cascades. Extract the watchdog and the public contract types to keep TerminalWebView under the line cap, and document the SVG xmlns percent-encoding transform. Co-authored-by: Orca <help@stably.ai> * test(mobile): unmount TerminalWebView renderers so watchdog timers can't leak across tests Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
f0278c116e |
fix(mobile): preserve terminal input mode on reentry (#7129)
Fixes #6972.\n\nPreserves mobile terminal buffered/live input mode across Android terminal re-entry and session refreshes. Includes follow-up hardening for pre-hydration preference edits and failed storage reads. |
||
|
|
c06507ee30 |
Open terminal file links on mobile (#7134)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
4f0a141951 |
Fix mobile terminal Korean IME composition on Android (#7011)
* Fix Korean IME composition by deferring live terminal preedit The mobile terminal capture field previously sent and cleared every TextInput change, which can break Hangul composition on Android keyboards. Introduce a small commit model and extracted live-input hook so composed text is flushed deliberately while ASCII remains immediate. Constraint: React Native TextInput has no portable composition event for this path; the fix uses a bounded commit delay for likely IME text. Rejected: Native-module IME integration | unnecessary for the confirmed JS dispatch/clear failure and higher maintenance risk. Confidence: high Scope-risk: moderate Directive: Keep terminal.send payload shape and buffered command input unchanged; do not claim physical Samsung Keyboard QA without device evidence. Tested: cd mobile && pnpm exec vitest run src/terminal/terminal-live-text-commit.test.ts src/terminal/terminal-live-input.test.ts src/terminal/terminal-text-input-normalization.test.ts src/terminal/terminal-keyboard-type.test.ts --reporter=verbose Tested: cd mobile && pnpm exec tsc --noEmit Tested: cd mobile && pnpm exec oxlint src/terminal/terminal-live-text-commit.ts src/terminal/terminal-live-text-commit.test.ts src/terminal/use-terminal-live-input-commit.ts app/h/[hostId]/session/[worktreeId].tsx Not-tested: Physical Galaxy Fold7/Samsung Keyboard and Android emulator/Gboard QA were unavailable; device probes recorded no attached Android device. * Preserve pending Korean IME text before mobile accessory controls Accessory keys share the same pending live-input commit gate as TextInput keypress and submit paths, so control bytes cannot race ahead of composed Hangul. Constraint: React Native mobile input does not expose portable composition events for Samsung/Gboard IME paths. Rejected: Let accessory buttons keep sending directly | Direct sends can drop pending Hangul before Tab/Esc/Enter/Backspace reaches the PTY. Confidence: high Scope-risk: narrow Directive: Keep all terminal control-byte paths behind the pending live-input flush/local-edit decision before sending to the PTY. Tested: pnpm --dir mobile test; pnpm --dir mobile lint; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile exec oxfmt --check src/terminal/terminal-live-text-commit.ts src/terminal/terminal-live-text-commit.test.ts src/terminal/use-terminal-live-input-commit.ts src/terminal/use-terminal-live-accessory-input-commit.ts app/h/[hostId]/session/[worktreeId].tsx; git diff --cached --check Not-tested: Physical Galaxy Fold7 Samsung keyboard manual QA is still external-device only. * Prevent stale IME timer flushes after mobile terminal teardown Pending live-input timers now clear on hook unmount, and accessory Delete documents why it stays local without trimming pending IME text. Constraint: React Native TextInput lacks a portable composition lifecycle, so pending IME text is guarded by a bounded timer that must not survive screen teardown. Rejected: Use clearPendingLiveInputCommit during unmount | it would also touch React state/native props during teardown when only timer/ref cleanup is required. Confidence: high Scope-risk: narrow Directive: Any delayed terminal input commit must have an owner-lifecycle cleanup path before sending to the PTY. Tested: pnpm --dir mobile test; pnpm --dir mobile lint; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile exec vitest run src/terminal/terminal-live-text-commit.test.ts --reporter=verbose; pnpm --dir mobile exec oxfmt --check src/terminal/terminal-live-text-commit.ts src/terminal/use-terminal-live-input-commit.ts; git diff --check Not-tested: Physical Galaxy Fold7 Samsung keyboard manual QA remains unavailable in this environment. * Use semantic accessory edits for mobile IME commits Accessory Backspace/Delete now carry semantic local-edit intent from built-in keys instead of inferring intent from raw bytes, and submit handling is reconnected to the pure submit-sequence model. Constraint: Custom terminal accessory keys may produce the same bytes as built-ins but should still flush pending IME text before sending rather than being silently treated as hidden-input edits. Rejected: Classify local accessory edits by raw bytes | That couples future custom controls to current built-in byte encodings. Confidence: high Scope-risk: narrow Directive: Keep semantic input intent separate from terminal byte payloads when pending IME text is present. Tested: pnpm --dir mobile test; pnpm --dir mobile lint; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile exec oxfmt --check src/terminal/terminal-live-text-commit.ts src/terminal/terminal-live-text-commit.test.ts src/terminal/use-terminal-live-input-commit.ts src/terminal/use-terminal-live-accessory-input-commit.ts app/h/[hostId]/session/[worktreeId].tsx; git diff --check Not-tested: Physical Galaxy Fold7 Samsung keyboard manual QA remains unavailable in this environment. * Respect IME flush failures before control input Propagate terminal.send success from pending Korean IME text before sending Enter, Tab, or accessory bytes, while keeping custom no-pending accessory bytes on the original direct path. Constraint: PR #7011 review required follow-up control bytes only after the pending composed text send actually succeeds. Rejected: Treating send invocation as success | It can still reject or no-op when RPC state changed. Confidence: high Scope-risk: narrow Directive: Keep pending IME flush paths async-success-aware before adding new terminal control inputs. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; pnpm --dir mobile exec oxfmt --check changed files; targeted no-excuse clean for mobile/src/terminal changed files. Not-tested: Physical Galaxy Fold7 Samsung keyboard; full session file no-excuse audit still reports pre-existing unrelated violations. * Serialize mobile IME flushes before live controls Treat terminal.send as successful only when the RPC response is ok and the runtime send result is accepted, then route all live-input control sends through a shared in-flight pending-flush barrier. Constraint: PR #7011 review found that resolved RPC promises and per-call sequencing were not enough to prove pending Hangul text reached the PTY before follow-up controls. Rejected: Only awaiting each flush-then-send call | Repeatable accessory keys and no-pending sends can arrive while the first flush is still in flight. Confidence: high Scope-risk: moderate Directive: Keep future mobile terminal control paths behind the pending-flush barrier whenever IME text may be in flight. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; pnpm --dir mobile exec oxfmt --check changed files; no-excuse clean for terminal changed files. Not-tested: Physical Galaxy Fold7 Samsung keyboard; full session file no-excuse audit still reports pre-existing unrelated violations. * Queue current IME snapshots behind active flushes Drain the pending snapshot captured by a control action after any already-active terminal send, and make accessory commit handling explicit so raw fallback is not encoded as an inverted boolean. Constraint: Architecture review found the previous single-slot barrier could wait for an older flush while skipping newly pending Hangul text. Rejected: Reusing the prior in-flight promise as the current flush result | It proves only an older snapshot, not the current pending buffer. Confidence: high Scope-risk: narrow Directive: New mobile terminal control paths must distinguish allow-raw, handled, and suppress-raw outcomes explicitly. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; pnpm --dir mobile exec oxfmt --check changed files; no-excuse clean for terminal changed files. Not-tested: Physical Galaxy Fold7 Samsung keyboard; full session file no-excuse audit still reports pre-existing unrelated violations. * Preserve accessory raw-send terminal targets Capture the terminal handle at accessory keypress time and suppress raw fallback if the active live terminal changes while waiting for pending IME flushes. Constraint: Independent review found raw accessory bytes could retarget to a different terminal after an async IME flush barrier. Rejected: Re-reading activeHandleRef as the send target after await | It can point at a different terminal than the keypress belonged to. Confidence: high Scope-risk: narrow Directive: Raw accessory fallback must use the keypress-time target and revalidate it after any await. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; pnpm --dir mobile exec oxfmt --check changed files; no-excuse clean for terminal changed files. Not-tested: Physical Galaxy Fold7 Samsung keyboard; full session file no-excuse audit still reports pre-existing unrelated violations. * Document accessory flush barrier intent Make the non-obvious raw accessory wait/suppress behavior explicit so future changes preserve IME-before-control ordering. Constraint: CodeRabbit requested a why-comment for the send-now accessory branch. Rejected: Leaving the barrier semantics implicit | The branch can otherwise look like unnecessary async defensive code. Confidence: high Scope-risk: narrow Directive: Keep comments focused on why raw accessory bytes wait behind IME flushes. Tested: targeted terminal vitest suite; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; oxfmt check for changed file. Not-tested: Physical Galaxy Fold7 Samsung keyboard. * Preserve buffered accessory raw sends Keep the stale-handle guard focused on the captured active terminal instead of live-input opt-in state, so buffered mode keeps existing accessory key behavior while async live-input waits still cannot retarget to another terminal. Constraint: Buffered command input behavior must remain unchanged while fixing mobile Korean IME live input ordering. Rejected: Requiring live-input enabled handles for raw accessory fallback | suppresses valid buffered-mode accessory sends. Confidence: high Scope-risk: narrow Directive: Do not use live-input opt-in state as terminal liveness for raw accessory sends; validate captured target, active terminal tab, connection, and client instead. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; oxfmt --check changed mobile terminal/session files; TypeScript no-excuse checker for changed terminal files. Not-tested: Physical Galaxy Fold7 Samsung Keyboard manual QA and GitHub Actions jobs, blocked by unavailable device and upstream fork workflow approval. * Keep Hangul IME text pending until explicit flush Avoid timer-driven PTY writes for Hangul candidates so paused Korean composition cannot leak intermediate jamo, while preserving the bounded settle timer for non-Hangul IME text. Also keep disabled live-input accessory fallback behind any existing pending flush barrier. Constraint: React Native TextInput does not expose a portable composition lifecycle on this mobile surface. Rejected: Fixed 150ms auto-flush for Hangul | can emit ㅎ or 하 if the user pauses mid-composition. Confidence: high Scope-risk: narrow Directive: Treat Hangul candidates as pending until submit/control/accessory flush; do not reintroduce idle timer commits for Hangul without device-level composition evidence. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; oxfmt --check changed mobile terminal/session files; TypeScript no-excuse checker for changed terminal files. Not-tested: Physical Galaxy Fold7 Samsung Keyboard manual QA and GitHub Actions jobs, blocked by unavailable device and upstream fork workflow approval. * Gate dictation toast on accepted live send Honor the async live-input sender contract so the mobile UI reports dictation insertion only after terminal.send is accepted. Constraint: sendLiveTerminalInput now returns false for stale, disconnected, oversized, or rejected terminal sends. Rejected: Toasting immediately after dispatch | reports success for sends that never reached the PTY. Confidence: high Scope-risk: narrow Directive: Treat live-input UI success as terminal.send acceptance, not request dispatch. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; oxfmt --check app/h/[hostId]/session/[worktreeId].tsx. Not-tested: Physical Galaxy Fold7 Samsung Keyboard manual QA and GitHub Actions jobs, blocked by unavailable device and upstream fork workflow approval. * Keep accessory edits on Hangul pending path Make accessory local edits reuse the Hangul-aware defer policy so built-in Backspace/Delete cannot reintroduce timer-driven Hangul PTY writes. Constraint: Hangul IME candidates must remain pending until explicit submit/control/accessory flush. Rejected: Reusing the non-Hangul 150ms settle timer for accessory local edits | can leak pending Hangul after Backspace/Delete. Confidence: high Scope-risk: narrow Directive: Any future pending-text reschedule must use getTerminalLiveDeferredTextDelayMs instead of a hardcoded timer. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; oxfmt --check changed mobile terminal/session files; TypeScript no-excuse checker for changed terminal files. Not-tested: Physical Galaxy Fold7 Samsung Keyboard manual QA and GitHub Actions jobs, blocked by unavailable device and upstream fork workflow approval. * Prove Hangul live-input hook ordering Add a direct hook-level regression so Android Korean IME fixes are covered at the orchestration boundary, not only by lower-level helpers. Constraint: React Native mobile TextInput lacks portable composition lifecycle events in this path. Rejected: Relying only on helper tests | misses hook-level pending flush and submit ordering. Confidence: high Scope-risk: narrow Directive: Keep Hangul candidates pending until an explicit terminal action flushes them. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; oxfmt --check changed mobile files; no-excuse on terminal modules Not-tested: Physical Galaxy Fold Samsung Keyboard manual QA is not available in this environment. * Keep accessory raw-send tests precise Remove a duplicate raw-target assertion whose title implied disabled live-input behavior that is covered at the accessory commit boundary instead. Constraint: Anti-slop cleanup must preserve existing Hangul/accessory behavior and stay within changed terminal tests. Rejected: Keeping the duplicate disabled-input wording | it tests the same active-terminal predicate as the preceding case. Confidence: high Scope-risk: narrow Directive: Test disabled live-input buffering in the accessory commit layer, not in the raw-target predicate helper. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; pnpm --dir mobile exec oxfmt --check changed mobile files; terminal no-excuse checker Not-tested: Physical Galaxy Fold Samsung Keyboard manual QA is not available in this environment. * Explain stale mobile terminal send gates Document why async IME flush paths re-check terminal/client refs before sending raw bytes or reporting live-send success. Constraint: CodeRabbit review requested short why comments for non-obvious stale-send safety gates. Rejected: Leaving the gates undocumented | future edits could remove the stale-target suppression contract. Confidence: high Scope-risk: narrow Directive: Keep async terminal sends guarded by current client, active handle, tab type, and connection state. Tested: pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; pnpm --dir mobile exec oxfmt --check changed mobile files; terminal no-excuse checker Not-tested: Physical Galaxy Fold Samsung Keyboard manual QA is not available in this environment. * Run mobile IME hook tests through effects Move the Hangul live-input hook regression from server rendering to react-test-renderer so effect cleanup and unmount timer cancellation are exercised. Constraint: @testing-library/react-native imports React Native's Flow entry under this Vitest setup, so the narrow effect-running renderer is the compatible test surface. Rejected: Keeping renderToString | it never runs useEffect cleanup and missed the pending timer cleanup path. Rejected: Adding @testing-library/react-native directly | it failed before tests with React Native Flow syntax under the current Vitest transform. Confidence: high Scope-risk: narrow Directive: Hook-level IME tests must use a renderer that runs effects when asserting pending flush cleanup. Tested: vitest targeted terminal tests; pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; oxfmt --check changed mobile files; terminal no-excuse checker Not-tested: Physical Galaxy Fold Samsung Keyboard manual QA is not available in this environment. * Keep hook lifecycle tests quiet Suppress only the react-test-renderer deprecation warning around the effect-running hook harness so real console errors still surface. Constraint: CodeRabbit flagged React 19 renderer warning noise; @testing-library/react-native remains incompatible with the current Vitest/RN Flow transform path. Rejected: Global console silencing | it would hide unrelated test failures. Confidence: high Scope-risk: narrow Directive: Keep the renderer warning suppression scoped to this hook harness and pass all other console errors through. Tested: vitest targeted terminal tests; pnpm --dir mobile test; pnpm --dir mobile exec tsc --noEmit; pnpm --dir mobile lint; oxfmt --check changed mobile files; terminal no-excuse checker Not-tested: Physical Galaxy Fold Samsung Keyboard manual QA is not available in this environment. * fix: flush pending mobile IME input before external sends * fix: guard terminal command finished event dispatch --------- Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> |
||
|
|
77a7798156 |
Fix mobile Source Control primary action styling (#6918)
Co-authored-by: Orca <help@stably.ai> |