Files
orca/src/main/local-project-runtime-resolution.ts
T
Brennan BensonandBrennan Benson 0b65d725c9 "Hide sleeping" never hides a workspace with an open agent session (#7197) (#7511)
* Keep running-agent workspaces visible under "Hide sleeping" (#7197)

The "Hide sleeping" sidebar filter judged a workspace active only when it
had a live PTY (or a browser tab), so a workspace with a running agent
whose live-PTY entry was momentarily absent — an SSH reconnect grace
window, an unmounted pane, a remote surface not yet `ready`, or an
orchestration worker reporting before its tab is mirrored — was
classified as "sleeping" and hidden while its session was still open.

The smart sort already treats a fresh `agentStatusByPaneKey` entry as
"working" independent of live-PTY, so the filter and sort disagreed. Add
`getWorktreeIdsWithLiveAgent`, which derives the worktrees with an open
agent session from the live agent-status map (sleep/teardown drop those
entries via dropAgentStatusByWorktree, so slept/hibernated workspaces
still hide), and consult it in `hasActiveWorkspaceActivity`. Wire it
through the sidebar list, Cmd+J jump palette, and kanban board.

* fix(agent-status): align live workspace attribution

* fix(mobile): preserve live-agent workspace activity

* fix(mobile): prefer newest agent status source

* chore: restore unrelated benchmark formatting

* fix(mobile): resolve projected agent worktree ids

* perf(mobile): index projected worktree summaries

* fix(mobile): preserve projected activity under limits

* fix(mobile): preserve POSIX path identity

* perf(mobile): cache projected summary fallbacks

* fix(mobile): preserve remote path and priority contracts

* perf(mobile): index projected paths by host flavor

* test(mobile): prove projected path index keys

* perf(mobile): bound projected path fallback

* perf(mobile): reuse projected repo platforms

* test(mobile): enforce projected lookup bounds

* chore(runtime): remove review instrumentation

* perf(mobile): skip unresolved repo platform scans

* perf(mobile): batch represented project runtimes

* perf(mobile): batch cold project runtime scans

* fix(mobile): couple worktree platform snapshots

* fix(sidebar): prioritize attributed headless agents

* fix(sidebar): activate smart sort for headless agents

* fix(sidebar): prefer mirrored agent ownership

* fix(mobile): follow mirrored agent ownership

* fix(sidebar): resolve mirrored unstamped agents

---------

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
2026-07-13 01:10:38 -07:00

107 lines
3.5 KiB
TypeScript

import type { Store } from './persistence'
import type { Project, Repo } from '../shared/types'
import {
resolveProjectExecutionRuntime,
type ProjectExecutionRuntimeResolution
} from '../shared/project-execution-runtime'
import {
getCachedWslAvailability,
getCachedWslDistros,
hasCachedWslAvailability,
hasCachedWslDistros
} from './wsl'
import { getRepoIdFromWorktreeId } from '../shared/worktree-id'
import { getRepoExecutionHostId, LOCAL_EXECUTION_HOST_ID } from '../shared/execution-host'
function canResolveProjectRuntimeForRepo(store: Store): boolean {
return typeof store.getProjects === 'function' && typeof store.getSettings === 'function'
}
function canResolveProjectRuntimeForWorktreeId(store: Store): boolean {
return canResolveProjectRuntimeForRepo(store) && typeof store.getRepo === 'function'
}
function resolveLocalProjectRuntime(
store: Store,
project: Project,
settings: ReturnType<Store['getSettings']> = store.getSettings()
): ProjectExecutionRuntimeResolution {
const wslAvailable = hasCachedWslAvailability()
? (getCachedWslAvailability() ?? undefined)
: undefined
const availableWslDistros = hasCachedWslDistros() ? getCachedWslDistros() : null
return resolveProjectExecutionRuntime({
appPlatform: process.platform,
projectId: project.id,
projectRuntimePreference: project.localWindowsRuntimePreference,
globalWindowsRuntimeDefault: settings.localWindowsRuntimeDefault,
wslAvailable,
availableWslDistros
})
}
export function resolveLocalProjectRuntimeForRepo(
store: Store,
repo: Repo
): ProjectExecutionRuntimeResolution | undefined {
if (
getRepoExecutionHostId(repo) !== LOCAL_EXECUTION_HOST_ID ||
!canResolveProjectRuntimeForRepo(store)
) {
return undefined
}
const project = store.getProjects().find((entry) => entry.sourceRepoIds.includes(repo.id))
if (!project) {
return undefined
}
return resolveLocalProjectRuntime(store, project)
}
export function resolveLocalProjectRuntimesForRepos(
store: Store,
repos: readonly Repo[]
): ReadonlyMap<string, ProjectExecutionRuntimeResolution> {
const runtimeByRepoId = new Map<string, ProjectExecutionRuntimeResolution>()
if (!canResolveProjectRuntimeForRepo(store)) {
return runtimeByRepoId
}
const requestedRepoIds = new Set(
repos
.filter((repo) => getRepoExecutionHostId(repo) === LOCAL_EXECUTION_HOST_ID)
.map((repo) => repo.id)
)
if (requestedRepoIds.size === 0) {
return runtimeByRepoId
}
const settings = store.getSettings()
for (const project of store.getProjects()) {
const matchingRepoIds = project.sourceRepoIds.filter(
(repoId) => requestedRepoIds.has(repoId) && !runtimeByRepoId.has(repoId)
)
if (matchingRepoIds.length === 0) {
continue
}
// Why: one project runtime applies to every source repo in that project;
// resolving it once prevents mobile polls from rescanning project settings.
const runtime = resolveLocalProjectRuntime(store, project, settings)
for (const repoId of matchingRepoIds) {
runtimeByRepoId.set(repoId, runtime)
}
}
return runtimeByRepoId
}
export function resolveLocalProjectRuntimeForWorktreeId(
store: Store | undefined,
worktreeId: string | undefined
): ProjectExecutionRuntimeResolution | undefined {
if (!store || !worktreeId) {
return undefined
}
if (!canResolveProjectRuntimeForWorktreeId(store)) {
return undefined
}
const repo = store.getRepo(getRepoIdFromWorktreeId(worktreeId))
return repo ? resolveLocalProjectRuntimeForRepo(store, repo) : undefined
}