fix(native-chat): text sent to a new agent claims an empty starting chat only in its own split

Notes sent from the right-hand split could land in, and focus, an empty chat still starting in the
left-hand one. A request with text now claims an empty starting chat only in the tab group it opens
in, the same rule a request without text follows; elsewhere it opens its own chat there.
This commit is contained in:
Brennan Benson
2026-10-04 16:00:26 -07:00
parent cf16d921fe
commit 6a86ad7e36
2 changed files with 35 additions and 5 deletions
@@ -103,7 +103,10 @@ vi.mock('@/store', () => ({
}
}))
import { appendStructuredAgentSessionOutboxMessage } from '@/components/native-chat/structured-agent-session-outbox-storage'
import {
appendStructuredAgentSessionOutboxMessage,
readOutbox
} from '@/components/native-chat/structured-agent-session-outbox-storage'
import {
appendNativeChatAttachmentCache,
clearNativeChatAttachmentCacheForTests
@@ -452,6 +455,34 @@ describe('a "new chat" with text', () => {
})
})
it('opens its own chat in its own split beside an empty chat starting in another', () => {
mocks.launch.mockImplementation(() => new Promise(() => undefined))
const blank = pick('plus-pick-1', { group: 'group-left' })
const notes = pick('notes-send', { prompt: 'review notes', group: 'group-right' })
expect(notes.sessionId).toBe(second.sessionId)
expect(
store.state.unifiedTabsByWorktree[WORKTREE_ID]?.find(
(tab) => tab.entityId === second.sessionId
)?.groupId
).toBe('group-right')
expect(mocks.focusGroup).not.toHaveBeenCalled()
expect(readOutbox(blank.sessionId)).toEqual([])
expect(readOutbox(second.sessionId).map((entry) => entry.body.blocks)).toEqual([
[{ type: 'text', text: 'review notes' }]
])
})
it('takes an empty chat starting in its own split', () => {
mocks.launch.mockImplementation(() => new Promise(() => undefined))
const blank = pick('plus-pick-1', { group: 'group-right' })
expect(pick('notes-send', { prompt: 'review notes', group: 'group-right' }).sessionId).toBe(
blank.sessionId
)
})
it('takes an empty chat still starting, as before', () => {
mocks.launch.mockImplementation(() => new Promise(() => undefined))
const blank = pick('plus-pick-1')
@@ -67,8 +67,8 @@ export function claimableStructuredLaunchAttempt(
return request.hasText ? emptyStructuredLaunchAttempt(state) : undefined
}
/** The launch a new start of `request` joins: one it re-delivers, else an empty starting chat, which
* a request with text claims and one without reuses in its own tab group. The newest wins. */
/** The launch a new start of `request` joins: one it re-delivers, else an empty starting chat in the
* tab group it opens in, which a request with text claims and one without reuses. The newest wins. */
export function getJoinableStructuredLaunchState(
identity: string,
request: StructuredLaunchRequest
@@ -79,8 +79,7 @@ export function getJoinableStructuredLaunchState(
structuredLaunchesHoldingIdentity(matches).findLast(
(state) =>
emptyStructuredLaunchAttempt(state) &&
(request.hasText ||
!request.groupId ||
(!request.groupId ||
structuredChatTabGroupId(state.intent.worktreeId, state.intent.sessionId) ===
request.groupId)
)