File size: 2,898 Bytes
064bfd6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | import { DIAMOND_FILLED, DIAMOND_OPEN } from '../constants/figures.js'
import { count } from '../utils/array.js'
import type { BackgroundTaskState } from './types.js'
/**
* Produces the compact footer-pill label for a set of background tasks.
* Used by both the footer pill and the turn-duration transcript line so the
* two surfaces agree on terminology.
*/
export function getPillLabel(tasks: BackgroundTaskState[]): string {
const n = tasks.length
const allSameType = tasks.every(t => t.type === tasks[0]!.type)
if (allSameType) {
switch (tasks[0]!.type) {
case 'local_bash': {
const monitors = count(
tasks,
t => t.type === 'local_bash' && t.kind === 'monitor',
)
const shells = n - monitors
const parts: string[] = []
if (shells > 0)
parts.push(shells === 1 ? '1 shell' : `${shells} shells`)
if (monitors > 0)
parts.push(monitors === 1 ? '1 monitor' : `${monitors} monitors`)
return parts.join(', ')
}
case 'in_process_teammate': {
const teamCount = new Set(
tasks.map(t =>
t.type === 'in_process_teammate' ? t.identity.teamName : '',
),
).size
return teamCount === 1 ? '1 team' : `${teamCount} teams`
}
case 'local_agent':
return n === 1 ? '1 local agent' : `${n} local agents`
case 'remote_agent': {
const first = tasks[0]!
// Per design mockup: ◇ open diamond while running/needs-input,
// ◆ filled once ExitPlanMode is awaiting approval.
if (n === 1 && first.type === 'remote_agent' && first.isUltraplan) {
switch (first.ultraplanPhase) {
case 'plan_ready':
return `${DIAMOND_FILLED} ultraplan ready`
case 'needs_input':
return `${DIAMOND_OPEN} ultraplan needs your input`
default:
return `${DIAMOND_OPEN} ultraplan`
}
}
return n === 1
? `${DIAMOND_OPEN} 1 cloud session`
: `${DIAMOND_OPEN} ${n} cloud sessions`
}
case 'local_workflow':
return n === 1 ? '1 background workflow' : `${n} background workflows`
case 'monitor_mcp':
return n === 1 ? '1 monitor' : `${n} monitors`
case 'dream':
return 'dreaming'
}
}
return `${n} background ${n === 1 ? 'task' : 'tasks'}`
}
/**
* True when the pill should show the dimmed " · ↓ to view" call-to-action.
* Per the state diagram: only the two attention states (needs_input,
* plan_ready) surface the CTA; plain running shows just the diamond + label.
*/
export function pillNeedsCta(tasks: BackgroundTaskState[]): boolean {
if (tasks.length !== 1) return false
const t = tasks[0]!
return (
t.type === 'remote_agent' &&
t.isUltraplan === true &&
t.ultraplanPhase !== undefined
)
}
|