mirror of
https://github.com/stablyai/orca.git
synced 2026-09-26 16:02:43 +00:00
fix(ai-vault): gate WSL home discovery on the cached distro list, not a probe
`listWslDistrosAsync()` resolves `[]` when the `wsl.exe` probe is rejected, so a transient failure made `getAiVaultWslHomeDirs()` conclude "no WSL distros" and skip discovery. That narrowed the allowed-roots set `ai-vault-delete` and `ai-vault-subagent-list` validate against, wrongly rejecting WSL-hosted paths. Gate on `hasCachedWslDistros()` / `getCachedWslDistros()` instead: a pure cache read that only skips discovery once a successful probe has reported zero user distros. It also never probes, so the AI Vault listing cannot be the first to cache `[]` and flip a configured distro to "missing" in runtime resolution.
This commit is contained in:
@@ -16,10 +16,11 @@ vi.mock('./session-scanner-worker-spawn', () => ({
|
||||
resetAiVaultScannerWorkerForTests: vi.fn()
|
||||
}))
|
||||
|
||||
import { _resetWslCachesForTests, _setWslCachesForTests } from '../wsl'
|
||||
import { _resetWslCachesForTests, _setWslCachesForTests, listWslDistrosAsync } from '../wsl'
|
||||
import { filterPathsToRunningWslDistrosAsync } from '../wsl-running-path-filter'
|
||||
import {
|
||||
configureAiVaultSessionSources,
|
||||
getAiVaultWslHomeDirs,
|
||||
listAiVaultSessions,
|
||||
resetAiVaultSessionListCacheForTests
|
||||
} from './cached-session-list'
|
||||
@@ -88,4 +89,22 @@ describe('AI Vault listing wsl.exe probes', () => {
|
||||
`${WSL_HOME}\\.codex`
|
||||
])
|
||||
})
|
||||
|
||||
// Why: a rejected `--list --quiet` yields [] without caching. Treating that as "no distro
|
||||
// installed" would narrow the allowed roots delete/subagent validation trusts.
|
||||
it('still discovers WSL homes after the installed-distro probe was rejected', async () => {
|
||||
execFileMock.mockImplementation((_command, args, _options, callback) => {
|
||||
if (args.includes('--running')) {
|
||||
callback(null, 'Ubuntu\n')
|
||||
} else if (args.includes('--list')) {
|
||||
callback(new Error('wsl.exe transient failure'), '')
|
||||
} else {
|
||||
callback(null, '/home/ada\n')
|
||||
}
|
||||
})
|
||||
await expect(listWslDistrosAsync()).resolves.toEqual([])
|
||||
|
||||
await expect(getAiVaultWslHomeDirs()).resolves.toEqual([WSL_HOME])
|
||||
expect(wslSpawns()).toContainEqual(['--list', '--running', '--quiet'])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,13 +3,15 @@ import type { AiVaultListResult } from '../../shared/ai-vault-types'
|
||||
|
||||
const {
|
||||
filterPathsToRunningWslDistrosAsync,
|
||||
getCachedWslDistros,
|
||||
hasCachedWslDistros,
|
||||
listRunningWslHomeDirsAsync,
|
||||
listWslDistrosAsync,
|
||||
scanAiVaultSessionsInWorker
|
||||
} = vi.hoisted(() => ({
|
||||
filterPathsToRunningWslDistrosAsync: vi.fn(async (paths: readonly string[]) => [...paths]),
|
||||
getCachedWslDistros: vi.fn((): string[] | null => null),
|
||||
hasCachedWslDistros: vi.fn(() => false),
|
||||
listRunningWslHomeDirsAsync: vi.fn().mockResolvedValue([]),
|
||||
listWslDistrosAsync: vi.fn().mockResolvedValue(['Ubuntu']),
|
||||
scanAiVaultSessionsInWorker: vi.fn()
|
||||
}))
|
||||
|
||||
@@ -18,8 +20,9 @@ vi.mock('./session-scanner-worker-spawn', () => ({
|
||||
resetAiVaultScannerWorkerForTests: vi.fn()
|
||||
}))
|
||||
vi.mock('../wsl', () => ({
|
||||
listRunningWslHomeDirsAsync,
|
||||
listWslDistrosAsync
|
||||
getCachedWslDistros,
|
||||
hasCachedWslDistros,
|
||||
listRunningWslHomeDirsAsync
|
||||
}))
|
||||
vi.mock('../wsl-running-path-filter', () => ({ filterPathsToRunningWslDistrosAsync }))
|
||||
|
||||
@@ -54,8 +57,9 @@ describe('invalidateAiVaultSessionListCache generation guard', () => {
|
||||
vi.spyOn(process, 'platform', 'get').mockImplementation(() => platform)
|
||||
resetAiVaultSessionListCacheForTests()
|
||||
filterPathsToRunningWslDistrosAsync.mockClear()
|
||||
getCachedWslDistros.mockReset().mockReturnValue(null)
|
||||
hasCachedWslDistros.mockReset().mockReturnValue(false)
|
||||
listRunningWslHomeDirsAsync.mockReset().mockResolvedValue([])
|
||||
listWslDistrosAsync.mockReset().mockResolvedValue(['Ubuntu'])
|
||||
scanAiVaultSessionsInWorker.mockReset()
|
||||
})
|
||||
afterEach(() => {
|
||||
@@ -103,13 +107,22 @@ describe('invalidateAiVaultSessionListCache generation guard', () => {
|
||||
expect(listRunningWslHomeDirsAsync).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('skips running-distro discovery when no WSL distro is installed', async () => {
|
||||
listWslDistrosAsync.mockResolvedValue([])
|
||||
it('skips running-distro discovery once a probe has reported no installed WSL distro', async () => {
|
||||
hasCachedWslDistros.mockReturnValue(true)
|
||||
getCachedWslDistros.mockReturnValue([])
|
||||
|
||||
await expect(getAiVaultWslHomeDirs()).resolves.toEqual([])
|
||||
expect(listRunningWslHomeDirsAsync).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('still discovers running distros before any distro probe has succeeded', async () => {
|
||||
hasCachedWslDistros.mockReturnValue(false)
|
||||
listRunningWslHomeDirsAsync.mockResolvedValue(['\\\\wsl.localhost\\Ubuntu\\home\\ada'])
|
||||
|
||||
await expect(getAiVaultWslHomeDirs()).resolves.toEqual(['\\\\wsl.localhost\\Ubuntu\\home\\ada'])
|
||||
expect(listRunningWslHomeDirsAsync).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('skips WSL home discovery off Windows', async () => {
|
||||
platform = 'linux'
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
resetAiVaultScannerBackgroundForTests,
|
||||
scanAiVaultSessionsInBackground
|
||||
} from './session-scanner-background'
|
||||
import { listRunningWslHomeDirsAsync, listWslDistrosAsync } from '../wsl'
|
||||
import { getCachedWslDistros, hasCachedWslDistros, listRunningWslHomeDirsAsync } from '../wsl'
|
||||
import { filterPathsToRunningWslDistrosAsync } from '../wsl-running-path-filter'
|
||||
import type { AiVaultListArgs, AiVaultListResult } from '../../shared/ai-vault-types'
|
||||
import { LOCAL_EXECUTION_HOST_ID } from '../../shared/execution-host'
|
||||
@@ -138,7 +138,10 @@ export async function getAiVaultWslHomeDirs(): Promise<string[]> {
|
||||
return []
|
||||
}
|
||||
// No installed distro can be running: spares WSL-less hosts the running-distro probe.
|
||||
if ((await listWslDistrosAsync()).length === 0) {
|
||||
// Cache read only: a rejected wsl.exe probe yields [] without caching, so it must not
|
||||
// narrow the WSL roots delete/subagent validation trusts; and probing here would let this
|
||||
// listing be the first to cache [] and flip a configured distro to "missing".
|
||||
if (hasCachedWslDistros() && getCachedWslDistros()?.length === 0) {
|
||||
return []
|
||||
}
|
||||
return listRunningWslHomeDirsAsync()
|
||||
|
||||
@@ -27,7 +27,7 @@ vi.mock('../ai-vault/remote-session-scanner', () => ({
|
||||
}))
|
||||
vi.mock('../wsl', () => ({
|
||||
listRunningWslHomeDirsAsync: vi.fn().mockResolvedValue([]),
|
||||
listWslDistrosAsync: vi.fn().mockResolvedValue(['Ubuntu'])
|
||||
hasCachedWslDistros: vi.fn(() => false)
|
||||
}))
|
||||
vi.mock('../wsl-running-path-filter', () => ({
|
||||
filterPathsToRunningWslDistrosAsync: vi.fn(async (paths: readonly string[]) => [...paths])
|
||||
|
||||
@@ -76,7 +76,7 @@ vi.mock('../ai-vault/session-scanner-parse-cache', async (importOriginal) => {
|
||||
vi.mock('../wsl', () => ({
|
||||
listRunningWslDistrosAsync: vi.fn().mockResolvedValue([]),
|
||||
listRunningWslHomeDirsAsync: vi.fn().mockResolvedValue([]),
|
||||
listWslDistrosAsync: vi.fn().mockResolvedValue(['Ubuntu'])
|
||||
hasCachedWslDistros: vi.fn(() => false)
|
||||
}))
|
||||
|
||||
vi.mock('../providers/ssh-filesystem-dispatch', () => ({
|
||||
|
||||
Reference in New Issue
Block a user