From ea3d4ee82abd7cf839363f1c457f50deda49692c Mon Sep 17 00:00:00 2001 From: Merge Sim Date: Wed, 9 Sep 2026 00:09:59 -0700 Subject: [PATCH] fix(codex): reserve the label's share of a qualified command row A child's label is raw provider text and was spliced into the command row unbounded, then the pair clipped to the description cap. A label at or past that cap clipped the command away entirely, leaving a row of kind 'command' that named an agent and showed no command - the failure qualification exists to remove, inverted. The same clip could also cut a surrogate pair, which boundSubagentField already guards against on the agent row two lines away. Give the label a reserved share and clip it the way the agent row does. --- .../codex/codex-background-command-tracker.ts | 26 ++++++++++++++-- .../codex-background-task-tracker.test.ts | 31 +++++++++++++++++-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/main/codex/codex-background-command-tracker.ts b/src/main/codex/codex-background-command-tracker.ts index becbb18a67c..11d0942c7a7 100644 --- a/src/main/codex/codex-background-command-tracker.ts +++ b/src/main/codex/codex-background-command-tracker.ts @@ -10,9 +10,31 @@ const MAX_DESCRIPTION_CHARS = 512 type Command = { threadId: string; task: AgentSessionBackgroundTask; bytes: number } -/** Stays within the retained bound, so read-time qualification cannot outgrow admission. */ +/** The label's reserved share of the description. Reserved, not merely capped: + * a label free to spend the whole budget clips away the command it qualifies, + * leaving a command row naming an agent and no command — the failure this + * qualification exists to remove, in the other direction. `bytes` is counted + * before qualification, so this share is also what a published row may exceed + * the admitted count by. */ +const MAX_LABEL_CHARS = 96 + +/** Clipped the way `boundSubagentField` clips the same provider string on the + * agent row: never mid surrogate pair, since a lone surrogate is lossy through + * any non-JSON UTF-8 hop. No ordinal, because the row's identity is its `id`. */ +function boundLabel(label: string): string { + if (label.length <= MAX_LABEL_CHARS) { + return label + } + const keep = MAX_LABEL_CHARS - 1 + const last = label.charCodeAt(keep - 1) + const end = last >= 0xd800 && last <= 0xdbff ? keep - 1 : keep + return `${label.slice(0, end)}…` +} + +/** Resolved on read, and capped at the bound the admitted description already respects. */ function qualifiedDescription(label: string, description: string | undefined): string { - return (description ? `${label} — ${description}` : label).slice(0, MAX_DESCRIPTION_CHARS) + const name = boundLabel(label) + return (description ? `${name} — ${description}` : name).slice(0, MAX_DESCRIPTION_CHARS) } export class CodexBackgroundCommandTracker { diff --git a/src/main/codex/codex-background-task-tracker.test.ts b/src/main/codex/codex-background-task-tracker.test.ts index 47987fe3fc0..9e7a5b5f7ec 100644 --- a/src/main/codex/codex-background-task-tracker.test.ts +++ b/src/main/codex/codex-background-task-tracker.test.ts @@ -22,7 +22,8 @@ function turn( function activity( kind = 'started', parentTurn = PARENT_TURN, - child = CHILD + child = CHILD, + name = 'count_a' ): CodexBackgroundTaskEvent { return { method: 'item/started', @@ -35,7 +36,7 @@ function activity( id: `activity-${kind}`, kind, agentThreadId: child, - agentPath: '/root/count_a' + agentPath: `/root/${name}` } } } @@ -261,6 +262,32 @@ describe('CodexBackgroundTaskTracker command integration', () => { }) }) + it('keeps the command visible under a label that would otherwise fill the row', () => { + const tracker = new CodexBackgroundTaskTracker(PRIMARY) + tracker.observe(turn('turn/started', PRIMARY, PARENT_TURN)) + tracker.observe(turn('turn/started', CHILD, CHILD_TURN)) + tracker.observe(activity('started', PARENT_TURN, CHILD, 'L'.repeat(600))) + tracker.observe(command(CHILD)) + tracker.observe(turn('turn/completed', PRIMARY, PARENT_TURN)) + tracker.observe(turn('turn/completed', CHILD, CHILD_TURN)) + const description = tracker.state?.tasks?.[0]?.description + expect(description).toContain('sleep 90') + expect(description).toBe(`${'L'.repeat(95)}… — sleep 90`) + }) + + it('never cuts a label mid surrogate pair', () => { + const tracker = new CodexBackgroundTaskTracker(PRIMARY) + tracker.observe(turn('turn/started', PRIMARY, PARENT_TURN)) + tracker.observe(turn('turn/started', CHILD, CHILD_TURN)) + tracker.observe(activity('started', PARENT_TURN, CHILD, `${'L'.repeat(94)}\u{1F600}bad`)) + tracker.observe(command(CHILD)) + tracker.observe(turn('turn/completed', PRIMARY, PARENT_TURN)) + tracker.observe(turn('turn/completed', CHILD, CHILD_TURN)) + const description = tracker.state?.tasks?.[0]?.description ?? '' + expect(description.isWellFormed()).toBe(true) + expect(description).toBe(`${'L'.repeat(94)}… — sleep 90`) + }) + it('names a child shell whose label only arrives after the command', () => { const tracker = new CodexBackgroundTaskTracker(PRIMARY) tracker.observe(turn('turn/started', PRIMARY, PARENT_TURN))