fix(codex): preserve nested agent identity

This commit is contained in:
Brennan Benson
2026-07-20 20:07:47 -07:00
parent d4a45c3ddc
commit f43e4a5805
4 changed files with 66 additions and 15 deletions
+12 -3
View File
@@ -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
})
})
+23 -11
View File
@@ -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 {