fix(pi): name workflow child rows by their step key, not only the agent (#26909)

pi-subagents never sends a description, so every child row fell back to
the agent name and two children on one agent read the same. Workflow
children do carry workflowKey, a bounded slug, so use it as the label.

Fixes #26908
This commit is contained in:
mmarabel
2026-10-09 23:08:22 -07:00
committed by GitHub
parent 2f267889e8
commit c13745c478
2 changed files with 38 additions and 3 deletions
@@ -180,6 +180,40 @@ describe('Pi child rows', () => {
])
})
it('names workflow children that share an agent by their workflow step key', async () => {
const harness = createAgentStatusExtensionHarness({ kind: 'pi' })
startWorkflow(harness)
// pi-subagents 0.76.1 tags each scripted-workflow child with the key it was launched under.
for (const key of ['review-code', 'review-rollout']) {
harness.emitPiEvent('subagent:async-started', {
id: `run-${key}`,
mode: 'single',
agent: 'reviewer',
pid: 4000,
parentWorkflowRunId: WORKFLOW,
workflowKey: key
})
}
await vi.advanceTimersByTimeAsync(0)
expect(posts(harness).at(-1)?.subagents).toEqual([
{
id: 'run-review-code',
state: 'working',
startedAt: expect.any(Number),
agentType: 'reviewer',
description: 'review-code'
},
{
id: 'run-review-rollout',
state: 'working',
startedAt: expect.any(Number),
agentType: 'reviewer',
description: 'review-rollout'
}
])
})
it('posts an OMP task child with its description', async () => {
const harness = createAgentStatusExtensionHarness({ kind: 'omp' })
await harness.callHook('session_start')
@@ -107,15 +107,16 @@ export function getPiSubagentRosterEventSourceLines(): string[] {
" const readLabel = (value: unknown): string | undefined => typeof value === 'string' && value.trim() ? value : undefined",
' lifecycleState.onEvent = (event: unknown, forcedStatus?: string): void => {',
" if (!event || typeof event !== 'object') return",
' const record = event as { id?: unknown; runId?: unknown; agent?: unknown; description?: unknown; mode?: unknown; parentWorkflowRunId?: unknown }',
' const record = event as { id?: unknown; runId?: unknown; agent?: unknown; description?: unknown; workflowKey?: unknown; mode?: unknown; parentWorkflowRunId?: unknown }',
" const id = typeof record.id === 'string' && record.id ? record.id : typeof record.runId === 'string' ? record.runId : ''",
' const status = forcedStatus ?? (event as { status?: unknown }).status',
' if (!id) return',
' if (isOmpRuntime() && !lifecycleState.ownsPane) return',
" if (status === 'started') {",
' lifecycleState.active.add(id)',
// Why: pi-subagents redacts task prompts, so only the agent name and OMP's short label are shown.
" if (!subagentDetails.has(id)) subagentDetails.set(id, { agentType: readLabel(record.agent), description: readLabel(record.description), startedAt: Date.now(), workflow: record.mode === 'workflow', parent: readLabel(record.parentWorkflowRunId), registration })",
// Why: pi-subagents redacts task prompts, so only the agent name, OMP's short label and a
// workflow child's step key are shown. The key tells apart siblings that share an agent.
" if (!subagentDetails.has(id)) subagentDetails.set(id, { agentType: readLabel(record.agent), description: readLabel(record.description) ?? readLabel(record.workflowKey), startedAt: Date.now(), workflow: record.mode === 'workflow', parent: readLabel(record.parentWorkflowRunId), registration })",
// Why: Pi re-opens only a posted completion; an earlier child must leave the idle check intact.
' if (!isTurnInFlight() && (isOmpRuntime() || lifecycleState.runGeneration === 0 || lifecycleState.completionPostedGeneration === lifecycleState.runGeneration)) {',
' lifecycleState.waiting = true',