mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 08:03:22 +00:00
fix(codex): preserve nested agent identity
This commit is contained in:
@@ -89,7 +89,7 @@ afterEach(() => {
|
||||
})
|
||||
|
||||
describe('AgentHookServer listener replay', () => {
|
||||
it('retains the root Codex session across child-driven waiting and completion states', () => {
|
||||
it('retains root Codex identity when relay child events omit it', () => {
|
||||
const server = new AgentHookServer()
|
||||
const providerSession = { key: 'session_id' as const, id: 'root-session' }
|
||||
|
||||
@@ -97,7 +97,12 @@ describe('AgentHookServer listener replay', () => {
|
||||
{
|
||||
paneKey: PANE,
|
||||
providerSession,
|
||||
payload: { state: 'working', prompt: 'coordinate reviewers', agentType: 'codex' }
|
||||
payload: {
|
||||
state: 'working',
|
||||
prompt: 'coordinate reviewers',
|
||||
agentType: 'codex',
|
||||
model: 'gpt-5.4'
|
||||
}
|
||||
},
|
||||
'conn-1'
|
||||
)
|
||||
@@ -109,7 +114,10 @@ describe('AgentHookServer listener replay', () => {
|
||||
},
|
||||
'conn-1'
|
||||
)
|
||||
expect(server.getStatusSnapshot()[0].providerSession).toEqual(providerSession)
|
||||
expect(server.getStatusSnapshot()[0]).toMatchObject({
|
||||
model: 'gpt-5.4',
|
||||
providerSession
|
||||
})
|
||||
|
||||
server.ingestRemote(
|
||||
{
|
||||
@@ -122,6 +130,7 @@ describe('AgentHookServer listener replay', () => {
|
||||
)
|
||||
expect(server.getStatusSnapshot()[0]).toMatchObject({
|
||||
state: 'done',
|
||||
model: 'gpt-5.4',
|
||||
providerSession
|
||||
})
|
||||
})
|
||||
|
||||
@@ -824,14 +824,26 @@ export class AgentHookServer {
|
||||
this.onAgentStatus?.(enriched)
|
||||
return enriched
|
||||
}
|
||||
// Why: Codex child hooks expose a child session id that normalization drops; retain the root resume identity across child-driven states.
|
||||
const providerSessionPreservingPayload =
|
||||
const previousCodexRoot =
|
||||
payload.payload.agentType === 'codex' &&
|
||||
payload.toolAgentId &&
|
||||
!payload.providerSession &&
|
||||
previous?.payload.agentType === 'codex' &&
|
||||
previous.providerSession
|
||||
? { ...payload, providerSession: previous.providerSession }
|
||||
previous?.payload.agentType === 'codex'
|
||||
? previous
|
||||
: undefined
|
||||
const preservedProviderSession = !payload.providerSession
|
||||
? previousCodexRoot?.providerSession
|
||||
: undefined
|
||||
const preservedRootModel = !payload.payload.model ? previousCodexRoot?.payload.model : undefined
|
||||
// Why: an SSH relay restart forgets root-only fields; child hooks must not erase durable resume/model identity.
|
||||
const rootContextPreservingPayload =
|
||||
preservedProviderSession || preservedRootModel
|
||||
? {
|
||||
...payload,
|
||||
...(preservedProviderSession ? { providerSession: preservedProviderSession } : {}),
|
||||
payload: preservedRootModel
|
||||
? { ...payload.payload, model: preservedRootModel }
|
||||
: payload.payload
|
||||
}
|
||||
: payload
|
||||
const identity = resolveAgentStatusIdentity({
|
||||
existing: previous
|
||||
@@ -841,7 +853,7 @@ export class AgentHookServer {
|
||||
updatedAt: previous.receivedAt
|
||||
}
|
||||
: undefined,
|
||||
incoming: providerSessionPreservingPayload.payload.agentType,
|
||||
incoming: rootContextPreservingPayload.payload.agentType,
|
||||
now
|
||||
})
|
||||
if (
|
||||
@@ -854,12 +866,12 @@ export class AgentHookServer {
|
||||
return previous
|
||||
}
|
||||
const identityResolvedPayload =
|
||||
identity.agentType === providerSessionPreservingPayload.payload.agentType
|
||||
? providerSessionPreservingPayload
|
||||
identity.agentType === rootContextPreservingPayload.payload.agentType
|
||||
? rootContextPreservingPayload
|
||||
: {
|
||||
...providerSessionPreservingPayload,
|
||||
...rootContextPreservingPayload,
|
||||
payload: {
|
||||
...providerSessionPreservingPayload.payload,
|
||||
...rootContextPreservingPayload.payload,
|
||||
agentType: identity.agentType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,6 +357,28 @@ describe('WorktreeCardAgents', () => {
|
||||
expect(markup).not.toContain('Prompt cache expires')
|
||||
})
|
||||
|
||||
it('does not repeat a subagent role used as its compact primary label', async () => {
|
||||
const { CompactAgentRow } = await import('./worktree-card-compact-agents')
|
||||
|
||||
const markup = renderToStaticMarkup(
|
||||
<CompactAgentRow
|
||||
agent={
|
||||
mockAgent({
|
||||
agentType: 'reviewer',
|
||||
rowSource: 'subagent',
|
||||
startedAt: 1000,
|
||||
prompt: 'reviewer'
|
||||
}) as DashboardAgentRowData
|
||||
}
|
||||
now={2000}
|
||||
onActivate={vi.fn()}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(markup).toContain('reviewer')
|
||||
expect(markup).not.toContain(' - Reviewer')
|
||||
})
|
||||
|
||||
it('marks only the focused agent row', async () => {
|
||||
mockAgentActivityDisplayMode = 'full'
|
||||
mockFocusedAgentPaneKey = 'tab-1:2'
|
||||
|
||||
@@ -63,7 +63,15 @@ function getCompactAgentSecondary(agent: DashboardAgentRowData): string {
|
||||
return toolName
|
||||
}
|
||||
}
|
||||
return agent.entry.lastAssistantMessage?.trim() || formatAgentTypeLabel(agent.agentType)
|
||||
const lastAssistantMessage = agent.entry.lastAssistantMessage?.trim()
|
||||
if (lastAssistantMessage) {
|
||||
return lastAssistantMessage
|
||||
}
|
||||
// Why: child rows without descriptions use their role as primary text; repeating its formatted label adds no information.
|
||||
if (agent.rowSource === 'subagent' && agent.entry.prompt?.trim() === agent.agentType.trim()) {
|
||||
return ''
|
||||
}
|
||||
return formatAgentTypeLabel(agent.agentType)
|
||||
}
|
||||
|
||||
function getCompactAgentTime(agent: DashboardAgentRowData, now: number): string | null {
|
||||
|
||||
Reference in New Issue
Block a user