mirror of
https://github.com/stablyai/orca.git
synced 2026-10-06 08:02:28 +00:00
* 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>
119 lines
4.2 KiB
TypeScript
119 lines
4.2 KiB
TypeScript
import { posix, win32 } from 'node:path'
|
|
import { isWindowsAbsolutePathLike } from '../../shared/cross-platform-path'
|
|
|
|
export function areWorktreePathsEqual(
|
|
leftPath: string,
|
|
rightPath: string,
|
|
platform = process.platform
|
|
): boolean {
|
|
if (looksLikePosixAbsolutePath(leftPath) || looksLikePosixAbsolutePath(rightPath)) {
|
|
// Why: local WSL projects run POSIX paths on a Windows desktop; comparing
|
|
// them with win32 rules can delete or dedupe the wrong runtime-owned path.
|
|
if (!looksLikePosixAbsolutePath(leftPath) || !looksLikePosixAbsolutePath(rightPath)) {
|
|
return false
|
|
}
|
|
const left = normalizePosixWorktreePathForComparison(leftPath, platform)
|
|
const right = normalizePosixWorktreePathForComparison(rightPath, platform)
|
|
return left === right
|
|
}
|
|
|
|
if (
|
|
platform === 'win32' ||
|
|
isWindowsAbsolutePathLike(leftPath) ||
|
|
isWindowsAbsolutePathLike(rightPath)
|
|
) {
|
|
const left = normalizeWindowsWorktreePathForComparison(leftPath)
|
|
const right = normalizeWindowsWorktreePathForComparison(rightPath)
|
|
// Why: Git can report the same Windows path with different slash styles or
|
|
// drive-letter casing; treating them as distinct creates duplicate worktrees.
|
|
return left === right
|
|
}
|
|
const left = normalizePosixWorktreePathForComparison(leftPath, platform)
|
|
const right = normalizePosixWorktreePathForComparison(rightPath, platform)
|
|
return left === right
|
|
}
|
|
|
|
export function worktreePathComparisonKey(pathValue: string, platform = process.platform): string {
|
|
if (looksLikePosixAbsolutePath(pathValue)) {
|
|
return `posix:${normalizePosixWorktreePathForComparison(pathValue, platform)}`
|
|
}
|
|
if (platform === 'win32' || isWindowsAbsolutePathLike(pathValue)) {
|
|
return `windows:${normalizeWindowsWorktreePathForComparison(pathValue)}`
|
|
}
|
|
return `posix:${normalizePosixWorktreePathForComparison(pathValue, platform)}`
|
|
}
|
|
|
|
export function dedupeWorktreesByPath<T extends { path: string }>(
|
|
worktrees: readonly T[],
|
|
platform = process.platform
|
|
): T[] {
|
|
// Why: large Git/relay listings should normalize each path once while still
|
|
// preserving the first row under the cross-platform equality contract above.
|
|
const unique: T[] = []
|
|
const posixAbsoluteKeys = new Set<string>()
|
|
const windowsKeys = new Set<string>()
|
|
const windowsPaths: string[] = []
|
|
const relativePaths: string[] = []
|
|
|
|
for (const worktree of worktrees) {
|
|
const pathValue = worktree.path
|
|
if (looksLikePosixAbsolutePath(pathValue)) {
|
|
const key = normalizePosixWorktreePathForComparison(pathValue, platform)
|
|
if (posixAbsoluteKeys.has(key)) {
|
|
continue
|
|
}
|
|
posixAbsoluteKeys.add(key)
|
|
unique.push(worktree)
|
|
continue
|
|
}
|
|
|
|
const windowsKey = normalizeWindowsWorktreePathForComparison(pathValue)
|
|
if (platform === 'win32' || isWindowsAbsolutePathLike(pathValue)) {
|
|
if (
|
|
windowsKeys.has(windowsKey) ||
|
|
relativePaths.some((existing) => areWorktreePathsEqual(existing, pathValue, platform))
|
|
) {
|
|
continue
|
|
}
|
|
windowsKeys.add(windowsKey)
|
|
windowsPaths.push(pathValue)
|
|
unique.push(worktree)
|
|
continue
|
|
}
|
|
|
|
// Why: Git normally reports absolute paths. Retain pair-aware comparison
|
|
// only for malformed/legacy relative rows whose flavor depends on its peer.
|
|
if (
|
|
relativePaths.some((existing) => areWorktreePathsEqual(existing, pathValue, platform)) ||
|
|
windowsPaths.some((existing) => areWorktreePathsEqual(existing, pathValue, platform))
|
|
) {
|
|
continue
|
|
}
|
|
relativePaths.push(pathValue)
|
|
unique.push(worktree)
|
|
}
|
|
return unique
|
|
}
|
|
|
|
function looksLikePosixAbsolutePath(pathValue: string): boolean {
|
|
return pathValue.startsWith('/') && !pathValue.startsWith('//')
|
|
}
|
|
|
|
function normalizeWindowsWorktreePathForComparison(pathValue: string): string {
|
|
return win32.normalize(win32.resolve(pathValue)).toLowerCase()
|
|
}
|
|
|
|
function normalizePosixWorktreePathForComparison(
|
|
pathValue: string,
|
|
platform: NodeJS.Platform
|
|
): string {
|
|
const normalized = posix.normalize(posix.resolve(pathValue))
|
|
if (platform !== 'darwin') {
|
|
return normalized
|
|
}
|
|
if (normalized === '/private/tmp') {
|
|
return '/tmp'
|
|
}
|
|
return normalized.startsWith('/private/tmp/') ? normalized.slice('/private'.length) : normalized
|
|
}
|