Add shared helper to abbreviate orchestration task specs for brief listi

- Normalizes whitespace and caps spec length at 160 chars, flagging
  truncation separately from whitespace-only changes
- Truncates on UTF-16 code point boundaries to avoid splitting
  surrogate pairs and emitting malformed strings
This commit is contained in:
Jinjing
2026-07-11 19:27:35 -07:00
parent dddc6cc452
commit e7189e2d52
2 changed files with 63 additions and 0 deletions
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest'
import { abbreviateOrchestrationTasks } from './orchestration-task-summary'
describe('abbreviateOrchestrationTasks', () => {
it('collapses whitespace and caps long task specs', () => {
const [task] = abbreviateOrchestrationTasks([
{ id: 'task_1', spec: `First line\n\n${'detail '.repeat(40)}` }
])
expect(task.id).toBe('task_1')
expect(task.spec).not.toContain('\n')
expect(task.spec).toHaveLength(160)
expect(task.spec.endsWith('…')).toBe(true)
expect(task.spec_truncated).toBe(true)
})
it('preserves a short one-line spec', () => {
const [task] = abbreviateOrchestrationTasks([{ spec: 'Short task' }])
expect(task).toEqual({ spec: 'Short task', spec_truncated: false })
})
it('does not report whitespace normalization as truncation', () => {
const [task] = abbreviateOrchestrationTasks([{ spec: 'Short\n\n task' }])
expect(task).toEqual({ spec: 'Short task', spec_truncated: false })
})
it('does not split a surrogate pair at the truncation boundary', () => {
// 158 chars + an astral emoji spanning UTF-16 units 158-159: a naive
// slice(0, 159) would cut the pair and leave a lone high surrogate.
const [task] = abbreviateOrchestrationTasks([{ spec: `${'a'.repeat(158)}😀${'b'.repeat(40)}` }])
expect(task.spec_truncated).toBe(true)
expect(task.spec.isWellFormed()).toBe(true)
expect(task.spec.endsWith('…')).toBe(true)
})
})
+25
View File
@@ -0,0 +1,25 @@
const TASK_SPEC_BRIEF_LENGTH = 160
export function abbreviateOrchestrationTasks<T extends { spec: string }>(
tasks: readonly T[]
): (T & { spec_truncated: boolean })[] {
return tasks.map((task) => {
const spec = task.spec.replace(/\s+/g, ' ').trim()
const truncated = spec.length > TASK_SPEC_BRIEF_LENGTH
return {
...task,
spec: truncated ? `${truncateAtCodePoint(spec).trimEnd()}…` : spec,
// Why: whitespace normalization alone is not truncation; flagging it
// would make agents re-fetch full specs that --brief already shows.
spec_truncated: truncated
}
})
}
function truncateAtCodePoint(spec: string): string {
const sliced = spec.slice(0, TASK_SPEC_BRIEF_LENGTH - 1)
// Why: a cut through a surrogate pair leaves a lone high surrogate that
// strict JSON consumers reject; drop it rather than emit malformed UTF-16.
const lastUnit = sliced.charCodeAt(sliced.length - 1)
return lastUnit >= 0xd800 && lastUnit <= 0xdbff ? sliced.slice(0, -1) : sliced
}