mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(mobile): reset agent history when switching desktops (#31695)
Switching desktops retains another host history and can scan its filesystem path on the new host. Reset content by host while preserving same-host reconnect identity.
This commit is contained in:
@@ -42,7 +42,11 @@ const SCOPE_TABS: { scope: AiVaultScope; label: string }[] = [
|
||||
{ scope: 'all', label: 'All' }
|
||||
]
|
||||
|
||||
export function MobileAgentSessionHistoryPanel({
|
||||
export function MobileAgentSessionHistoryPanel(props: MobileAgentSessionHistoryPanelProps) {
|
||||
return <MobileAgentSessionHistoryPanelContent key={props.hostId} {...props} />
|
||||
}
|
||||
|
||||
function MobileAgentSessionHistoryPanelContent({
|
||||
hostId,
|
||||
worktreeId,
|
||||
name = ''
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { act, create, type ReactTestInstance, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { AiVaultSession } from '../../../src/shared/ai-vault-types'
|
||||
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
|
||||
import type { RpcResponse } from '../transport/types'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type {
|
||||
ForceReconnect,
|
||||
@@ -142,3 +145,88 @@ describe("the agent-history panel's Retry while the host is unreachable", () =>
|
||||
expect(client.requests.map((request) => request.method)).toContain('status.get')
|
||||
})
|
||||
})
|
||||
|
||||
it.each([
|
||||
['host-b', 0, false],
|
||||
['host-a', 1, true]
|
||||
] as const)(
|
||||
'owns the history catalog across a client replacement for %s',
|
||||
async (hostId, retainedLists, alreadyScanning) => {
|
||||
const pair = createFakeBridgePortPair()
|
||||
await pair.flush()
|
||||
const session: AiVaultSession = {
|
||||
id: 'claude:host-a',
|
||||
executionHostId: 'local',
|
||||
agent: 'claude',
|
||||
sessionId: 'host-a-session',
|
||||
title: 'Host A session',
|
||||
cwd: '/host-a/workspace',
|
||||
branch: null,
|
||||
model: null,
|
||||
filePath: '/host-a/session.jsonl',
|
||||
codexHome: null,
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
modifiedAt: '2026-10-21T00:00:00.000Z',
|
||||
messageCount: 2,
|
||||
totalTokens: 10,
|
||||
previewMessages: [],
|
||||
queuedMessageCount: 0,
|
||||
subagentTranscriptCount: 0,
|
||||
resumeCommand: '',
|
||||
subagent: null
|
||||
}
|
||||
const reply = (result: unknown): RpcResponse => ({ id: 'history', ok: true, result })
|
||||
const previous = new FakeSession('connected')
|
||||
previous.sendRequest.mockImplementation(async (method) =>
|
||||
reply(
|
||||
method === 'status.get'
|
||||
? { capabilities: ['aiVault.v1'], hostPlatform: 'darwin' }
|
||||
: method === 'worktree.ps'
|
||||
? { worktrees: [{ worktreeId: 'wt-1', repoId: 'repo', path: '/host-a/workspace' }] }
|
||||
: { sessions: [session], issues: [] }
|
||||
)
|
||||
)
|
||||
doubles.connection = { client: previous, state: 'connected' }
|
||||
const renderHost = (hostId: string) => (
|
||||
<RpcClientProvider client={pair.client}>
|
||||
<MobileAgentSessionHistoryPanel hostId={hostId} worktreeId="wt-1" />
|
||||
</RpcClientProvider>
|
||||
)
|
||||
await act(async () => {
|
||||
tree = create(renderHost('host-a'))
|
||||
})
|
||||
expect(tree?.root.findAll((node) => String(node.type) === 'SectionList')).toHaveLength(1)
|
||||
const next = new FakeSession('connected')
|
||||
let answerWorktrees: (reply: RpcResponse) => void = () => {}
|
||||
const worktrees = new Promise<RpcResponse>((resolve) => {
|
||||
answerWorktrees = resolve
|
||||
})
|
||||
next.sendRequest.mockImplementation(async (method) => {
|
||||
if (method === 'worktree.ps') {
|
||||
return worktrees
|
||||
}
|
||||
if (method === 'status.get') {
|
||||
return reply({ capabilities: ['aiVault.v1'], hostPlatform: 'linux' })
|
||||
}
|
||||
return new Promise<RpcResponse>(() => {})
|
||||
})
|
||||
doubles.connection = { client: next, state: 'connected' }
|
||||
await act(async () => tree?.update(renderHost(hostId)))
|
||||
expect(tree?.root.findAll((node) => String(node.type) === 'SectionList')).toHaveLength(
|
||||
retainedLists
|
||||
)
|
||||
expect(next.sendRequest.mock.calls.some(([method]) => method === 'aiVault.listSessions')).toBe(
|
||||
alreadyScanning
|
||||
)
|
||||
await act(async () =>
|
||||
answerWorktrees(
|
||||
reply({ worktrees: [{ worktreeId: 'wt-1', repoId: 'repo', path: '/host-b/workspace' }] })
|
||||
)
|
||||
)
|
||||
expect(next.sendRequest).toHaveBeenCalledWith(
|
||||
'aiVault.listSessions',
|
||||
expect.objectContaining({ scopePaths: ['/host-b/workspace'] })
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user