From bd36dcc172b3ea9cdd46ba771366be2265da254f Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 18 Oct 2026 16:12:21 -0400 Subject: [PATCH] feat(session-ui): show subagents ungrouped by default --- packages/session-ui/src/timeline/detail.ts | 4 +- .../timeline/research-and-agents.stories.tsx | 46 +++++++++++++++++++ .../src/timeline/rows-current.test.ts | 41 +++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/packages/session-ui/src/timeline/detail.ts b/packages/session-ui/src/timeline/detail.ts index 3716935a55c..668a699fcd4 100644 --- a/packages/session-ui/src/timeline/detail.ts +++ b/packages/session-ui/src/timeline/detail.ts @@ -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" }, diff --git a/packages/session-ui/src/timeline/research-and-agents.stories.tsx b/packages/session-ui/src/timeline/research-and-agents.stories.tsx index 43c98a60b0b..29d51b374e0 100644 --- a/packages/session-ui/src/timeline/research-and-agents.stories.tsx +++ b/packages/session-ui/src/timeline/research-and-agents.stories.tsx @@ -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" }) => ( +
+ + + +
+ ), +} + function CodebaseExplorationStory() { const [state, setState] = createStore({ read: false, glob: false }) diff --git a/packages/session-ui/src/timeline/rows-current.test.ts b/packages/session-ui/src/timeline/rows-current.test.ts index 9eb93268b3e..7192179193c 100644 --- a/packages/session-ui/src/timeline/rows-current.test.ts +++ b/packages/session-ui/src/timeline/rows-current.test.ts @@ -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([