Skip to content

Commit 2481567

Browse files
committed
fix(webapp): stop the CLI's AI help link dead-ending without an AI surface
Where neither Ask AI nor the agent can open, the redirect left `?aiHelp=` on the environment page with nothing to read it. Send the question to the docs instead.
1 parent 3f8bbd7 commit 2481567

3 files changed

Lines changed: 56 additions & 3 deletions

File tree

apps/webapp/app/components/dashboard-agent/ask-ai-channels.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
22
import { describe, expect, it } from "vitest";
33
import {
44
agentDeepLinkParams,
5+
aiHelpDocsUrl,
56
aiHelpRedirectUrl,
67
askAiCanOpen,
78
askAiChannelTarget,
@@ -153,4 +154,21 @@ describe("wiring", () => {
153154
it("builds the CLI redirect through the shared helper", () => {
154155
expect(cliRoute).toContain("aiHelpRedirectUrl(");
155156
});
157+
158+
// Structural: the loader needs a session and a database, so the branch is asserted on source.
159+
it("sends the CLI link to the docs when neither surface can open it", () => {
160+
expect(cliRoute).toContain("if (!canOpenSomething)");
161+
expect(cliRoute).toContain("redirect(aiHelpDocsUrl(query))");
162+
expect(cliRoute).toContain("askAiCanOpen(");
163+
expect(cliRoute).toContain("canAccessDashboardAgent(");
164+
});
165+
});
166+
167+
describe("aiHelpDocsUrl", () => {
168+
it("carries the question to the docs", () => {
169+
const url = new URL(aiHelpDocsUrl("Error: task timed out & failed"));
170+
171+
expect(url.origin + url.pathname).toBe("https://trigger.dev/docs");
172+
expect(url.searchParams.get("q")).toBe("Error: task timed out & failed");
173+
});
156174
});

apps/webapp/app/components/dashboard-agent/ask-ai-channels.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ export function agentDeepLinkParams(availability: AskAiAvailability): readonly D
4747
return askAiChannelTarget(availability) === "ask-ai" ? NO_PARAMS : ASK_AI_PARAMS;
4848
}
4949

50+
/**
51+
* Where the same link lands when neither surface can open it: nothing in the dashboard would
52+
* read the deep link, so the question goes to the docs rather than to a page that ignores it.
53+
*/
54+
export function aiHelpDocsUrl(query: string): string {
55+
const docs = new URL("https://trigger.dev/docs");
56+
docs.searchParams.set("q", query);
57+
return docs.toString();
58+
}
59+
5060
/**
5161
* Where `trigger dev`'s "Get a fix for this error using AI" link lands. Always on `origin`: an
5262
* absolute or protocol-relative `environmentPath` would otherwise decide the host itself, and

apps/webapp/app/routes/projects.$projectRef.ai-help.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
import { type LoaderFunctionArgs, redirect } from "@remix-run/server-runtime";
22
import { z } from "zod";
3-
import { aiHelpRedirectUrl } from "~/components/dashboard-agent/ask-ai-channels";
3+
import {
4+
aiHelpDocsUrl,
5+
aiHelpRedirectUrl,
6+
askAiCanOpen,
7+
} from "~/components/dashboard-agent/ask-ai-channels";
48
import { prisma } from "~/db.server";
59
import { env } from "~/env.server";
6-
import { requireUserId } from "~/services/session.server";
10+
import { featuresForRequest } from "~/features.server";
11+
import { hasAdminDisplayAccess, requireUser } from "~/services/session.server";
12+
import { canAccessDashboardAgent } from "~/v3/canAccessDashboardAgent.server";
713
import { v3EnvironmentPath } from "~/utils/pathBuilder";
814

915
const ParamsSchema = z.object({
1016
projectRef: z.string(),
1117
});
1218

1319
export async function loader({ params, request }: LoaderFunctionArgs) {
14-
const userId = await requireUserId(request);
20+
const user = await requireUser(request);
21+
const userId = user.id;
1522

1623
const validatedParams = ParamsSchema.parse(params);
1724

@@ -42,6 +49,24 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
4249
return new Response("No query", { status: 404 });
4350
}
4451

52+
const showAdminUi = hasAdminDisplayAccess(user);
53+
const canOpenSomething =
54+
askAiCanOpen({
55+
isManagedCloud: featuresForRequest(request).isManagedCloud,
56+
kapaWebsiteId: env.KAPA_AI_WEBSITE_ID,
57+
}) ||
58+
(await canAccessDashboardAgent({
59+
userId,
60+
isAdmin: showAdminUi && user.admin,
61+
isImpersonating: showAdminUi && user.isImpersonating,
62+
organizationSlug: project.organization.slug,
63+
orgFeatureFlags: (project.organization.featureFlags as Record<string, unknown>) ?? {},
64+
}));
65+
66+
if (!canOpenSomething) {
67+
return redirect(aiHelpDocsUrl(query));
68+
}
69+
4570
return redirect(
4671
aiHelpRedirectUrl({
4772
environmentPath: v3EnvironmentPath(

0 commit comments

Comments
 (0)