mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
* fix(agents): keep outer agent identity over vendor helpers * fix(agents): preserve outer identity across relay scans --------- Co-authored-by: Merge Sim <sim@local>
81 lines
2.3 KiB
TypeScript
81 lines
2.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 ||
|
|
!recognized.every((entry) =>
|
|
isAncestorOrSelf(outer.candidate, entry.candidate, candidatesByPid)
|
|
)
|
|
) {
|
|
// 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
|
|
}
|
|
|
|
function isAncestorOrSelf(
|
|
ancestor: ForegroundProcessCandidate,
|
|
descendant: ForegroundProcessCandidate,
|
|
candidatesByPid: ReadonlyMap<number, ForegroundProcessCandidate>
|
|
): boolean {
|
|
let currentPid = descendant.pid
|
|
while (currentPid !== ancestor.pid) {
|
|
const current = candidatesByPid.get(currentPid)
|
|
if (!current) {
|
|
return false
|
|
}
|
|
currentPid = current.ppid
|
|
}
|
|
return true
|
|
}
|