Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,41 @@ describe('recordAgentProviderSession', () => {
})
})

it('keeps launch flags on the live record of a pane that woke from sleep', () => {
const store = createTestStore()
store.setState({
tabsByWorktree: { 'wt-1': [makeTab({ id: 'tab-1', worktreeId: 'wt-1' })] }
} as Partial<AppState>)
const paneKey = 'tab-1:leaf-1'
const launchConfig = {
agentArgs: '--dangerously-skip-permissions --model haiku',
agentEnv: { CLAUDE_PROFILE: 'work' }
}
store.getState().registerAgentLaunchConfig(paneKey, launchConfig, {
agentType: 'claude',
launchToken: 'wake-1',
tabId: 'tab-1',
leafId: 'leaf-1'
})
// The woken pane's spawn consumes the sleeping record it resumed.
store.getState().clearSleepingAgentSession(paneKey)
store
.getState()
.recordAgentProviderSession(
paneKey,
'claude',
{ key: 'session_id', id: 'claude-session-1' },
{ updatedAt: 20 },
{ tabId: 'tab-1', worktreeId: 'wt-1', connectionId: null },
{ launchToken: 'wake-1' }
)

// This record is what the next cold restore resumes from.
expect(store.getState().sleepingAgentSessionsByPaneKey[paneKey]?.launchConfig).toEqual(
launchConfig
)
})

it('keeps the activity cutoff and manual-unread stamp across a heartbeat for the same pane', () => {
const store = createTestStore()
store.setState({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ describe('captureAllSleepingAgentSessions', () => {
})
})

it('clears multiple sleeping records and launch configs in one update', () => {
it('clears multiple sleeping records in one update and leaves launch configs untouched', () => {
const store = createTestStore()
const sleeping = {
'tab-1:leaf-1': { paneKey: 'tab-1:leaf-1' },
Expand Down Expand Up @@ -594,7 +594,7 @@ describe('captureAllSleepingAgentSessions', () => {
const state = store.getState()
expect(changedUpdates).toBe(1)
expect(Object.keys(state.sleepingAgentSessionsByPaneKey)).toEqual(['tab-3:leaf-3'])
expect(Object.keys(state.agentLaunchConfigByPaneKey)).toEqual(['tab-3:leaf-3'])
expect(state.agentLaunchConfigByPaneKey).toBe(launchConfigs)
})

it('clears launch config registry entries when invalid sleeping sessions are pruned', () => {
Expand Down
18 changes: 3 additions & 15 deletions src/renderer/src/store/slices/agent-status-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,38 +184,26 @@ export function createAgentStatusRuntime(
}
})

// Why: launch configs retire with the pane; a woken pane registers its config before consuming this record.
const clearSleepingAgentSessionsByPaneKey = (paneKeys: readonly string[]): void => {
if (paneKeys.length === 0) {
return
}
const uniquePaneKeys = new Set(paneKeys)
set((s) => {
let nextSleeping = s.sleepingAgentSessionsByPaneKey
let nextLaunchConfigs = s.agentLaunchConfigByPaneKey
for (const paneKey of uniquePaneKeys) {
if (paneKey in nextSleeping) {
if (nextSleeping === s.sleepingAgentSessionsByPaneKey) {
nextSleeping = { ...nextSleeping }
}
delete nextSleeping[paneKey]
}
if (paneKey in nextLaunchConfigs) {
if (nextLaunchConfigs === s.agentLaunchConfigByPaneKey) {
nextLaunchConfigs = { ...nextLaunchConfigs }
}
delete nextLaunchConfigs[paneKey]
}
}
if (
nextSleeping === s.sleepingAgentSessionsByPaneKey &&
nextLaunchConfigs === s.agentLaunchConfigByPaneKey
) {
if (nextSleeping === s.sleepingAgentSessionsByPaneKey) {
return s
}
return {
sleepingAgentSessionsByPaneKey: nextSleeping,
agentLaunchConfigByPaneKey: nextLaunchConfigs
}
return { sleepingAgentSessionsByPaneKey: nextSleeping }
})
}

Expand Down
Loading