feat(session-ui): show subagents ungrouped by default

This commit is contained in:
Dax Raad
2026-10-18 16:12:21 -04:00
Unverified
parent 15d4afd089
commit bd36dcc172
3 changed files with 89 additions and 2 deletions
+2 -2
View File
@@ -50,7 +50,7 @@ export const timelinePresets = [
shell: { placement: "grouped", details: "collapsed" },
edit: { placement: "grouped", details: "collapsed" },
thinking: { placement: "grouped", details: "collapsed" },
subagents: { placement: "grouped" },
subagents: { placement: "separate" },
notices: { placement: "grouped" },
tools: { placement: "grouped" },
commentary: { placement: "separate" },
@@ -62,7 +62,7 @@ export const timelinePresets = [
shell: { placement: "hidden", details: "collapsed" },
edit: { placement: "grouped", details: "collapsed" },
thinking: { placement: "hidden", details: "collapsed" },
subagents: { placement: "grouped" },
subagents: { placement: "separate" },
notices: { placement: "hidden" },
tools: { placement: "hidden" },
commentary: { placement: "separate" },
@@ -14,6 +14,7 @@ import {
webResearchDocument,
} from "../storybook/current-session-fixtures"
import { storyDocument, storyPatchFile, storyTool } from "../storybook/current-session-scenarios"
import { timelinePresets } from "./detail"
import { SessionTimeline } from "./session-timeline"
export default {
@@ -75,6 +76,51 @@ export const DelegateFocusedTasks = {
),
}
const delegationDocument = storyDocument([
storyTool("tool_delegation_glob", "glob", "completed", { pattern: "src/timeline/**/*.ts" }),
storyTool("tool_delegation_grep", "grep", "completed", { pattern: "timelineDetail" }),
storyTool("tool_delegation_read", "read", "completed", { path: "src/timeline/projection.ts" }),
storyTool(
"tool_delegation_explore",
"subagent",
"completed",
{ agent: "explore", description: "Map the timeline grouping rules", prompt: "Find where tool rows are grouped." },
{ output: "Grouping lives in the timeline projection.", metadata: { sessionID: "session_child_explore" } },
),
storyTool(
"tool_delegation_review",
"subagent",
"completed",
{ agent: "review", description: "Review the detail presets", prompt: "Check each timeline preset." },
{ output: "Every preset is consistent.", metadata: { sessionID: "session_child_review" } },
),
storyTool("tool_delegation_read_tests", "read", "completed", { path: "src/timeline/rows-current.test.ts" }),
storyTool("tool_delegation_grep_tests", "grep", "completed", { pattern: "subagent" }),
storyTool(
"tool_delegation_general",
"subagent",
"completed",
{ agent: "general", description: "Run the focused timeline tests", prompt: "Run the timeline tests." },
{ output: "All timeline tests pass.", metadata: { sessionID: "session_child_general" } },
),
{ type: "text", text: "Each delegated task keeps its own row while the reads and searches between them stay grouped." },
])
export const DelegateBetweenTools = {
args: { subagents: "separate" },
argTypes: { subagents: { control: { type: "inline-radio" }, options: ["separate", "grouped"] } },
render: (args: { subagents: "separate" | "grouped" }) => (
<section class="mx-auto min-h-screen w-full max-w-[720px] bg-v2-background-bg-base p-6">
<CurrentSessionProviders document={delegationDocument}>
<SessionTimeline
document={delegationDocument}
timelineDetail={{ ...timelinePresets[2].value, subagents: { placement: args.subagents } }}
/>
</CurrentSessionProviders>
</section>
),
}
function CodebaseExplorationStory() {
const [state, setState] = createStore({ read: false, glob: false })
@@ -1104,6 +1104,47 @@ describe("current session timeline rows", () => {
])
})
test.each([
{
name: "default",
grouped: false,
groups: [["context", "read", "grep"], ["part", "agent_1"], ["part", "agent_2"], ["context", "glob", "read_2"]],
},
{ name: "opted in", grouped: true, groups: [["context", "read", "grep", "agent_1", "agent_2", "glob", "read_2"]] },
])("gives each subagent its own row unless subagent grouping is enabled ($name)", (input) => {
const compact = timelinePresets.find((preset) => preset.id === "compact")!.value
const rows = Timeline.constructSessionMessageRows(
storyDocument([
storyTool("read", "read", "completed", { path: "src/a.ts" }),
storyTool("grep", "grep", "completed", { pattern: "agent" }),
storyTool("agent_1", "subagent", "completed", { agent: "explore", description: "Map the code" }),
storyTool("agent_2", "subagent", "completed", { agent: "general", description: "Check the tests" }),
storyTool("glob", "glob", "completed", { pattern: "*.ts" }),
storyTool("read_2", "read", "completed", { path: "src/b.ts" }),
]).messages,
true,
{ type: "idle" },
undefined,
false,
false,
undefined,
input.grouped ? { ...compact, subagents: { placement: "grouped" } } : compact,
).rows
expect(
rows.flatMap((row) =>
!Predicate.isTagged(row, "AssistantPart")
? []
: [
[
row.group.type,
...(row.group.type === "part" ? [row.group.ref] : row.group.refs).map((ref) => ref.partID),
],
],
),
).toEqual(input.groups.map((group) => [...group]))
})
test("merges adjacent separate reads into one read row split by other parts", () => {
const rows = Timeline.constructSessionMessageRows(
storyDocument([