Repository navigation
iOS: launch agent workspaces from the task composer #7670
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
202 commits
Select commit
Hold shift + click to select a range
95b79c2
iOS: floating task composer creates workspaces with agent templates
lawrencecchen d5dd73b
Fix package-conventions-lint: replace free function taskTemplateIcon …
lawrencecchen 2c9418b
Fix Swift file length budget and policy findings: split god-file rout…
lawrencecchen c0671d8
Address review findings: submit lifecycle, emoji seeding, API surface
lawrencecchen 7aa4b90
Localize seeded task template names
lawrencecchen 7c9c8d8
Task composer: block dismissal during submit; keep directory in sync …
lawrencecchen bf3faad
Task composer: don't preselect a forgotten Mac
lawrencecchen 804c7d7
Task composer: brand agent icons, OpenCode seed, tighter FAB padding
lawrencecchen feaa150
Flatten agent icon asset names to catalog root
lawrencecchen f692cc8
Load agent brand icons from loose package PNGs, not the asset catalog
lawrencecchen ffe31f0
Task composer: failing test — stale rejected create reports success
lawrencecchen 240da60
Task composer: report stale create failures instead of success
lawrencecchen dc7076e
Task composer: re-dial when the selected Mac's connection dropped
lawrencecchen 6f77ed1
Polish task composer lifecycle and accessibility
azooz2003-bit d14cab6
Preserve task composer directory on Mac fallback
azooz2003-bit 06b8a6e
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
azooz2003-bit 15a06ca
Harden task composer submission boundaries
azooz2003-bit caeb741
Set empty task prompt environment explicitly
azooz2003-bit 9af6b77
test: cover task composer retry idempotency
azooz2003-bit 0f5c958
test: reject stale task composer draft writes
azooz2003-bit 71f648f
fix: make task creation retries idempotent
azooz2003-bit 95a0c03
chore: keep terminal controller line neutral
azooz2003-bit f7bdfdf
test: cover task composer compatibility gates
azooz2003-bit 7c343b8
fix: harden task composer submission compatibility
azooz2003-bit 42a1672
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
azooz2003-bit 92e9f09
test: cover task composer data boundaries
azooz2003-bit 0986767
fix: isolate task composer execution and state
azooz2003-bit 2ca5d32
test: cover task directory validation boundaries
azooz2003-bit 9220a2b
fix: validate mobile task directories off main actor
azooz2003-bit 1c548c6
test: cover bounded task directory validation
azooz2003-bit 5e75f97
fix: serialize mobile task directory validation
azooz2003-bit a96f278
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
ba41a52
test: cover interrupted composer edge cases
25d03c3
fix: harden task composer retries and validation
92d0f12
test: cover composer routing and navigation races
b13207a
fix: pin task creation and compact navigation
882d3a6
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
ca2e262
test: cover immutable task submission policies
5feee8c
fix: harden task composer settlement
1a57034
test: cover task prompt composition edges
b31997e
fix: preserve explicit task prompt inputs
0e118d3
test: clarify controlled deadline suspension
20a8c25
docs: document task submission snapshot
b9a5a7d
test: stress concurrent mount snapshots
4e199dd
fix: isolate concurrent mount snapshots
70ade2e
fix: narrow task composer import visibility
ff38df3
test: cover session-scoped draft clearing
55a67e8
fix: scope task draft clearing to session
1789c19
test: cover whitespace-only task commands
756760e
fix: treat blank task commands as plain shell
ec9199b
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
09dc074
test: reject working directory alias bypasses
adc1667
fix: reject working directory alias bypasses
bf3cd87
test: cover prompt append before comments
6c9d6be
test: cover request-equivalent task edits
a700687
test: preserve hash token append semantics
80ef2ba
test: assert request identity rotation policy
3e00678
test: cover prompt append before shell separators
4865d96
test: cover mobile cwd validation alias
fc54dc6
test: reconcile restored task tombstones
1bbf6cd
fix: append task prompts before trailing shell syntax
c7e0364
fix: preserve identity for equivalent task requests
9980a20
fix: validate mobile cwd aliases
367bf45
fix: reconcile restored task tombstones
9475ab4
test: preserve trailing task command trivia
f60fd44
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
35624e7
test: normalize task commands for request identity
a2a1027
fix: compare normalized task create requests
c6d6f53
refactor: keep task composer within file budgets
b74a849
test: cover comment-only task templates
7a7eafe
test: cover shell programs without commands
96a044c
test: classify shell programs without commands
a905dfe
fix: classify task templates without commands
f48a3ba
refactor: isolate task and restore actions
3c63701
feat: describe task composer controls for VoiceOver
3a27cf4
test: decline prompt injection for complex shell scripts
b8a79e6
fix: decline prompt injection for complex shell scripts
b21e65b
fix: inject session restore cache explicitly
944bc44
refactor: isolate shell grammar composer tests
8e2044a
test: preserve manual directory across template changes
782642d
fix: preserve manual directory across template changes
fe30301
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
36cf256
test: cover task composer review regressions
e079c86
fix: address task composer review feedback
870d20d
test: cover task composer accessibility targets
a4d8532
fix: expose task composer actions to VoiceOver
0c89325
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
83334d7
docs: document task composer accessibility preview
baf6d0b
test: cover task composer review regressions
fcc91af
fix: close task composer review findings
cc1e55f
test: cover final task composer review findings
8e70bf6
fix: close final task composer review findings
e36ba1d
test: cover final session and shell expansion races
e970a56
fix: guard composer settlement by session
7db28ea
test(ios): model signed-in composer preview
40c8cf6
test: cover task composer review regressions
7a3694e
fix: address task composer review findings
6d9f025
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
d2d2743
test: cover cancelled create rejection race
31f2e52
fix: preserve definite create rejections
a0e56b2
test: cover settled rejection and heredoc prompts
a4253a8
fix: preserve rpc settlement and heredoc literals
5421d35
Split task composer parsing helpers
47999b5
Test nested shell contexts in task templates
799ab0d
Track nested task template shell contexts
0129f02
Test verbatim task template command contract
7dbede3
Preserve task template commands verbatim
b785bcf
Test verbatim task command transport
5b04467
Preserve task commands through host startup
5a1c7c2
Test authoritative restored task tombstones
1ca1b51
Honor task tombstones across restarts
5062392
Test task creation transaction boundaries
66ae0f5
Make task creation transaction atomic
abc883d
Test composer cancellation during Mac switch
33f2f35
Cancel owned Mac switch with composer task
a90efbe
Test cancellation and tombstone persistence failures
34e821a
Make task submission settlement crash-safe
0b0beda
Test task composer directory and presentation UX
7b1d205
Stabilize task composer directory selection
5389540
Test mobile task directory discovery RPC
470e502
Test stale task directory search capability metadata
c395332
Discover task directories from connected Macs
16aeb69
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
824d3fc
Scope task directory candidate assembly
99aab43
Split task composer files for source budgets
e9d75ff
Add regressions for interrupted task recovery
6d70d7c
Fix task recovery and directory search cancellation
3d36c07
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
2b22f82
Add regressions for composer review findings
396ec9d
Fix composer review edge cases
899491c
Fix iOS test Foundation import
c248fc7
Add regressions for final composer review
e5a6b4e
Fix final task composer review findings
c614e29
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
ec7b0fd
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
8c3e770
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
acbbcc4
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
67dbdc2
Untrack generated iOS workspace Package.resolved
90a42f8
Replace hidden-window resize sleeps with deterministic tmux size waits
4ab0364
Restore tracked iOS workspace lockfile
bafa0a4
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
f0bbbd8
Fix instance-tag forwarding after main merge
7060c2d
Polish iOS task composer launch flow
ef70ab6
Fix task composer accessibility targets
806f682
Test task composer launch readiness
9e6fce2
Require prompts before launching agents
7a1d2f2
Test bundled task agent icons
d656bac
Load bundled task agent icons
846bffd
Test immediate paired Mac visibility
bfd3142
Refresh paired Macs after attach
00de4f5
Fix iOS reconnect and build isolation (#8299)
azooz2003-bit 54469cd
Test seeded directory search fast path
bba67d7
Prioritize seeded directories in search
f552e1a
Test cold directory search responsiveness
1302090
Prioritize exact cold directory searches
90b9d34
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
fddad9d
Test exact project root against seeded descendant
e2b0ff6
Prioritize exact project roots over seeded descendants
dfc4001
Test exact directory basename ordering
ab19101
Rank exact directory names ahead of context
fe417e8
Test glanceable directory result labels
ad189da
Make directory results glanceable
c691209
Let folder results breathe at large text sizes
0a1560d
Test batched task-template deletion
061842e
Close task composer review findings
c3cbfcd
Test accessibility task composer action sizing
aec7dc7
Keep task composer usable at large text sizes
4c44001
Add Shell icon lab and task composer proofs
d5fb2a3
Keep UX experiments in debug-only CMUX Labs
98d6885
test(ios): require created workspace identity
a9c5913
feat(ios): complete prompt-to-agent launch route
be33419
test(ios): define task retry phase policy
0e46f6b
fix(ios): make task submission retries explicit
c134a0e
Add failing directory pagination recovery tests
fd2fd7a
Fix task composer directory pagination recovery
d8e544d
test(mobile): cover filesystem job quota
ea9f98b
fix(mobile): cap concurrent directory jobs
442fe1b
Localize task composer recovery copy
97f8719
test(ios): cover task retry recovery
9b8c515
Add failing pagination recovery UI coverage
4c50392
Clarify pagination recovery failures
cd057ac
Localize task composer debug state
a61e249
Expand directory picker action targets
9278093
Harden pagination recovery UI proof
dc745b9
test(ios): expose manual pairing to composer
2e6ace7
test(ios): cover connected task creation
07f03f9
fix(ios): refresh Macs after manual pairing
ae4ae8d
fix(ios): finish composer debug compilation
ea04121
test(ios): make connected composer proof deterministic
e065996
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
abc44f3
test(ios): align mock host with tagged build isolation
6bf5bb8
test(ios): prove labs stay out of release
36fe8e8
fix(ios): keep labs experiments out of release
8b5a4ed
test(ios): cover task composer autofocus
3f3c81e
test(ios): require visible task composer keyboard
46ec346
fix(ios): own task composer initial focus at sheet lifecycle
bf989cf
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
7b2b247
Refine iOS task composer hierarchy
31e9cb3
test(ios): cover directory browser scroll responsiveness
ea6fe74
fix(ios): stabilize task directory browsing
91ea857
Gate iOS task composer behind beta setting
4fbd5c8
Merge remote-tracking branch 'origin/main' into feat-ios-task-composer
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
...ges/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+RouteSelection.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| internal import CMUXMobileCore | ||
| internal import Foundation | ||
|
|
||
| // Route selection for reconnect/attach: which published route a phone should | ||
| // dial for a Mac, and why loopback routes are only valid on the simulator. | ||
| // Extracted from MobileShellComposite.swift (Swift file length budget). | ||
| extension MobileShellComposite { | ||
| /// Whether route selection should avoid loopback routes. A loopback route | ||
| /// (`.debugLoopback`, `127.0.0.1`) names the host it runs on, so on a | ||
| /// physical device it can only ever reach the phone itself, never a remote | ||
| /// Mac. On the simulator `127.0.0.1` IS the host Mac, so loopback is valid | ||
| /// (and is how the dev/UI-test mock host attaches). | ||
| static var prefersNonLoopbackRoutes: Bool { | ||
| #if targetEnvironment(simulator) | ||
| false | ||
| #else | ||
| true | ||
| #endif | ||
| } | ||
|
|
||
| /// The first reachable host/port route to a Mac, in priority order. | ||
| /// | ||
| /// When `preferNonLoopback` is set (physical devices), a real route | ||
| /// (`.tailscale` etc.) is always chosen over a `.debugLoopback` route even | ||
| /// if the loopback route has a lower (more-preferred) priority, because a | ||
| /// loopback route can never reach a remote Mac from a physical phone. A | ||
| /// loopback route is used only when it is the sole supported route — the | ||
| /// on-device XCUITest mock host, which serves a real listener on `127.0.0.1` | ||
| /// inside the test runner. This is what lets a restored Mac (whose published | ||
| /// routes include both `debug_loopback` and `tailscale`) actually connect | ||
| /// over Tailscale instead of dialing the phone's own loopback and failing. | ||
| static func firstReconnectHostPortRoute( | ||
| _ routes: [CmxAttachRoute], | ||
| supportedKinds: [CmxAttachTransportKind], | ||
| preferNonLoopback: Bool = false | ||
| ) -> (String, Int)? { | ||
| let supportedKinds = Set(supportedKinds) | ||
| let ordered = routes.sorted(by: Self.routeSortsBefore) | ||
| func firstHostPort(where predicate: (CmxAttachRoute) -> Bool) -> (String, Int)? { | ||
| for route in ordered { | ||
| if !supportedKinds.isEmpty, !supportedKinds.contains(route.kind) { | ||
| continue | ||
| } | ||
| guard predicate(route), case let .hostPort(host, port) = route.endpoint else { | ||
| continue | ||
| } | ||
| return (host, port) | ||
| } | ||
| return nil | ||
| } | ||
| if preferNonLoopback { | ||
| // Among non-loopback routes, prefer one whose host is a numeric IP: a | ||
| // raw tailscale/LAN IP is dialable without DNS, whereas a MagicDNS | ||
| // hostname (e.g. "<node>.<tailnet>.ts.net") depends on the client | ||
| // having tailscale DNS active and resolving it. On devices where | ||
| // MagicDNS isn't resolving, dialing the hostname times out and the Mac | ||
| // silently drops out of the list, even though its IP route is fine. | ||
| if let ip = firstHostPort(where: { route in | ||
| guard route.kind != .debugLoopback, | ||
| case let .hostPort(host, _) = route.endpoint else { return false } | ||
| return Self.isIPLiteralHost(host) | ||
| }) { | ||
| return ip | ||
| } | ||
| if let real = firstHostPort(where: { $0.kind != .debugLoopback }) { | ||
| return real | ||
| } | ||
| } | ||
| return firstHostPort(where: { _ in true }) | ||
| } | ||
|
|
||
| /// Whether `host` is a numeric IP literal (IPv4 or IPv6) rather than a name | ||
| /// that needs DNS resolution. Used to prefer directly-dialable IP routes over | ||
| /// MagicDNS hostnames, which fail to resolve on some clients. | ||
| static func isIPLiteralHost(_ host: String) -> Bool { | ||
| if host.contains(":") { return true } // IPv6 literal | ||
| let octets = host.split(separator: ".", omittingEmptySubsequences: false) | ||
| return octets.count == 4 && octets.allSatisfy { part in | ||
| guard let value = Int(part), (0...255).contains(value), !part.isEmpty else { return false } | ||
| return String(value) == part // reject leading zeros / non-canonical | ||
| } | ||
| } | ||
| } | ||
29 changes: 29 additions & 0 deletions
29
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+TaskComposer.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| public import CmuxMobilePairedMac | ||
| public import CmuxMobileShellModel | ||
| internal import Foundation | ||
|
|
||
| extension MobileShellComposite { | ||
| /// Submit a task-composer workspace create request to the selected Mac. | ||
| /// - Parameters: | ||
| /// - macDeviceID: Target Mac device id. | ||
| /// - spec: Workspace-create parameters derived from the selected template. | ||
| /// - Returns: `success` when the workspace was created; otherwise the failure to display. | ||
| @discardableResult | ||
| public func submitTaskComposer( | ||
| macDeviceID: String, | ||
| spec: MobileWorkspaceCreateSpec | ||
| ) async -> Result<Void, MobileWorkspaceMutationFailure> { | ||
| if macDeviceID != foregroundMacDeviceID { | ||
| guard await switchToMac(macDeviceID: macDeviceID) else { | ||
| return .failure(.notConnected(hostDisplayName: taskComposerTargetName(macDeviceID: macDeviceID))) | ||
| } | ||
| } | ||
| return await createWorkspaceRequest(spec: spec) | ||
|
cursor[bot] marked this conversation as resolved.
Resolved
|
||
| } | ||
|
|
||
| private func taskComposerTargetName(macDeviceID: String) -> String { | ||
| displayPairedMacs.first { $0.macDeviceID == macDeviceID }?.resolvedName | ||
| ?? pairedMacs.first { $0.macDeviceID == macDeviceID }?.resolvedName | ||
| ?? macDeviceID | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.