fix(composer): past-tense plan and thought headlines in the activity pill

- Normalize plan items in getActivityHeadline (agentSessionTranscriptPresentation.ts):
  the fallthrough previously returned the raw item title, so ACP plan
  updates headlined the pill as a bare "Plan" / "Plan updated" next to the
  past-tense tool verbs ("Read foo.ts", "Ran …") — now "Created plan" for
  the initial plan and "Updated plan" for update markers (keyed off isUpdate)
- Normalize thought items the same way: "Thinking" → "Thought", and the
  plan-titled thought that rendered "Planning" → "Planned"
- Assistant-message headlines (first line of the streamed reply) are
  deliberately untouched — they are content, not action labels
- Add test coverage for all four normalized cases

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-08-05 16:42:02 -07:00
committed by Carl
parent 3669a0d280
commit e5b77b9b15
2 changed files with 64 additions and 1 deletions
@@ -94,6 +94,57 @@ test("getActivityHeadline basenames path-like tool objects", () => {
);
});
test("getActivityHeadline renders plan and thought items in past tense", () => {
// Bare "Plan" / "Thinking" titles must not headline next to the past-tense
// tool verbs ("Read foo.ts", "Ran …").
assert.equal(
getActivityHeadline({
id: "plan:1",
type: "plan",
renderClass: "plan",
title: "Plan",
text: "1. Do the thing",
timestamp: baseTimestamp,
}),
"Created plan",
);
assert.equal(
getActivityHeadline({
id: "plan:1:update:x",
type: "plan",
renderClass: "plan",
title: "Plan updated",
text: "Now doing step 2",
timestamp: baseTimestamp,
isUpdate: true,
targetId: "plan:1",
}),
"Updated plan",
);
assert.equal(
getActivityHeadline({
id: "thought:1",
type: "thought",
renderClass: "thought",
title: "Thinking",
text: "hmm",
timestamp: baseTimestamp,
}),
"Thought",
);
assert.equal(
getActivityHeadline({
id: "thought:2",
type: "thought",
renderClass: "thought",
title: "Plan",
text: "plan-shaped thought",
timestamp: baseTimestamp,
}),
"Planned",
);
});
test("getActivityHeadline keeps shell commands whole (no basenaming)", () => {
assert.equal(
getActivityHeadline(
@@ -64,6 +64,11 @@ function terseActionObject(summary: CompactToolSummary): string | null {
* reduced to their basename), e.g. "Read foo.ts" rather than
* "Read file · src/agents/ui/foo.ts" — so the composer pill's 200px cap
* shows the informative part instead of ellipsizing a long preview.
*
* Action headlines are past-tense verb phrases throughout ("Read foo.ts",
* "Thought", "Updated plan") — plan and thought items are normalized here
* so no bare noun ("Plan") or present participle ("Thinking") leaks into
* the pill next to the past-tense tool verbs.
*/
export function getActivityHeadline(item: TranscriptItem): string | null {
if (item.type === "tool") {
@@ -93,7 +98,14 @@ export function getActivityHeadline(item: TranscriptItem): string | null {
}
if (item.type === "thought") {
return item.title === "Plan" ? "Planning" : item.title;
if (item.title === "Plan") {
return "Planned";
}
return item.title === "Thinking" ? "Thought" : item.title;
}
if (item.type === "plan") {
return item.isUpdate ? "Updated plan" : "Created plan";
}
if (item.type === "metadata") {