Skip to content

Commit 005b72b

Browse files
wwwillchen-botwwwillchenclaude
authored
fix: increase tool call limit to 50 and show clear pause message (#2828)
## Summary - Increase MAX_TOOL_CALL_STEPS from 25 to 50 to allow longer multi-step tasks - Add step limit detection to track total steps across all passes - Show a clear `<dyad-step-limit>` message when the limit is reached, instructing users to send "continue" to resume - Create DyadStepLimit component for displaying the pause notification Fixes #2754 ## Test plan - Run the local agent and perform a task that requires many tool calls - Verify the agent pauses at 50 tool calls instead of 25 - Verify a clear message is shown explaining why it paused and how to continue - Type "continue" to verify the agent resumes working 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2828" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> --------- Co-authored-by: Will Chen <willchen90@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent b517b45 commit 005b72b

File tree

7 files changed

+399
-3
lines changed

7 files changed

+399
-3
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type {
2+
LocalAgentFixture,
3+
Turn,
4+
} from "../../../../testing/fake-llm-server/localAgentTypes";
5+
6+
/**
7+
* Fixture that triggers the step limit by generating 50 tool call turns.
8+
* The AI SDK's stepCountIs(50) will stop after 50 steps, and the handler
9+
* will append a <dyad-step-limit> notice to the response.
10+
*/
11+
const toolCallTurns: Turn[] = Array.from({ length: 50 }, (_, i) => ({
12+
text: `Step ${i + 1}: reading file.`,
13+
toolCalls: [
14+
{
15+
name: "read_file",
16+
args: { path: "package.json" },
17+
},
18+
],
19+
}));
20+
21+
// Final text-only turn (won't be reached because stepCountIs(50) stops first)
22+
const finalTurn: Turn = {
23+
text: "All steps completed.",
24+
};
25+
26+
export const fixture: LocalAgentFixture = {
27+
description:
28+
"Triggers step limit by making 50+ tool call rounds, causing a pause notification",
29+
turns: [...toolCallTurns, finalTurn],
30+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { expect } from "@playwright/test";
2+
import { Timeout, testSkipIfWindows } from "./helpers/test_helper";
3+
4+
/**
5+
* E2E test for the step limit feature.
6+
* When the local agent hits 50 tool call steps, it pauses and shows
7+
* a <dyad-step-limit> notification card.
8+
*/
9+
10+
testSkipIfWindows("local-agent - step limit pause", async ({ po }) => {
11+
await po.setUpDyadPro({ localAgent: true });
12+
await po.importApp("minimal");
13+
await po.chatActions.selectLocalAgentMode();
14+
15+
await po.sendPrompt("tc=local-agent/step-limit");
16+
17+
// Verify the step limit card is visible
18+
await expect(
19+
po.page.getByText("Paused after 50 tool calls", { exact: true }),
20+
).toBeVisible({
21+
timeout: Timeout.EXTRA_LONG,
22+
});
23+
24+
// Verify the "Continue" button is shown
25+
await expect(po.page.getByRole("button", { name: "Continue" })).toBeVisible({
26+
timeout: Timeout.MEDIUM,
27+
});
28+
29+
// Click the "Continue" button
30+
await po.page.getByRole("button", { name: "Continue" }).click();
31+
32+
await po.snapshotMessages();
33+
});
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
- paragraph: /Generate an AI_RULES\.md file for this app\. Describe the tech stack in 5-\d+ bullet points and describe clear rules about what libraries to use for what\./
2+
- button "file1.txt file1.txt Edit":
3+
- img
4+
- text: ""
5+
- button "Edit":
6+
- img
7+
- text: ""
8+
- img
9+
- paragraph: More EOM
10+
- button "Copy":
11+
- img
12+
- img
13+
- text: Approved
14+
- img
15+
- text: claude-opus-4-5
16+
- img
17+
- text: less than a minute ago
18+
- img
19+
- text: (1 files changed)
20+
- button "Copy Request ID":
21+
- img
22+
- text: ""
23+
- paragraph: tc=local-agent/step-limit
24+
- paragraph: "Step 1: reading file."
25+
- img
26+
- text: Read package.json
27+
- paragraph: "Step 2: reading file."
28+
- img
29+
- text: Read package.json
30+
- paragraph: "Step 3: reading file."
31+
- img
32+
- text: Read package.json
33+
- paragraph: "Step 4: reading file."
34+
- img
35+
- text: Read package.json
36+
- paragraph: "Step 5: reading file."
37+
- img
38+
- text: Read package.json
39+
- paragraph: "Step 6: reading file."
40+
- img
41+
- text: Read package.json
42+
- paragraph: "Step 7: reading file."
43+
- img
44+
- text: Read package.json
45+
- paragraph: "Step 8: reading file."
46+
- img
47+
- text: Read package.json
48+
- paragraph: "Step 9: reading file."
49+
- img
50+
- text: Read package.json
51+
- paragraph: "/Step \\d+: reading file\\./"
52+
- img
53+
- text: Read package.json
54+
- paragraph: "/Step \\d+: reading file\\./"
55+
- img
56+
- text: Read package.json
57+
- paragraph: "/Step \\d+: reading file\\./"
58+
- img
59+
- text: Read package.json
60+
- paragraph: "/Step \\d+: reading file\\./"
61+
- img
62+
- text: Read package.json
63+
- paragraph: "/Step \\d+: reading file\\./"
64+
- img
65+
- text: Read package.json
66+
- paragraph: "/Step \\d+: reading file\\./"
67+
- img
68+
- text: Read package.json
69+
- paragraph: "/Step \\d+: reading file\\./"
70+
- img
71+
- text: Read package.json
72+
- paragraph: "/Step \\d+: reading file\\./"
73+
- img
74+
- text: Read package.json
75+
- paragraph: "/Step \\d+: reading file\\./"
76+
- img
77+
- text: Read package.json
78+
- paragraph: "/Step \\d+: reading file\\./"
79+
- img
80+
- text: Read package.json
81+
- paragraph: "/Step \\d+: reading file\\./"
82+
- img
83+
- text: Read package.json
84+
- paragraph: "/Step \\d+: reading file\\./"
85+
- img
86+
- text: Read package.json
87+
- paragraph: "/Step \\d+: reading file\\./"
88+
- img
89+
- text: Read package.json
90+
- paragraph: "/Step \\d+: reading file\\./"
91+
- img
92+
- text: Read package.json
93+
- paragraph: "/Step \\d+: reading file\\./"
94+
- img
95+
- text: Read package.json
96+
- paragraph: "/Step \\d+: reading file\\./"
97+
- img
98+
- text: Read package.json
99+
- paragraph: "/Step \\d+: reading file\\./"
100+
- img
101+
- text: Read package.json
102+
- paragraph: "/Step \\d+: reading file\\./"
103+
- img
104+
- text: Read package.json
105+
- paragraph: "/Step \\d+: reading file\\./"
106+
- img
107+
- text: Read package.json
108+
- paragraph: "/Step \\d+: reading file\\./"
109+
- img
110+
- text: Read package.json
111+
- paragraph: "/Step \\d+: reading file\\./"
112+
- img
113+
- text: Read package.json
114+
- paragraph: "/Step \\d+: reading file\\./"
115+
- img
116+
- text: Read package.json
117+
- paragraph: "/Step \\d+: reading file\\./"
118+
- img
119+
- text: Read package.json
120+
- paragraph: "/Step \\d+: reading file\\./"
121+
- img
122+
- text: Read package.json
123+
- paragraph: "/Step \\d+: reading file\\./"
124+
- img
125+
- text: Read package.json
126+
- paragraph: "/Step \\d+: reading file\\./"
127+
- img
128+
- text: Read package.json
129+
- paragraph: "/Step \\d+: reading file\\./"
130+
- img
131+
- text: Read package.json
132+
- paragraph: "/Step \\d+: reading file\\./"
133+
- img
134+
- text: Read package.json
135+
- paragraph: "/Step \\d+: reading file\\./"
136+
- img
137+
- text: Read package.json
138+
- paragraph: "/Step \\d+: reading file\\./"
139+
- img
140+
- text: Read package.json
141+
- paragraph: "/Step \\d+: reading file\\./"
142+
- img
143+
- text: Read package.json
144+
- paragraph: "/Step \\d+: reading file\\./"
145+
- img
146+
- text: Read package.json
147+
- paragraph: "/Step \\d+: reading file\\./"
148+
- img
149+
- text: Read package.json
150+
- paragraph: "/Step \\d+: reading file\\./"
151+
- img
152+
- text: Read package.json
153+
- paragraph: "/Step \\d+: reading file\\./"
154+
- img
155+
- text: Read package.json
156+
- paragraph: "/Step \\d+: reading file\\./"
157+
- img
158+
- text: Read package.json
159+
- paragraph: "/Step \\d+: reading file\\./"
160+
- img
161+
- text: Read package.json
162+
- paragraph: "/Step \\d+: reading file\\./"
163+
- img
164+
- text: Read package.json
165+
- paragraph: "/Step \\d+: reading file\\./"
166+
- img
167+
- text: Read package.json
168+
- paragraph: "/Step \\d+: reading file\\./"
169+
- img
170+
- text: Read package.json
171+
- paragraph: "/Step \\d+: reading file\\./"
172+
- img
173+
- text: Read package.json
174+
- img
175+
- text: /Paused after \d+ tool calls/
176+
- button "Continue":
177+
- img
178+
- text: ""
179+
- text: /Automatically paused after \d+ tool calls\./
180+
- button "Copy":
181+
- img
182+
- img
183+
- text: Approved
184+
- img
185+
- text: claude-opus-4-5
186+
- img
187+
- text: less than a minute ago
188+
- button "Copy Request ID":
189+
- img
190+
- text: ""
191+
- paragraph: Continue
192+
- button "file1.txt file1.txt Edit":
193+
- img
194+
- text: ""
195+
- button "Edit":
196+
- img
197+
- text: ""
198+
- img
199+
- paragraph: More EOM
200+
- button "Copy":
201+
- img
202+
- img
203+
- text: claude-opus-4-5
204+
- img
205+
- text: less than a minute ago
206+
- button "Copy Request ID":
207+
- img
208+
- text: ""
209+
- button "Undo":
210+
- img
211+
- text: ""
212+
- button "Retry":
213+
- img
214+
- text: ""

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/chat/DyadMarkdownParser.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { DyadCompaction } from "./DyadCompaction";
4040
import { DyadWritePlan } from "./DyadWritePlan";
4141
import { DyadExitPlan } from "./DyadExitPlan";
4242
import { DyadQuestionnaire } from "./DyadQuestionnaire";
43+
import { DyadStepLimit } from "./DyadStepLimit";
4344
import { mapActionToButton } from "./ChatInput";
4445
import { SuggestedAction } from "@/lib/schemas";
4546
import { FixAllErrorsButton } from "./FixAllErrorsButton";
@@ -82,6 +83,8 @@ const DYAD_CUSTOM_TAGS = [
8283
"dyad-write-plan",
8384
"dyad-exit-plan",
8485
"dyad-questionnaire",
86+
// Step limit notification
87+
"dyad-step-limit",
8588
];
8689

8790
interface DyadMarkdownParserProps {
@@ -802,6 +805,21 @@ function renderCustomTag(
802805
case "dyad-questionnaire":
803806
return <DyadQuestionnaire>{content}</DyadQuestionnaire>;
804807

808+
case "dyad-step-limit":
809+
return (
810+
<DyadStepLimit
811+
node={{
812+
properties: {
813+
steps: attributes.steps,
814+
limit: attributes.limit,
815+
state: getState({ isStreaming, inProgress }),
816+
},
817+
}}
818+
>
819+
{content}
820+
</DyadStepLimit>
821+
);
822+
805823
default:
806824
return null;
807825
}

0 commit comments

Comments
 (0)