mirror of
https://github.com/stablyai/orca.git
synced 2026-09-25 08:02:31 +00:00
* perf: memoize ancestry when selecting foreground agents * perf(foreground): scope the ancestry memo to its ancestor and process snapshot --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
106 lines
3.3 KiB
TypeScript
106 lines
3.3 KiB
TypeScript
import {
|
|
recognizeAgentProcessFromCommandLine,
|
|
type RecognizedAgentProcess
|
|
} from './agent-process-recognition'
|
|
|
|
export type ForegroundProcessCandidate = {
|
|
pid: number
|
|
ppid: number
|
|
command: string
|
|
depth: number
|
|
stat?: string
|
|
}
|
|
|
|
export type SelectedForegroundProcess = {
|
|
candidate: ForegroundProcessCandidate
|
|
recognized: RecognizedAgentProcess
|
|
}
|
|
|
|
/**
|
|
* Select a foreground agent without letting a vendor helper steal an outer
|
|
* agent's identity when both names occur in one process lineage.
|
|
*/
|
|
export function selectForegroundProcessCandidate(
|
|
candidates: readonly ForegroundProcessCandidate[],
|
|
ancestryCandidates: readonly ForegroundProcessCandidate[] = candidates
|
|
): SelectedForegroundProcess | null {
|
|
const recognized = candidates.flatMap((candidate) => {
|
|
const agent = recognizeAgentProcessFromCommandLine(candidate.command)
|
|
return agent ? [{ candidate, recognized: agent }] : []
|
|
})
|
|
if (recognized.length === 0) {
|
|
return null
|
|
}
|
|
|
|
const agentNames = new Set(recognized.map(({ recognized: agent }) => agent.agent))
|
|
if (agentNames.size > 1) {
|
|
const candidatesByPid = new Map(
|
|
ancestryCandidates.map((candidate) => [candidate.pid, candidate])
|
|
)
|
|
const outer = [...recognized].sort(
|
|
(left, right) => left.candidate.depth - right.candidate.depth
|
|
)[0]
|
|
if (!outer) {
|
|
return null
|
|
}
|
|
const descendsFromOuter = makeAncestorReachabilityTest(outer.candidate, candidatesByPid)
|
|
if (!recognized.every((entry) => descendsFromOuter(entry.candidate))) {
|
|
// Distinct sibling agents do not provide a trustworthy identity.
|
|
return null
|
|
}
|
|
return outer
|
|
}
|
|
|
|
return recognized.reduce((best, current) =>
|
|
foregroundCandidateScore(current.candidate) > foregroundCandidateScore(best.candidate)
|
|
? current
|
|
: best
|
|
)
|
|
}
|
|
|
|
function foregroundCandidateScore(candidate: ForegroundProcessCandidate): number {
|
|
return (candidate.stat?.includes('+') ? 10_000 : 0) + candidate.depth
|
|
}
|
|
|
|
/**
|
|
* "Does this candidate's parent chain reach `ancestor`?", memoized per pid. The memo
|
|
* is captured by the returned closure alongside the one ancestor and one process-table
|
|
* snapshot it was computed against, so it cannot be reused across a different ancestor
|
|
* or a later capture — every call site builds a fresh test from a fresh snapshot.
|
|
*/
|
|
function makeAncestorReachabilityTest(
|
|
ancestor: ForegroundProcessCandidate,
|
|
candidatesByPid: ReadonlyMap<number, ForegroundProcessCandidate>
|
|
): (descendant: ForegroundProcessCandidate) => boolean {
|
|
const reaches = new Map<number, boolean>()
|
|
return (descendant) => {
|
|
let currentPid = descendant.pid
|
|
// Every pid on the walk shares the walk's verdict, and `visited` also stops a
|
|
// ppid cycle (a reparented or wrapped table can report one) from spinning forever.
|
|
const visited = new Set<number>()
|
|
let matches = true
|
|
while (currentPid !== ancestor.pid) {
|
|
const cached = reaches.get(currentPid)
|
|
if (cached !== undefined) {
|
|
matches = cached
|
|
break
|
|
}
|
|
if (visited.has(currentPid)) {
|
|
matches = false
|
|
break
|
|
}
|
|
visited.add(currentPid)
|
|
const current = candidatesByPid.get(currentPid)
|
|
if (!current) {
|
|
matches = false
|
|
break
|
|
}
|
|
currentPid = current.ppid
|
|
}
|
|
for (const pid of visited) {
|
|
reaches.set(pid, matches)
|
|
}
|
|
return matches
|
|
}
|
|
}
|