Merge remote-tracking branch 'origin/main' into bb/pets-merge

# Conflicts:
#	hermes_cli/commands.py
#	tui_gateway/server.py
This commit is contained in:
Brooklyn Nicholson 2026-06-23 19:05:22 -05:00
commit e495b33bf1
251 changed files with 23395 additions and 2720 deletions

View file

@ -105,6 +105,46 @@ const baseProps = {
voiceLabel: ''
}
describe('StatusRule background-subagent indicator', () => {
it('renders ⛓ N on a wide terminal when subagents are running', () => {
const element = StatusRule({
...baseProps,
usage: { ...baseProps.usage, active_subagents: 3 }
})
expect(textContent(element)).toContain('⛓ 3')
})
it('omits the segment when no subagents are running', () => {
const element = StatusRule({
...baseProps,
usage: { ...baseProps.usage, active_subagents: 0 }
})
expect(textContent(element)).not.toContain('⛓')
})
it('omits the segment when the field is absent', () => {
const element = StatusRule({ ...baseProps })
expect(textContent(element)).not.toContain('⛓')
})
it('drops the subagent segment before the bg segment on a narrow terminal', () => {
// cols=44 is below the subagents breakpoint (92) but the bg breakpoint
// (88) too — both gone. Assert the lower-priority subagent indicator is
// not shown when space is tight even with a live count.
const element = StatusRule({
...baseProps,
cols: 44,
bgCount: 1,
usage: { ...baseProps.usage, active_subagents: 2 }
})
expect(textContent(element)).not.toContain('⛓')
})
})
describe('StatusRule session count click target', () => {
it('makes the live session count itself clickable', () => {
const openSwitcher = vi.fn()

View file

@ -68,6 +68,7 @@ describe('statusBarSegments', () => {
compressions: true,
voice: true,
bg: true,
subagents: true,
cost: true
})
})
@ -89,6 +90,7 @@ describe('statusBarSegments', () => {
'compressions',
'voice',
'bg',
'subagents',
'cost'
]

View file

@ -250,6 +250,7 @@ export interface StatusBarSegments {
compressions: boolean
cost: boolean
duration: boolean
subagents: boolean
voice: boolean
}
@ -263,6 +264,7 @@ export function statusBarSegments(cols: number): StatusBarSegments {
compressions: w >= 80,
voice: w >= 84,
bg: w >= 88,
subagents: w >= 92,
cost: w >= 96
}
}
@ -512,6 +514,8 @@ export function StatusRule({
const showVoice = segs.voice && !!voiceLabel && fits(SEP + stringWidth(voiceLabel))
const showSessionCount = !!sessionCountText && fits(SEP + stringWidth(sessionCountText))
const showBg = segs.bg && bgCount > 0 && fits(SEP + stringWidth(`${bgCount} bg`))
const subagentCount = typeof usage.active_subagents === 'number' ? usage.active_subagents : 0
const showSubagents = segs.subagents && subagentCount > 0 && fits(SEP + stringWidth(`${subagentCount}`))
const showCostSeg = segs.cost && showCost && !!costText && fits(SEP + stringWidth(costText))
// No segs flag / no showCost coupling — it's a server-gated dev readout, lowest priority,
// so it consumes tail budget LAST and drops first on a narrow terminal.
@ -619,6 +623,12 @@ export function StatusRule({
{bgCount} bg
</Text>
) : null}
{showSubagents ? (
<Text color={t.color.muted} wrap="truncate-end">
{' │ '}
{subagentCount}
</Text>
) : null}
{showCostSeg ? (
<Text color={t.color.muted} wrap="truncate-end">
{' │ '}

View file

@ -310,6 +310,7 @@ export interface SessionUndoResponse {
}
export interface SessionUsageResponse {
active_subagents?: number
cache_read?: number
cache_write?: number
calls?: number

View file

@ -167,6 +167,7 @@ export interface SessionInfo {
}
export interface Usage {
active_subagents?: number
calls: number
compressions?: number
context_max?: number