Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions e2e-tests/fixtures/engine/local-agent/step-limit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type {
LocalAgentFixture,
Turn,
} from "../../../../testing/fake-llm-server/localAgentTypes";

/**
* Fixture that triggers the step limit by generating 50 tool call turns.
* The AI SDK's stepCountIs(50) will stop after 50 steps, and the handler
* will append a <dyad-step-limit> notice to the response.
*/
const toolCallTurns: Turn[] = Array.from({ length: 50 }, (_, i) => ({
text: `Step ${i + 1}: reading file.`,
toolCalls: [
{
name: "read_file",
args: { path: "package.json" },
},
],
}));

// Final text-only turn (won't be reached because stepCountIs(50) stops first)
const finalTurn: Turn = {
text: "All steps completed.",
};

export const fixture: LocalAgentFixture = {
description:
"Triggers step limit by making 50+ tool call rounds, causing a pause notification",
turns: [...toolCallTurns, finalTurn],
};
33 changes: 33 additions & 0 deletions e2e-tests/local_agent_step_limit.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { expect } from "@playwright/test";
import { Timeout, testSkipIfWindows } from "./helpers/test_helper";

/**
* E2E test for the step limit feature.
* When the local agent hits 50 tool call steps, it pauses and shows
* a <dyad-step-limit> notification card.
*/

testSkipIfWindows("local-agent - step limit pause", async ({ po }) => {
await po.setUpDyadPro({ localAgent: true });
await po.importApp("minimal");
await po.chatActions.selectLocalAgentMode();

await po.sendPrompt("tc=local-agent/step-limit");

// Verify the step limit card is visible
await expect(
po.page.getByText("Paused after 50 tool calls", { exact: true }),
).toBeVisible({
timeout: Timeout.EXTRA_LONG,
});

// Verify the "Continue" button is shown
await expect(po.page.getByRole("button", { name: "Continue" })).toBeVisible({
timeout: Timeout.MEDIUM,
});

// Click the "Continue" button
await po.page.getByRole("button", { name: "Continue" }).click();

await po.snapshotMessages();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
- 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\./
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- button "Copy":
- img
- img
- text: Approved
- img
- text: claude-opus-4-5
- img
- text: less than a minute ago
- img
- text: (1 files changed)
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/step-limit
- paragraph: "Step 1: reading file."
- img
- text: Read package.json
- paragraph: "Step 2: reading file."
- img
- text: Read package.json
- paragraph: "Step 3: reading file."
- img
- text: Read package.json
- paragraph: "Step 4: reading file."
- img
- text: Read package.json
- paragraph: "Step 5: reading file."
- img
- text: Read package.json
- paragraph: "Step 6: reading file."
- img
- text: Read package.json
- paragraph: "Step 7: reading file."
- img
- text: Read package.json
- paragraph: "Step 8: reading file."
- img
- text: Read package.json
- paragraph: "Step 9: reading file."
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- paragraph: "/Step \\d+: reading file\\./"
- img
- text: Read package.json
- img
- text: /Paused after \d+ tool calls/
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This snapshot is too permissive and does not verify the new 50-call limit. Assert the exact number in the pause text so regressions in the configured threshold are caught.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At e2e-tests/snapshots/local_agent_step_limit.spec.ts_local-agent---step-limit-pause-1.aria.yml, line 175:

<comment>This snapshot is too permissive and does not verify the new 50-call limit. Assert the exact number in the pause text so regressions in the configured threshold are caught.</comment>

<file context>
@@ -0,0 +1,214 @@
+- img
+- text: Read package.json
+- img
+- text: /Paused after \d+ tool calls/
+- button "Continue":
+  - img
</file context>
Fix with Cubic

- button "Continue":
- img
- text: ""
- text: /Automatically paused after \d+ tool calls\./
- button "Copy":
- img
- img
- text: Approved
- img
- text: claude-opus-4-5
- img
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: Continue
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- button "Copy":
- img
- img
- text: claude-opus-4-5
- img
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions src/components/chat/DyadMarkdownParser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { DyadCompaction } from "./DyadCompaction";
import { DyadWritePlan } from "./DyadWritePlan";
import { DyadExitPlan } from "./DyadExitPlan";
import { DyadQuestionnaire } from "./DyadQuestionnaire";
import { DyadStepLimit } from "./DyadStepLimit";
import { mapActionToButton } from "./ChatInput";
import { SuggestedAction } from "@/lib/schemas";
import { FixAllErrorsButton } from "./FixAllErrorsButton";
Expand Down Expand Up @@ -82,6 +83,8 @@ const DYAD_CUSTOM_TAGS = [
"dyad-write-plan",
"dyad-exit-plan",
"dyad-questionnaire",
// Step limit notification
"dyad-step-limit",
];

interface DyadMarkdownParserProps {
Expand Down Expand Up @@ -802,6 +805,21 @@ function renderCustomTag(
case "dyad-questionnaire":
return <DyadQuestionnaire>{content}</DyadQuestionnaire>;

case "dyad-step-limit":
return (
<DyadStepLimit
node={{
properties: {
steps: attributes.steps,
limit: attributes.limit,
state: getState({ isStreaming, inProgress }),
},
}}
>
{content}
</DyadStepLimit>
);

default:
return null;
}
Expand Down
Loading