-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Propagate phone transport state to iOS surface errors #10072
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
Changes from 44 commits
b93e85c
6562e6d
0c9075f
5405e40
1a36c4b
02a4489
154338f
a9278fb
4c27e4b
4d5c60c
de31571
b4fced9
e013273
64a01a3
bb84fe7
83851cf
defa6fc
048e75c
fef0fcb
0404383
9281413
c44a813
5027c64
ee9de32
9f8a578
56322fa
804816c
8e38ba1
e505fb0
e2c3ce8
23e069b
83d33e9
cddb949
db647b2
0278f2c
b9db308
5ce35d2
8a8564e
bebe0b4
43b494d
2438bbf
c4fe257
48c83a0
628807f
28aaec4
bd3b27b
294dcef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /// A mobile surface kind identified by its open wire string. | ||
| /// | ||
| /// Known kinds have static constants, while unknown raw values remain valid so | ||
| /// older clients can preserve and route surface kinds introduced by newer Macs. | ||
| public struct MobileSurfaceKind: RawRepresentable, Codable, Hashable, Sendable { | ||
| /// The surface kind's wire identifier. | ||
| public let rawValue: String | ||
|
|
||
| /// Creates a surface kind from its wire identifier. | ||
| /// - Parameter rawValue: The open surface-kind string. | ||
| public init(rawValue: String) { | ||
| self.rawValue = rawValue | ||
| } | ||
|
|
||
| /// Decodes the kind directly from its open wire string. | ||
| public init(from decoder: any Decoder) throws { | ||
| rawValue = try decoder.singleValueContainer().decode(String.self) | ||
| } | ||
|
|
||
| /// Encodes the kind directly as its open wire string. | ||
| public func encode(to encoder: any Encoder) throws { | ||
| var container = encoder.singleValueContainer() | ||
| try container.encode(rawValue) | ||
| } | ||
|
|
||
| /// A Ghostty terminal surface. | ||
| public static let terminal = MobileSurfaceKind(rawValue: "terminal") | ||
| /// A browser surface. | ||
| public static let browser = MobileSurfaceKind(rawValue: "browser") | ||
| /// A markdown preview surface. | ||
| public static let markdown = MobileSurfaceKind(rawValue: "markdown") | ||
| /// A file preview surface. | ||
| public static let filePreview = MobileSurfaceKind(rawValue: "filePreview") | ||
| /// A right-sidebar tool hosted as a surface. | ||
| public static let rightSidebarTool = MobileSurfaceKind(rawValue: "rightSidebarTool") | ||
| /// A custom sidebar hosted as a surface. | ||
| public static let customSidebar = MobileSurfaceKind(rawValue: "customSidebar") | ||
| /// An agent-session surface. | ||
| public static let agentSession = MobileSurfaceKind(rawValue: "agentSession") | ||
| /// A project surface. | ||
| public static let project = MobileSurfaceKind(rawValue: "project") | ||
| /// A browser surface owned by an extension. | ||
| public static let extensionBrowser = MobileSurfaceKind(rawValue: "extensionBrowser") | ||
| /// A workspace todo surface. | ||
| public static let todo = MobileSurfaceKind(rawValue: "todo") | ||
| /// A transient Cloud VM loading surface. | ||
| public static let cloudVMLoading = MobileSurfaceKind(rawValue: "cloudVMLoading") | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| /// One bounded checklist item synced with a workspace todo surface. | ||
| public struct MobileTodoItem: Codable, Equatable, Identifiable, Sendable { | ||
| /// The maximum number of characters accepted for one item's normalized text. | ||
| public static let maxTextLength = 500 | ||
|
|
||
| /// The Mac-owned stable item identifier. | ||
| public let id: String | ||
| /// The normalized item text. | ||
| public let text: String | ||
| /// The item's progress state. | ||
| public let state: MobileTodoItemState | ||
| /// Who created the item. | ||
| public let origin: MobileTodoItemOrigin | ||
|
|
||
| /// Creates a mobile checklist item. | ||
| /// - Parameters: | ||
| /// - id: The Mac-owned stable item identifier. | ||
| /// - text: The normalized item text. | ||
| /// - state: The item's progress state. | ||
| /// - origin: Who created the item. | ||
| public init( | ||
| id: String, | ||
| text: String, | ||
| state: MobileTodoItemState, | ||
| origin: MobileTodoItemOrigin | ||
| ) { | ||
| self.id = id | ||
| self.text = text | ||
| self.state = state | ||
| self.origin = origin | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| /// The creator of a mobile checklist item. | ||
| public enum MobileTodoItemOrigin: String, Codable, CaseIterable, Sendable { | ||
| /// A person created the item. | ||
| case user | ||
| /// An agent created the item. | ||
| case agent | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| /// A mobile checklist item's progress state. | ||
| public enum MobileTodoItemState: String, Codable, CaseIterable, Sendable { | ||
| /// Work has not started. | ||
| case pending | ||
| /// Work is actively progressing. | ||
| case inProgress = "in_progress" | ||
| /// Work is complete. | ||
| case completed | ||
|
|
||
| /// The next state in the mobile tap cycle. | ||
| public var next: MobileTodoItemState { | ||
| switch self { | ||
| case .pending: .inProgress | ||
| case .inProgress: .completed | ||
| case .completed: .pending | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| /// The bounded todo payload attached to a synced todo surface. | ||
| public struct MobileTodoSnapshot: Codable, Equatable, Sendable { | ||
| /// The maximum number of checklist items carried by one mobile snapshot. | ||
| public static let maxItems = 50 | ||
|
|
||
| /// The effective status after applying any valid manual override. | ||
| public let status: MobileTodoStatus | ||
| /// Whether the workspace opted out of showing its status lane. | ||
| public let statusHidden: Bool | ||
| /// Checklist items in the Mac's storage order. | ||
| public let items: [MobileTodoItem] | ||
|
|
||
| /// Creates a todo snapshot. | ||
| /// - Parameters: | ||
| /// - status: The effective workspace status. | ||
| /// - statusHidden: Whether status presentation is hidden. | ||
| /// - items: Checklist items in storage order. | ||
| public init(status: MobileTodoStatus, statusHidden: Bool, items: [MobileTodoItem]) { | ||
| self.status = status | ||
| self.statusHidden = statusHidden | ||
| self.items = items | ||
|
Comment on lines
+18
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win Enforce the todo item limit before synchronization.
Apply 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| private enum CodingKeys: String, CodingKey { | ||
| case status | ||
| case statusHidden = "status_hidden" | ||
| case items | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| /// A workspace's effective todo status on the mobile wire. | ||
| public enum MobileTodoStatus: String, Codable, CaseIterable, Sendable { | ||
| /// Work has not started. | ||
| case todo | ||
| /// Work is actively progressing. | ||
| case working | ||
| /// Work is waiting for attention or input. | ||
| case needsAttention = "needs-attention" | ||
| /// Work is ready for review. | ||
| case review | ||
| /// Work is complete. | ||
| case done | ||
|
|
||
| /// The next status in the same cycle used by the Mac todo controls. | ||
| public var next: MobileTodoStatus { | ||
| let statuses = Self.allCases | ||
| guard let index = statuses.firstIndex(of: self) else { return .todo } | ||
| return statuses[(index + 1) % statuses.count] | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Enforce the todo text limit at the model boundary.
maxTextLengthis never checked. This initializer and synthesized decoding accept text longer than 500 characters. An oversized sync item can bypass the bounded-snapshot contract.Validate the length during initialization and decoding. Reject invalid payloads instead of truncating them.
🤖 Prompt for AI Agents
Source: Coding guidelines