feat(coding-agent): refactored eager todo messaging for cleaner categorization
- Changed eager todo reminder message role from 'developer' to 'custom' with customType field for better message categorization. - Removed userRequest parameter from eager todo prelude generation to simplify prompt template rendering. - Updated eager todo prompt to avoid redundant todo_write calls unless task state materially changed. - Modified eager todo reminder message to use string content with display: false property instead of array format.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
# 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
|
||||
@@ -14,6 +13,8 @@
|
||||
|
||||
### Changed
|
||||
|
||||
- Modified eager todo prelude to use hidden custom message type instead of visible developer message, preventing duplicate prompt text in session history
|
||||
- Updated eager todo prompt to remove dynamic user request injection, simplifying the template and preventing request repetition in displayed messages
|
||||
- Modified eager todo enforcement to prepend the todo reminder to the first user turn instead of executing it as a separate synthetic turn, reducing unnecessary prompt calls
|
||||
- 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
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
<system-reminder>
|
||||
Before doing substantive work on the upcoming user request, create a comprehensive phased todo first.
|
||||
|
||||
The todo **MUST** cover this request:
|
||||
<user-request>
|
||||
{{userRequest}}
|
||||
</user-request>
|
||||
|
||||
You **MUST** call `todo_write` first in this turn.
|
||||
You **MUST** initialize the todo list with a single `replace` op.
|
||||
You **MUST** cover the entire request from investigation through implementation and verification — not just the next immediate step.
|
||||
@@ -14,4 +9,5 @@ You **MUST** keep task `content` to a short label (5-10 words). Put file paths,
|
||||
You **MUST** keep exactly one task `in_progress` and all later tasks `pending`.
|
||||
|
||||
After the initial `todo_write` call succeeds, continue with the user's request in the same turn.
|
||||
Do not emit another `todo_write` call unless task state materially changed.
|
||||
</system-reminder>
|
||||
@@ -1959,7 +1959,7 @@ export class AgentSession {
|
||||
return;
|
||||
}
|
||||
|
||||
const eagerTodoPrelude = !options?.synthetic ? this.#createEagerTodoPrelude(expandedText) : undefined;
|
||||
const eagerTodoPrelude = !options?.synthetic ? this.#createEagerTodoPrelude() : undefined;
|
||||
|
||||
const userContent: (TextContent | ImageContent)[] = [{ type: "text", text: expandedText }];
|
||||
if (options?.images) {
|
||||
@@ -3515,7 +3515,7 @@ export class AgentSession {
|
||||
}
|
||||
}
|
||||
|
||||
#createEagerTodoPrelude(userRequest: string): { message: AgentMessage; toolChoice: ToolChoice } | undefined {
|
||||
#createEagerTodoPrelude(): { message: AgentMessage; toolChoice: ToolChoice } | undefined {
|
||||
const eagerTodosEnabled = this.settings.get("todo.eager");
|
||||
const todosEnabled = this.settings.get("todo.enabled");
|
||||
if (!eagerTodosEnabled || !todosEnabled) {
|
||||
@@ -3545,14 +3545,14 @@ export class AgentSession {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const eagerTodoReminder = renderPromptTemplate(eagerTodoPrompt, {
|
||||
userRequest,
|
||||
});
|
||||
const eagerTodoReminder = renderPromptTemplate(eagerTodoPrompt);
|
||||
|
||||
return {
|
||||
message: {
|
||||
role: "developer",
|
||||
content: [{ type: "text", text: eagerTodoReminder }],
|
||||
role: "custom",
|
||||
customType: "eager-todo-prelude",
|
||||
content: eagerTodoReminder,
|
||||
display: false,
|
||||
attribution: "agent",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
|
||||
@@ -20,6 +20,7 @@ type ObservedPromptCall = {
|
||||
toolChoice: string | undefined;
|
||||
toolNames: string[];
|
||||
messageRoles: AgentMessage["role"][];
|
||||
messageTexts: string[];
|
||||
lastMessageRole: AgentMessage["role"];
|
||||
lastMessageText: string;
|
||||
};
|
||||
@@ -164,6 +165,7 @@ describe("AgentSession eager todo enforcement", () => {
|
||||
toolChoice: getToolChoiceName(options?.toolChoice),
|
||||
toolNames: (context.tools ?? []).map(tool => tool.name),
|
||||
messageRoles: context.messages.map(message => message.role),
|
||||
messageTexts: context.messages.map(message => getMessageText(message)),
|
||||
lastMessageRole: lastMessage.role,
|
||||
lastMessageText: getMessageText(lastMessage),
|
||||
});
|
||||
@@ -200,21 +202,24 @@ describe("AgentSession eager todo enforcement", () => {
|
||||
tempDir.removeSync();
|
||||
});
|
||||
|
||||
it("prepends the eager todo reminder to the first real user turn", async () => {
|
||||
it("prepends a hidden eager todo reminder without repeating the prompt text", async () => {
|
||||
await session.prompt("list all work trees");
|
||||
|
||||
const dumpText = session.formatSessionAsText();
|
||||
|
||||
expect(observedCalls).toHaveLength(1);
|
||||
expect(observedCalls[0]).toEqual({
|
||||
toolChoice: "todo_write",
|
||||
toolNames: ["todo_write", "bash"],
|
||||
messageRoles: ["developer", "user"],
|
||||
messageRoles: ["user", "user"],
|
||||
messageTexts: [
|
||||
expect.stringContaining("Before doing substantive work on the upcoming user request"),
|
||||
"list all work trees",
|
||||
],
|
||||
lastMessageRole: "user",
|
||||
lastMessageText: "list all work trees",
|
||||
});
|
||||
expect(dumpText).toContain("## Developer");
|
||||
expect(dumpText).toContain("Before doing substantive work on the upcoming user request");
|
||||
expect(observedCalls[0]?.messageTexts.filter(text => text.includes("list all work trees"))).toHaveLength(1);
|
||||
expect(observedCalls[0]?.messageTexts[0]).not.toContain("list all work trees");
|
||||
expect(session.formatSessionAsText()).not.toContain("<user-request>");
|
||||
});
|
||||
|
||||
it("initializes todos once, then continues within the same user turn", async () => {
|
||||
@@ -242,7 +247,11 @@ describe("AgentSession eager todo enforcement", () => {
|
||||
expect(observedCalls[0]).toEqual({
|
||||
toolChoice: "todo_write",
|
||||
toolNames: ["todo_write", "bash"],
|
||||
messageRoles: ["developer", "user"],
|
||||
messageRoles: ["user", "user"],
|
||||
messageTexts: [
|
||||
expect.stringContaining("Before doing substantive work on the upcoming user request"),
|
||||
"list all work trees",
|
||||
],
|
||||
lastMessageRole: "user",
|
||||
lastMessageText: "list all work trees",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user