fix: normalize wrapper-prefixed OMP/Pi terminal titles (#8033)
Multiplexer/session wrappers prefix pane titles as "prefix | pane-title", pushing the Pi/OMP identity after " | ". Inspect each " | " suffix and prefer a re-ownable compatible identity over the wrapper text, so wrapped OMP/Pi titles normalize to the owner instead of flickering. Includes guard tests for braille-inner labels and no-identity wrappers.
This commit is contained in:
parent
7b6eb3cc06
commit
ebeb947dee
|
|
@ -118,13 +118,26 @@ describe('Pi-compatible title detection', () => {
|
|||
['π: tmp', 'omp', 'OMP ready'],
|
||||
['\u280b π: tmp', 'omp', '\u280b OMP'],
|
||||
['\u280b π - tmp', 'omp', '\u280b OMP'],
|
||||
['\u280b OMP', 'pi', '\u280b Pi']
|
||||
['\u280b OMP', 'pi', '\u280b Pi'],
|
||||
['lucky-echidna | \u283c π - Diagnose Orca terminal title flicker - test', 'omp', '\u280b OMP'],
|
||||
['lucky-echidna | Pi ready', 'omp', 'OMP ready'],
|
||||
['Codex | Pi ready', 'omp', 'OMP ready'],
|
||||
// Why: the wrapped whole reads as a braille Claude title, but the re-ownable
|
||||
// synthetic pane suffix must still win.
|
||||
['lucky-echidna | ⠋ OMP', 'omp', '⠋ OMP'],
|
||||
['lucky-echidna | \u283c π - Diagnose | test', 'omp', '\u280b OMP']
|
||||
] as const)('normalizes %s to the authoritative %s owner', (title, owner, expectedTitle) => {
|
||||
expect(normalizeCompatibleAgentTitleForOwner(title, owner)).toBe(expectedTitle)
|
||||
})
|
||||
|
||||
it('preserves Pi-compatible custom titles and unrelated owners', () => {
|
||||
expect(normalizeCompatibleAgentTitleForOwner('Fix pi bugs', 'omp')).toBe('Fix pi bugs')
|
||||
// Why: a wrapped title with no re-ownable identity in any " | " segment
|
||||
// passes through untouched instead of collapsing to the owner label.
|
||||
expect(normalizeCompatibleAgentTitleForOwner('lucky-echidna | Fix pi bugs', 'omp')).toBe(
|
||||
'lucky-echidna | Fix pi bugs'
|
||||
)
|
||||
expect(normalizeCompatibleAgentTitleForOwner('xxPi ready', 'omp')).toBe('xxPi ready')
|
||||
expect(normalizeCompatibleAgentTitleForOwner('\u280b Pi', 'codex')).toBe('\u280b Pi')
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,17 @@ import { isLegacyPiCompatibleTitle } from './pi-compatible-synthetic-title'
|
|||
|
||||
type TitleProfileMatch = {
|
||||
profile: SyntheticAgentTitleProfile
|
||||
sourceTitle: string
|
||||
}
|
||||
|
||||
type TitleLabelProfileMatch = Pick<TitleProfileMatch, 'profile'>
|
||||
|
||||
const COMPATIBLE_IDLE_TITLE_RE = /(?<![\w./\\-])(?:ready|idle|done)(?![\w-])/i
|
||||
|
||||
/**
|
||||
* Resolves the synthetic title profile matching a given agent label.
|
||||
*/
|
||||
function getProfileForTitleLabel(label: string | null): TitleProfileMatch | null {
|
||||
function getProfileForTitleLabel(label: string | null): TitleLabelProfileMatch | null {
|
||||
if (!label) {
|
||||
return null
|
||||
}
|
||||
|
|
@ -33,14 +36,35 @@ function getProfileForTitleLabel(label: string | null): TitleProfileMatch | null
|
|||
* Resolves the synthetic title profile matching a given terminal title.
|
||||
*/
|
||||
function getProfileForTitle(title: string): TitleProfileMatch | null {
|
||||
const labelProfile = getProfileForTitleLabel(getAgentLabel(title))
|
||||
if (labelProfile) {
|
||||
return labelProfile
|
||||
// Multiplexers/session wrappers prefix dynamic titles with ` | `, so inspect
|
||||
// each suffix to preserve the inner compatible agent identity.
|
||||
const candidates = [title]
|
||||
let wrapperSeparatorIndex = title.indexOf(' | ')
|
||||
while (wrapperSeparatorIndex >= 0) {
|
||||
const wrappedPaneTitle = title.slice(wrapperSeparatorIndex + 3).trim()
|
||||
if (wrappedPaneTitle && !candidates.includes(wrappedPaneTitle)) {
|
||||
candidates.push(wrappedPaneTitle)
|
||||
}
|
||||
wrapperSeparatorIndex = title.indexOf(' | ', wrapperSeparatorIndex + 3)
|
||||
}
|
||||
if (isLegacyPiCompatibleTitle(title)) {
|
||||
return getProfileForTitleLabel('Pi')
|
||||
|
||||
let fallback: TitleProfileMatch | null = null
|
||||
for (const candidate of candidates) {
|
||||
const labelProfile = getProfileForTitleLabel(getAgentLabel(candidate))
|
||||
const legacyProfile = isLegacyPiCompatibleTitle(candidate)
|
||||
? getProfileForTitleLabel('Pi')
|
||||
: null
|
||||
const candidateProfile = labelProfile ?? legacyProfile
|
||||
if (!candidateProfile) {
|
||||
continue
|
||||
}
|
||||
const match = { ...candidateProfile, sourceTitle: candidate }
|
||||
if (candidateProfile.profile.titleIdentityGroup) {
|
||||
return match
|
||||
}
|
||||
fallback ??= match
|
||||
}
|
||||
return null
|
||||
return fallback
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -132,7 +156,7 @@ export function normalizeCompatibleAgentTitleForOwner(
|
|||
) {
|
||||
return title
|
||||
}
|
||||
const sourceStatus = getSourceTitleStatus(title)
|
||||
const sourceStatus = getSourceTitleStatus(source.sourceTitle)
|
||||
if (sourceStatus === 'working') {
|
||||
return `\u280b ${ownerProfile.workingLabel}`
|
||||
}
|
||||
|
|
@ -142,10 +166,10 @@ export function normalizeCompatibleAgentTitleForOwner(
|
|||
if (sourceStatus === 'idle') {
|
||||
return ownerProfile.idleLabel
|
||||
}
|
||||
if (hasPermissionSuffix(title, source.profile)) {
|
||||
if (hasPermissionSuffix(source.sourceTitle, source.profile)) {
|
||||
return ownerProfile.permissionLabel
|
||||
}
|
||||
if (hasIdleSuffix(title, source.profile)) {
|
||||
if (hasIdleSuffix(source.sourceTitle, source.profile)) {
|
||||
return ownerProfile.idleLabel
|
||||
}
|
||||
return ownerProfile.workingLabel
|
||||
|
|
|
|||
Loading…
Reference in New Issue