mirror of
https://github.com/stablyai/orca.git
synced 2026-10-19 16:03:26 +00:00
eb9e2a8908ce92e44a3bd6a68e66cbbc7ff519b5
227
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ccd72f5909 |
Add unified mobile onboarding for session view and notifications (#9478)
* 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 |
||
|
|
c6f0ac4040 |
refactor(comments): slim verbose comments in mobile (#9547)
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.
Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.
Area: mobile. 11 files changed, 339 insertions(+), 1137 deletions(-).
Co-authored-by: Orca <help@stably.ai>
|
||
|
|
5fcf777617 |
feat(mobile): Quick Commands (terminal + agent-prompt presets) (#9298)
* feat(mobile): add Quick Commands (terminal + agent-prompt presets)
Brings the desktop Terminal Quick Commands feature to mobile: saved
agent-prompt or terminal-command presets that launch a new terminal tab.
Entry point sits in the session tab strip next to the "+" new-terminal
button (with a divider) — quick commands spawn a tab, so they live with
tab creation, mirroring desktop's tab-bar split button.
- Launcher button + Quick Commands bottom sheet (search, This project /
Global groups, run/edit/delete rows, add row).
- Add/Edit sheet mirroring desktop TerminalQuickCommandDialog: Label,
Action toggle (Terminal Command | Agent Prompt), Agent select, Prompt /
Command Text, Advanced (Append Enter, Scope Global/Project), validation
and save-failure feedback.
- Launch reuses handleCreateTerminal (extended with enter + toast copy):
agent prompts launch the agent then deliver the prompt; terminal
commands run the (Enter-appended) command text.
- Expose terminalQuickCommands over the remote/mobile RPC surface
(getClientSettings/updateClientSettings allowlists, RuntimeStore type,
and the strict SettingsUpdate zod schema).
- Mirror the agent-prompt support predicate mobile-side (stdin-after-start
agents are unsupported) with a parity test guarding drift from desktop.
- Mock server: sample quick commands + settings.update handler for QA.
* fix(mobile): harden quick command execution
* fix(mobile): harden quick command persistence and launch
* test(mobile): preserve unexpected quick command errors
* fix(mobile): harden quick command launch performance
* fix(runtime): reject malformed quick command updates
* refactor(mobile): reuse shared quick-command logic instead of mirroring
The mobile quick-commands mirror was built on a false premise — that
runtime-importing src/shared/terminal-quick-commands breaks the RN bundle
/ Vitest. It doesn't: tui-agent-config → orca-cli-command-name is a pure
leaf with no module-load Node APIs (verified via probe + bundle-graph).
- Mobile now reuses the canonical desktop helpers (action/agent/scope/
matchesRepo/support/flatten) directly from src/shared; only genuinely
mobile-specific pieces (agent-branded labels, native row truncation,
the launch plan) stay local.
- Multiline runnable terminal commands now flatten via the shared
flattenTerminalQuickCommand (";"-join) — unity with desktop, so a
command saved on one runs identically on the other.
- Drop the MOBILE_TUI_AGENT_PROMPT_COMMAND_UNSUPPORTED mirror + its parity
test; use the shared supportsTerminalAgentQuickCommand predicate.
- Export the shared MAX_QUICK_COMMAND_* length caps for reuse.
* fix(mobile): protect quick command data boundaries
* fix(mobile): enforce quick command limits
* fix(mobile): make quick command updates atomic
* fix(mobile): keep quick command filters recoverable
* fix(mobile): use filled play icon for quick commands
* Revert "fix(mobile): use filled play icon for quick commands"
This reverts commit
|
||
|
|
d67ede1594 |
Implement confirm-only PR panel composer with classified error blocking (#9428)
* Clarify PR panel guidance: classify errors and confirm-only composer Replace the ambiguous GitHub hosted-review boolean with a four-state evidence model (found/positive_unresolved/not_found/unknown) so "No PR found" never appears without an accepted lookup result. Classify GitHub refresh failures into types (rate_limited, auth, network, permission, repo_unavailable, gh_unavailable, unknown) for stable, honest copy. Confirmed-only composer: preserve drafts across transient failures; hide Create during hard errors and positive-unresolved evidence. Hard errors clear only when an eligibility request starts after the error and returns an accepted outcome. Propagate error types and unified retry schedule through the store. Sync mobile parity with shouldOpenChecksPanelCreateComposer gating. Localize all new copy. * Clarify PR panel guidance: classify errors and confirm-only composer Add reviewLookupOutcome to hosted-review eligibility and thread it through the panel so it never claims "No PR found" without accepted evidence. A failed lookup is unavailable, not a settled no-PR. Fail closed on positive unresolved evidence, hard refresh errors, and unavailable lookups. Add structured GitHub refresh-error classification with Retry-After parsing. Implement confirmed-only composer gating based on fresh, matching-context eligibility with hard-error clearing. Mobile gates on reviewLookupOutcome to prevent false Create claims. Surface throwOnFailure variants for each provider so transport failures cross the RPC boundary instead of collapsing to null. (Design success criteria 1–4; invariant 8.) * Add exec-error helpers for subprocess error classification Extracts stderr/stdout parsing and Retry-After detection into a lightweight module that can be imported without pulling in the heavier runner machinery. Supports PR-refresh error classification and proper rate-limit handling for gh commands. * test(mobile): include reviewLookupOutcome in create eligibility fixtures Create / Push & Create now fails closed unless the lookup is not_found. Update mobile test fixtures so accepted-no-PR cases can still proceed. * Add OrThrow mock variants to forge-provider test mocks forge-provider resolves branch reviews via the OrThrow variant so lookup failures surface as unavailable instead of "no PR found". |
||
|
|
e719ef1a57 |
fix(mobile): survive connection-migration cutovers during worktree create (#9234)
* fix(mobile): survive connection-migration cutovers during worktree create A worktree.create in flight when the mobile transport migrates (relay/direct hand-off on shoddy cellular, relay lease rotation, relay recovery) rejects with "RPC interrupted by connection migration" even though the host completed it — leaving the Create Workspace modal stuck while the worktree exists on desktop. A naive retry hits a name collision and spawns a duplicate. Mirror the existing mobile terminal-create idempotency: worktree.create now accepts an optional clientMutationId that the host dedupes (in-flight + brief post-success TTL), and mobile mints one key per candidate name and re-issues the create on a cutover so the retry reconciles instead of duplicating. * fix(mobile): gate worktree cutover replay by capability * fix(mobile): await worktree replay capability |
||
|
|
44686f324c |
feat(native-chat): add mobile default-view toggle for native chat (#9084)
* 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 |
||
|
|
a03a3dd51b |
Render png on mobile (#9087)
* Add mobile image-diff previews via shared data-URI builder - Extracts a `buildImageDataUri` helper (src/shared/image-data-uri.ts) shared by the desktop ImageViewer and mobile, so both trim whitespace-wrapped base64 and skip non-previewable mimes (e.g. application/pdf) the same way. - Adds mobile-diff-image-preview.ts to render binary git.diff results (add/modify/ delete) as images instead of falling back to "Binary preview unavailable". - Extracts resolveMobileFileTabDoc to consolidate the session file-tab loading logic (diff/image/html/text) out of the route file for testability. * Fix stale binary image fallback for empty modified diffs and relay reads - mobileDiffImageDataUri now distinguishes a true deletion (modified side absent) from a modify whose binary bytes arrived empty (relay/size-cap cases), returning null instead of the stale pre-change image - readWorkingDiffFile passes the file path to bufferToBlob so relay working-tree reads can detect previewable image extensions instead of always reporting empty binary content - add mobile-file-tab-doc.test.ts covering diff/image/binary/text resolution paths * Regenerate skill bundle manifest for 1.4.144-rc.2 Co-authored-by: Orca <help@stably.ai> * fix(review): trim comments to AGENTS.md's one/two-line why-only rule Comments in mobile-diff-image-preview.ts and mobile-file-tab-doc.ts ran 3-6 lines and narrated mechanism instead of stating only the non-obvious reason, per AGENTS.md's "Code Comments: Document the Why, Briefly" rule. Co-authored-by: Orca <help@stably.ai> * Distinguish read failures from true deletions in binary diff results - Working-tree stat/readFile errors and relay reads previously collapsed onto the same empty-content signal as a genuine deletion, letting previewers fall back to stale original bytes on a failed read. - Add modifiedDeleted/missing flags through status.ts, git-handler-ops, and git-working-file-read so only proven deletions trigger the original-bytes fallback; failed reads now return null. - Tighten buildImageDataUri to accept only image/* mimes instead of special-casing application/pdf. * fix(relay): expect missing:false on index blob maxBuffer overflow readBlobAtIndex now returns a missing flag so staged deletions are distinct from size-capped binary reads; update the overflow test. * Allow opening deleted files to show pre-delete text or image diffs Deleted files can now be opened to view their pre-delete content via git.diff (including images via modifiedDeleted). Only unresolved conflicts remain unopenable. Centralizes the canOpen rule in canOpenMobileGitStatusEntry() to keep opener guards consistent across the mobile source control UI. --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
64181fdd42 |
feat(native-chat): native chat view across mobile, desktop, and web (#5824)
* feat(native-chat): add native chat view across mobile
* fix(native-chat): address review findings and CodeRabbit threads
Correctness:
- Restore an independent initial readSession seed and surface initial-drain
errors as snapshot frames so the chat view can never strand on 'loading'
- Pair mobile tool results to calls by ordinal FIFO (parallel calls no longer
misgraft results); clear a pending ask only when its own call resolves
- Show a new streaming reply immediately (same-turn suppression, not length)
- Delegate mobile noise filtering to the shared harness-injected classifier
- Admit soft-leaving mobile clients in beginMobileInputFloor (parity with
mobileTookFloor) so grace-window writes aren't dropped
- Self-heal a stale 'working' status once this turn's reply lands
- Catch RPC rejections in mobile file-open helpers; guard sanitizeToolInput
key collisions; settle web/runtime transports on unrecognized first frames
and forward snapshot errors
Perf:
- Throttle the mobile streaming bubble (50ms) so per-part status frames stop
re-parsing the whole accumulated markdown
- Short-circuit markdown path detection on dot-less or oversized runs
(quadratic backtracking guard)
UX/minor:
- Wire hold-mode dictation through the native chat composer
- Allow scoped-package (@) paths in file-path detection
- Move caret after mid-text autocomplete insertion; index-prefixed ask option
keys; single scroll-to-end effect; bounded wait + toast when image attach
races a resubscribe; count-based pending reconciliation; cache-hit search
cancels stale debounce; chat-tab toggle wins over in-flight preference load
- Share shouldStepNativeChatAskAnswer between desktop and mobile; import
block guards/source priority from shared instead of local copies
- Defensive non-positive transcript limits; test strengthening (TTL expiry,
post-unsubscribe stale frame, lease readiness, filtered console.error)
* refactor(native-chat): share desktop/mobile chat logic in src/shared
Extract the parity-mirrored native-chat modules into shared implementations
both surfaces re-export: ask parsing (registry, parseAskFromStatus,
extractPendingAsk, formatAskAnswer), answer stepping offsets/scheduler, diff
detection/parsing, harness-noise filtering, tool fold/pair/split, and tool
summaries. Removes the hand-synced copies and their stale Metro comments.
Divergence reconciliations take the safer side of each: diffs truncate at
120 lines/32KB everywhere (desktop previously unbounded), tool-run summaries
cap at 3 parts with bounded-depth previews, nameless tool calls are skipped,
and basenames split on both separators.
Also: settle and kill every sibling quick-open pass when one reaches
maxResults (main rg/git and relay git; relay rg already did) so a capped
search cannot leave a scan walking a huge tree; fold window-bounding into
the shared merger's applyAppend; localize the web 'Pair a host' snapshot
error.
* fix(native-chat): address CodeRabbit follow-ups on shared modules
- Attachment lease gate re-checks connection/target/tab after the bounded
wait, so a tab/host switch or disconnect mid-wait can't send into a stale
terminal; a moved-away target drops silently like the pre-wait guard and
only an unrecovered lease surfaces the toast. Adds hook tests.
- extractPendingAsk parses transcript tool-calls through the same
registered-parser + canonical-shape fallback as live status, so a custom
question tool that rendered live survives reconnect/replay.
- Direct unit tests for the shared ask parser (FIFO ordering, fallback,
malformed payloads) and tool-summary bounded preview (depth/collection
caps, circular refs, basename/command branches).
* fix(native-chat): treat initialLimit 0 as a valid empty window
Both engine guards used truthiness, so an explicit zero limit skipped the
bounded tail reader and fell back to an unbounded incremental read. Latent
only (every caller clamps positive), hardened for consistency with the
tail reader's non-positive-limit handling.
* fix(mobile): native-chat composer lock UX + send-failure feedback
- Distinguish input-lock reasons: transport 'disconnected' shows Reconnecting…
instead of mislabeling a reconnect as locked-by-another-client
- Guard the composer lock behind a 600ms hold so connState blips / lease
hand-offs don't flicker the placeholder; unlock stays instant
- Surface a rejected send inline above the composer (a bottom toast hides
behind the keyboard); auto-dismisses after 4s
- waiting-session hint invites the first message instead of implying the
agent is still starting
* test(mobile): sync answer-send pacing test to the 500ms advance buffer
Missed in merge
|
||
|
|
79551c38f5 |
feat(mobile): edit saved host endpoints (#8294)
* feat(mobile): edit saved host endpoints * fix(mobile): reject ambiguous numeric host addresses * fix(mobile): label edit host inputs * fix(mobile): make host edit save atomic and remove superseded mutators Two independent review rounds found the same class of foot-gun: a superseded mutator (updateHostEndpoint, then renameHost) left in host-store.ts after the atomic updateHostNameAndEndpoint refactor, with zero remaining callers. Either could be reintroduced by a future caller and silently regress the non-atomic name/endpoint race the atomic function was written to close, so both are removed. Also covers reconnect-rejection and endpoint-only save paths that were missing test coverage, and merges origin/main (#8789) so this lands without reverting the mobile terminal restore fix. Co-authored-by: Orca <help@stably.ai> * Simplify save-race comment and reword host-removed error message - Trims the redundant comment explaining the savingRef race guard down to one line. - Changes the "no longer saved" load-error copy to "was removed" for clearer phrasing, updating the matching test expectation. --------- Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai> |
||
|
|
c12ade54d6 |
fix(mobile): dedupe host cards by pinned public key on re-pair (STA-1840)
Re-pairing a desktop that was already paired created a duplicate host card (STA-1840). Pairing now resolves the durable host identity by the desktop's pinned publicKeyB64 and reuses the existing id/name, collapses any already-stored duplicates for that key, clears stale relay overlays on a direct-only re-pair, fails closed on unreadable storage, and closes the host's client on pairing success so a reused id reconnects on the newly-paired endpoint. Mobile only. Full mobile suite (1,719 tests) + typecheck/lint/format pass. |
||
|
|
7baaf1911a |
Separate mobile host card metadata (#8819)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
58f222b43f |
Fix retrying interrupted Relay pairing (#8809)
Co-authored-by: Orca <help@stably.ai> |
||
|
|
42ee45f392 |
Fix restored mobile terminals and workspace visibility parity (#8789)
* Fix mobile cutover activation and usage refresh loops Co-authored-by: Orca <help@stably.ai> * Fix restored mobile terminal state parity Co-authored-by: Orca <help@stably.ai> * Fix migrated PTY workspace attribution Co-authored-by: Orca <help@stably.ai> * Fix overlapping mobile terminal surface swaps Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
d9f7fd08f7 | fix(mobile): harden notification opt-in onboarding (#8792) | ||
|
|
53c8a55833 |
Add mobile notification opt-in onboarding (#8780)
* feat(mobile): add notification opt-in onboarding * fix(mobile): deliver alerts despite desktop focus |
||
|
|
77b154d5dd |
Add Orca Relay desktop and mobile transport (#8536)
* feat(mobile): define relay protocol groundwork Co-authored-by: Orca <help@stably.ai> * feat(mobile): implement replay-safe E2EE v2 sessions Co-authored-by: Orca <help@stably.ai> * test(auth): lock cloud refresh single-flight Co-authored-by: Orca <help@stably.ai> * test(mobile): complete E2EE v2 adversarial coverage Co-authored-by: Orca <help@stably.ai> * refactor(runtime): unify mobile socket wiring Co-authored-by: Orca <help@stably.ai> * feat(runtime): add relay control and data clients Co-authored-by: Orca <help@stably.ai> * feat(runtime): coordinate desktop relay sessions Co-authored-by: Orca <help@stably.ai> * fix(auth): fence stale cloud session mutations Co-authored-by: Orca <help@stably.ai> * feat(runtime): add relay pairing and durable revoke Co-authored-by: Orca <help@stably.ai> * feat(runtime): add relay credential pairing RPCs Co-authored-by: Orca <help@stably.ai> * feat(settings): show Orca Relay sign-in status Co-authored-by: Orca <help@stably.ai> * test(relay): prove desktop lifecycle and E2EE splice Co-authored-by: Orca <help@stably.ai> * feat(mobile): persist relay pairing state Co-authored-by: Orca <help@stably.ai> * feat(mobile): race direct and relay pairing Co-authored-by: Orca <help@stably.ai> * feat(mobile): recover pairing through relay director Co-authored-by: Orca <help@stably.ai> * fix(relay): preserve origin controls during drain Co-authored-by: Orca <help@stably.ai> * feat(mobile): recover interrupted relay pairing Co-authored-by: Orca <help@stably.ai> * feat(mobile): add stable relay RPC sessions Co-authored-by: Orca <help@stably.ai> * feat(mobile): supervise direct and relay endpoints Co-authored-by: Orca <help@stably.ai> * Cover mobile relay director fallback matrix Co-authored-by: Orca <help@stably.ai> * Fix relay settings component test isolation Co-authored-by: Orca <help@stably.ai> * Remove unrelated merge formatting drift Co-authored-by: Orca <help@stably.ai> * Update runtime connection count integration assertion Co-authored-by: Orca <help@stably.ai> * Run mobile typecheck through pnpm Co-authored-by: Orca <help@stably.ai> * feat(relay): gate desktop controls on mobile demand Co-authored-by: Orca <help@stably.ai> * test(mobile): cover served relay recovery Co-authored-by: Orca <help@stably.ai> * feat(mobile): upgrade direct pairings to relay Co-authored-by: Orca <help@stably.ai> * fix(relay): harden mobile reconnect and teardown Co-authored-by: Orca <help@stably.ai> * fix(auth): clarify account sign-in state Co-authored-by: Orca <help@stably.ai> * fix(auth): polish sign-in completion flow Co-authored-by: Orca <help@stably.ai> * fix(auth): clarify sign-out confirmation Co-authored-by: Orca <help@stably.ai> * fix(auth): simplify sign-in completion page Co-authored-by: Orca <help@stably.ai> * feat(mobile): add per-device pairing connection mode Co-authored-by: Orca <help@stably.ai> * fix(mobile): stabilize pairing option layout Co-authored-by: Orca <help@stably.ai> * fix(mobile): give pairing choices stable space Co-authored-by: Orca <help@stably.ai> * fix(mobile): stabilize pairing QR regeneration Co-authored-by: Orca <help@stably.ai> * Animate mobile pairing flow height Co-authored-by: Orca <help@stably.ai> * Configure auth in packaged builds Co-authored-by: Orca <help@stably.ai> * Make Orca Relay pairing an opt-in beta Co-authored-by: Orca <help@stably.ai> * Show Relay beta details on hover Co-authored-by: Orca <help@stably.ai> * Refine mobile relay pairing choice Co-authored-by: Orca <help@stably.ai> * Polish Orca Relay pairing controls Co-authored-by: Orca <help@stably.ai> * Keep mobile contract fallback test additive Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
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. |