diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.ts b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.ts index 8173aae5761..2b57c80b414 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.ts +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.ts @@ -12,6 +12,7 @@ import { findProjectBySlug } from "~/models/project.server"; import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server"; import { dashboardAgentApiOrigin, + dashboardAgentUserApiOrigin, mintDashboardAgentUserActorToken, resolveDashboardAgentRepoSnapshot, } from "~/services/dashboardAgent.server"; @@ -92,6 +93,7 @@ export async function action({ request, params }: ActionFunctionArgs) { if (!upstreamPath) return json({ error: "Not found" }, { status: 404 }); const apiOrigin = dashboardAgentApiOrigin(); + const userApiOrigin = dashboardAgentUserApiOrigin(); const url = new URL(request.url); const upstreamUrl = `${apiOrigin.replace(/\/$/, "")}/${upstreamPath}${url.search}`; @@ -167,7 +169,7 @@ export async function action({ request, params }: ActionFunctionArgs) { parsed.payload.metadata = { ...pickAgentClientMetadata(parsed.payload.metadata), userActorToken, - apiOrigin, + apiOrigin: userApiOrigin, projectRef: project.externalRef, // Server-owned: the eval opt-out and every tenancy check key on these. organizationId: project.organizationId, diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.ts b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.ts index 89677bf43d5..3564b42849d 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.ts +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.ts @@ -42,7 +42,7 @@ import { submitDashboardAgentWatch, } from "~/services/dashboardAgentWatches.server"; import { - dashboardAgentApiOrigin, + dashboardAgentUserApiOrigin, dashboardAgentWakeFeedCounter, isDashboardAgentConfigured, mintDashboardAgentToken, @@ -345,7 +345,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { userActorToken: await mintDashboardAgentUserActorToken(userId, { environmentId: runtimeEnv.id, }), - apiOrigin: dashboardAgentApiOrigin(), + apiOrigin: dashboardAgentUserApiOrigin(), projectRef: project.externalRef, // Server-owned, like the `in` proxy: the eval opt-out and every tenancy check // key on these, so the client can't set them at all. diff --git a/apps/webapp/app/services/dashboardAgent.server.ts b/apps/webapp/app/services/dashboardAgent.server.ts index 96b7eea73ce..5ff460ae33f 100644 --- a/apps/webapp/app/services/dashboardAgent.server.ts +++ b/apps/webapp/app/services/dashboardAgent.server.ts @@ -46,12 +46,14 @@ export const DASHBOARD_AGENT_UAT_CAP = [ // the agent's run payload expires quickly. 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.DASHBOARD_AGENT_BASE_URL ?? "https://api.trigger.dev"; } +export function dashboardAgentUserApiOrigin(): string { + return env.API_ORIGIN ?? env.APP_ORIGIN; +} + // Mint a short-lived, read-only delegated token for the signed-in user. Self // service from the dashboard session (never a PAT), so a user can only ever // mint a token for themselves. The `in` proxy injects this into the turn's diff --git a/apps/webapp/app/services/dashboardAgentWatchInvestigate.server.ts b/apps/webapp/app/services/dashboardAgentWatchInvestigate.server.ts index 96ae8444f19..53277a91400 100644 --- a/apps/webapp/app/services/dashboardAgentWatchInvestigate.server.ts +++ b/apps/webapp/app/services/dashboardAgentWatchInvestigate.server.ts @@ -10,6 +10,7 @@ import { ApiClient } from "@trigger.dev/core/v3"; import { type AuthenticatedEnvironment } from "~/services/apiAuth.server"; import { dashboardAgentApiOrigin, + dashboardAgentUserApiOrigin, mintDashboardAgentUserActorToken, } from "~/services/dashboardAgent.server"; import { dashboardAgentEnvironmentAddress } from "~/services/dashboardAgentEnvironmentAddress.server"; @@ -57,6 +58,7 @@ export async function kickWatchInvestigation(params: { if (!accessToken) throw new Error("DASHBOARD_AGENT_SECRET_KEY is not set"); const apiOrigin = dashboardAgentApiOrigin(); + const userApiOrigin = dashboardAgentUserApiOrigin(); // The watch's immutable tenancy plus the delegated token that lets the turn read. const metadata = { userId: watch.userId, @@ -65,7 +67,7 @@ export async function kickWatchInvestigation(params: { environmentId: watch.environmentId, projectRef: watch.projectRef ?? environment.project.externalRef, ...dashboardAgentEnvironmentAddress(environment), - apiOrigin, + apiOrigin: userApiOrigin, userActorToken: await mintDashboardAgentUserActorToken(watch.userId, { environmentId: watch.environmentId, }), diff --git a/apps/webapp/app/services/dashboardAgentWatches.server.ts b/apps/webapp/app/services/dashboardAgentWatches.server.ts index ceb5309304e..e341f8ee51d 100644 --- a/apps/webapp/app/services/dashboardAgentWatches.server.ts +++ b/apps/webapp/app/services/dashboardAgentWatches.server.ts @@ -56,6 +56,7 @@ import { authIncludeWithParent, toAuthenticated } from "~/models/runtimeEnvironm import { isReportKey } from "~/presenters/v3/reports/report-registry"; import { dashboardAgentApiOrigin, + dashboardAgentUserApiOrigin, isDashboardAgentConfigured as isDashboardAgentConfiguredDefault, } from "~/services/dashboardAgent.server"; import { dashboardAgentDb } from "~/services/dashboardAgentDb.server"; @@ -1043,11 +1044,12 @@ export async function scheduleWatchTick(params: { if (!accessToken) throw new Error("DASHBOARD_AGENT_SECRET_KEY is not set"); const apiOrigin = dashboardAgentApiOrigin(); + const userApiOrigin = dashboardAgentUserApiOrigin(); const client = new TriggerClient({ baseURL: apiOrigin, accessToken }); await client.tasks.trigger( WATCH_TASK_ID, - { watchId: params.watchId, token: params.token, apiOrigin, tick: params.tick }, + { watchId: params.watchId, token: params.token, apiOrigin: userApiOrigin, tick: params.tick }, { delay: `${params.delayMinutes}m`, // Keyed on the generation the payload carries, so a retried schedule can't double-tick. @@ -1138,6 +1140,7 @@ export async function scheduleWatchBatchTick(params: { if (!accessToken) throw new Error("DASHBOARD_AGENT_SECRET_KEY is not set"); const apiOrigin = dashboardAgentApiOrigin(); + const userApiOrigin = dashboardAgentUserApiOrigin(); const client = new TriggerClient({ baseURL: apiOrigin, accessToken }); const token = await mintDashboardAgentWatchBatchToken({ environmentId: params.environmentId, @@ -1149,7 +1152,7 @@ export async function scheduleWatchBatchTick(params: { { environmentId: params.environmentId, cadenceMinutes: params.cadenceMinutes, - apiOrigin, + apiOrigin: userApiOrigin, token, epoch: params.epoch, tick: params.tick, @@ -1173,6 +1176,7 @@ export async function scheduleWatchDelivery(watch: { id: string; expiresAt: Date if (!accessToken) throw new Error("DASHBOARD_AGENT_SECRET_KEY is not set"); const apiOrigin = dashboardAgentApiOrigin(); + const userApiOrigin = dashboardAgentUserApiOrigin(); const client = new TriggerClient({ baseURL: apiOrigin, accessToken }); const token = await mintDashboardAgentWatchToken({ watchId: watch.id, @@ -1181,7 +1185,7 @@ export async function scheduleWatchDelivery(watch: { id: string; expiresAt: Date await client.tasks.trigger( WATCH_TASK_ID, - { watchId: watch.id, token, apiOrigin, tick: 0, deliverOnly: true }, + { watchId: watch.id, token, apiOrigin: userApiOrigin, tick: 0, deliverOnly: true }, { idempotencyKey: `watch:${watch.id}:deliver`, idempotencyKeyTTL: "10m", diff --git a/apps/webapp/test/dashboardAgentClientMetadata.test.ts b/apps/webapp/test/dashboardAgentClientMetadata.test.ts index da4e9c10a8b..b0c065d03ce 100644 --- a/apps/webapp/test/dashboardAgentClientMetadata.test.ts +++ b/apps/webapp/test/dashboardAgentClientMetadata.test.ts @@ -27,6 +27,7 @@ vi.mock("~/models/runtimeEnvironment.server", () => ({ })); vi.mock("~/services/dashboardAgent.server", () => ({ dashboardAgentApiOrigin: () => "https://api.trigger.dev", + dashboardAgentUserApiOrigin: () => "https://api.trigger.dev", isDashboardAgentConfigured: () => true, mintDashboardAgentToken: async () => "pat_public", mintDashboardAgentUserActorToken: async () => "tr_uat_real", diff --git a/apps/webapp/test/dashboardAgentCreateChatOrdering.test.ts b/apps/webapp/test/dashboardAgentCreateChatOrdering.test.ts index a30c0a9d945..d464900307b 100644 --- a/apps/webapp/test/dashboardAgentCreateChatOrdering.test.ts +++ b/apps/webapp/test/dashboardAgentCreateChatOrdering.test.ts @@ -35,7 +35,8 @@ vi.mock("~/models/runtimeEnvironment.server", () => ({ findEnvironmentBySlug: mocks.findEnvironmentBySlug, })); vi.mock("~/services/dashboardAgent.server", () => ({ - dashboardAgentApiOrigin: () => "https://api.trigger.dev", + dashboardAgentApiOrigin: () => "https://agent.trigger.dev", + dashboardAgentUserApiOrigin: () => "https://api.trigger.dev", isDashboardAgentConfigured: () => true, mintDashboardAgentToken: mocks.mintPublicToken, mintDashboardAgentUserActorToken: mocks.mintUserActorToken, diff --git a/apps/webapp/test/dashboardAgentForeignChat.test.ts b/apps/webapp/test/dashboardAgentForeignChat.test.ts index 6bcaf3c5d6a..2fb0785fdcd 100644 --- a/apps/webapp/test/dashboardAgentForeignChat.test.ts +++ b/apps/webapp/test/dashboardAgentForeignChat.test.ts @@ -26,6 +26,7 @@ vi.mock("~/models/project.server", () => ({ vi.mock("~/models/runtimeEnvironment.server", () => ({ findEnvironmentBySlug: vi.fn() })); vi.mock("~/services/dashboardAgent.server", () => ({ dashboardAgentApiOrigin: () => "https://api.trigger.dev", + dashboardAgentUserApiOrigin: () => "https://api.trigger.dev", isDashboardAgentConfigured: () => true, mintDashboardAgentToken: vi.fn(), mintDashboardAgentUserActorToken: vi.fn(), diff --git a/apps/webapp/test/dashboardAgentInProxyMintFailure.test.ts b/apps/webapp/test/dashboardAgentInProxyMintFailure.test.ts index fb24ef87b45..3304e631d6e 100644 --- a/apps/webapp/test/dashboardAgentInProxyMintFailure.test.ts +++ b/apps/webapp/test/dashboardAgentInProxyMintFailure.test.ts @@ -25,6 +25,7 @@ vi.mock("~/models/runtimeEnvironment.server", () => ({ })); vi.mock("~/services/dashboardAgent.server", () => ({ dashboardAgentApiOrigin: () => "https://api.trigger.dev", + dashboardAgentUserApiOrigin: () => "https://api.trigger.dev", mintDashboardAgentUserActorToken: mocks.mint, resolveDashboardAgentRepoSnapshot: async () => null, })); diff --git a/apps/webapp/test/dashboardAgentWatchInvestigate.test.ts b/apps/webapp/test/dashboardAgentWatchInvestigate.test.ts index 599f3ab6d8c..2ebf04f02c9 100644 --- a/apps/webapp/test/dashboardAgentWatchInvestigate.test.ts +++ b/apps/webapp/test/dashboardAgentWatchInvestigate.test.ts @@ -46,6 +46,7 @@ vi.mock("~/services/dashboardAgentWatches.server", () => ({ const mints = vi.hoisted(() => [] as Array<{ userId: string; environmentId?: string }>); vi.mock("~/services/dashboardAgent.server", () => ({ dashboardAgentApiOrigin: () => "https://api.example.com", + dashboardAgentUserApiOrigin: () => "https://api.example.com", dashboardAgentEnvironmentName: (type: string | undefined) => type === "PRODUCTION" ? "prod" : undefined, mintDashboardAgentUserActorToken: async (