mirror of
https://github.com/stablyai/orca.git
synced 2026-10-23 08:03:39 +00:00
fix(mobile): scope session matching by execution host
This commit is contained in:
@@ -209,6 +209,34 @@ describe('mobile AI Vault resume target guards', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('uses repo ownership when legacy worktree rows omit hostId', () => {
|
||||
const target = resolveMobileAiVaultSessionResumeTarget({
|
||||
session: session({ cwd: '/mnt/c/Users/ada/orca/feature/src' }),
|
||||
activeWorktreeId: 'route-wt',
|
||||
worktrees: [
|
||||
worktree({
|
||||
worktreeId: 'ssh-lookalike',
|
||||
repoId: 'ssh-repo',
|
||||
path: '/mnt/c/Users/ada/orca/feature'
|
||||
}),
|
||||
worktree({
|
||||
worktreeId: 'local-wsl',
|
||||
path: String.raw`C:\Users\ada\orca\feature`
|
||||
}),
|
||||
worktree({ worktreeId: 'route-wt', path: String.raw`C:\Users\ada\orca\main` })
|
||||
],
|
||||
repos
|
||||
})
|
||||
|
||||
expect(target).toEqual({
|
||||
status: 'ready',
|
||||
worktreeId: 'local-wsl',
|
||||
targetStatus: 'local',
|
||||
workspacePath: String.raw`C:\Users\ada\orca\feature`,
|
||||
terminalPlatform: null
|
||||
})
|
||||
})
|
||||
|
||||
it('falls back to the active route worktree when the session worktree is archived', () => {
|
||||
const target = resolveMobileAiVaultSessionResumeTarget({
|
||||
session: session({ cwd: '/Users/ada/repo/archive/src' }),
|
||||
|
||||
@@ -125,6 +125,7 @@ export function resolveMobileAiVaultSessionResumeTarget(args: {
|
||||
const sessionWorktree = resolveMobileAgentHistorySessionWorktree({
|
||||
session: args.session,
|
||||
worktrees: args.worktrees,
|
||||
repos: args.repos,
|
||||
activeWorktreeId: args.activeWorktreeId
|
||||
})
|
||||
const sessionWorktreeId = canResumeInMobileSessionWorktree(sessionWorktree)
|
||||
|
||||
@@ -4,7 +4,11 @@ import {
|
||||
normalizedWslPathCandidateAliases,
|
||||
wslAliasedPathDepth
|
||||
} from '../../../src/shared/wsl-path-aliases'
|
||||
import { normalizeExecutionHostId } from '../../../src/shared/execution-host'
|
||||
import {
|
||||
getWorktreeExecutionHostId,
|
||||
normalizeExecutionHostId,
|
||||
type ExecutionHostId
|
||||
} from '../../../src/shared/execution-host'
|
||||
import { splitWorktreeIdForFilesystem } from '../../../src/shared/worktree/id'
|
||||
import type { AiVaultSession } from '../../../src/shared/ai-vault-types'
|
||||
import type { Worktree } from '../worktree/workspace-list-types'
|
||||
@@ -20,11 +24,18 @@ export type MobileAgentHistorySessionWorktreeInfo = {
|
||||
type WorktreeCandidate = {
|
||||
worktree: WorktreeWithPriorIds
|
||||
path: string
|
||||
hostId: ExecutionHostId
|
||||
pathDepth: number
|
||||
ownsNormalizedCwd: (normalizedCwd: string) => boolean
|
||||
source: 'current-path' | 'prior-path'
|
||||
}
|
||||
|
||||
type WorktreeRepoHost = {
|
||||
id: string
|
||||
connectionId?: string | null
|
||||
executionHostId?: ExecutionHostId | null
|
||||
}
|
||||
|
||||
type WorktreeWithPriorIds = Worktree & {
|
||||
priorWorktreeIds?: readonly string[]
|
||||
}
|
||||
@@ -32,6 +43,7 @@ type WorktreeWithPriorIds = Worktree & {
|
||||
export function resolveMobileAgentHistorySessionWorktree(args: {
|
||||
session: Pick<AiVaultSession, 'cwd' | 'executionHostId'>
|
||||
worktrees: readonly Worktree[]
|
||||
repos?: readonly WorktreeRepoHost[]
|
||||
activeWorktreeId: string | null
|
||||
}): MobileAgentHistorySessionWorktreeInfo | null {
|
||||
if (!args.session.cwd) {
|
||||
@@ -40,7 +52,7 @@ export function resolveMobileAgentHistorySessionWorktree(args: {
|
||||
const sessionHostId = normalizeExecutionHostId(args.session.executionHostId)
|
||||
const cwdAliases = normalizedWslPathCandidateAliases(args.session.cwd)
|
||||
const best = findBestMobileWorktreeCandidate(
|
||||
buildMobileWorktreeCandidates(args.worktrees),
|
||||
buildMobileWorktreeCandidates(args.worktrees, args.repos ?? []),
|
||||
sessionHostId,
|
||||
cwdAliases
|
||||
)
|
||||
@@ -67,8 +79,7 @@ function findBestMobileWorktreeCandidate(
|
||||
): WorktreeCandidate | null {
|
||||
let best: WorktreeCandidate | null = null
|
||||
for (const candidate of candidates) {
|
||||
const worktreeHostId = normalizeExecutionHostId(candidate.worktree.hostId)
|
||||
if (sessionHostId && worktreeHostId && worktreeHostId !== sessionHostId) {
|
||||
if (sessionHostId && candidate.hostId !== sessionHostId) {
|
||||
continue
|
||||
}
|
||||
if (!normalizedCwdAliases.some(candidate.ownsNormalizedCwd)) {
|
||||
@@ -87,13 +98,28 @@ export function canResumeInMobileSessionWorktree(
|
||||
return Boolean(worktreeInfo && worktreeInfo.status !== 'archived')
|
||||
}
|
||||
|
||||
function buildMobileWorktreeCandidates(worktrees: readonly Worktree[]): WorktreeCandidate[] {
|
||||
function buildMobileWorktreeCandidates(
|
||||
worktrees: readonly Worktree[],
|
||||
repos: readonly WorktreeRepoHost[]
|
||||
): WorktreeCandidate[] {
|
||||
const candidates: WorktreeCandidate[] = []
|
||||
const repoById = new Map(repos.map((repo) => [repo.id, repo]))
|
||||
for (const worktree of worktrees as readonly WorktreeWithPriorIds[]) {
|
||||
const repo = repoById.get(worktree.repoId)
|
||||
const hostId = getWorktreeExecutionHostId(
|
||||
worktree,
|
||||
repo
|
||||
? {
|
||||
connectionId: repo.connectionId ?? null,
|
||||
executionHostId: repo.executionHostId ?? null
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
if (hasUsablePath(worktree.path)) {
|
||||
candidates.push({
|
||||
worktree,
|
||||
path: worktree.path,
|
||||
hostId,
|
||||
pathDepth: wslAliasedPathDepth(worktree.path),
|
||||
ownsNormalizedCwd: createWslAliasedPathInsideOrEqualMatcher(worktree.path),
|
||||
source: 'current-path'
|
||||
@@ -107,6 +133,7 @@ function buildMobileWorktreeCandidates(worktrees: readonly Worktree[]): Worktree
|
||||
candidates.push({
|
||||
worktree,
|
||||
path: parsed.worktreePath,
|
||||
hostId,
|
||||
pathDepth: wslAliasedPathDepth(parsed.worktreePath),
|
||||
ownsNormalizedCwd: createWslAliasedPathInsideOrEqualMatcher(parsed.worktreePath),
|
||||
source: 'prior-path'
|
||||
|
||||
Reference in New Issue
Block a user