feat(coding-agent/task): added optional assignment field to track raw task text separately from templates

- Added optional `assignment` field to task result and progress interfaces to track raw per-task assignment text separately from full templated task.
- Updated task rendering to display assignment text instead of full task template when available, improving clarity of task display.
- Modified task section rendering to show trimmed assignment text with fallback to task field if assignment is not available.
- Propagated assignment field through task executor, template renderer, and result objects to maintain consistency across task processing pipeline.
This commit is contained in:
can1357
2026-03-11 01:11:20 +01:00
parent 2a5968ec47
commit a1be87ad9b
6 changed files with 27 additions and 12 deletions
+3 -1
View File
@@ -1,9 +1,9 @@
# Changelog
## [Unreleased]
### Added
- Added `assignment` field to task progress and result objects to track the raw per-task assignment text separately from the full templated task
- Added `details` field to todo items for storing implementation specifics, file paths, and edge cases (shown only when task is active)
- Added support for multi-line details in todo items with automatic indentation in interactive and reminder displays
- Added `todo.eager` setting to automatically create a comprehensive todo list after the first user message
@@ -13,6 +13,8 @@
### Changed
- Updated task rendering to display assignment text instead of full task template when available, reducing noise in progress and result displays
- Modified task section rendering to show trimmed assignment text without stripping context blocks, simplifying the display logic
- Updated todo item display to show `details` field indented below active tasks in both interactive mode and todo reminder component
- Modified tool choice resolution to support per-turn tool choice overrides via `consumeNextToolChoiceOverride()`
- Updated tool documentation to clarify that `path` parameter accepts files, directories, glob patterns, or comma/space-separated path lists
@@ -123,6 +123,7 @@ export interface ExecutorOptions {
worktree?: string;
agent: AgentDefinition;
task: string;
assignment?: string;
description?: string;
index: number;
id: string;
@@ -442,6 +443,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
cwd,
agent,
task,
assignment,
index,
id,
worktree,
@@ -462,6 +464,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
agentSource: agent.source,
status: "running",
task,
assignment,
description: options.description,
lastIntent: undefined,
recentTools: [],
@@ -480,6 +483,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
agent: agent.name,
agentSource: agent.source,
task,
assignment,
description: options.description,
exitCode: 1,
output: "",
@@ -622,6 +626,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
agent: agent.name,
agentSource: agent.source,
task,
assignment,
progress: { ...progress },
});
}
@@ -711,6 +716,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
agent: agent.name,
agentSource: agent.source,
task,
assignment,
event,
});
}
@@ -1231,6 +1237,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
agent: agent.name,
agentSource: agent.source,
task,
assignment,
description: options.description,
lastIntent: progress.lastIntent,
exitCode,
+6
View File
@@ -224,6 +224,7 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
agentSource: fallbackAgentSource,
status: "pending",
task: renderedTask.task,
assignment: renderedTask.assignment,
description: renderedTask.description,
recentTools: [],
recentOutput: [],
@@ -732,6 +733,7 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
agentSource: agent.source,
status: "pending",
task: t.task,
assignment: t.assignment,
recentTools: [],
recentOutput: [],
toolCount: 0,
@@ -749,6 +751,7 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
cwd: this.session.cwd,
agent,
task: task.task,
assignment: task.assignment,
description: task.description,
index,
id: task.id,
@@ -801,6 +804,7 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
worktree: isolationDir,
agent,
task: task.task,
assignment: task.assignment,
description: task.description,
index,
id: task.id,
@@ -886,6 +890,7 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
agent: agent.name,
agentSource: agent.source,
task: task.task,
assignment: task.assignment,
description: task.description,
exitCode: 1,
output: "",
@@ -929,6 +934,7 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
agent: agentName,
agentSource: agent.source,
task: task.task,
assignment: task.assignment,
description: task.description,
exitCode: 1,
output: "",
+5 -10
View File
@@ -374,16 +374,11 @@ function renderTaskSection(
maxExpanded = 20,
): string[] {
const lines: string[] = [];
const trimmed = task.trimEnd();
const trimmed = task.trim();
if (!expanded || !trimmed) return lines;
// Strip the shared <context>...</context> block — it's the same
// across all tasks and just adds noise when expanded.
const stripped = trimmed.replace(/<context>[\s\S]*?<\/context>\s*/, "").trimStart();
if (!stripped) return lines;
lines.push(`${continuePrefix}${theme.fg("dim", "Task")}`);
const taskLines = stripped.split("\n");
const taskLines = trimmed.split("\n");
for (const line of taskLines.slice(0, maxExpanded)) {
lines.push(`${continuePrefix} ${theme.fg("dim", truncateToWidth(replaceTabs(line), 70))}`);
}
@@ -526,7 +521,7 @@ function renderAgentProgress(
if (progress.status === "running") {
if (!description) {
const taskPreview = truncateToWidth(progress.task, 40);
const taskPreview = truncateToWidth(progress.assignment ?? progress.task, 40);
statusLine += ` ${theme.fg("muted", taskPreview)}`;
}
if (progress.toolCount > 0) {
@@ -546,7 +541,7 @@ function renderAgentProgress(
lines.push(statusLine);
lines.push(...renderTaskSection(progress.task, continuePrefix, expanded, theme));
lines.push(...renderTaskSection(progress.assignment ?? progress.task, continuePrefix, expanded, theme));
// Current tool (if running) or most recent completed tool
if (progress.status === "running") {
@@ -781,7 +776,7 @@ function renderAgentResult(result: SingleResult, isLast: boolean, expanded: bool
lines.push(statusLine);
lines.push(...renderTaskSection(result.task, continuePrefix, expanded, theme));
lines.push(...renderTaskSection(result.assignment ?? result.task, continuePrefix, expanded, theme));
if (aborted && result.abortReason) {
lines.push(
+4 -1
View File
@@ -5,6 +5,8 @@ import type { TaskItem } from "./types";
interface RenderResult {
/** Full task text sent to the subagent */
task: string;
/** Raw per-task assignment text, without prompt template boilerplate */
assignment: string;
id: string;
description: string;
}
@@ -20,10 +22,11 @@ export function renderTemplate(context: string | undefined, task: TaskItem): Ren
context = context?.trim();
if (!context || !assignment) {
return { task: assignment || context!, id, description };
return { task: assignment || context!, assignment: assignment || context!, id, description };
}
return {
task: renderPromptTemplate(subagentUserPromptTemplate, { context, assignment }),
assignment,
id,
description,
};
+2
View File
@@ -136,6 +136,7 @@ export interface AgentProgress {
agentSource: AgentSource;
status: "pending" | "running" | "completed" | "failed" | "aborted";
task: string;
assignment?: string;
description?: string;
lastIntent?: string;
currentTool?: string;
@@ -158,6 +159,7 @@ export interface SingleResult {
agent: string;
agentSource: AgentSource;
task: string;
assignment?: string;
description?: string;
lastIntent?: string;
exitCode: number;