Unverified 提交 96101a74 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Update problems report when run type check tools is run (#2212)

<!-- CURSOR_SUMMARY --> > [!NOTE] > Updates the Problems panel in real time when the local agent runs TypeScript checks and adds E2E coverage. > > - Adds `agent-tool:problems-update` IPC event, `AgentProblemsUpdatePayload`, and `IpcClient.onAgentProblemsUpdate` subscription > - Extends `AgentContext` with `appId` and sends problem reports from `run_type_checks` via `safeSend` after `generateProblemReport` > - Renderer listens for problems updates and writes to TanStack Query cache `['problems', appId]` to refresh the Problems panel > - Preload whitelists the new receive channel > - Adds local-agent E2E test, fixture, and snapshot validating Problems panel updates (`e2e-tests/local_agent_run_type_checks.spec.ts`) > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 9134d5f0053079e7294da8e6665f5684a047a15a. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated description by cubic. --> ## Summary by cubic Pushes Problems panel updates when the local agent runs type checks by emitting a problems report over IPC and updating the UI cache in real time. Adds an E2E test to verify errors appear and the Fix button is enabled. - **New Features** - run_type_checks now sends "agent-tool:problems-update" with { appId, problems } using safeSend; AgentContext includes appId. - IPC: added AgentProblemsUpdatePayload, whitelisted the channel in preload, and exposed onAgentProblemsUpdate in IpcClient. - Renderer listens for problems updates and writes to TanStack Query cache ["problems", appId]. - E2E: local-agent test creates TS errors, runs type checks, and snapshots Problems panel (verifies error rows and Fix button). <sup>Written for commit 9134d5f0053079e7294da8e6665f5684a047a15a. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. -->
上级 e9c97939
import type { LocalAgentFixture } from "../../../../testing/fake-llm-server/localAgentTypes";
export const fixture: LocalAgentFixture = {
description:
"Write a file with TypeScript errors and then run type checks to update the Problems panel",
turns: [
{
text: "I'll create a file with some TypeScript errors for testing.",
toolCalls: [
{
name: "write_file",
args: {
path: "src/test-errors.ts",
content: `// File with TypeScript errors for testing
const x: number = "not a number";
const y: string = 123;
nonExistentFunction();
`,
description: "Create file with type errors",
},
},
],
},
{
text: "Now let me run type checks to identify the errors.",
toolCalls: [
{
name: "run_type_checks",
args: {
paths: ["src/test-errors.ts"],
},
},
],
},
{
text: "The type check found the errors. The Problems panel should now show these TypeScript errors.",
},
],
};
import { testSkipIfWindows, Timeout } from "./helpers/test_helper";
import { expect } from "@playwright/test";
/**
* E2E test for run_type_checks tool in local-agent mode
* Tests that running type checks updates the Problems panel in the UI
*/
testSkipIfWindows(
"local-agent - run_type_checks updates problems panel",
async ({ po }) => {
await po.setUpDyadPro({ localAgent: true });
await po.importApp("minimal");
await po.selectLocalAgentMode();
// Ensure pnpm install has run so TypeScript is available
await po.ensurePnpmInstall();
// Switch to Problems panel first to observe the update
await po.selectPreviewMode("problems");
// Initially there should be no problems
const fixButton = po.page.getByTestId("fix-all-button");
// The button may not exist if there are no problems, so we check for the "No problems found" text
await expect(
po.page.getByText(/No problems found|No Problems Report/),
).toBeVisible({ timeout: Timeout.MEDIUM });
// Send prompt that triggers write_file with TS errors, then run_type_checks
await po.sendPrompt("tc=local-agent/run-type-checks");
// After the agent runs type checks, the Problems panel should show errors
// Wait for the fix button to be enabled and show errors
await expect(fixButton).toBeEnabled({ timeout: Timeout.LONG });
await expect(fixButton).toContainText(/Fix \d+ problem/);
// Verify the problems are displayed
const problemRows = po.page.getByTestId("problem-row");
await expect(problemRows.first()).toBeVisible({ timeout: Timeout.MEDIUM });
// Take a snapshot of the problems pane for regression testing
await po.snapshotProblemsPane();
},
);
- img
- text: 3 errors
- button "Run checks":
- img
- button "Clear all"
- button "Fix 3 problems":
- img
- checkbox "Select problem src/test-errors.ts 2:7 Type 'string' is not assignable to type 'number'." [checked]:
- checkbox "Select problem" [checked]:
- img
- img
- img
- paragraph: Type 'string' is not assignable to type 'number'.
- checkbox "Select problem src/test-errors.ts 3:7 Type 'number' is not assignable to type 'string'." [checked]:
- checkbox "Select problem" [checked]:
- img
- img
- img
- paragraph: Type 'number' is not assignable to type 'string'.
- checkbox "Select problem src/test-errors.ts 4:1 Cannot find name 'nonExistentFunction'." [checked]:
- checkbox "Select problem" [checked]:
- img
- img
- img
- paragraph: Cannot find name 'nonExistentFunction'.
\ No newline at end of file
......@@ -83,6 +83,7 @@ import type {
AgentToolConsentRequestPayload,
AgentToolConsentResponseParams,
AgentTodosUpdatePayload,
AgentProblemsUpdatePayload,
TelemetryEventPayload,
GithubSyncOptions,
ConsoleEntry,
......@@ -145,6 +146,9 @@ export class IpcClient {
private mcpConsentHandlers: Map<string, (payload: any) => void>;
private agentConsentHandlers: Map<string, (payload: any) => void>;
private agentTodosHandlers: Set<(payload: AgentTodosUpdatePayload) => void>;
private agentProblemsHandlers: Set<
(payload: AgentProblemsUpdatePayload) => void
>;
private telemetryEventHandlers: Set<(payload: TelemetryEventPayload) => void>;
// Global handlers called for any chat stream start (used for cleanup)
private globalChatStreamStartHandlers: Set<(chatId: number) => void>;
......@@ -158,6 +162,7 @@ export class IpcClient {
this.mcpConsentHandlers = new Map();
this.agentConsentHandlers = new Map();
this.agentTodosHandlers = new Set();
this.agentProblemsHandlers = new Set();
this.telemetryEventHandlers = new Set();
this.globalChatStreamStartHandlers = new Set();
this.globalChatStreamEndHandlers = new Set();
......@@ -315,6 +320,13 @@ export class IpcClient {
}
});
// Agent problems update from main
this.ipcRenderer.on("agent-tool:problems-update", (payload) => {
for (const handler of this.agentProblemsHandlers) {
handler(payload as unknown as AgentProblemsUpdatePayload);
}
});
// Telemetry events from main to renderer
this.ipcRenderer.on("telemetry:event", (payload) => {
if (payload && typeof payload === "object" && "eventName" in payload) {
......@@ -1151,6 +1163,19 @@ export class IpcClient {
};
}
/**
* Subscribe to agent problems updates from the local agent.
* Called when the agent runs type checks and updates the problems report.
*/
public onAgentProblemsUpdate(
handler: (payload: AgentProblemsUpdatePayload) => void,
) {
this.agentProblemsHandlers.add(handler);
return () => {
this.agentProblemsHandlers.delete(handler);
};
}
/**
* Subscribe to be notified when any chat stream starts.
* Useful for cleanup tasks like clearing pending consent requests.
......
......@@ -732,6 +732,11 @@ export interface AgentTodosUpdatePayload {
todos: AgentTodo[];
}
export interface AgentProblemsUpdatePayload {
appId: number;
problems: ProblemReport;
}
export interface TelemetryEventPayload {
eventName: string;
properties?: Record<string, unknown>;
......
......@@ -199,6 +199,8 @@ const validReceiveChannels = [
"agent-tool:consent-request",
// Agent todos update from main to renderer
"agent-tool:todos-update",
// Agent problems update from main to renderer
"agent-tool:problems-update",
// Telemetry events from main to renderer
"telemetry:event",
] as const;
......
......@@ -164,6 +164,7 @@ export async function handleLocalAgentStream(
// Build tool execute context
const ctx: AgentContext = {
event,
appId: chat.app.id,
appPath,
chatId: chat.id,
supabaseProjectId: chat.app.supabaseProjectId,
......
......@@ -7,6 +7,7 @@ import {
} from "./types";
import { generateProblemReport } from "@/ipc/processors/tsc";
import type { Problem } from "@/ipc/ipc_types";
import { safeSend } from "@/ipc/utils/safe_sender";
import { normalizePath } from "../../../../../../../shared/normalizePath";
......@@ -98,6 +99,12 @@ export const runTypeChecksTool: ToolDefinition<
appPath: ctx.appPath,
});
// Send the full problem report to update the Problems panel in the UI
safeSend(ctx.event.sender, "agent-tool:problems-update", {
appId: ctx.appId,
problems: problemReport,
});
let problems = problemReport.problems;
// Filter by paths if specified
......
......@@ -33,6 +33,7 @@ export type Todo = AgentTodo;
export interface AgentContext {
event: IpcMainInvokeEvent;
appId: number;
appPath: string;
chatId: number;
supabaseProjectId: string | null;
......
......@@ -198,6 +198,15 @@ function App() {
return () => unsubscribe();
}, []);
// Agent problems updates - update the TanStack Query cache when the agent runs type checks
useEffect(() => {
const ipc = IpcClient.getInstance();
const unsubscribe = ipc.onAgentProblemsUpdate((payload) => {
queryClient.setQueryData(["problems", payload.appId], payload.problems);
});
return () => unsubscribe();
}, []);
return <RouterProvider router={router} />;
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论