fix(desktop): preserve bounded bot hydration budget
This commit is contained in:
@@ -5634,6 +5634,12 @@ async function openStoredBotChat(owner, storedId, summary) {
|
||||
const hasAuthoritativeCount =
|
||||
typeof summary?.message_count === 'number' && Number.isFinite(summary.message_count)
|
||||
const expectHistory = hasAuthoritativeCount ? summary.message_count > 0 : true
|
||||
// Current SDKs export the Bot-specific budget. The fallback preserves
|
||||
// compatibility with older hosts and isolated plugin test harnesses.
|
||||
const hydrationTimeoutMs =
|
||||
typeof sdk !== 'undefined' && Number.isFinite(sdk.BOT_CHAT_SESSION_HYDRATION_TIMEOUT_MS)
|
||||
? sdk.BOT_CHAT_SESSION_HYDRATION_TIMEOUT_MS
|
||||
: 60_000
|
||||
|
||||
// A profile backend that just woke up can lose the hydration-timeout race
|
||||
// even though the session is fine (hermes-agent#89617) — clicking Retry
|
||||
@@ -5655,6 +5661,7 @@ async function openStoredBotChat(owner, storedId, summary) {
|
||||
awaitHydration: true,
|
||||
expectHistory,
|
||||
forceResume: true,
|
||||
hydrationTimeoutMs,
|
||||
keepAllProfilesScope: true,
|
||||
workspaceMode: 'bots',
|
||||
workspaceOwnerKey: ownerKey,
|
||||
|
||||
@@ -77,6 +77,11 @@ test('pref defaults OFF and persists via ctx.storage under activity-toasts', ()
|
||||
assert.match(source, /storage\?\.get\?\.\('activity-toasts'\)/)
|
||||
})
|
||||
|
||||
test('Bot Chat opens pass the exported 60-second hydration budget', () => {
|
||||
assert.match(source, /sdk\.BOT_CHAT_SESSION_HYDRATION_TIMEOUT_MS/)
|
||||
assert.match(source, /hydrationTimeoutMs,/)
|
||||
})
|
||||
|
||||
test('activity in the hidden canonical Bot Chat still badges (the "6d ago" class)', () => {
|
||||
// The canonical Bot Chat is hidden from session lists, so last_session
|
||||
// never advances when a DM lands there — only canonical_session does.
|
||||
|
||||
@@ -311,7 +311,12 @@ const $activeConnectionId = computed($connection, connection => {
|
||||
return connection.mode === 'local' ? 'local' : null
|
||||
})
|
||||
|
||||
const DEFAULT_SESSION_HYDRATION_TIMEOUT_MS = 20_000
|
||||
/** Ordinary session opens fail fast when their gateway or socket is dead. */
|
||||
export const DEFAULT_SESSION_HYDRATION_TIMEOUT_MS = 20_000
|
||||
/** Cold Bot profiles get a larger per-attempt budget to start their backend
|
||||
* and paint durable history. Bot Mode opts into one retry, so its effective
|
||||
* ceiling is two bounded attempts rather than an unbounded wait. */
|
||||
export const BOT_CHAT_SESSION_HYDRATION_TIMEOUT_MS = 60_000
|
||||
let openSessionGeneration = 0
|
||||
|
||||
export interface PluginOpenSessionOptions {
|
||||
|
||||
@@ -104,7 +104,9 @@ vi.mock('@/store/gateway', async () => {
|
||||
}
|
||||
})
|
||||
|
||||
const { host } = await import('./index')
|
||||
const { BOT_CHAT_SESSION_HYDRATION_TIMEOUT_MS, DEFAULT_SESSION_HYDRATION_TIMEOUT_MS, host } =
|
||||
await import('./index')
|
||||
|
||||
const { openSession: openSessionCore } = await import('@/app/open-session')
|
||||
const { deleteProfile, hermesApi } = await import('@/hermes')
|
||||
|
||||
@@ -834,6 +836,65 @@ describe('profile-aware plugin session opens', () => {
|
||||
expect($gatewaySwapTarget.get()).toBeNull()
|
||||
})
|
||||
|
||||
it('keeps the ordinary session hydration default at 20 seconds', async () => {
|
||||
vi.useFakeTimers()
|
||||
|
||||
try {
|
||||
expect(DEFAULT_SESSION_HYDRATION_TIMEOUT_MS).toBe(20_000)
|
||||
$activeGatewayProfile.set('hyoseob')
|
||||
|
||||
const outcome = host
|
||||
.openSession('slow-bot-chat', {
|
||||
profile: 'hyoseob',
|
||||
awaitHydration: true,
|
||||
expectHistory: true
|
||||
})
|
||||
.then(
|
||||
() => 'resolved',
|
||||
error => String(error)
|
||||
)
|
||||
|
||||
await vi.advanceTimersByTimeAsync(19_999)
|
||||
expect(setResumeExhaustedSessionId).not.toHaveBeenCalled()
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1)
|
||||
expect(await outcome).toMatch(/timed out loading/i)
|
||||
expect(setResumeExhaustedSessionId).toHaveBeenCalledWith('slow-bot-chat')
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
it('honors the exported 60-second Bot Chat hydration override', async () => {
|
||||
vi.useFakeTimers()
|
||||
|
||||
try {
|
||||
expect(BOT_CHAT_SESSION_HYDRATION_TIMEOUT_MS).toBe(60_000)
|
||||
$activeGatewayProfile.set('hyoseob')
|
||||
|
||||
const outcome = host
|
||||
.openSession('slow-bot-chat', {
|
||||
profile: 'hyoseob',
|
||||
awaitHydration: true,
|
||||
expectHistory: true,
|
||||
hydrationTimeoutMs: BOT_CHAT_SESSION_HYDRATION_TIMEOUT_MS
|
||||
})
|
||||
.then(
|
||||
() => 'resolved',
|
||||
error => String(error)
|
||||
)
|
||||
|
||||
await vi.advanceTimersByTimeAsync(BOT_CHAT_SESSION_HYDRATION_TIMEOUT_MS - 1)
|
||||
expect(setResumeExhaustedSessionId).not.toHaveBeenCalled()
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1)
|
||||
expect(await outcome).toMatch(/timed out loading/i)
|
||||
expect(setResumeExhaustedSessionId).toHaveBeenCalledWith('slow-bot-chat')
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
it('retries a Bot Chat hydration timeout once without ever arming the Retry surface (#89617)', async () => {
|
||||
$activeGatewayProfile.set('hyoseob')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user