fix(bot-mode): match scoped bot selection in reset guard
This commit is contained in:
@@ -21,6 +21,8 @@
|
||||
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { RosterRow } from './types'
|
||||
|
||||
interface MentionCompletionItem {
|
||||
display: string
|
||||
insert: string
|
||||
@@ -58,7 +60,7 @@ const ROSTER = {
|
||||
}
|
||||
|
||||
const { cache, hostMock, live } = vi.hoisted(() => {
|
||||
const live = { focused: 'default', profile: 'default' }
|
||||
const live = { focused: 'default', profile: 'default', stored: null as string | null }
|
||||
|
||||
return {
|
||||
cache: new Map<string, { key: unknown[]; value: unknown }>(),
|
||||
@@ -69,7 +71,7 @@ const { cache, hostMock, live } = vi.hoisted(() => {
|
||||
state: {
|
||||
connectionId: { get: () => 'local', listen: () => () => undefined },
|
||||
focusedSessionProfile: { get: () => live.focused, listen: () => () => undefined },
|
||||
focusedStoredSessionId: { get: () => null, listen: () => () => undefined },
|
||||
focusedStoredSessionId: { get: () => live.stored, listen: () => () => undefined },
|
||||
gateway: { get: () => null, listen: () => () => undefined },
|
||||
profile: { get: () => live.profile, listen: () => () => undefined }
|
||||
}
|
||||
@@ -173,6 +175,54 @@ beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
live.focused = 'default'
|
||||
live.profile = 'default'
|
||||
live.stored = null
|
||||
})
|
||||
|
||||
describe('Bot Chat reset guard', () => {
|
||||
it.each([
|
||||
{ connectionId: 'local', sourceScoped: true },
|
||||
{ connectionId: 'remote', remoteSource: true, sourceScoped: true },
|
||||
{}
|
||||
])('compacts only the selected bot canonical chat: %j', async source => {
|
||||
const { handler } = await contributions()
|
||||
const { $lastRoster } = await import('./data')
|
||||
const { saveSelectedRosterBot } = await import('./bot-state')
|
||||
|
||||
const selected: RosterRow = {
|
||||
...source,
|
||||
name: 'writer',
|
||||
canonical_session: { id: 'bot-root', resolved_id: 'bot-tip' }
|
||||
}
|
||||
|
||||
// An identically named bot on another connection must not win the lookup.
|
||||
$lastRoster.set([
|
||||
{ name: 'writer', connectionId: 'other', sourceScoped: true, canonical_session: { id: 'other-chat' } },
|
||||
selected
|
||||
])
|
||||
saveSelectedRosterBot(selected)
|
||||
|
||||
for (const stored of ['bot-root', 'bot-tip']) {
|
||||
live.stored = stored
|
||||
|
||||
for (const text of ['/new', '/reset']) {
|
||||
hostMock.notify.mockClear()
|
||||
expect(await handler({ text })).toEqual({ text: '/compact' })
|
||||
expect(hostMock.notify).toHaveBeenCalledOnce()
|
||||
expect(hostMock.notify).toHaveBeenCalledWith(expect.objectContaining({ title: 'This chat never resets' }))
|
||||
}
|
||||
}
|
||||
|
||||
for (const stored of ['scratch-chat', 'other-chat', null]) {
|
||||
live.stored = stored
|
||||
|
||||
for (const text of ['/new', '/reset']) {
|
||||
hostMock.notify.mockClear()
|
||||
const draft = { text }
|
||||
expect(await handler(draft)).toBe(draft)
|
||||
expect(hostMock.notify).not.toHaveBeenCalled()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('@-mention completions', () => {
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
$lastRoster,
|
||||
botHandle,
|
||||
botMentionTag,
|
||||
botSelectionKey,
|
||||
cachedUnionRoster,
|
||||
isActiveRosterBot,
|
||||
migrateBotMeta,
|
||||
@@ -665,7 +666,7 @@ export default {
|
||||
// server-side by name), matching either the durable row id or
|
||||
// the compression-lineage tip currently on screen.
|
||||
const roster = $lastRoster.get()
|
||||
const row = Array.isArray(roster) ? roster.find(bot => bot?.name === activeBot) : null
|
||||
const row = Array.isArray(roster) ? roster.find(bot => botSelectionKey(bot) === activeBot) : null
|
||||
|
||||
// The STORED id, which is the id space canonical_session is keyed
|
||||
// in. `host.state.activeSessionId` is the runtime id and could
|
||||
|
||||
Reference in New Issue
Block a user