mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
* fix(pi): resolve OMP tab identity from the outer wrapper, not the wrapped pi (#6364) OMP runs as a `shell → omp → pi` process tree and Orca recognizes both `omp` and `pi` as distinct agents. The foreground-process readers scored the deepest `+` foreground descendant, so they returned the wrapped `pi` engine instead of the `omp` the user actually launched — and because the sampled `+` frame moves between omp and pi across command boundaries, the read oscillated and the tab icon/label flickered OMP↔Pi. It surfaced on Remote Host / SSH (only in a worktree, only while working) where a mirrored/restored pane has no client-side launchAgent to anchor identity, so the (host-read) foreground was the only pi/omp signal — and it was wrong. Fix the detection at its source so foregroundAgent is trustworthy everywhere (local tab icon, exit detection, and the remote mirror): - Add `resolveOuterWrapperForegroundProcess` (src/shared/foreground-wrapper-agent.ts): when the recognized foreground winner shares a `titleIdentityGroup` with a shallower recognized ancestor, return the shallowest — the outer wrapper. Stable regardless of which of omp/pi holds the foreground at the sample; bare Pi and cross-group reads (Codex, etc.) are unchanged. - Wire it into both readers: local `agent-foreground-process.ts` and the SSH relay `pty-shell-utils.ts`. Also harden the relay's fallback short-circuit so a pi-compatible process reported by node-pty rescans for the omp wrapper instead of trusting `pi` raw (never downgrading below the fallback). Also unify the host-side publish path onto the shared owner resolver: the mobile session snapshot builders now resolve pane ownership via `resolvePaneAgentOwner` ({launchAgent, hookAgent}) instead of a bespoke `launchAgent ?? foregroundAgent`, so the host stops being a divergent fourth identity path and the hook's host-stamped identity survives when launchAgent is dropped on a mirrored pane. Tests: unit coverage for the wrapper resolver, `shell→omp→pi` ps trees for both readers (outer omp, oscillating-frame, bare pi, node-pty pi fallback), and a host-snapshot regression that keeps an OMP hook labeled OMP when the wrapped pi child owns the foreground. * fix(pi): harden wrapper foreground ownership * fix(pi): preserve OMP ownership in daemon foreground reads * fix(pi): retain OMP owner when process scans fail
57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
import {
|
|
recognizeAgentProcessFromCommandLine,
|
|
type RecognizedAgentProcess
|
|
} from './agent-process-recognition'
|
|
import { getSyntheticAgentTitleProfile } from './synthetic-agent-title'
|
|
|
|
export type ForegroundAgentCandidate = {
|
|
pid: number
|
|
ppid: number
|
|
command: string
|
|
name?: string
|
|
}
|
|
|
|
export function shouldInspectOuterWrapperForegroundProcess(
|
|
process: RecognizedAgentProcess
|
|
): boolean {
|
|
// Why: only Pi is currently embedded by a same-group wrapper; scanning OMP would add a subprocess to every relay poll.
|
|
return process.agent === 'pi'
|
|
}
|
|
|
|
/**
|
|
* Collapse a foreground read onto its outermost same-title-group ancestor.
|
|
* Why: OMP embeds Pi, while depth alone cannot distinguish wrappers from sibling jobs.
|
|
*/
|
|
export function resolveOuterWrapperForegroundProcess(
|
|
winner: RecognizedAgentProcess,
|
|
winnerCandidate: ForegroundAgentCandidate,
|
|
descendants: readonly ForegroundAgentCandidate[]
|
|
): string {
|
|
const winnerGroup = getSyntheticAgentTitleProfile(winner.agent)?.titleIdentityGroup
|
|
if (!winnerGroup) {
|
|
return winner.processName
|
|
}
|
|
const candidatesByPid = new Map(descendants.map((candidate) => [candidate.pid, candidate]))
|
|
const seen = new Set<number>([winnerCandidate.pid])
|
|
let outerProcessName = winner.processName
|
|
let parentPid = winnerCandidate.ppid
|
|
while (!seen.has(parentPid)) {
|
|
seen.add(parentPid)
|
|
const candidate = candidatesByPid.get(parentPid)
|
|
if (!candidate) {
|
|
break
|
|
}
|
|
const recognized =
|
|
recognizeAgentProcessFromCommandLine(candidate.command) ??
|
|
recognizeAgentProcessFromCommandLine(candidate.name)
|
|
if (
|
|
recognized &&
|
|
getSyntheticAgentTitleProfile(recognized.agent)?.titleIdentityGroup === winnerGroup
|
|
) {
|
|
outerProcessName = recognized.processName
|
|
}
|
|
parentPid = candidate.ppid
|
|
}
|
|
return outerProcessName
|
|
}
|