mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 18:17:36 +00:00
Programs can tell Orca what they are doing through the Program Status Protocol (OSC 7501); Claude Code, Codex, Grok, pi and oh-my-pi already do. Orca answers the protocol's feature-detection probe (visible xterm and the hidden-pane emulator), parses reports from PTY output on the execution host (src/shared/program-status/), and keeps two lanes: - Lifecycle state keeps one owner per pane. Hooks win; OSC 7501 drives the row when hooks are off, missing, or silent on a state the program has left (server/program-status/hook-precedence). - The program's own report (progress, its one-line message, blocks such as sign-in) is a live-only `programStatus` facet kept per pane (server/program-status/facet-store), stitched onto rows at publish time and carried to the renderer store, mobile, paired clients and `worktree ps`. Facet-only changes patch just the facet. Title-driven readers (idle edges, delivery, tui-idle, exit inference) classify by the program's OSC 7501 activity while it reports one and fall back to title glyphs only for agents that report nothing explicit, so they keep working when Claude stops painting its title glyph after the probe.
100 lines
3.6 KiB
TypeScript
100 lines
3.6 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { collectRuntimeWorktreeAgentSources } from './runtime-worktree-agent-sources'
|
|
import type { AgentStatusIpcPayload } from '../../shared/agent-status-types'
|
|
|
|
const paneKey = 'worktree:tab:0'
|
|
const now = Date.now()
|
|
const hookRow: AgentStatusIpcPayload = {
|
|
paneKey,
|
|
tabId: 'tab',
|
|
terminalHandle: 'term_row',
|
|
worktreeId: 'worktree',
|
|
connectionId: null,
|
|
state: 'working',
|
|
prompt: 'implement',
|
|
agentType: 'codex',
|
|
stateStartedAt: now,
|
|
receivedAt: now
|
|
}
|
|
const base = {
|
|
hookSnapshots: [hookRow],
|
|
mirroredWorktreeIdByTabId: new Map<string, string>(),
|
|
connectedPtyEvidence: {
|
|
tabIds: new Set<string>(),
|
|
paneKeys: new Set<string>(),
|
|
ptyIdByTerminalHandle: new Map<string, string>()
|
|
}
|
|
}
|
|
const connected = {
|
|
...base,
|
|
connectedPtyEvidence: {
|
|
tabIds: new Set(['tab']),
|
|
paneKeys: new Set([paneKey]),
|
|
ptyIdByTerminalHandle: new Map([['term_row', 'pty']])
|
|
}
|
|
}
|
|
|
|
describe('worktree agent source admission', () => {
|
|
it('rejects a disconnected local terminal before row assembly', () => {
|
|
expect(collectRuntimeWorktreeAgentSources(base).size).toBe(0)
|
|
expect(collectRuntimeWorktreeAgentSources(connected).get(paneKey)?.state).toBe('working')
|
|
})
|
|
|
|
it('keeps remote evidence and resolves mirrored workspace ownership', () => {
|
|
const remote = { ...hookRow, connectionId: 'ssh-connection' }
|
|
expect(collectRuntimeWorktreeAgentSources({ ...base, hookSnapshots: [remote] }).size).toBe(1)
|
|
const sources = collectRuntimeWorktreeAgentSources({
|
|
...base,
|
|
mirroredWorktreeIdByTabId: new Map([['tab', 'remote-worktree']])
|
|
})
|
|
expect(sources.get(paneKey)?.worktreeId).toBe('remote-worktree')
|
|
})
|
|
|
|
it('rejoins the row to the connected PTY behind its terminal handle', () => {
|
|
expect(collectRuntimeWorktreeAgentSources(connected).get(paneKey)?.ptyId).toBe('pty')
|
|
// The handle is the last rescue once a controller incarnation nulls the pane binding.
|
|
const bindingCleared = collectRuntimeWorktreeAgentSources({
|
|
...base,
|
|
connectedPtyEvidence: {
|
|
...base.connectedPtyEvidence,
|
|
ptyIdByTerminalHandle: new Map([['term_row', 'pty']])
|
|
}
|
|
})
|
|
expect(bindingCleared.get(paneKey)?.ptyId).toBe('pty')
|
|
// No connected PTY answers to the handle and no pane evidence: the row is not admitted.
|
|
expect(collectRuntimeWorktreeAgentSources(base).size).toBe(0)
|
|
})
|
|
|
|
it('carries the row own working mode and drops non-live rows', () => {
|
|
const monitoring = collectRuntimeWorktreeAgentSources({
|
|
...connected,
|
|
hookSnapshots: [{ ...hookRow, workingMode: 'monitoring' as const }]
|
|
})
|
|
expect(monitoring.get(paneKey)).toMatchObject({ updatedAt: now, workingMode: 'monitoring' })
|
|
|
|
const restored = collectRuntimeWorktreeAgentSources({
|
|
...connected,
|
|
hookSnapshots: [{ ...hookRow, restoredUnconfirmed: true as const }]
|
|
})
|
|
expect(restored.size).toBe(0)
|
|
|
|
const providerSessionOnly = collectRuntimeWorktreeAgentSources({
|
|
...connected,
|
|
hookSnapshots: [{ ...hookRow, providerSessionOnly: true }]
|
|
})
|
|
expect(providerSessionOnly.size).toBe(0)
|
|
})
|
|
|
|
it('carries the pane facet from the hook row to the source', () => {
|
|
const programStatus = { state: 'working' as const, progress: 42, observedAt: now }
|
|
const sources = collectRuntimeWorktreeAgentSources({
|
|
...connected,
|
|
hookSnapshots: [{ ...hookRow, programStatus }]
|
|
})
|
|
expect(sources.get(paneKey)?.programStatus).toEqual(programStatus)
|
|
expect(collectRuntimeWorktreeAgentSources(connected).get(paneKey)).not.toHaveProperty(
|
|
'programStatus'
|
|
)
|
|
})
|
|
})
|