* fix(apps): open session desktop on its machine
* fix(ui): scope the desktop session lookup to the key's own agent
`sessions.list` has no exact-key filter, so the viewer resolves a `session=`
parameter by searching for the key and matching it exactly in the response. A
key that prefixes longer ones — `agent:main:main` alongside user-named sessions
that start the same way — could push the exact row outside a five-row page and
silently fall back to the picker.
Session keys encode their agent and the list API accepts `agentId`, so scope the
search to that agent and widen the page.
* refactor(ui): stop parking a session key in the desktop environment id
Document-mode inventory failures stashed the requested session key in
`environmentId` purely so the Retry button's non-null guard would pass, even
though document-mode retry refreshes the inventory and never reads the value.
A session key only names a machine once the inventory loads, so it now stays
out of `environmentId`, and the retry branch that ignores it runs before the
guard. Adds E2E coverage for recovering a session-preselected desktop.
* fix(ui): resolve the desktop session with an exact-key lookup
The session-preselect path searched `sessions.list` and scoped the search to
the key's own agent, but a bounded search cannot rule a key out: 25 newer
same-agent sessions sharing the requested key's prefix would push the exact row
off the page, and the viewer would report the source as unavailable for a
session that exists. `sessions.describe` is the exact-key operation and already
projects placement, so the panel calls it directly and the app-root resolver
plumbing goes away with it.
* refactor(ui): move the desktop document keyboard bridge into its own controller
`desktop-panel.ts` crossed the 700-line cap. The mobile keyboard bridge — the
padded sentinel, the value diffing that turns composed input into backspaces
and text, and the field focus/reset helpers — is a self-contained concern, so
it moves into a `DesktopMobileKeyboard` controller alongside the existing
fullscreen controller instead of taking a `max-lines` suppression.
* test(ios): drain text fields instead of assuming a delete burst lands
`testReleaseChatScreenshot` typed a 5-character probe, sent 5 deletes in one
`typeText`, then asserted the field was empty. CI dropped one synthetic
keystroke under simulator load and the assertion failed with a leftover "f".
XCUITest makes no lossless-burst guarantee, so clearing now re-sends against
whatever the field actually still holds, bounded. The two gateway-setup fields
that overtyped through the same burst use the helper for the same reason.
* fix(ui): keep the chat placement owner out of the startup chunk
`desktop-document-mode.ts` is imported by bootstrap, so importing the chat
placement owner from it pulled the chat page's dependency tree into the startup
bundle and pushed startup JS past its gzip budget (331075 B against a 330507 B
allowance). The route module now only parses the URL; resolving a session to its
machine moves next to the lazily loaded desktop panel, which is the only caller.
Startup JS is back to 329710 B.
* feat(ios): guide first launch through pairing explainer and permissions
* fix(ios): keep first-run flow resumable and surface limited grants in onboarding
* chore(ios): regenerate native i18n after rebase
* chore(android): project refreshed native translations into strings.xml
* fix(ios): move i18n wrapper contract to shared permission row and sync catalog
* fix(ios): make onboarding Retry Connection use current manual input
Retry Connection on the auth step replayed connectLastKnown(), which
silently returns when no last connection is stored and otherwise redials
the stale persisted endpoint, ignoring host/port/TLS and token edits made
on screen. Manual retries now dial the current form input; discovered
gateway retries keep the previous behavior.
* fix(ios): repair onboarding credential retry
Co-authored-by: Abdullah Taş <29359748+abdullahtas0@users.noreply.github.com>
---------
Co-authored-by: abdullahtas0 <“dev.abdullahtas@gmail.com”>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(ios): return chat to originating control detail
Co-authored-by: Colin <colin@solvely.net>
* fix(ios): reset retained state before control chat handoff
Co-authored-by: Colin <colin@solvely.net>
---------
Co-authored-by: Colin <colin@solvely.net>
* fix(ios): back from settings details returns to the originating screen
Detail views reachable from multiple places (gateway, voice, notifications
settings) previously opened by switching to the canonical Settings tab, so
Back landed on the Settings root instead of the screen the user came from.
Phone tabs now push SettingsRoute details on their own NavigationStack via
PhoneTabSettingsHost, the Control hub pushes the gateway detail onto its own
stack, and the Approvals->Notifications shortcut appends instead of replacing
the path (phone and iPad). Global flows (deep links, onboarding, problem
banner, notification dialog) still jump to the canonical Settings tab.
* chore(i18n): sync native app i18n inventory line references
* test(ios): prove settings back navigation
* test(ios): prove settings back navigation
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(ios): refine control and talk visual hierarchy
* feat(ios): refine control and talk visual hierarchy
* feat(ios): refine control and talk visual hierarchy