feat(agents): per-runner recent working directories synced via sessions#4509
feat(agents): per-runner recent working directories synced via sessions#4509msfstef wants to merge 4 commits into
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4509 +/- ##
===========================================
- Coverage 69.21% 36.73% -32.48%
===========================================
Files 77 232 +155
Lines 9238 19382 +10144
Branches 2878 6670 +3792
===========================================
+ Hits 6394 7120 +726
- Misses 2826 12229 +9403
- Partials 18 33 +15
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 ReviewSummaryIncremental review (iteration 4). Since iteration 3, one new commit ( What's Working Well
Issues FoundCritical (Must Fix)None. Important (Should Fix)None. Suggestions (Nice to Have)None new. All prior suggestions are now resolved or confirmed deliberate (see below). Issue ConformanceNo linked issue (per-convention nit, unchanged). PR body remains thorough and accurate. Changeset present ( The one remaining self-flagged item is unchanged and is a verification step, not a code issue: the live E2E check (spawn from mobile into a picked directory against a real runner and confirm the session Previous Review Status
Nothing outstanding on the code itself. The PR is in good shape pending the author's E2E verification. Review iteration: 4 | 2026-06-04 |
…sandbox profiles A working directory only takes effect through a sandbox-profile factory, so when a runner advertises no profiles the chosen directory would be silently ignored — the same no-op this PR fixes for mobile. Gate the working-directory section (and the spawn arg) on an actually-selected non-remote profile, on both desktop and mobile. Addresses the review-bot suggestion on #4509. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Re: the two suggestions carried over in review iteration 2 — both are deliberate, documenting the reasoning here: #2 — mobile defaults to "Runner default" while desktop defaults to the most-recent dir. Intentional, decided during design: mobile's cold-start flow is runner default + free-text path, while desktop preserves its pre-existing "resume where you last worked" behavior. Strict parity isn't the goal — mobile users typically spawn into a remote runner whose paths they may not know, so defaulting to the runner's configured directory is the safer default. If usage shows mobile users always tapping the newest recent, pre-selecting it is a one-line follow-up. #3 — the desktop default-selection effect interplay isn't unit-tested. Acknowledged coverage boundary: Remaining before un-drafting: the live E2E check (spawn from mobile into a picked directory against a real runner and confirm the session 🤖 Generated with Claude Code |
Derive the spawn prompt's recent-working-directories list from the already-synced entities collection (spawn_args.workingDirectory + dispatch_policy runner target) instead of per-device localStorage, so the same per-runner recents appear on desktop, web, and mobile. - agents-server-ui: new recentWorkingDirsForRunner lib helper; the working-directory picker takes per-runner recents and defaults to the selected runner's most recent path; sandbox-profile helpers extracted to lib/sandboxProfiles.ts for reuse; localStorage recents hook removed - agents-mobile: runners shape gains sandbox_profiles and entities shape gains dispatch_policy; the new-session screen gains sandbox-profile and working-directory sections (recents, free-text path, runner default, hidden for remote profiles); spawnEntity sends sandbox.profile with args.workingDirectory — required, since the runtime only honours the working directory through a sandbox profile factory Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sandbox profiles A working directory only takes effect through a sandbox-profile factory, so when a runner advertises no profiles the chosen directory would be silently ignored — the same no-op this PR fixes for mobile. Gate the working-directory section (and the spawn arg) on an actually-selected non-remote profile, on both desktop and mobile. Addresses the review-bot suggestion on #4509. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… expert review Findings from security / version-skew / UX+a11y review passes: - OptionCard now sets accessibilityRole/State/Label (selection was conveyed by color only, below the bar SessionRow et al. set) - Recent-directory cards tildify + abbreviate paths and ellipsize from the head: absolute paths share long prefixes and differ at the tail, so tail truncation rendered distinct projects identically - Working directory is only offered/sent for the default (horton) agent, mirroring the desktop composer — other agent types have their own creation schemas and may reject unknown args - Free-text path input gains an accessibilityLabel Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2cc3a89 to
1054bc2
Compare
…idth Drop the fixed 28-char abbreviatePath pre-clip on mobile recent-dir cards; tildify only and let ellipsizeMode="head" truncate at the rendered width. Adapts to device width and font scale, and screen readers now announce the full path (review suggestion #4). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>


Goal
The Horton spawn prompt needs a working directory. On desktop a native folder picker works, but when spawning onto a runner on another machine — or from the mobile app — there's no way to browse the runner's filesystem. Mobile previously hardcoded "Mobile uses the server default working directory for now", and desktop recents were a per-device localStorage list with no runner awareness.
This PR gives every client a per-runner recent working directories picker that syncs across the user's devices, and makes mobile able to choose a sandbox profile + working directory at all.
Design decisions
1. Recents are derived from synced sessions, not stored.
We originally planned to attach recents as metadata/state on the user's principal entity. Review showed everything needed is already synced to every device: each session row carries
spawn_args.workingDirectoryand itsdispatch_policyrunner target, scoped to the user. So recents are just a live query: filter sessions by runner → dedupe paths keeping newestupdated_at→ top 10 (recentWorkingDirsForRunnerinagents-server-ui/src/lib/recentWorkingDirectories.ts). Zero new server state, no migration, no new auth surface, and existing sessions populate the list retroactively.Accepted trade-offs: recents live only as long as the sessions that reference them, and there's no per-row "remove" (they're facts, not a curated list). The principal-entity-state design remains the follow-up if curation/durability is ever needed — the derived list could seed it.
2. Mobile sends the sandbox profile with the working directory (bug fix).
args.workingDirectoryis only honoured through a sandbox-profile factory (packages/agents/src/bootstrap.tsresolveCwd); a spawn with nosandboxfalls back tounrestrictedSandbox(process.cwd())and silently ignores the arg (process-wake.ts). Desktop always sent a profile; mobile sent none — so without this fix the chosen directory would be a no-op. Mobile now syncssandbox_profileson its runners shape, shows a profile picker (advertised order, first = default), and sendssandbox: { profile, key: entityUrl }like desktop.3. Remote profiles take no working directory.
For local profiles (local/unrestricted, Docker) the working-directory section applies; for
remote: trueprofiles the workspace lives in the provider VM, so the section is hidden and no directory is sent — same gating as desktop (isSandboxProfileRemote).4. Shared helpers extracted, not duplicated.
pickDefaultSandboxProfile/useSandboxProfileSelection/isSandboxProfileRemotemoved fromNewSessionView.tsxintoagents-server-ui/src/lib/sandboxProfiles.ts, deep-imported by mobile per the existing convention.5. Desktop picker becomes per-runner; localStorage hook retired.
A desktop user targeting a remote runner needs that runner's recents, which localStorage can't provide. The picker now takes derived per-runner recents and defaults to the selected runner's most recent path (explicit picks win; switching runner re-derives, since paths from one machine may not exist on another). No migration needed — existing sessions already encode real recents.
Changes
agents-server-ui: newlib/recentWorkingDirectories.ts+ tests, newlib/sandboxProfiles.ts(pure extraction),WorkingDirectoryPickertakesrecentsprop (✕-remove dropped),NewSessionViewwires per-runner recents + default, deletedhooks/useRecentWorkingDirectories.tsagents-mobile: entities shape +dispatch_policy, runners shape +sandbox_profiles,spawnEntitygainssandboxProfile/workingDirectory(+ tests),NewSessionScreengains Sandbox and Working-directory sections (recents cards, free-text absolute path, runner default)Testing
pwds there) — flagged for review🤖 Generated with Claude Code