Skip to content

Commit dad982c

Browse files
authored
🤖 fix: require title for sub-agent tasks (#1263)
## Problem Sub-agent workspaces were showing up as `agent_explore_...` in the sidebar when agents didn't provide a `description` when spawning sub-tasks. The sidebar falls back to the auto-generated workspace name when no title is set. ## Solution Made the `title` field **required** for the `task` tool (renamed from `description` for clarity): - **`TaskToolArgsSchema`** - Changed `description: z.string().optional()` to `title: z.string().min(1)` - Updated all usages across taskService, orpc, tools, storybook mocks, and UI components Now agents **must** provide a title when spawning sub-tasks, which will be displayed in the sidebar instead of the cryptic workspace name like `agent_explore_abc123`. ## Testing - All existing tests updated and passing - `make static-check` passes --- _Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high`_
1 parent 038aede commit dad982c

File tree

9 files changed

+38
-23
lines changed

9 files changed

+38
-23
lines changed

src/browser/components/tools/TaskToolCall.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export const TaskToolCall: React.FC<TaskToolCallProps> = ({ args, result, status
143143
const isBackground = args.run_in_background ?? false;
144144
const agentType = args.subagent_type;
145145
const prompt = args.prompt;
146-
const description = args.description;
146+
const title = args.title;
147147

148148
// Derive task state from result
149149
const taskId = result?.taskId;
@@ -174,7 +174,7 @@ export const TaskToolCall: React.FC<TaskToolCallProps> = ({ args, result, status
174174
<div className="task-surface mt-1 rounded-md p-3">
175175
<div className="task-divider mb-2 flex items-center gap-2 border-b pb-2">
176176
<span className="text-task-mode text-[12px] font-semibold">
177-
{reportTitle ?? description ?? "Sub-agent Task"}
177+
{reportTitle ?? title}
178178
</span>
179179
{taskId && <TaskId id={taskId} />}
180180
{taskStatus && <TaskStatusBadge status={taskStatus} />}

src/browser/stories/App.task.stories.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ export const TaskWorkflow: AppStory = {
4040
createTaskTool("tc1", {
4141
subagent_type: "explore",
4242
prompt: "Analyze the frontend React components in src/browser/",
43-
description: "Frontend analysis",
43+
title: "Frontend analysis",
4444
run_in_background: true,
4545
taskId: "task-fe-001",
4646
status: "running",
4747
}),
4848
createTaskTool("tc2", {
4949
subagent_type: "exec",
5050
prompt: "Run linting on the backend code in src/node/",
51-
description: "Backend linting",
51+
title: "Backend linting",
5252
run_in_background: true,
5353
taskId: "task-be-002",
5454
status: "queued",
@@ -138,7 +138,7 @@ export const TaskWithReport: AppStory = {
138138
subagent_type: "explore",
139139
prompt:
140140
"Find all test files in this project. Look for patterns like *.test.ts, *.spec.ts, and test directories.",
141-
description: "Exploring test file structure",
141+
title: "Exploring test file structure",
142142
taskId: "task-abc123",
143143
reportMarkdown: `# Test File Analysis
144144
@@ -156,7 +156,7 @@ Found **47 test files** across the project:
156156
- Test files are co-located with implementation
157157
- Uses \`bun test\` for unit tests
158158
- Uses \`bun x jest\` for integration tests`,
159-
title: "Test File Analysis",
159+
reportTitle: "Test File Analysis",
160160
}),
161161
],
162162
}),

src/browser/stories/mockFactory.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ export function createTaskTool(
704704
opts: {
705705
subagent_type: "explore" | "exec";
706706
prompt: string;
707-
description?: string;
707+
title: string;
708708
run_in_background?: boolean;
709709
taskId: string;
710710
status: "queued" | "running";
@@ -718,7 +718,7 @@ export function createTaskTool(
718718
input: {
719719
subagent_type: opts.subagent_type,
720720
prompt: opts.prompt,
721-
description: opts.description,
721+
title: opts.title,
722722
run_in_background: opts.run_in_background ?? false,
723723
},
724724
output: {
@@ -734,10 +734,10 @@ export function createCompletedTaskTool(
734734
opts: {
735735
subagent_type: "explore" | "exec";
736736
prompt: string;
737-
description?: string;
737+
title: string;
738738
taskId?: string;
739739
reportMarkdown: string;
740-
title?: string;
740+
reportTitle?: string;
741741
}
742742
): MuxPart {
743743
return {
@@ -748,14 +748,14 @@ export function createCompletedTaskTool(
748748
input: {
749749
subagent_type: opts.subagent_type,
750750
prompt: opts.prompt,
751-
description: opts.description,
751+
title: opts.title,
752752
run_in_background: false,
753753
},
754754
output: {
755755
status: "completed",
756756
taskId: opts.taskId,
757757
reportMarkdown: opts.reportMarkdown,
758-
title: opts.title,
758+
title: opts.reportTitle,
759759
},
760760
};
761761
}
@@ -766,7 +766,7 @@ export function createPendingTaskTool(
766766
opts: {
767767
subagent_type: "explore" | "exec";
768768
prompt: string;
769-
description?: string;
769+
title: string;
770770
run_in_background?: boolean;
771771
}
772772
): MuxPart {
@@ -778,7 +778,7 @@ export function createPendingTaskTool(
778778
input: {
779779
subagent_type: opts.subagent_type,
780780
prompt: opts.prompt,
781-
description: opts.description,
781+
title: opts.title,
782782
run_in_background: opts.run_in_background ?? false,
783783
},
784784
};

src/common/orpc/schemas/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ export const tasks = {
488488
kind: z.literal("agent"),
489489
agentType: z.string(),
490490
prompt: z.string(),
491-
description: z.string().optional(),
491+
title: z.string().min(1),
492492
modelString: z.string().optional(),
493493
thinkingLevel: z.string().optional(),
494494
}),

src/common/utils/tools/toolDefinitions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export const TaskToolArgsSchema = z
9797
.object({
9898
subagent_type: SubagentTypeSchema,
9999
prompt: z.string().min(1),
100-
description: z.string().optional(),
100+
title: z.string().min(1),
101101
run_in_background: z.boolean().default(false),
102102
})
103103
.strict();

src/node/orpc/router.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,7 @@ export const router = (authToken?: string) => {
11221122
kind: input.kind,
11231123
agentType: input.agentType,
11241124
prompt: input.prompt,
1125-
description: input.description,
1125+
title: input.title,
11261126
modelString: input.modelString,
11271127
thinkingLevel,
11281128
});

src/node/services/taskService.test.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ describe("TaskService", () => {
253253
kind: "agent",
254254
agentType: "explore",
255255
prompt: "explore this repo",
256+
title: "Test task",
256257
});
257258
expect(first.success).toBe(true);
258259
if (!first.success) return;
@@ -262,6 +263,7 @@ describe("TaskService", () => {
262263
kind: "agent",
263264
agentType: "explore",
264265
prompt: "nested explore",
266+
title: "Test task",
265267
});
266268
expect(second.success).toBe(false);
267269
if (!second.success) {
@@ -334,6 +336,7 @@ describe("TaskService", () => {
334336
kind: "agent",
335337
agentType: "explore",
336338
prompt: "task 1",
339+
title: "Test task",
337340
});
338341
expect(running.success).toBe(true);
339342
if (!running.success) return;
@@ -343,6 +346,7 @@ describe("TaskService", () => {
343346
kind: "agent",
344347
agentType: "explore",
345348
prompt: "task 2",
349+
title: "Test task",
346350
});
347351
expect(queued.success).toBe(true);
348352
if (!queued.success) return;
@@ -428,6 +432,7 @@ describe("TaskService", () => {
428432
kind: "agent",
429433
agentType: "explore",
430434
prompt: "parent task",
435+
title: "Test task",
431436
});
432437
expect(parentTask.success).toBe(true);
433438
if (!parentTask.success) return;
@@ -439,6 +444,7 @@ describe("TaskService", () => {
439444
kind: "agent",
440445
agentType: "explore",
441446
prompt: "child task",
447+
title: "Test task",
442448
});
443449
expect(childTask.success).toBe(true);
444450
if (!childTask.success) return;
@@ -528,6 +534,7 @@ describe("TaskService", () => {
528534
kind: "agent",
529535
agentType: "explore",
530536
prompt: "task 1",
537+
title: "Test task",
531538
});
532539
expect(running.success).toBe(true);
533540
if (!running.success) return;
@@ -540,6 +547,7 @@ describe("TaskService", () => {
540547
kind: "agent",
541548
agentType: "explore",
542549
prompt: "task 2",
550+
title: "Test task",
543551
});
544552
expect(queued.success).toBe(true);
545553
if (!queued.success) return;
@@ -706,6 +714,7 @@ describe("TaskService", () => {
706714
kind: "agent",
707715
agentType: "explore",
708716
prompt: "task 1",
717+
title: "Test task",
709718
});
710719
expect(first.success).toBe(true);
711720
if (!first.success) return;
@@ -716,6 +725,7 @@ describe("TaskService", () => {
716725
kind: "agent",
717726
agentType: "explore",
718727
prompt: "task 2",
728+
title: "Test task",
719729
});
720730
expect(second.success).toBe(true);
721731
if (!second.success) return;
@@ -726,6 +736,7 @@ describe("TaskService", () => {
726736
kind: "agent",
727737
agentType: "explore",
728738
prompt: "task 3",
739+
title: "Test task",
729740
});
730741
expect(third.success).toBe(true);
731742
if (!third.success) return;
@@ -766,6 +777,7 @@ describe("TaskService", () => {
766777
kind: "agent",
767778
agentType: "explore",
768779
prompt: "run task from local workspace",
780+
title: "Test task",
769781
});
770782
expect(created.success).toBe(true);
771783
if (!created.success) return;
@@ -820,6 +832,7 @@ describe("TaskService", () => {
820832
kind: "agent",
821833
agentType: "explore",
822834
prompt: "run task with overrides",
835+
title: "Test task",
823836
});
824837
expect(created.success).toBe(true);
825838
if (!created.success) return;
@@ -1368,6 +1381,7 @@ describe("TaskService", () => {
13681381
kind: "agent",
13691382
agentType: "explore",
13701383
prompt: "do the thing",
1384+
title: "Test task",
13711385
});
13721386

13731387
expect(created.success).toBe(false);
@@ -1435,7 +1449,7 @@ describe("TaskService", () => {
14351449
type: "dynamic-tool",
14361450
toolCallId: "task-call-1",
14371451
toolName: "task",
1438-
input: { subagent_type: "explore", prompt: "do the thing" },
1452+
input: { subagent_type: "explore", prompt: "do the thing", title: "Test task" },
14391453
state: "input-available",
14401454
},
14411455
]
@@ -1709,7 +1723,7 @@ describe("TaskService", () => {
17091723
type: "dynamic-tool",
17101724
toolCallId: "task-call-1",
17111725
toolName: "task",
1712-
input: { subagent_type: "explore", prompt: "do the thing" },
1726+
input: { subagent_type: "explore", prompt: "do the thing", title: "Test task" },
17131727
state: "input-available",
17141728
},
17151729
]

src/node/services/taskService.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ export interface TaskCreateArgs {
3939
kind: TaskKind;
4040
agentType: string;
4141
prompt: string;
42-
description?: string;
42+
/** Human-readable title for the task (displayed in sidebar) */
43+
title: string;
4344
modelString?: string;
4445
thinkingLevel?: ThinkingLevel;
4546
}
@@ -439,7 +440,7 @@ export class TaskService {
439440
path: workspacePath,
440441
id: taskId,
441442
name: workspaceName,
442-
title: args.description,
443+
title: args.title,
443444
createdAt,
444445
runtimeConfig: taskRuntimeConfig,
445446
aiSettings: { model: canonicalModel, thinkingLevel: effectiveThinkingLevel },
@@ -524,7 +525,7 @@ export class TaskService {
524525
path: workspacePath,
525526
id: taskId,
526527
name: workspaceName,
527-
title: args.description,
528+
title: args.title,
528529
createdAt,
529530
runtimeConfig: taskRuntimeConfig,
530531
aiSettings: { model: canonicalModel, thinkingLevel: effectiveThinkingLevel },

src/node/services/tools/task.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const createTaskTool: ToolFactory = (config: ToolConfiguration) => {
3434
kind: "agent",
3535
agentType: args.subagent_type,
3636
prompt: args.prompt,
37-
description: args.description,
37+
title: args.title,
3838
modelString,
3939
thinkingLevel,
4040
});

0 commit comments

Comments
 (0)