From 2822edf86021315f14beaf5d90062c4627076d7b Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Thu, 20 Aug 2026 13:34:21 +0100 Subject: [PATCH] fix(webapp): let the dashboard agent use a configurable base URL The agent's session start, token mint, head start, in-proxy and the client transport all assumed the agent project runs on the same Trigger instance as the webapp, deriving the agent's base URL from the webapp's own origin. When the agent project runs on a different instance, its secret key belongs to that instance, so sending it to the webapp's own API is rejected with an "Invalid API key" and the chat can't start. Add an optional DASHBOARD_AGENT_BASE_URL. The agent's base URL now resolves to it, or the SDK default when unset, and never the webapp's own origin. A concrete default rather than an unset value keeps it independent of TRIGGER_API_URL, which a webapp may point at a different host. The server call sites already funnel through one helper; the client transport reads the value from the root loader via a new useDashboardAgentBaseUrl hook. --- .../app/components/dashboard-agent/DashboardAgentPanel.tsx | 4 ++-- apps/webapp/app/env.server.ts | 1 + .../hooks/{useApiOrigin.ts => useDashboardAgentBaseUrl.ts} | 4 ++-- apps/webapp/app/root.tsx | 1 + apps/webapp/app/services/dashboardAgent.server.ts | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) rename apps/webapp/app/hooks/{useApiOrigin.ts => useDashboardAgentBaseUrl.ts} (65%) diff --git a/apps/webapp/app/components/dashboard-agent/DashboardAgentPanel.tsx b/apps/webapp/app/components/dashboard-agent/DashboardAgentPanel.tsx index c034aa0c7cc..4b03f76a948 100644 --- a/apps/webapp/app/components/dashboard-agent/DashboardAgentPanel.tsx +++ b/apps/webapp/app/components/dashboard-agent/DashboardAgentPanel.tsx @@ -5,7 +5,7 @@ import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "r import { AgentSpinner } from "~/components/primitives/Spinner"; import { useToast } from "~/components/primitives/Toast"; import { useAgentPageContext } from "~/hooks/useAgentPageContext"; -import { useApiOrigin } from "~/hooks/useApiOrigin"; +import { useDashboardAgentBaseUrl } from "~/hooks/useDashboardAgentBaseUrl"; import { useEnvironment } from "~/hooks/useEnvironment"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; @@ -104,7 +104,7 @@ export function DashboardAgentPanel({ const project = useProject(); const environment = useEnvironment(); const user = useUser(); - const apiOrigin = useApiOrigin(); + const apiOrigin = useDashboardAgentBaseUrl(); const location = useLocation(); const pageContext = useAgentPageContext(); const toast = useToast(); diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index 07f2122b64b..0b86f83b492 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -192,6 +192,7 @@ const EnvironmentSchema = z // standard chat.agent SDK flow. When unset, the live agent is disabled — the // conversation store / History still work, no chat can start. DASHBOARD_AGENT_SECRET_KEY: z.string().optional(), + DASHBOARD_AGENT_BASE_URL: z.string().optional(), // Pins agent sessions to a specific deployed version (paired with // --skip-promotion deploys); unset => the project env's current version. DASHBOARD_AGENT_VERSION: z.string().optional(), diff --git a/apps/webapp/app/hooks/useApiOrigin.ts b/apps/webapp/app/hooks/useDashboardAgentBaseUrl.ts similarity index 65% rename from apps/webapp/app/hooks/useApiOrigin.ts rename to apps/webapp/app/hooks/useDashboardAgentBaseUrl.ts index b26d0caff01..538f88ffe56 100644 --- a/apps/webapp/app/hooks/useApiOrigin.ts +++ b/apps/webapp/app/hooks/useDashboardAgentBaseUrl.ts @@ -1,8 +1,8 @@ import { useTypedRouteLoaderData } from "remix-typedjson"; import type { loader } from "../root"; -export function useApiOrigin() { +export function useDashboardAgentBaseUrl() { const routeMatch = useTypedRouteLoaderData("root"); - return routeMatch!.apiOrigin; + return routeMatch!.dashboardAgentBaseUrl; } diff --git a/apps/webapp/app/root.tsx b/apps/webapp/app/root.tsx index 43812d5653d..b1d38ef2ef7 100644 --- a/apps/webapp/app/root.tsx +++ b/apps/webapp/app/root.tsx @@ -120,6 +120,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { appEnv: env.APP_ENV, appOrigin: env.APP_ORIGIN, apiOrigin: env.API_ORIGIN ?? env.APP_ORIGIN, + dashboardAgentBaseUrl: env.DASHBOARD_AGENT_BASE_URL ?? "https://api.trigger.dev", triggerCliTag: env.TRIGGER_CLI_TAG, kapa, timezone, diff --git a/apps/webapp/app/services/dashboardAgent.server.ts b/apps/webapp/app/services/dashboardAgent.server.ts index 1c2e0b5f1cf..96b7eea73ce 100644 --- a/apps/webapp/app/services/dashboardAgent.server.ts +++ b/apps/webapp/app/services/dashboardAgent.server.ts @@ -49,7 +49,7 @@ const DASHBOARD_AGENT_UAT_TTL_SECONDS = 10 * 60; // The Trigger instance this webapp runs against — the same origin the agent // task calls back to (as the user) for its read tools. export function dashboardAgentApiOrigin(): string { - return env.API_ORIGIN ?? env.APP_ORIGIN; + return env.DASHBOARD_AGENT_BASE_URL ?? "https://api.trigger.dev"; } // Mint a short-lived, read-only delegated token for the signed-in user. Self