Skip to content

Commit 76f7586

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat-cmux-remotes-cli
# Conflicts: # .github/swift-file-length-budget.tsv
2 parents 4928c5c + fce1524 commit 76f7586

149 files changed

Lines changed: 19786 additions & 172 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/scheduled_tasks.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"sessionId":"4311956b-e43f-405e-8b49-c3990850966d","pid":43365,"procStart":"Sun Jun 14 21:55:11 2026","acquiredAt":1781485012958}
1+
{"sessionId":"5c2a62d0-99c2-42c0-9452-f67b6fbaacda","pid":75304,"procStart":"Thu Jun 4 23:34:54 2026","acquiredAt":1780616564932}

.github/swift-file-length-budget.tsv

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
# Format: max_lines<TAB>relative path
33
# Reduce counts as files shrink. CI fails if tracked files exceed this budget.
44
34475 CLI/cmux.swift
5-
17893 Sources/AppDelegate.swift
5+
17894 Sources/AppDelegate.swift
66
16696 Sources/ContentView.swift
7-
14824 Sources/TerminalController.swift
7+
14829 Sources/TerminalController.swift
88
13358 Sources/Panels/BrowserPanel.swift
9-
12998 Sources/Workspace.swift
9+
13017 Sources/Workspace.swift
1010
12124 Sources/GhosttyTerminalView.swift
1111
12046 cmuxTests/AppDelegateShortcutRoutingTests.swift
1212
9331 cmuxTests/CLINotifyProcessIntegrationRegressionTests.swift
@@ -19,17 +19,17 @@
1919
6153 CLI/cmux_open.swift
2020
6074 Sources/TextBoxInput.swift
2121
5925 cmuxTests/TerminalAndGhosttyTests.swift
22+
5553 Packages/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
2223
5526 cmuxTests/BrowserConfigTests.swift
23-
5113 Packages/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
2424
4920 Sources/cmuxApp.swift
2525
4467 Sources/Panels/FilePreviewPanel.swift
2626
4400 cmuxTests/BrowserPanelTests.swift
2727
4227 Sources/BrowserWindowPortal.swift
2828
3937 Sources/Feed/FeedPanelView.swift
2929
3926 cmuxTests/TabManagerUnitTests.swift
3030
3903 cmuxTests/WindowAndDragTests.swift
31+
3734 Packages/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
3132
3699 cmuxTests/CLIGenericHookPersistenceTests.swift
32-
3672 Packages/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
3333
3397 Sources/CmuxConfig.swift
3434
3331 cmuxTests/TabManagerSessionSnapshotTests.swift
3535
3055 Sources/Update/UpdateTitlebarAccessory.swift
@@ -100,6 +100,7 @@
100100
905 Sources/CmuxSSHURLRequest.swift
101101
901 Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift
102102
878 Sources/Panels/TerminalPanel.swift
103+
877 Packages/CmuxAgentChat/Tests/CmuxAgentChatTests/ChatConversationStoreTests.swift
103104
868 Sources/Panels/BrowserScreenshotSnapshotter.swift
104105
859 Packages/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlCommandCoordinator+Workspace.swift
105106
847 cmuxTests/AgentSessionAutoResumeSettingsTests.swift
@@ -119,10 +120,13 @@
119120
752 cmuxUITests/CloseWorkspaceCmdDUITests.swift
120121
749 Packages/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+Input.swift
121122
746 Sources/App/MenuBarExtraController.swift
123+
744 Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalComposerView.swift
122124
738 Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcodeProjectAdapter.swift
123125
736 Packages/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Coordinator/AuthCoordinator.swift
126+
726 Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
124127
726 cmuxTests/CLICodexHookTimeoutRegressionTests.swift
125128
725 Sources/RightSidebarPanelView.swift
129+
722 Packages/CmuxAgentChat/Sources/CmuxAgentChat/Store/ChatConversationStore.swift
126130
716 Sources/TaskManagerSnapshot.swift
127131
715 Sources/AppleScriptSupport.swift
128132
710 Sources/TerminalSSHSessionDetector.swift
@@ -173,6 +177,7 @@
173177
574 Sources/Feed/FeedTextEditorDebugWindowController.swift
174178
568 Packages/CMUXMobileCore/Sources/CMUXMobileCore/MobileTerminalRenderGrid.swift
175179
566 Packages/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizer.swift
180+
562 Packages/CmuxAgentChatUI/Sources/CmuxAgentChatUI/Transcript/ChatTranscriptTableView.swift
176181
562 cmuxTests/AgentExecutableResolverTests.swift
177182
561 cmuxTests/GhosttyConfigPathResolverTests.swift
178183
558 Packages/CmuxGit/Sources/CmuxGit/Parsing/GitMetadataService+Config.swift
@@ -199,7 +204,6 @@
199204
520 cmuxTests/MainWindowVisibilityControllerTests.swift
200205
519 Packages/CmuxSwiftRender/Tests/CmuxSwiftRenderTests/Corpus/stress-two-column-cockpit-sidebar.swift
201206
519 Sources/CmuxConfigExecutor.swift
202-
518 Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
203207
518 Packages/CmuxSwiftRender/Tests/CmuxSwiftRenderTests/Corpus/stress-git-review-queue-command-deck.swift
204208
517 Sources/TerminalImageTransfer.swift
205209
514 Packages/CmuxSwiftRender/Sources/CmuxSwiftRender/ExpressionEvaluator.swift
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version: 6.0
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "CmuxAgentChat",
7+
platforms: [
8+
.iOS(.v18),
9+
.macOS(.v14),
10+
],
11+
products: [
12+
.library(
13+
name: "CmuxAgentChat",
14+
targets: ["CmuxAgentChat"]
15+
),
16+
],
17+
targets: [
18+
.target(
19+
name: "CmuxAgentChat",
20+
swiftSettings: [.swiftLanguageMode(.v6)]
21+
),
22+
.testTarget(
23+
name: "CmuxAgentChatTests",
24+
dependencies: ["CmuxAgentChat"],
25+
swiftSettings: [.swiftLanguageMode(.v6)]
26+
),
27+
]
28+
)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import Foundation
2+
3+
/// Which coding agent runtime a session belongs to.
4+
///
5+
/// Raw values match the `_source` strings the agent hook integrations emit,
6+
/// so hook events map directly. Unknown sources round-trip through
7+
/// ``ChatAgentKind/other(_:)``.
8+
public enum ChatAgentKind: Sendable, Equatable, Hashable {
9+
/// Claude Code.
10+
case claude
11+
/// OpenAI Codex CLI.
12+
case codex
13+
/// Any other agent runtime, identified by its raw `_source` string.
14+
case other(String)
15+
16+
/// Creates a kind from a raw `_source` string.
17+
///
18+
/// - Parameter source: The hook event source identifier.
19+
public init(source: String) {
20+
switch source {
21+
case "claude": self = .claude
22+
case "codex": self = .codex
23+
default: self = .other(source)
24+
}
25+
}
26+
27+
/// The raw `_source` string this kind round-trips to.
28+
public var sourceName: String {
29+
switch self {
30+
case .claude: return "claude"
31+
case .codex: return "codex"
32+
case .other(let source): return source
33+
}
34+
}
35+
36+
/// Short human-readable display name (e.g. "Claude").
37+
public var displayName: String {
38+
switch self {
39+
case .claude: return "Claude"
40+
case .codex: return "Codex"
41+
case .other(let source): return source.capitalized
42+
}
43+
}
44+
}
45+
46+
extension ChatAgentKind: Codable {
47+
public init(from decoder: any Decoder) throws {
48+
let container = try decoder.singleValueContainer()
49+
self.init(source: try container.decode(String.self))
50+
}
51+
52+
public func encode(to encoder: any Encoder) throws {
53+
var container = encoder.singleValueContainer()
54+
try container.encode(sourceName)
55+
}
56+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import Foundation
2+
3+
/// The live activity state of an agent session.
4+
///
5+
/// Drives the typing indicator and the session header dot. This is
6+
/// transient presence, not transcript content; it never becomes a
7+
/// ``ChatMessage`` row.
8+
public enum ChatAgentState: Sendable, Equatable {
9+
/// The agent is idle, awaiting input.
10+
case idle
11+
/// The agent has been working since the associated time.
12+
case working(since: Date)
13+
/// The agent is blocked on the user (question or permission) since the
14+
/// associated time.
15+
case needsInput(since: Date)
16+
/// The agent process ended.
17+
case ended
18+
19+
/// Whether the user's attention is required.
20+
public var needsAttention: Bool {
21+
if case .needsInput = self { return true }
22+
return false
23+
}
24+
}
25+
26+
extension ChatAgentState: Codable {
27+
private enum CodingKeys: String, CodingKey {
28+
case state
29+
case since
30+
}
31+
32+
private enum StateName: String {
33+
case idle
34+
case working
35+
case needsInput = "needs_input"
36+
case ended
37+
}
38+
39+
public init(from decoder: any Decoder) throws {
40+
let container = try decoder.container(keyedBy: CodingKeys.self)
41+
let raw = try container.decode(String.self, forKey: .state)
42+
switch StateName(rawValue: raw) {
43+
case .idle:
44+
self = .idle
45+
case .working:
46+
self = .working(since: try container.decode(Date.self, forKey: .since))
47+
case .needsInput:
48+
self = .needsInput(since: try container.decode(Date.self, forKey: .since))
49+
case .ended:
50+
self = .ended
51+
case .none:
52+
self = .idle
53+
}
54+
}
55+
56+
public func encode(to encoder: any Encoder) throws {
57+
var container = encoder.container(keyedBy: CodingKeys.self)
58+
switch self {
59+
case .idle:
60+
try container.encode(StateName.idle.rawValue, forKey: .state)
61+
case .working(let since):
62+
try container.encode(StateName.working.rawValue, forKey: .state)
63+
try container.encode(since, forKey: .since)
64+
case .needsInput(let since):
65+
try container.encode(StateName.needsInput.rawValue, forKey: .state)
66+
try container.encode(since, forKey: .since)
67+
case .ended:
68+
try container.encode(StateName.ended.rawValue, forKey: .state)
69+
}
70+
}
71+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/// An image or file the user attached to a prompt.
2+
///
3+
/// The binary payload travels out-of-band (image paste RPC); the transcript
4+
/// message carries only display metadata.
5+
public struct ChatAttachment: Sendable, Equatable, Codable {
6+
/// The attachment's media category.
7+
public enum Media: String, Sendable, Equatable, Codable {
8+
/// A raster image (photo, screenshot).
9+
case image
10+
/// Any other file.
11+
case file
12+
}
13+
14+
/// The attachment's media category.
15+
public let media: Media
16+
17+
/// Display name, when one is known (e.g. the original filename).
18+
public let displayName: String?
19+
20+
/// Path on the host where the attachment was materialized, when known.
21+
/// Lets renderers reference what the agent sees.
22+
public let hostPath: String?
23+
24+
/// Creates attachment metadata.
25+
///
26+
/// - Parameters:
27+
/// - media: The media category.
28+
/// - displayName: Display name when known.
29+
/// - hostPath: Host-side materialized path when known.
30+
public init(media: Media, displayName: String? = nil, hostPath: String? = nil) {
31+
self.media = media
32+
self.displayName = displayName
33+
self.hostPath = hostPath
34+
}
35+
36+
private enum CodingKeys: String, CodingKey {
37+
case media
38+
case displayName = "display_name"
39+
case hostPath = "host_path"
40+
}
41+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/// A file modification by the agent; renders as a diff card.
2+
public struct ChatFileEdit: Sendable, Equatable, Codable {
3+
/// The nature of the modification.
4+
public enum Operation: String, Sendable, Equatable, Codable {
5+
/// An in-place edit of an existing file.
6+
case edit
7+
/// A whole-file write (create or overwrite).
8+
case write
9+
/// A file deletion.
10+
case delete
11+
}
12+
13+
/// Path of the modified file, as the agent reported it.
14+
public let filePath: String
15+
16+
/// The nature of the modification.
17+
public let operation: Operation
18+
19+
/// Count of added lines, when computable.
20+
public let additions: Int?
21+
22+
/// Count of removed lines, when computable.
23+
public let deletions: Int?
24+
25+
/// A unified-diff rendering of the change, possibly truncated at the
26+
/// producing side. `nil` when the producer could not construct one.
27+
public let unifiedDiff: String?
28+
29+
/// Creates a file edit record.
30+
///
31+
/// - Parameters:
32+
/// - filePath: Path of the modified file.
33+
/// - operation: The nature of the modification.
34+
/// - additions: Added-line count when computable.
35+
/// - deletions: Removed-line count when computable.
36+
/// - unifiedDiff: Unified-diff text, possibly truncated.
37+
public init(
38+
filePath: String,
39+
operation: Operation,
40+
additions: Int? = nil,
41+
deletions: Int? = nil,
42+
unifiedDiff: String? = nil
43+
) {
44+
self.filePath = filePath
45+
self.operation = operation
46+
self.additions = additions
47+
self.deletions = deletions
48+
self.unifiedDiff = unifiedDiff
49+
}
50+
51+
private enum CodingKeys: String, CodingKey {
52+
case filePath = "file_path"
53+
case operation
54+
case additions
55+
case deletions
56+
case unifiedDiff = "unified_diff"
57+
}
58+
}

0 commit comments

Comments
 (0)