Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
37590ac
Add restored agent icon exit regression coverage
austinywang Jul 10, 2026
aef76d9
Clear restored agent tab icon after shell exit
austinywang Jul 10, 2026
aec7ea1
Preserve auto-resume agent icon during startup
austinywang Jul 10, 2026
528e6e0
Decouple restored agent metadata from observed tab icons
austinywang Jul 10, 2026
52cd890
Add prompt-idle title-derived icon regression
austinywang Jul 10, 2026
b8af58b
Clear title-derived agent icons at shell prompt
austinywang Jul 10, 2026
b2c9019
Merge remote-tracking branch 'origin/main' into issue-7822-restored-a…
austinywang Jul 10, 2026
95455ea
Add prompt-idle live-agent icon regression
austinywang Jul 10, 2026
9d90da5
Suppress observed agent icons at shell prompt
austinywang Jul 10, 2026
1374b3f
Preserve restored metadata after agent prompt exit
austinywang Jul 10, 2026
c48b0e0
Keep restore icons for unstated resume snapshots
austinywang Jul 10, 2026
dbc9363
Add terminal-only tab icon regression
austinywang Jul 10, 2026
b7df744
Restore terminal-only tab icons
austinywang Jul 10, 2026
79a61ea
Merge remote-tracking branch 'origin/main' into issue-7822-restored-a…
austinywang Jul 10, 2026
e395602
Merge remote-tracking branch 'origin/main' into issue-7822-restored-a…
austinywang Jul 10, 2026
fbb60b5
Add completed-agent input routing regression
austinywang Jul 10, 2026
744ab7e
Stop completed agents affecting input routing
austinywang Jul 10, 2026
43b5b02
Add completed-agent fork regression
austinywang Jul 10, 2026
b2e8ec5
Stop completed agents driving fork actions
austinywang Jul 10, 2026
c3a5a83
Add completed-agent shell command regression
austinywang Jul 10, 2026
40f398d
Preserve completed-agent metadata through shell commands
austinywang Jul 10, 2026
98dd67c
Add completed-agent replacement regression
austinywang Jul 10, 2026
80f5698
Allow newer agent sessions after completed restores
austinywang Jul 10, 2026
46ee5b8
Merge remote-tracking branch 'origin/main' into issue-7822-restored-a…
austinywang Jul 10, 2026
f258561
Track completed restored-agent generations
austinywang Jul 10, 2026
449c8de
Address restored-agent review feedback
austinywang Jul 10, 2026
eac677c
Route package tests to dual-Xcode runner
austinywang Jul 10, 2026
304f169
Harden completed-agent generation handoff
austinywang Jul 10, 2026
f2d93f4
Fix completed-agent transfer compilation
austinywang Jul 10, 2026
53a2149
Import CmuxWorkspaces in dock reattach tests
austinywang Jul 10, 2026
9c0ab58
Provide node for shard-4 CLI wrapper regressions
austinywang Jul 10, 2026
06a7430
Merge remote-tracking branch 'origin/main' into issue-7822-restored-a…
austinywang Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions Sources/AppDelegate+ResolvedIconRendering.swift

This file was deleted.

46 changes: 41 additions & 5 deletions Sources/ContentView+ForkAgentConversation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ extension ContentView {
panelId: panelId
)

let fallbackSnapshot = currentContext.workspace.restoredAgentSnapshotsByPanelId[panelId]
let allowsAgentContinuation = currentContext.workspace.allowsAgentContinuation(forPanelId: panelId)
let fallbackSnapshot = currentContext.workspace.restoredAgentSnapshotForContinuation(panelId: panelId)
let isRemoteContext = currentContext.workspace.isRemoteTerminalSurface(panelId)
let selection = Self.commandPaletteImmediateForkExecutionSnapshotSelection(
workspaceId: workspaceId,
Expand All @@ -52,7 +53,8 @@ extension ContentView {
supportedRemoteContextsByPanelKey: commandPaletteForkableAgentRemoteContextsByPanelKey,
snapshotFingerprintsByPanelKey: commandPaletteForkableAgentSnapshotFingerprintsByPanelKey,
fallbackSnapshot: fallbackSnapshot,
cachedSnapshot: commandPaletteForkableAgentSnapshotsByPanelKey[panelKey]
cachedSnapshot: commandPaletteForkableAgentSnapshotsByPanelKey[panelKey],
allowsAgentContinuation: allowsAgentContinuation
)
guard let selection else {
clearCommandPaletteForkableAgentCache(panelKey: panelKey)
Expand Down Expand Up @@ -149,6 +151,36 @@ extension ContentView {
let usedFallbackSnapshot: Bool
}

static func commandPalettePanelHasForkableAgent(
workspaceId: UUID,
panelId: UUID,
supportedPanelKeys: Set<String>,
supportedRemoteContextsByPanelKey: [String: Bool] = [:],
fallbackSnapshot: SessionRestorableAgentSnapshot?,
isRemoteTerminal: Bool = false,
allowsAgentContinuation: Bool = true
) -> Bool {
guard allowsAgentContinuation else { return false }
Comment thread
coderabbitai[bot] marked this conversation as resolved.
let panelKey = commandPaletteForkableAgentPanelKey(
workspaceId: workspaceId,
panelId: panelId
)
if supportedPanelKeys.contains(panelKey) {
if let supportedRemoteContext = supportedRemoteContextsByPanelKey[panelKey],
supportedRemoteContext != isRemoteTerminal {
return false
}
if let fallbackSnapshot {
return commandPaletteSnapshotForkAvailability(
fallbackSnapshot,
isRemoteTerminal: isRemoteTerminal
) != .unsupported
}
return true
}
return false
}

static func commandPaletteImmediateForkExecutionSnapshot(
workspaceId: UUID,
panelId: UUID,
Expand All @@ -157,7 +189,8 @@ extension ContentView {
supportedRemoteContextsByPanelKey: [String: Bool],
snapshotFingerprintsByPanelKey: [String: String],
fallbackSnapshot: SessionRestorableAgentSnapshot?,
cachedSnapshot: SessionRestorableAgentSnapshot?
cachedSnapshot: SessionRestorableAgentSnapshot?,
allowsAgentContinuation: Bool = true
) -> SessionRestorableAgentSnapshot? {
commandPaletteImmediateForkExecutionSnapshotSelection(
workspaceId: workspaceId,
Expand All @@ -167,7 +200,8 @@ extension ContentView {
supportedRemoteContextsByPanelKey: supportedRemoteContextsByPanelKey,
snapshotFingerprintsByPanelKey: snapshotFingerprintsByPanelKey,
fallbackSnapshot: fallbackSnapshot,
cachedSnapshot: cachedSnapshot
cachedSnapshot: cachedSnapshot,
allowsAgentContinuation: allowsAgentContinuation
)?.snapshot
}

Expand All @@ -179,8 +213,10 @@ extension ContentView {
supportedRemoteContextsByPanelKey: [String: Bool],
snapshotFingerprintsByPanelKey: [String: String],
fallbackSnapshot: SessionRestorableAgentSnapshot?,
cachedSnapshot: SessionRestorableAgentSnapshot?
cachedSnapshot: SessionRestorableAgentSnapshot?,
allowsAgentContinuation: Bool = true
) -> CommandPaletteForkSnapshotSelection? {
guard allowsAgentContinuation else { return nil }
let panelKey = commandPaletteForkableAgentPanelKey(
workspaceId: workspaceId,
panelId: panelId
Expand Down
48 changes: 16 additions & 32 deletions Sources/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5687,34 +5687,6 @@ struct ContentView: View {
cachedResultHadFallback ?? true
}

static func commandPalettePanelHasForkableAgent(
workspaceId: UUID,
panelId: UUID,
supportedPanelKeys: Set<String>,
supportedRemoteContextsByPanelKey: [String: Bool] = [:],
fallbackSnapshot: SessionRestorableAgentSnapshot?,
isRemoteTerminal: Bool = false
) -> Bool {
let panelKey = commandPaletteForkableAgentPanelKey(
workspaceId: workspaceId,
panelId: panelId
)
if supportedPanelKeys.contains(panelKey) {
if let supportedRemoteContext = supportedRemoteContextsByPanelKey[panelKey],
supportedRemoteContext != isRemoteTerminal {
return false
}
if let fallbackSnapshot {
return commandPaletteSnapshotForkAvailability(
fallbackSnapshot,
isRemoteTerminal: isRemoteTerminal
) != .unsupported
}
return true
}
return false
}

private func refreshCommandPaletteForkableAgentAvailabilityIfNeeded(scope: CommandPaletteListScope) {
guard scope == .commands,
let panelContext = focusedPanelContext,
Expand All @@ -5730,7 +5702,17 @@ struct ContentView: View {
let panelKey = Self.commandPaletteForkableAgentPanelKey(workspaceId: workspaceId, panelId: panelId)
let panelChanged = commandPaletteForkableAgentActivePanelKey != panelKey
commandPaletteForkableAgentActivePanelKey = panelKey
let fallbackSnapshot = panelContext.workspace.restoredAgentSnapshotsByPanelId[panelId]
let allowsAgentContinuation = panelContext.workspace.allowsAgentContinuation(forPanelId: panelId)
guard allowsAgentContinuation else {
cancelCommandPaletteForkableAgentAvailabilityProbe(for: panelKey)
commandPaletteForkableAgentSupportedPanelKeys.remove(panelKey)
commandPaletteForkableAgentSnapshotsByPanelKey.removeValue(forKey: panelKey)
commandPaletteForkableAgentSnapshotFingerprintsByPanelKey.removeValue(forKey: panelKey)
commandPaletteForkableAgentRemoteContextsByPanelKey.removeValue(forKey: panelKey)
commandPaletteForkableAgentResultHadFallbackByPanelKey.removeValue(forKey: panelKey)
return
}
let fallbackSnapshot = panelContext.workspace.restoredAgentSnapshotForContinuation(panelId: panelId)

if let fallbackSnapshot {
let fallbackFingerprint = Self.commandPaletteForkSnapshotFingerprint(fallbackSnapshot)
Expand Down Expand Up @@ -5920,7 +5902,7 @@ struct ContentView: View {
let currentContext = focusedPanelContext,
currentContext.workspace.id == workspaceId,
currentContext.panelId == panelId,
let currentFallbackSnapshot = currentContext.workspace.restoredAgentSnapshotsByPanelId[panelId],
let currentFallbackSnapshot = currentContext.workspace.restoredAgentSnapshotForContinuation(panelId: panelId),
Self.commandPaletteForkSnapshotFingerprint(currentFallbackSnapshot) != fallbackFingerprint {
commandPaletteForkableAgentProbeIDsByPanelKey.removeValue(forKey: panelKey)
commandPaletteForkableAgentProbeFingerprintsByPanelKey.removeValue(forKey: panelKey)
Expand Down Expand Up @@ -6203,7 +6185,8 @@ struct ContentView: View {
)
snapshot.setBool(CommandPaletteContextKeys.panelIsTerminal, panelIsTerminal)
snapshot.setBool(CommandPaletteContextKeys.panelHasPane, workspace.paneId(forPanelId: panelId) != nil)
let fallbackForkableSnapshot = workspace.restoredAgentSnapshotsByPanelId[panelId]
let allowsAgentContinuation = workspace.allowsAgentContinuation(forPanelId: panelId)
let fallbackForkableSnapshot = workspace.restoredAgentSnapshotForContinuation(panelId: panelId)
snapshot.setBool(
CommandPaletteContextKeys.panelHasForkableAgent,
Self.commandPalettePanelHasForkableAgent(
Expand All @@ -6212,7 +6195,8 @@ struct ContentView: View {
supportedPanelKeys: commandPaletteForkableAgentSupportedPanelKeys,
supportedRemoteContextsByPanelKey: commandPaletteForkableAgentRemoteContextsByPanelKey,
fallbackSnapshot: fallbackForkableSnapshot,
isRemoteTerminal: panelIsRemoteTerminal
isRemoteTerminal: panelIsRemoteTerminal,
allowsAgentContinuation: allowsAgentContinuation
)
)
snapshot.setBool(CommandPaletteContextKeys.panelHasCustomName, workspace.panelCustomTitles[panelId] != nil)
Expand Down
19 changes: 2 additions & 17 deletions Sources/DockSplitStore+SurfaceTransfer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,13 @@ extension DockSplitStore {
panelCancellables.removeValue(forKey: panelId)
surfaceIdToPanelId.removeValue(forKey: tabId)
panels.removeValue(forKey: panelId)
let preservedTitleDerivedAgentStatusKey = titleDerivedAgentStatusKeysByPanelId.removeValue(forKey: panelId)

forceCloseDockTabIds.insert(tabId)
defer { forceCloseDockTabIds.remove(tabId) }
guard bonsplitController.closeTab(tabId) else {
// Close rejected: re-take ownership so the Dock stays consistent.
panels[panelId] = panel
surfaceIdToPanelId[tabId] = panelId
if let preservedTitleDerivedAgentStatusKey {
titleDerivedAgentStatusKeysByPanelId[panelId] = preservedTitleDerivedAgentStatusKey
}
if let preservedTransfer {
detachedSurfaceTransfersByPanelId[panelId] = preservedTransfer
}
Expand Down Expand Up @@ -226,22 +222,11 @@ extension DockSplitStore {
// read is unavailable.
detachedSurfaceTransfersByPanelId[detached.panelId] = detached
let kind = detached.kind ?? ((panel.panelType == .browser) ? "browser" : "terminal")
if kind == "terminal" {
_ = updateTitleDerivedTerminalAgentStatusKey(
forPanelId: detached.panelId,
title: detached.cachedTitle ?? detached.panel.displayTitle
)
}
let restoredIconPayload: (imageData: Data?, assetName: String?) = {
guard detached.panel is TerminalPanel else { return (detached.iconImageData, nil) }
let payload = terminalTabAgentIconPayload(forPanelId: detached.panelId)
return (payload.imageData, payload.assetName)
}()
let restoredIconImageData = detached.panel is TerminalPanel ? nil : detached.iconImageData
guard let newTabId = bonsplitController.createTab(
title: detached.title,
icon: detached.icon,
iconImageData: restoredIconPayload.imageData,
iconAsset: restoredIconPayload.assetName,
iconImageData: restoredIconImageData,
kind: kind,
isDirty: panel.isDirty,
isLoading: detached.isLoading,
Expand Down
19 changes: 2 additions & 17 deletions Sources/DockSplitStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ final class DockSplitStore: BonsplitDelegate {
var panels: [UUID: any Panel] = [:]
var surfaceIdToPanelId: [TabID: UUID] = [:]
var panelCancellables: [UUID: AnyCancellable] = [:]
@ObservationIgnored
var titleDerivedAgentStatusKeysByPanelId: [UUID: String] = [:]
@ObservationIgnored var detachedSurfaceTransfersByPanelId: [UUID: Workspace.DetachedSurfaceTransfer] = [:]
private var hasLoadedConfiguration = false
private var configurationLoadTask: Task<Void, Never>?
Expand Down Expand Up @@ -599,19 +597,8 @@ final class DockSplitStore: BonsplitDelegate {
// unchanged, so a terminal re-emitting the same title does not
// re-render the Dock tree.
let resolvedTitle = terminal.displayTitle
let didMutateTitleDerivedAgent = self.updateTitleDerivedTerminalAgentStatusKey(
forPanelId: terminal.id,
title: resolvedTitle
)
let titleUpdate: String? = existing.title == resolvedTitle ? nil : resolvedTitle
let iconPayloadUpdate = didMutateTitleDerivedAgent ? self.terminalTabAgentIconPayload(forPanelId: terminal.id) : nil
guard titleUpdate != nil || iconPayloadUpdate != nil else { return }
self.bonsplitController.updateTab(
tabId,
title: titleUpdate,
iconImageData: iconPayloadUpdate.map(\.imageData),
iconAsset: iconPayloadUpdate.map(\.assetName)
)
guard existing.title != resolvedTitle else { return }
self.bonsplitController.updateTab(tabId, title: resolvedTitle)
}
panelCancellables[panel.id] = cancellable
}
Expand All @@ -630,7 +617,6 @@ final class DockSplitStore: BonsplitDelegate {
panelCancellables.removeValue(forKey: panelId)
AppDelegate.shared?.notificationStore?.clearNotifications(forTabId: workspaceId, surfaceId: panelId)
detachedSurfaceTransfersByPanelId.removeValue(forKey: panelId)
titleDerivedAgentStatusKeysByPanelId.removeValue(forKey: panelId)
if let panel = panels.removeValue(forKey: panelId) { panel.close() }
}
}
Expand Down Expand Up @@ -674,7 +660,6 @@ final class DockSplitStore: BonsplitDelegate {
for panel in panels.values { panel.close() }
panels.removeAll(); surfaceIdToPanelId.removeAll()
detachedSurfaceTransfersByPanelId.removeAll()
titleDerivedAgentStatusKeysByPanelId.removeAll()
panelCancellables.values.forEach { $0.cancel() }
panelCancellables.removeAll()
}
Expand Down
89 changes: 0 additions & 89 deletions Sources/ResolvedIconAppearanceObserver.swift

This file was deleted.

Loading
Loading