mirror of
https://github.com/block/buzz.git
synced 2026-11-01 23:11:58 +00:00
Integrate joelbrilliant’s channel activity popover work on top of the composer live-activity branch, preserving per-agent elapsed ages, channel-scoped observer headlines, and activity-panel click-through. Co-authored-by: joelbrilliant <joelbrilliant1@gmail.com> Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
467 lines
12 KiB
JavaScript
467 lines
12 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
getActivityHeadline,
|
|
getLatestActivityHeadline,
|
|
isMeaningfulItem,
|
|
isSpineItem,
|
|
shouldShowTranscriptRowTimestamp,
|
|
} from "./agentSessionTranscriptPresentation.ts";
|
|
|
|
const baseTimestamp = "2026-06-14T19:00:00.000Z";
|
|
|
|
function makeTool(overrides = {}) {
|
|
return {
|
|
id: "tool:1",
|
|
type: "tool",
|
|
title: "Send Message",
|
|
toolName: "send_message",
|
|
buzzToolName: "send_message",
|
|
status: "executing",
|
|
args: { channel_id: "abc" },
|
|
result: "",
|
|
isError: false,
|
|
timestamp: baseTimestamp,
|
|
startedAt: baseTimestamp,
|
|
completedAt: null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function makeMessage(overrides = {}) {
|
|
return {
|
|
id: "msg:1",
|
|
type: "message",
|
|
role: "assistant",
|
|
title: "Assistant",
|
|
text: "Looking into that now.",
|
|
timestamp: baseTimestamp,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test("getActivityHeadline formats tool actions tersely and assistant text", () => {
|
|
// Tool items use the terse action tier (verb + object), not label · preview.
|
|
assert.equal(getActivityHeadline(makeTool()), "Sent abc");
|
|
assert.equal(
|
|
getActivityHeadline(makeMessage({ text: "First line\nSecond line" })),
|
|
"First line",
|
|
);
|
|
assert.equal(getActivityHeadline(makeMessage({ text: " " })), "Responding");
|
|
});
|
|
|
|
test("getActivityHeadline basenames path-like tool objects", () => {
|
|
assert.equal(
|
|
getActivityHeadline(
|
|
makeTool({
|
|
title: "Read file",
|
|
toolName: "dev__read_file",
|
|
buzzToolName: null,
|
|
args: { path: "src/agents/ui/foo.ts" },
|
|
descriptor: {
|
|
renderClass: "file-read",
|
|
label: "Read file",
|
|
preview: "src/agents/ui/foo.ts",
|
|
action: { verb: "Read", object: "src/agents/ui/foo.ts" },
|
|
source: "harness",
|
|
groupKey: "read_file",
|
|
},
|
|
}),
|
|
),
|
|
"Read foo.ts",
|
|
);
|
|
assert.equal(
|
|
getActivityHeadline(
|
|
makeTool({
|
|
title: "Edit file",
|
|
toolName: "dev__str_replace",
|
|
buzzToolName: null,
|
|
args: { path: "desktop/src/features/channels/ui/ChannelPane.tsx" },
|
|
descriptor: {
|
|
renderClass: "file-edit",
|
|
label: "Edited file",
|
|
preview: "desktop/src/features/channels/ui/ChannelPane.tsx",
|
|
action: {
|
|
verb: "Edited",
|
|
object: "desktop/src/features/channels/ui/ChannelPane.tsx",
|
|
},
|
|
source: "harness",
|
|
groupKey: "file-edit:str_replace",
|
|
},
|
|
}),
|
|
),
|
|
"Edited ChannelPane.tsx",
|
|
);
|
|
});
|
|
|
|
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(
|
|
makeTool({
|
|
title: "Shell",
|
|
toolName: "dev__shell",
|
|
buzzToolName: null,
|
|
args: { command: "cat src/agents/ui/foo.ts" },
|
|
descriptor: {
|
|
renderClass: "shell",
|
|
label: "Ran command",
|
|
preview: "cat src/agents/ui/foo.ts",
|
|
action: { verb: "Ran", object: "cat src/agents/ui/foo.ts" },
|
|
source: "harness",
|
|
groupKey: "shell:command",
|
|
},
|
|
}),
|
|
),
|
|
"Ran cat src/agents/ui/foo.ts",
|
|
);
|
|
});
|
|
|
|
test("isMeaningfulItem ignores lifecycle noise and raw JSON-RPC metadata", () => {
|
|
assert.equal(
|
|
isMeaningfulItem({
|
|
id: "life:1",
|
|
type: "lifecycle",
|
|
title: "Turn started",
|
|
text: "",
|
|
timestamp: baseTimestamp,
|
|
}),
|
|
false,
|
|
"turn started is lifecycle noise → not meaningful",
|
|
);
|
|
assert.equal(
|
|
isMeaningfulItem({
|
|
id: "meta:raw",
|
|
type: "metadata",
|
|
renderClass: "raw-rail",
|
|
title: "Raw ACP payload",
|
|
sections: [],
|
|
timestamp: baseTimestamp,
|
|
acpSource: "raw_json_rpc",
|
|
}),
|
|
false,
|
|
"raw_json_rpc metadata is infrastructure noise → not meaningful",
|
|
);
|
|
assert.equal(
|
|
isMeaningfulItem({
|
|
id: "meta:ctx",
|
|
type: "metadata",
|
|
renderClass: "raw-rail",
|
|
title: "Prompt context",
|
|
sections: [],
|
|
timestamp: baseTimestamp,
|
|
acpSource: "session/prompt:context",
|
|
}),
|
|
true,
|
|
"prompt context metadata is semantic → meaningful",
|
|
);
|
|
assert.equal(
|
|
isMeaningfulItem({
|
|
id: "meta:sys",
|
|
type: "metadata",
|
|
renderClass: "raw-rail",
|
|
title: "System prompt",
|
|
sections: [],
|
|
timestamp: baseTimestamp,
|
|
}),
|
|
true,
|
|
"system prompt metadata (no acpSource) is semantic → meaningful",
|
|
);
|
|
assert.equal(
|
|
isMeaningfulItem({
|
|
id: "life:2",
|
|
type: "lifecycle",
|
|
title: "Turn error",
|
|
text: "boom",
|
|
timestamp: baseTimestamp,
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("getActivityHeadline falls back to label · preview when a descriptor has no action", () => {
|
|
assert.equal(
|
|
getActivityHeadline(
|
|
makeTool({
|
|
title: "Shell",
|
|
toolName: "dev__shell",
|
|
buzzToolName: null,
|
|
args: { command: "buzz messages send --content hi" },
|
|
descriptor: {
|
|
renderClass: "message",
|
|
label: "Send Message",
|
|
preview: "hi",
|
|
source: "shell",
|
|
groupKey: "buzz-cli:messages.send",
|
|
},
|
|
}),
|
|
),
|
|
"Send Message · hi",
|
|
);
|
|
});
|
|
|
|
test("isMeaningfulItem ignores suppressed tools", () => {
|
|
assert.equal(
|
|
isMeaningfulItem(
|
|
makeTool({
|
|
renderClass: "suppressed",
|
|
descriptor: {
|
|
renderClass: "suppressed",
|
|
label: "Checked todos",
|
|
preview: null,
|
|
source: "harness",
|
|
groupKey: "suppressed:stop-hook",
|
|
},
|
|
}),
|
|
),
|
|
false,
|
|
);
|
|
});
|
|
|
|
const metadataSystemPrompt = {
|
|
id: "meta:sys",
|
|
type: "metadata",
|
|
renderClass: "raw-rail",
|
|
title: "System prompt",
|
|
sections: [],
|
|
timestamp: baseTimestamp,
|
|
};
|
|
|
|
const metadataPromptContext = {
|
|
id: "meta:ctx",
|
|
type: "metadata",
|
|
renderClass: "raw-rail",
|
|
title: "Prompt context",
|
|
sections: [],
|
|
timestamp: baseTimestamp,
|
|
acpSource: "session/prompt:context",
|
|
};
|
|
|
|
test("isSpineItem excludes metadata items (reads recede)", () => {
|
|
assert.equal(
|
|
isSpineItem(metadataSystemPrompt),
|
|
false,
|
|
"system prompt metadata is not spine work",
|
|
);
|
|
assert.equal(
|
|
isSpineItem(metadataPromptContext),
|
|
false,
|
|
"prompt context metadata is not spine work",
|
|
);
|
|
assert.equal(isSpineItem(makeTool()), true, "tool items are spine work");
|
|
assert.equal(
|
|
isSpineItem(makeMessage()),
|
|
true,
|
|
"message items are spine work",
|
|
);
|
|
assert.equal(
|
|
isSpineItem({
|
|
id: "meta:raw",
|
|
type: "metadata",
|
|
renderClass: "raw-rail",
|
|
title: "Raw ACP payload",
|
|
sections: [],
|
|
timestamp: baseTimestamp,
|
|
acpSource: "raw_json_rpc",
|
|
}),
|
|
false,
|
|
"raw_json_rpc is already filtered by isMeaningfulItem → not spine",
|
|
);
|
|
});
|
|
|
|
test("isSpineItem: metadata still meaningful via isMeaningfulItem (feed visibility unchanged)", () => {
|
|
// isMeaningfulItem must still return true for non-raw metadata — the feed
|
|
// renders metadata items independently of isSpineItem.
|
|
assert.equal(isMeaningfulItem(metadataSystemPrompt), true);
|
|
assert.equal(isMeaningfulItem(metadataPromptContext), true);
|
|
});
|
|
|
|
test("two-tier headline: metadata excluded when spine work is present", () => {
|
|
// Simulate what BotActivityBar does: when any spine item exists, only spine
|
|
// items are eligible for the headline rotation.
|
|
const transcript = [metadataSystemPrompt, makeTool()];
|
|
const hasSpine = transcript.some(isSpineItem);
|
|
const passFilter = hasSpine ? isSpineItem : isMeaningfulItem;
|
|
|
|
const headlines = transcript
|
|
.filter(passFilter)
|
|
.map((item) => getActivityHeadline(item))
|
|
.filter(Boolean);
|
|
|
|
assert.ok(
|
|
!headlines.includes("System prompt"),
|
|
"System prompt should not headline when spine work exists",
|
|
);
|
|
assert.ok(
|
|
headlines.some((h) => h?.includes("Sent")),
|
|
"Tool headline should appear",
|
|
);
|
|
});
|
|
|
|
test("two-tier headline: metadata headlines when it is the only activity (session start / idle)", () => {
|
|
// When no spine items exist, fall back to isMeaningfulItem so the bar is not
|
|
// empty at session start.
|
|
const transcript = [metadataSystemPrompt, metadataPromptContext];
|
|
const hasSpine = transcript.some(isSpineItem);
|
|
const passFilter = hasSpine ? isSpineItem : isMeaningfulItem;
|
|
|
|
const headlines = transcript
|
|
.filter(passFilter)
|
|
.map((item) => getActivityHeadline(item))
|
|
.filter(Boolean);
|
|
|
|
assert.ok(
|
|
headlines.includes("System prompt"),
|
|
"System prompt should headline when no spine work exists",
|
|
);
|
|
assert.ok(
|
|
headlines.includes("Prompt context"),
|
|
"Prompt context should headline when no spine work exists",
|
|
);
|
|
});
|
|
|
|
// Render-tier tests (raw_json_rpc → <pre>, non-raw → polished accordion) live in
|
|
// activityRenderClasses/RawRailActivity.render.test.mjs — they use
|
|
// renderToStaticMarkup and would fail if the isRawPayload branch in
|
|
// RawRailActivity were removed.
|
|
|
|
test("shouldShowTranscriptRowTimestamp: off by default (enabled=false)", () => {
|
|
assert.equal(
|
|
shouldShowTranscriptRowTimestamp(makeTool(), {
|
|
enabled: false,
|
|
variant: "default",
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("shouldShowTranscriptRowTimestamp: enabled shows for tools, thoughts, assistant messages", () => {
|
|
const options = { enabled: true, variant: "default" };
|
|
assert.equal(shouldShowTranscriptRowTimestamp(makeTool(), options), true);
|
|
assert.equal(
|
|
shouldShowTranscriptRowTimestamp(
|
|
makeMessage({ role: "assistant" }),
|
|
options,
|
|
),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
shouldShowTranscriptRowTimestamp(
|
|
{
|
|
id: "thought:1",
|
|
type: "thought",
|
|
title: "Thinking",
|
|
text: "hmm",
|
|
timestamp: baseTimestamp,
|
|
},
|
|
options,
|
|
),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("shouldShowTranscriptRowTimestamp: user message bubbles keep their own footer (excluded)", () => {
|
|
assert.equal(
|
|
shouldShowTranscriptRowTimestamp(
|
|
makeMessage({ role: "user", title: "tho" }),
|
|
{ enabled: true, variant: "default" },
|
|
),
|
|
false,
|
|
"user bubbles already render a timestamp footer",
|
|
);
|
|
});
|
|
|
|
test("shouldShowTranscriptRowTimestamp: compact preview stays dense", () => {
|
|
assert.equal(
|
|
shouldShowTranscriptRowTimestamp(makeTool(), {
|
|
enabled: true,
|
|
variant: "compactPreview",
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("getLatestActivityHeadline prefers latest spine work and scopes by channel", () => {
|
|
const items = [
|
|
makeMessage({
|
|
id: "msg:old",
|
|
text: "Old reply",
|
|
channelId: "chan-a",
|
|
timestamp: "2026-06-14T19:00:00.000Z",
|
|
}),
|
|
makeTool({
|
|
id: "tool:1",
|
|
channelId: "chan-a",
|
|
timestamp: "2026-06-14T19:00:10.000Z",
|
|
startedAt: "2026-06-14T19:00:10.000Z",
|
|
}),
|
|
makeMessage({
|
|
id: "msg:other",
|
|
text: "Other channel",
|
|
channelId: "chan-b",
|
|
timestamp: "2026-06-14T19:00:20.000Z",
|
|
}),
|
|
];
|
|
assert.equal(
|
|
getLatestActivityHeadline(items, "chan-a"),
|
|
"Send Message · abc",
|
|
);
|
|
assert.equal(getLatestActivityHeadline(items, "chan-b"), "Other channel");
|
|
assert.equal(getLatestActivityHeadline(items, "chan-missing"), null);
|
|
});
|
|
|
|
test("getLatestActivityHeadline returns null for empty transcripts", () => {
|
|
assert.equal(getLatestActivityHeadline([]), null);
|
|
});
|