mirror of
https://github.com/block/buzz.git
synced 2026-11-08 02:11:36 +00:00
**Category:** feature **User Impact:** In the wide/standalone agent session panel the observer transcript now reads like a conversation — the human's prompt as a right-aligned bubble, the agent's reply as unboxed prose at a comfortable measure, reasoning behind a "Thinking…/Thought for Ns" disclosure, the plan as a live checklist, and session/status noise receded to quiet dividers. **Problem:** The transcript had two presentations — the dense `default` activity feed and `compactPreview` — both tuned for a narrow panel. Read at full width they are a scan surface, not something you read a turn in. **Solution:** A third, purely additive `conversation` variant, threaded through `ManagedAgentSessionPanel` and `AgentSessionThreadPanel` as an optional prop with a width-based default. Tool items deliberately route through the same `ToolActivity` presenter as `default` so the parallel tool-chain-card work merges cleanly, and the drawer/shell is untouched. The byte-for-byte guarantee for the existing variants is enforced by a captured-markup fixture, not by inspection: the baseline was produced by mounting the same transcript on unmodified code, and the test fails on any drift in `default` or `compactPreview` output. Co-authored-by: Bradley Axen <baxen@squareup.com> Signed-off-by: Bradley Axen <baxen@squareup.com>
55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
/**
|
|
* Parse the markdown checklist a `plan` transcript item carries into structured
|
|
* entries, so the `conversation` variant can render it as a checklist card
|
|
* instead of a block of markdown.
|
|
*
|
|
* The text is produced by `formatPlanEntry` in agentSessionTranscriptHelpers:
|
|
* `- [x] content` for completed entries, `- [ ] content` otherwise, with an
|
|
* ` (in progress)` suffix when the ACP entry status is `in_progress`. Adapters
|
|
* that send free-form `content` text instead of `entries[]` yield text with no
|
|
* checklist lines at all — those return no entries and the card falls back to
|
|
* rendering the raw markdown.
|
|
*/
|
|
export type PlanChecklistEntry = {
|
|
label: string;
|
|
status: "completed" | "in_progress" | "pending";
|
|
};
|
|
|
|
export type PlanChecklist = {
|
|
completedCount: number;
|
|
entries: PlanChecklistEntry[];
|
|
};
|
|
|
|
const CHECKLIST_LINE = /^\s*[-*]\s+\[([ xX])\]\s*(.*)$/;
|
|
const IN_PROGRESS_SUFFIX = /\s*\(in progress\)\s*$/i;
|
|
|
|
export function parsePlanChecklist(text: string): PlanChecklist {
|
|
const entries: PlanChecklistEntry[] = [];
|
|
|
|
for (const line of text.split(/\r?\n/)) {
|
|
const match = line.match(CHECKLIST_LINE);
|
|
if (!match) continue;
|
|
const completed = match[1].toLowerCase() === "x";
|
|
const rawLabel = match[2].trim();
|
|
const inProgress = !completed && IN_PROGRESS_SUFFIX.test(rawLabel);
|
|
entries.push({
|
|
label: rawLabel.replace(IN_PROGRESS_SUFFIX, "").trim(),
|
|
status: completed ? "completed" : inProgress ? "in_progress" : "pending",
|
|
});
|
|
}
|
|
|
|
return {
|
|
completedCount: entries.filter((entry) => entry.status === "completed")
|
|
.length,
|
|
entries,
|
|
};
|
|
}
|
|
|
|
/** "2/5 complete" progress caption, or null when there is nothing to count. */
|
|
export function formatPlanChecklistProgress(
|
|
checklist: PlanChecklist,
|
|
): string | null {
|
|
if (checklist.entries.length === 0) return null;
|
|
return `${checklist.completedCount}/${checklist.entries.length} complete`;
|
|
}
|