mirror of
https://github.com/stablyai/orca.git
synced 2026-10-22 16:03:47 +00:00
fix(ai-vault): normalize Cursor scopes before shared caps
This commit is contained in:
@@ -179,6 +179,52 @@ describe('local Cursor sidecar discovery bounds', () => {
|
||||
expect(result.truncated.buckets).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps lowercase Windows scope buckets exact through the 64-path cap', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'orca-cursor-win-scope-cap-'))
|
||||
tempRoots.push(root)
|
||||
const roots = isolatedScanRoots(root)
|
||||
const scopePaths = Array.from(
|
||||
{ length: CURSOR_REMOTE_MAX_SCOPE_PATHS - 1 },
|
||||
(_, index) => `c:\\workspaces\\repo-${String(index).padStart(3, '0')}`
|
||||
)
|
||||
let targetScope = ''
|
||||
for (let index = 0; !targetScope; index += 1) {
|
||||
const candidate = `c:\\workspaces\\target-${index}`
|
||||
if (cursorBucketForCwd(candidate, 'win32').startsWith('f')) {
|
||||
targetScope = candidate
|
||||
}
|
||||
}
|
||||
scopePaths.push(targetScope)
|
||||
const targetBucket = cursorBucketForCwd(targetScope, 'win32')
|
||||
const targetMeta = await addSession(
|
||||
roots.cursorChatsDir,
|
||||
targetBucket,
|
||||
'lowercase-drive-session'
|
||||
)
|
||||
|
||||
await Promise.all(
|
||||
Array.from({ length: CURSOR_REMOTE_MAX_BUCKETS }, (_, index) =>
|
||||
mkdir(join(roots.cursorChatsDir, index.toString(16).padStart(32, '0')), {
|
||||
recursive: true
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
const result = await scanAiVaultSessions({
|
||||
...roots,
|
||||
platform: 'win32',
|
||||
executionHostId: 'local',
|
||||
scopePaths,
|
||||
limit: 5
|
||||
})
|
||||
|
||||
expect(result.sessions.some((session) => session.filePath === targetMeta)).toBe(true)
|
||||
expect(result.sessions.find((session) => session.filePath === targetMeta)?.cwd).toBe(
|
||||
targetScope
|
||||
)
|
||||
expect(result.issues.some((issue) => issue.message.includes('scope paths limit'))).toBe(false)
|
||||
})
|
||||
|
||||
it('detects scope-path truncation before applying the cap', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'orca-cursor-scope-trunc-'))
|
||||
tempRoots.push(root)
|
||||
|
||||
@@ -3,6 +3,7 @@ import { join } from 'node:path'
|
||||
import type { AiVaultScanIssue } from '../../shared/ai-vault-types'
|
||||
import {
|
||||
cursorBucketForCwd,
|
||||
cursorContextPathForHash,
|
||||
cursorLegacySlug,
|
||||
cursorSessionActivityMtimeMs,
|
||||
cursorScopeCwdCandidates,
|
||||
@@ -71,16 +72,7 @@ async function discoverCursorSidecars(args: {
|
||||
issues: AiVaultScanIssue[]
|
||||
}): Promise<SessionFileDiscovery> {
|
||||
const startedAt = Date.now()
|
||||
const rawScopePaths = args.options.scopePaths ?? []
|
||||
// Detect truncation on the raw caller list before any conversion work/cap.
|
||||
const scopePathsTruncated = rawScopePaths.length > CURSOR_SCOPE_PATH_LIMIT
|
||||
const scopePaths = await localScopeCandidates({
|
||||
...args,
|
||||
options: {
|
||||
...args.options,
|
||||
scopePaths: rawScopePaths.slice(0, CURSOR_SCOPE_PATH_LIMIT)
|
||||
}
|
||||
})
|
||||
const scopePaths = localSidecarScopeCandidates(args)
|
||||
let discovery
|
||||
try {
|
||||
discovery = await discoverLocalCursorSidecarsBounded({
|
||||
@@ -112,17 +104,6 @@ async function discoverCursorSidecars(args: {
|
||||
...discovery.counters,
|
||||
elapsedMs: Math.max(0, Date.now() - startedAt)
|
||||
}
|
||||
const truncated = {
|
||||
...discovery.truncated,
|
||||
scopePaths: discovery.truncated.scopePaths || scopePathsTruncated
|
||||
}
|
||||
if (scopePathsTruncated && !discovery.truncated.scopePaths) {
|
||||
args.issues.push({
|
||||
agent: 'cursor',
|
||||
path: 'cursor sidecar scan',
|
||||
message: 'Cursor sidecar scan truncated by the scope paths limit.'
|
||||
})
|
||||
}
|
||||
if (!discovery.rootRealPath) {
|
||||
return {
|
||||
agent: 'cursor',
|
||||
@@ -131,7 +112,7 @@ async function discoverCursorSidecars(args: {
|
||||
cursorLayout: 'sidecar',
|
||||
cursorStorageContextKey: args.roots.storageContextKey,
|
||||
cursorDiscoveryCounters: counters,
|
||||
cursorDiscoveryTruncated: truncated
|
||||
cursorDiscoveryTruncated: discovery.truncated
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +133,7 @@ async function discoverCursorSidecars(args: {
|
||||
cursorCwdEvidenceByPath: discovery.evidenceByPath,
|
||||
cursorExpectedRootRealPath: discovery.rootRealPath,
|
||||
cursorDiscoveryCounters: counters,
|
||||
cursorDiscoveryTruncated: truncated
|
||||
cursorDiscoveryTruncated: discovery.truncated
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +189,7 @@ async function localScopeCandidates(args: {
|
||||
options: AiVaultScanOptions
|
||||
}): Promise<string[]> {
|
||||
const candidates = new Set<string>()
|
||||
for (const scopePath of args.options.scopePaths ?? []) {
|
||||
for (const scopePath of (args.options.scopePaths ?? []).slice(0, CURSOR_SCOPE_PATH_LIMIT)) {
|
||||
for (const cwd of cursorScopeCwdCandidates({
|
||||
scopePath,
|
||||
platform: args.roots.targetPlatform,
|
||||
@@ -232,6 +213,24 @@ async function localScopeCandidates(args: {
|
||||
return [...candidates]
|
||||
}
|
||||
|
||||
function localSidecarScopeCandidates(args: {
|
||||
roots: CursorRootPair
|
||||
options: AiVaultScanOptions
|
||||
}): string[] {
|
||||
const candidates = new Set<string>()
|
||||
for (const scopePath of args.options.scopePaths ?? []) {
|
||||
const candidate = cursorContextPathForHash(
|
||||
scopePath,
|
||||
args.roots.storageContextKey,
|
||||
args.roots.targetPlatform
|
||||
)
|
||||
if (candidate) {
|
||||
candidates.add(candidate)
|
||||
}
|
||||
}
|
||||
return [...candidates]
|
||||
}
|
||||
|
||||
function dedupeFiles(files: readonly FileWithMtime[]): FileWithMtime[] {
|
||||
const byPath = new Map<string, FileWithMtime>()
|
||||
for (const file of files) {
|
||||
|
||||
Reference in New Issue
Block a user