diff --git a/src/main/agent-hooks/server.test.ts b/src/main/agent-hooks/server.test.ts index 896ff4bb6b3..a11b4b757a4 100644 --- a/src/main/agent-hooks/server.test.ts +++ b/src/main/agent-hooks/server.test.ts @@ -6182,10 +6182,7 @@ describe('AgentHookServer reported cwd merge', () => { envelope: Record, connectionId = 'conn-1' ): void => { - server.ingestRemote( - envelope as Parameters[0], - connectionId - ) + server.ingestRemote(envelope as Parameters[0], connectionId) } const workingPayload = (overrides: Record = {}) => ({ state: 'working', diff --git a/src/renderer/src/components/sidebar/WorktreeCardAgents.live-worktree-mismatch.test.tsx b/src/renderer/src/components/sidebar/WorktreeCardAgents.live-worktree-mismatch.test.tsx new file mode 100644 index 00000000000..0c6cfde0c41 --- /dev/null +++ b/src/renderer/src/components/sidebar/WorktreeCardAgents.live-worktree-mismatch.test.tsx @@ -0,0 +1,187 @@ +import { renderToStaticMarkup } from 'react-dom/server' +import type { ReactNode } from 'react' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +type MockAgentOptions = { + paneKey?: string + agentType?: string + startedAt?: number + stateStartedAt?: number + prompt?: string + lastAssistantMessage?: string + liveWorktreeMismatch?: { destinationWorktreeId: string; destinationLabel: string } +} + +function mockAgent({ + paneKey = 'tab-1:1', + agentType, + startedAt, + stateStartedAt = 1000, + prompt, + lastAssistantMessage, + liveWorktreeMismatch +}: MockAgentOptions = {}): unknown { + return { + paneKey, + tab: { id: paneKey.split(':')[0] }, + agentType, + rowSource: 'live', + liveWorktreeMismatch, + state: 'working', + startedAt, + entry: { + prompt, + lastAssistantMessage, + state: 'working', + stateStartedAt, + stateHistory: prompt === undefined ? undefined : [] + } + } +} + +let mockAgents: unknown[] = [] + +vi.mock('@/store', () => ({ + useAppStore: (selector: (state: unknown) => unknown) => + selector({ + agentActivityDisplayMode: 'compact', + acknowledgedAgentsByPaneKey: {}, + cacheTimerByKey: {}, + dropAgentStatus: vi.fn(), + dismissRetainedAgent: vi.fn(), + acknowledgeAgents: vi.fn(), + agentSendPopoverTargetMode: null, + agentStatusByPaneKey: {}, + tabsByWorktree: {}, + terminalLayoutsByTabId: {}, + sendPromptToSidebarAgentTarget: vi.fn(), + settings: { promptCacheTimerEnabled: true, promptCacheTtlMs: 60_000 } + }) +})) + +vi.mock('./useWorktreeAgentRows', () => ({ + useWorktreeAgentRows: vi.fn(() => mockAgents) +})) + +vi.mock('@/components/dashboard/useNow', () => ({ + useNow: vi.fn(() => 2000) +})) + +vi.mock('./prompt-cache-countdown-clock', () => ({ + usePromptCacheCountdownNow: vi.fn(() => 10_000) +})) + +vi.mock('@/components/dashboard/DashboardAgentRow', () => ({ + default: ({ agent }: { agent: { paneKey: string } }) => ( +
+ ) +})) + +vi.mock('./focused-agent-row-highlight', () => ({ + useFocusedAgentPaneKey: vi.fn(() => null) +})) + +vi.mock('@/components/ui/tooltip', () => ({ + Tooltip: ({ children }: { children: ReactNode }) => <>{children}, + TooltipContent: ({ children }: { children: ReactNode }) => <>{children}, + TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children} +})) + +describe('WorktreeCardAgents live worktree mismatch', () => { + beforeEach(() => { + vi.clearAllMocks() + mockAgents = [] + }) + + it('notes the destination worktree on a compact row without dropping its details', async () => { + mockAgents = [ + mockAgent({ + agentType: 'codex', + startedAt: 1000, + prompt: 'Run tests', + lastAssistantMessage: 'Inspecting changes', + liveWorktreeMismatch: { + destinationWorktreeId: 'wt-scratch', + destinationLabel: 'scratch-fix' + } + }) + ] + const { default: WorktreeCardAgents } = await import('./WorktreeCardAgents') + + const markup = renderToStaticMarkup() + + expect(markup).toContain('data-agent-live-worktree-mismatch') + expect(markup).toContain(' · in scratch-fix') + expect(markup).toContain('Run tests') + expect(markup).toContain(' - Inspecting changes') + expect(markup).toContain('title="Run tests · in scratch-fix - Inspecting changes"') + }, 30_000) + + it('summarizes hidden panes running elsewhere on the collapsed compact affordance', async () => { + mockAgents = [ + mockAgent({ + agentType: 'codex', + startedAt: 1000, + prompt: 'One', + liveWorktreeMismatch: { + destinationWorktreeId: 'wt-scratch', + destinationLabel: 'scratch-fix' + } + }), + mockAgent({ + paneKey: 'tab-1:2', + agentType: 'claude', + startedAt: 1500, + stateStartedAt: 1500, + prompt: 'Two', + liveWorktreeMismatch: { + destinationWorktreeId: 'wt-other', + destinationLabel: 'other-fix' + } + }), + mockAgent({ + paneKey: 'tab-1:3', + agentType: 'gemini', + startedAt: 1700, + stateStartedAt: 1700, + prompt: 'Three' + }) + ] + const { default: WorktreeCardAgents } = await import('./WorktreeCardAgents') + + const markup = renderToStaticMarkup() + + expect(markup).toContain('aria-expanded="false"') + expect(markup).toContain('data-agent-live-worktree-mismatch-summary') + expect(markup).toContain('2 elsewhere') + expect(markup).toContain('2 agents in another worktree') + expect(markup).not.toContain('3 agents in another worktree') + }) + + it('uses the singular hidden-elsewhere phrasing for a single mismatched pane', async () => { + mockAgents = [ + mockAgent({ + agentType: 'codex', + startedAt: 1000, + prompt: 'One', + liveWorktreeMismatch: { + destinationWorktreeId: 'wt-scratch', + destinationLabel: 'scratch-fix' + } + }), + mockAgent({ + paneKey: 'tab-1:2', + agentType: 'claude', + startedAt: 1500, + stateStartedAt: 1500, + prompt: 'Two' + }) + ] + const { default: WorktreeCardAgents } = await import('./WorktreeCardAgents') + + const markup = renderToStaticMarkup() + + expect(markup).toContain('1 elsewhere') + expect(markup).toContain('1 agent in another worktree') + }) +}) diff --git a/src/renderer/src/components/sidebar/WorktreeCardAgents.send-target.test.tsx b/src/renderer/src/components/sidebar/WorktreeCardAgents.send-target.test.tsx index cab570fc781..a0241b95b9a 100644 --- a/src/renderer/src/components/sidebar/WorktreeCardAgents.send-target.test.tsx +++ b/src/renderer/src/components/sidebar/WorktreeCardAgents.send-target.test.tsx @@ -235,4 +235,40 @@ describe('WorktreeCardAgents send targets', () => { expect(markup).not.toContain('data-agent-send-target="disabled"') expect(markup).not.toContain('title="Agent is working"') }) + + it('keeps every compact send target visible instead of summarizing panes elsewhere', async () => { + const now = Date.now() + mockAgents = [ + { + ...(agentRow(READY_PANE_KEY, 'done', now) as Record), + rowSource: 'live', + liveWorktreeMismatch: { + destinationWorktreeId: 'wt-scratch', + destinationLabel: 'scratch-fix' + } + }, + { + ...(agentRow(WORKING_PANE_KEY, 'working', now) as Record), + rowSource: 'live', + liveWorktreeMismatch: { + destinationWorktreeId: 'wt-other', + destinationLabel: 'other-fix' + } + } + ] + mockStoreState = { + ...targetStoreState(now), + agentActivityDisplayMode: 'compact', + agentSendPopoverTargetMode: activeTargetMode() + } + const { default: WorktreeCardAgents } = await import('./WorktreeCardAgents') + + const markup = renderToStaticMarkup() + + // Why: target selection lists every pane, so a collapsed "N elsewhere" badge would hide choices. + expect(markup).not.toContain('data-agent-live-worktree-mismatch-summary') + expect(markup).not.toContain('elsewhere') + expect(markup).toContain('in scratch-fix') + expect(markup).toContain('in other-fix') + }) }) diff --git a/src/renderer/src/components/sidebar/WorktreeCardAgents.test.tsx b/src/renderer/src/components/sidebar/WorktreeCardAgents.test.tsx index 3a017d97570..d5591b9e9d5 100644 --- a/src/renderer/src/components/sidebar/WorktreeCardAgents.test.tsx +++ b/src/renderer/src/components/sidebar/WorktreeCardAgents.test.tsx @@ -29,7 +29,6 @@ type MockAgentOptions = { isLastSibling: boolean childCount: number } - liveWorktreeMismatch?: { destinationWorktreeId: string; destinationLabel: string } } function mockAgent({ @@ -44,15 +43,13 @@ function mockAgent({ stateStartedAt = 1000, terminalHandle, orchestration, - lineage, - liveWorktreeMismatch + lineage }: MockAgentOptions = {}): unknown { return { paneKey, tab: { id: tabId }, agentType, rowSource, - liveWorktreeMismatch, state, startedAt, entry: { @@ -898,105 +895,4 @@ describe('WorktreeCardAgents', () => { expect(markup).not.toContain('Child A') expect(markup).not.toContain('compact-agent-row') }) - - it('notes the destination worktree on a compact row without dropping its details', async () => { - mockAgentActivityDisplayMode = 'compact' - mockAgents = [ - mockAgent({ - agentType: 'codex', - rowSource: 'live', - startedAt: 1000, - prompt: 'Run tests', - lastAssistantMessage: 'Inspecting changes', - liveWorktreeMismatch: { - destinationWorktreeId: 'wt-scratch', - destinationLabel: 'scratch-fix' - } - }) - ] - const { default: WorktreeCardAgents } = await import('./WorktreeCardAgents') - - const markup = renderToStaticMarkup() - - expect(markup).toContain('data-agent-live-worktree-mismatch') - expect(markup).toContain(' · in scratch-fix') - expect(markup).toContain('Run tests') - expect(markup).toContain(' - Inspecting changes') - expect(markup).toContain('title="Run tests · in scratch-fix - Inspecting changes"') - }) - - it('summarizes hidden panes running elsewhere on the collapsed compact affordance', async () => { - mockAgentActivityDisplayMode = 'compact' - mockAgents = [ - mockAgent({ - agentType: 'codex', - rowSource: 'live', - startedAt: 1000, - prompt: 'One', - liveWorktreeMismatch: { - destinationWorktreeId: 'wt-scratch', - destinationLabel: 'scratch-fix' - } - }), - mockAgent({ - paneKey: 'tab-1:2', - agentType: 'claude', - rowSource: 'live', - startedAt: 1500, - stateStartedAt: 1500, - prompt: 'Two', - liveWorktreeMismatch: { - destinationWorktreeId: 'wt-other', - destinationLabel: 'other-fix' - } - }), - mockAgent({ - paneKey: 'tab-1:3', - agentType: 'gemini', - rowSource: 'live', - startedAt: 1700, - stateStartedAt: 1700, - prompt: 'Three' - }) - ] - const { default: WorktreeCardAgents } = await import('./WorktreeCardAgents') - - const markup = renderToStaticMarkup() - - expect(markup).toContain('aria-expanded="false"') - expect(markup).toContain('data-agent-live-worktree-mismatch-summary') - expect(markup).toContain('2 elsewhere') - expect(markup).toContain('2 agents in another worktree') - expect(markup).not.toContain('3 agents in another worktree') - }) - - it('uses the singular hidden-elsewhere phrasing for a single mismatched pane', async () => { - mockAgentActivityDisplayMode = 'compact' - mockAgents = [ - mockAgent({ - agentType: 'codex', - rowSource: 'live', - startedAt: 1000, - prompt: 'One', - liveWorktreeMismatch: { - destinationWorktreeId: 'wt-scratch', - destinationLabel: 'scratch-fix' - } - }), - mockAgent({ - paneKey: 'tab-1:2', - agentType: 'claude', - rowSource: 'live', - startedAt: 1500, - stateStartedAt: 1500, - prompt: 'Two' - }) - ] - const { default: WorktreeCardAgents } = await import('./WorktreeCardAgents') - - const markup = renderToStaticMarkup() - - expect(markup).toContain('1 elsewhere') - expect(markup).toContain('1 agent in another worktree') - }) }) diff --git a/src/renderer/src/components/sidebar/agent-live-worktree-mismatch.test.ts b/src/renderer/src/components/sidebar/agent-live-worktree-mismatch.test.ts index 5c8b30bea63..6d97b5f8355 100644 --- a/src/renderer/src/components/sidebar/agent-live-worktree-mismatch.test.ts +++ b/src/renderer/src/components/sidebar/agent-live-worktree-mismatch.test.ts @@ -104,11 +104,12 @@ function resolve( describe('resolveAgentLiveWorktreeMismatch', () => { it('resolves a nested scratch destination over the owner checkout', () => { - expect(resolve('/repo/.claude/worktrees/scratch/src', [candidate(OWNER), candidate(SCRATCH)])) - .toEqual({ - destinationWorktreeId: SCRATCH.id, - destinationLabel: '10572-live-repro' - }) + expect( + resolve('/repo/.claude/worktrees/scratch/src', [candidate(OWNER), candidate(SCRATCH)]) + ).toEqual({ + destinationWorktreeId: SCRATCH.id, + destinationLabel: '10572-live-repro' + }) }) it('returns no mismatch for the owner checkout, unknown paths, and folder workspaces', () => { @@ -148,8 +149,9 @@ describe('resolveAgentLiveWorktreeMismatch', () => { path: '/repo/.claude/worktrees/scratch', hostId: 'ssh:target-b' }) - expect(resolve('/repo/.claude/worktrees/scratch', [candidate(sameIdOtherHost)], sshOwner)) - .toBeNull() + expect( + resolve('/repo/.claude/worktrees/scratch', [candidate(sameIdOtherHost)], sshOwner) + ).toBeNull() // Why: matching is by current path only; a renamed folder's old id must not match. const renamed = makeWorktree({ id: 'repo-1::/repo/.claude/worktrees/renamed', @@ -158,7 +160,9 @@ describe('resolveAgentLiveWorktreeMismatch', () => { priorWorktreeIds: ['repo-1::/repo/.claude/worktrees/old'], branch: 'refs/heads/renamed' }) - expect(resolve('/repo/.claude/worktrees/old', [candidate(OWNER), candidate(renamed)])).toBeNull() + expect( + resolve('/repo/.claude/worktrees/old', [candidate(OWNER), candidate(renamed)]) + ).toBeNull() }) it('rejects a candidate from another repo bucket', () => { @@ -244,7 +248,10 @@ describe('resolveAgentLiveWorktreeMismatch', () => { it('collapses dot segments, duplicate and trailing separators', () => { expect( - resolve('/repo/./.claude//worktrees/other/../scratch/', [candidate(OWNER), candidate(SCRATCH)]) + resolve('/repo/./.claude//worktrees/other/../scratch/', [ + candidate(OWNER), + candidate(SCRATCH) + ]) ).toEqual({ destinationWorktreeId: SCRATCH.id, destinationLabel: '10572-live-repro' }) }) @@ -304,7 +311,11 @@ describe('resolveAgentLiveWorktreeMismatch', () => { // Why: the distro alias folds case, the Linux tail does not. expect(resolve('/home/dev/repo/Scratch', [candidate(wslScratch)], wslOwner)).toBeNull() expect( - resolve('\\\\wsl.localhost\\Debian\\home\\dev\\repo\\scratch', [candidate(wslScratch)], wslOwner) + resolve( + '\\\\wsl.localhost\\Debian\\home\\dev\\repo\\scratch', + [candidate(wslScratch)], + wslOwner + ) ).toBeNull() // Why: a bare Linux cwd only bridges into the owner's proven distro. expect(resolve('/home/dev/repo/scratch', [candidate(otherDistro)], wslOwner)).toBeNull() @@ -321,11 +332,11 @@ describe('resolveAgentLiveWorktreeMismatch', () => { }) it('falls back from branch to display name to path basename for the label', () => { - const noBranch = { ...SCRATCH, branch: undefined } as Worktree - expect(resolve('/repo/.claude/worktrees/scratch', [candidate(noBranch)])?.destinationLabel).toBe( - 'scratch' - ) - const noNames = { ...SCRATCH, branch: undefined, displayName: '' } as Worktree + const noBranch: Worktree = { ...SCRATCH, branch: '' } + expect( + resolve('/repo/.claude/worktrees/scratch', [candidate(noBranch)])?.destinationLabel + ).toBe('scratch') + const noNames: Worktree = { ...SCRATCH, branch: '', displayName: '' } expect(resolve('/repo/.claude/worktrees/scratch', [candidate(noNames)])?.destinationLabel).toBe( 'scratch' ) @@ -402,7 +413,10 @@ describe('buildAgentLiveWorktreeMismatchCandidates', () => { describe('attachAgentLiveWorktreeMismatch', () => { function makeRow( - overrides: Partial & { paneKey: string } + overrides: Omit, 'entry'> & { + paneKey: string + entry?: Partial + } ): DashboardAgentRow { const entry = { paneKey: overrides.paneKey, @@ -411,10 +425,9 @@ describe('attachAgentLiveWorktreeMismatch', () => { stateStartedAt: 1, stateHistory: [], agentType: 'claude', - ...(overrides.entry ?? {}) + ...overrides.entry } as AgentStatusEntry return { - paneKey: overrides.paneKey, tab: { id: 'tab-1', worktreeId: OWNER.id } as TerminalTab, agentType: 'claude', rowSource: 'live', @@ -466,4 +479,42 @@ describe('attachAgentLiveWorktreeMismatch', () => { ] expect(attachAgentLiveWorktreeMismatch(rows, args)).toBe(rows) }) + + it('starts annotating the same row once the destination is discovered', () => { + const rows = [ + makeRow({ + paneKey: 'tab-1:pane-a', + entry: { reportedCwd: '/repo/.claude/worktrees/scratch' } as Partial + }) + ] + // Why: the scratch checkout usually appears in a later catalog refresh than + // the hook event that first reports its path. + const beforeDiscovery = buildAgentLiveWorktreeMismatchCandidates({ + ownerWorktree: OWNER, + ownerRepo: OWNER_REPO, + visibleWorktrees: [OWNER], + detected: undefined + }) + expect( + attachAgentLiveWorktreeMismatch(rows, { + ownerWorktree: OWNER, + ownerRepo: OWNER_REPO, + candidates: beforeDiscovery + })[0].liveWorktreeMismatch + ).toBeUndefined() + + const afterDiscovery = buildAgentLiveWorktreeMismatchCandidates({ + ownerWorktree: OWNER, + ownerRepo: OWNER_REPO, + visibleWorktrees: [OWNER], + detected: makeDetected([SCRATCH], { local: { authoritative: true, source: 'git' } }) + }) + expect( + attachAgentLiveWorktreeMismatch(rows, { + ownerWorktree: OWNER, + ownerRepo: OWNER_REPO, + candidates: afterDiscovery + })[0].liveWorktreeMismatch?.destinationLabel + ).toBe('10572-live-repro') + }) }) diff --git a/src/renderer/src/store/slices/agent-status-reported-cwd.test.ts b/src/renderer/src/store/slices/agent-status-reported-cwd.test.ts index d5e57fcf6f8..15081e7ec5f 100644 --- a/src/renderer/src/store/slices/agent-status-reported-cwd.test.ts +++ b/src/renderer/src/store/slices/agent-status-reported-cwd.test.ts @@ -22,7 +22,7 @@ function seedTabs(store: ReturnType): void { function setStatusWithMetadata( store: ReturnType, - payload: { state: 'working' | 'done'; prompt?: string; agentType?: 'claude' | 'codex' | 'pi' }, + payload: { state: 'working' | 'done'; prompt: string; agentType?: 'claude' | 'codex' | 'pi' }, metadata?: { reportedCwd?: string | null } ): void { store @@ -46,31 +46,76 @@ describe('agent status reported cwd', () => { const store = createTestStore() seedTabs(store) - setStatusWithMetadata(store, { state: 'working', prompt: 'go', agentType: 'claude' }, { - reportedCwd: SCRATCH_CWD - }) + setStatusWithMetadata( + store, + { state: 'working', prompt: 'go', agentType: 'claude' }, + { + reportedCwd: SCRATCH_CWD + } + ) expect(reportedCwd(store)).toBe(SCRATCH_CWD) // Why: a same-root transition with no cwd must not blank the known location. setStatusWithMetadata(store, { state: 'done', prompt: 'go', agentType: 'claude' }) expect(reportedCwd(store)).toBe(SCRATCH_CWD) - setStatusWithMetadata(store, { state: 'working', prompt: 'again', agentType: 'claude' }, { - reportedCwd: OWNER_CWD - }) + setStatusWithMetadata( + store, + { state: 'working', prompt: 'again', agentType: 'claude' }, + { + reportedCwd: OWNER_CWD + } + ) expect(reportedCwd(store)).toBe(OWNER_CWD) }) + it('publishes a fresh entry for an update that only moves the location', () => { + const store = createTestStore() + seedTabs(store) + + setStatusWithMetadata( + store, + { state: 'working', prompt: 'go', agentType: 'claude' }, + { + reportedCwd: OWNER_CWD + } + ) + const before = store.getState().agentStatusByPaneKey[PANE_KEY] + + setStatusWithMetadata( + store, + { state: 'working', prompt: 'go', agentType: 'claude' }, + { + reportedCwd: SCRATCH_CWD + } + ) + const after = store.getState().agentStatusByPaneKey[PANE_KEY] + + // Why: sidebar cards diff entry identity, so a cwd-only move must publish a new object. + expect(after).not.toBe(before) + expect(after.reportedCwd).toBe(SCRATCH_CWD) + expect(after.state).toBe(before.state) + expect(after.prompt).toBe(before.prompt) + }) + it('clears the location when main sends an explicit null', () => { const store = createTestStore() seedTabs(store) - setStatusWithMetadata(store, { state: 'working', prompt: 'go', agentType: 'claude' }, { - reportedCwd: SCRATCH_CWD - }) - setStatusWithMetadata(store, { state: 'working', prompt: 'go', agentType: 'claude' }, { - reportedCwd: null - }) + setStatusWithMetadata( + store, + { state: 'working', prompt: 'go', agentType: 'claude' }, + { + reportedCwd: SCRATCH_CWD + } + ) + setStatusWithMetadata( + store, + { state: 'working', prompt: 'go', agentType: 'claude' }, + { + reportedCwd: null + } + ) expect(reportedCwd(store)).toBeUndefined() expect('reportedCwd' in store.getState().agentStatusByPaneKey[PANE_KEY]).toBe(false) @@ -81,13 +126,21 @@ describe('agent status reported cwd', () => { const store = createTestStore() seedTabs(store) - setStatusWithMetadata(store, { state: 'working', prompt: 'root', agentType: 'claude' }, { - reportedCwd: OWNER_CWD - }) + setStatusWithMetadata( + store, + { state: 'working', prompt: 'root', agentType: 'claude' }, + { + reportedCwd: OWNER_CWD + } + ) // Why: a foreign agentType while the root turn is live is nested child traffic. - setStatusWithMetadata(store, { state: 'working', prompt: 'child', agentType: 'codex' }, { - reportedCwd: SCRATCH_CWD - }) + setStatusWithMetadata( + store, + { state: 'working', prompt: 'child', agentType: 'codex' }, + { + reportedCwd: SCRATCH_CWD + } + ) expect(store.getState().agentStatusByPaneKey[PANE_KEY].agentType).toBe('claude') expect(reportedCwd(store)).toBe(OWNER_CWD) @@ -98,9 +151,13 @@ describe('agent status reported cwd', () => { const store = createTestStore() seedTabs(store) - setStatusWithMetadata(store, { state: 'working', prompt: 'root', agentType: 'claude' }, { - reportedCwd: SCRATCH_CWD - }) + setStatusWithMetadata( + store, + { state: 'working', prompt: 'root', agentType: 'claude' }, + { + reportedCwd: SCRATCH_CWD + } + ) setStatusWithMetadata(store, { state: 'done', prompt: 'root', agentType: 'claude' }) setStatusWithMetadata(store, { state: 'working', prompt: 'next', agentType: 'codex' }) @@ -112,9 +169,13 @@ describe('agent status reported cwd', () => { const store = createTestStore() seedTabs(store) - setStatusWithMetadata(store, { state: 'done', prompt: 'finished', agentType: 'claude' }, { - reportedCwd: SCRATCH_CWD - }) + setStatusWithMetadata( + store, + { state: 'done', prompt: 'finished', agentType: 'claude' }, + { + reportedCwd: SCRATCH_CWD + } + ) const retained = collectHibernatedCompletionEvidenceForWorktree(store.getState(), 'wt-1', [ PANE_KEY @@ -130,19 +191,17 @@ describe('agent status reported cwd', () => { const store = createTestStore() seedTabs(store) - store - .getState() - .setAgentStatus( - PANE_KEY, - { state: 'working', prompt: 'sleep me', agentType: 'codex' }, - undefined, - undefined, - { tabId: 'tab-1', worktreeId: 'wt-1' }, - { - reportedCwd: SCRATCH_CWD, - providerSession: { key: 'session_id', id: 'codex-session-1' } - } - ) + store.getState().setAgentStatus( + PANE_KEY, + { state: 'working', prompt: 'sleep me', agentType: 'codex' }, + undefined, + undefined, + { tabId: 'tab-1', worktreeId: 'wt-1' }, + { + reportedCwd: SCRATCH_CWD, + providerSession: { key: 'session_id', id: 'codex-session-1' } + } + ) const records = collectSleepingAgentSessionRecordsForWorktree(store.getState(), 'wt-1') expect(records[PANE_KEY]).toBeDefined() diff --git a/src/shared/agent-reported-cwd.test.ts b/src/shared/agent-reported-cwd.test.ts index d4771bb2b60..a5a217d024e 100644 --- a/src/shared/agent-reported-cwd.test.ts +++ b/src/shared/agent-reported-cwd.test.ts @@ -56,9 +56,9 @@ describe('extractAgentReportedCwdUpdate', () => { it('accepts the workspaceRoot aliases and honours alias precedence', () => { expect(extractAgentReportedCwdUpdate({ workspaceRoot: '/repo/a' })).toBe('/repo/a') expect(extractAgentReportedCwdUpdate({ workspace_root: '/repo/b' })).toBe('/repo/b') - expect( - extractAgentReportedCwdUpdate({ cwd: '/repo/a', workspaceRoot: '/repo/b' }) - ).toBe('/repo/a') + expect(extractAgentReportedCwdUpdate({ cwd: '/repo/a', workspaceRoot: '/repo/b' })).toBe( + '/repo/a' + ) expect( extractAgentReportedCwdUpdate({ workspaceRoot: '/repo/b', workspace_root: '/repo/c' }) ).toBe('/repo/b')