feat(agents-mobile): session sharing, access management & copy/share links (desktop parity)#4564
feat(agents-mobile): session sharing, access management & copy/share links (desktop parity)#4564msfstef wants to merge 2 commits into
Conversation
…links (desktop parity) Ports the desktop ShareEntityDialog feature set to mobile with mobile-native UX: a Share session modal (link pill -> native share sheet, people-with-access list, General access section, search-first add people, per-row role commits via bottom-sheet picker), tap-to-copy session ids in the session menu and long-press sheet, and a sessionWebUrl() helper that targets /__agent_ui/ directly so Cloud tenant prefixes survive. Grant CRUD goes through the manage-protected REST endpoints since the synced effective-permissions shape is self-scoped. Extracts userDisplay/initials from the desktop dialog into agents-server-ui lib for reuse. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4564 +/- ##
==========================================
+ Coverage 58.06% 58.46% +0.39%
==========================================
Files 369 373 +4
Lines 40459 40735 +276
Branches 11468 11567 +99
==========================================
+ Hits 23494 23816 +322
+ Misses 16890 16845 -45
+ Partials 75 74 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Electric Agents Mobile BuildLocal mobile checks ran for commit The EAS Android preview build was skipped because the |
Claude Code ReviewSummaryBrings session sharing / access management to What's Working Well
Issues FoundCritical (Must Fix)None. Important (Should Fix)None. Suggestions (Nice to Have)1. File: The follow-up correctly added const loaded = await listEntityGrants({ baseUrl: serverUrl, entityUrl })
setGrants(loaded)
setAccessDenied(false)
setError(null) // make the success path self-healing tooIssue ConformanceNo linked issue (informational — the description references the parity track #4553/#4546 and is unusually thorough with an explicit manual-QA checklist). Changeset present, scoping both touched packages. The only assumption not statically verifiable (link resolves under a Cloud Previous Review StatusAll three iteration-1 items resolved or down to a trace:
Verified the supporting refactors are safe: Review iteration: 2 | 2026-06-11 |
- Diff re-added users against their actual grants (exposed via ShareAccessModel.grantsByUserId) so granting a role to a user with a partial, role-less grant set no longer creates duplicate grant rows. - Use the exact rolePermissionsMatchGrants check in applyRole instead of the coarse role compare, so re-selecting a role normalizes partial grant sets (desktop Update parity). - Guard applyRole/removeAccess while a save is in flight to prevent cross-row savingKey clobbering and racing grant refetches. - Filter the owner out of the access rows (already pinned as a dedicated row) and reword the all-users remove confirmation. - Dedupe userSearchText into agents-server-ui's userDisplay lib, un-export the internal initials helper, and trim unused optional fields from the mobile grant type. - Minor polish: hitSlop consistency on the copy-id pill, accessibility label on the share-link pill. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simulator.Screen.Recording.-.iPhone.15.Pro.-.2026-06-11.at.16.44.01.mov |
Summary
Brings session sharing to
agents-mobile, closing the parity gap with the desktopShareEntityDialogwhile using mobile-native UX paradigms. Mobile previously only consumed effective permissions to gate actions; it can now manage who has access to a session, copy the session id, and share a browser-openable session link.Part of the agents-mobile ↔ agents-desktop parity track (follows #4553, #4546).
What's included
1. Share session screen (
ShareSessionScreen+app/session-share.tsxmodal route)Opened from the session kebab menu's Share entry. Single-column layout, top to bottom:
entity.created_by(omitted when null), then one row per grantee (avatar/initials, name/email, role pill). Desktop doesn't show the owner; mobile adds it so the list is never confusingly empty.subject_kind: principal_kind / user) as its own section (Google-Drive style) rather than mixed into the people list like desktop.userstable), excluding self/owner/existing grantees.2. Copy session id
The session menu's status header now shows the id (in place of the agent type label) with a tap-to-copy affordance; same treatment on the long-press row sheet's id line. Mirrors the desktop
EntityHeadercopy-id pattern (1.2s icon swap, via the newuseCopyFeedbackhook +expo-clipboard).3. Session web links
sessionWebUrl(serverUrl, entityUrl)builds{serverUrl}/__agent_ui/#/entity/{id}.Key decisions & why
GET /_electric/entities/:type/:id/grants, not syncentity_effective_permissionsshape is scoped to the current principal only (server-utils.tsbuildCurrentPrincipalEntityEffectivePermissionsWhere), so it cannot list other people's access. Same approach as desktop'sloadGrants(). Refetch-after-mutation (no optimistic grant state) because the server owns grant ids needed for subsequent diffs./__agent_ui/directly, not the server root302redirect uses an absolutelocation: /__agent_ui/, which would drop a Cloud/t/<service-id>/v1tenant prefix. The session id stays un-encoded to match the web UI's hash splat route (/entity/$).managegate on the menu entry)userDisplay()/initials()extracted toagents-server-ui/src/lib/userDisplay.tsprincipals,sharePermissions,entity-api,auth-fetch); extraction avoids drift vs. duplicating. Internal lib file only — no public API change, desktop dialog updated to import it.Architecture notes (for future sessions on this PR)
src/lib/entityGrants.ts— all grant logic, pure where possible: REST wrappers (listEntityGrants/createEntityGrant/deleteEntityGrant, errors throwGrantsRequestErrorcarryingstatus),diffGrantsForRole(set-based create/delete diff, ignores non-share custom permissions, handles duplicate rows),grantIdsForRemoval,buildShareAccessModel(groups grants → all-users + per-user entries with roles, drops current user / system principals / role-less subjects),setSubjectRole/removeSubjectAccessorchestration. Unit-tested with the repo's hoistedserverFetchmock pattern.src/lib/sessionLinks.ts—sessionIdFromEntityUrl,sessionWebUrl(pure, unit-tested incl. Cloud prefix + malformed-URL fallback).agents-server-ui/src/lib/sharePermissions.ts: View=[read,fork], Chat=[read,write,signal,fork,schedule,spawn], Manage=[manage,delete];roleFromGrants/rolePermissionsMatchGrantsreused.SessionMenutakes an optionalonShareprop threaded fromapp/session.tsx→SessionScreen(router push to/session-share?entityUrl=…).BottomSheetItemalways reserves a 22px icon slot — give items icons or they look indented (why the role options carry glyphs).SearchBarmetrics (fixed 36px height, zero-padding input) or the placeholder sits off-centre on Android.Testing
sessionLinks.test.ts,entityGrants.test.ts) — written test-first; full mobile suite 83/83,agents-server-ui88/88, both packages typecheck clean.Manual QA checklist
expo-clipboardis a new native module; old dev binaries won't load this JS (the one build-impacting change)./__agent_ui/#/entity/{id}resolves under the/t/<svc>/v1prefix — the one assumption not verifiable statically).Out of scope / follow-ups
expo-haptics).🤖 Generated with Claude Code