From c2085e6cc67fa83fddebc59bde942836b6eac99a Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:31:52 +0000 Subject: [PATCH 01/25] feat(dashboard): link git sha and ref to GitHub on settings page (#3034) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the git SHA and git ref in the org settings sidebar clickable links to GitHub — SHA links to the commit, ref links to the branch/tag. --- .../OrganizationSettingsSideMenu.tsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx b/apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx index 8758e181f..9069620c9 100644 --- a/apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx +++ b/apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx @@ -141,7 +141,14 @@ export function OrganizationSettingsSideMenu({
- {buildInfo.gitRefName} + + {buildInfo.gitRefName} +
)} @@ -149,7 +156,14 @@ export function OrganizationSettingsSideMenu({
- {buildInfo.gitSha.slice(0, 9)} + + {buildInfo.gitSha.slice(0, 9)} +
)} From 062bcaece8ad7f1046097977efab18c1fcc0ee42 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Thu, 12 Feb 2026 16:14:25 +0000 Subject: [PATCH 02/25] feat(mcp): add timeout parameter to wait_for_run_to_complete tool (#3035) ## Summary - Adds an optional `timeoutInSeconds` parameter (default 60s) to the `wait_for_run_to_complete` MCP tool - If the run doesn't complete within the timeout, returns the current run state instead of blocking indefinitely - Uses `AbortSignal.timeout()` combined with the existing MCP signal Fixes #3032 --- .changeset/mcp-wait-timeout.md | 5 +++ packages/cli-v3/src/mcp/config.ts | 2 +- packages/cli-v3/src/mcp/schemas.ts | 11 +++++++ packages/cli-v3/src/mcp/tools/runs.ts | 45 +++++++++++++++++++-------- 4 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 .changeset/mcp-wait-timeout.md diff --git a/.changeset/mcp-wait-timeout.md b/.changeset/mcp-wait-timeout.md new file mode 100644 index 000000000..02d6c9823 --- /dev/null +++ b/.changeset/mcp-wait-timeout.md @@ -0,0 +1,5 @@ +--- +"trigger.dev": patch +--- + +Add optional `timeoutInSeconds` parameter to the `wait_for_run_to_complete` MCP tool. Defaults to 60 seconds. If the run doesn't complete within the timeout, the current state of the run is returned instead of waiting indefinitely. diff --git a/packages/cli-v3/src/mcp/config.ts b/packages/cli-v3/src/mcp/config.ts index 206b5910f..5a1ec45cb 100644 --- a/packages/cli-v3/src/mcp/config.ts +++ b/packages/cli-v3/src/mcp/config.ts @@ -68,7 +68,7 @@ export const toolsMetadata = { name: "wait_for_run_to_complete", title: "Wait for Run to Complete", description: - "Wait for a run to complete. The run ID is the ID of the run that was triggered. It starts with run_", + "Wait for a run to complete. The run ID is the ID of the run that was triggered. It starts with run_. Has an optional timeoutInSeconds parameter (default 60s) - if the run doesn't complete within that time, the current state of the run will be returned.", }, cancel_run: { name: "cancel_run", diff --git a/packages/cli-v3/src/mcp/schemas.ts b/packages/cli-v3/src/mcp/schemas.ts index b98faca0d..8afb10f38 100644 --- a/packages/cli-v3/src/mcp/schemas.ts +++ b/packages/cli-v3/src/mcp/schemas.ts @@ -123,6 +123,17 @@ export const CommonRunsInput = CommonProjectsInput.extend({ export type CommonRunsInput = z.output; +export const WaitForRunInput = CommonRunsInput.extend({ + timeoutInSeconds: z + .number() + .describe( + "The maximum time in seconds to wait for the run to complete. If the run doesn't complete within this time, the current state of the run will be returned. Defaults to 60 seconds." + ) + .default(60), +}); + +export type WaitForRunInput = z.output; + export const GetRunDetailsInput = CommonRunsInput.extend({ maxTraceLines: z .number() diff --git a/packages/cli-v3/src/mcp/tools/runs.ts b/packages/cli-v3/src/mcp/tools/runs.ts index 13fe601da..056544e3c 100644 --- a/packages/cli-v3/src/mcp/tools/runs.ts +++ b/packages/cli-v3/src/mcp/tools/runs.ts @@ -1,7 +1,7 @@ import { AnyRunShape } from "@trigger.dev/core/v3"; import { toolsMetadata } from "../config.js"; import { formatRun, formatRunList, formatRunShape, formatRunTrace } from "../formatters.js"; -import { CommonRunsInput, GetRunDetailsInput, ListRunsInput } from "../schemas.js"; +import { CommonRunsInput, GetRunDetailsInput, ListRunsInput, WaitForRunInput } from "../schemas.js"; import { respondWithError, toolHandler } from "../utils.js"; export const getRunDetailsTool = { @@ -65,8 +65,8 @@ export const waitForRunToCompleteTool = { name: toolsMetadata.wait_for_run_to_complete.name, title: toolsMetadata.wait_for_run_to_complete.title, description: toolsMetadata.wait_for_run_to_complete.description, - inputSchema: CommonRunsInput.shape, - handler: toolHandler(CommonRunsInput.shape, async (input, { ctx, signal }) => { + inputSchema: WaitForRunInput.shape, + handler: toolHandler(WaitForRunInput.shape, async (input, { ctx, signal }) => { ctx.logger?.log("calling wait_for_run_to_complete", { input }); if (ctx.options.devOnly && input.environment !== "dev") { @@ -87,20 +87,35 @@ export const waitForRunToCompleteTool = { branch: input.branch, }); - const runSubscription = apiClient.subscribeToRun(input.runId, { signal }); + const timeoutMs = input.timeoutInSeconds * 1000; + const timeoutSignal = AbortSignal.timeout(timeoutMs); + const combinedSignal = signal + ? AbortSignal.any([signal, timeoutSignal]) + : timeoutSignal; + + const runSubscription = apiClient.subscribeToRun(input.runId, { signal: combinedSignal }); const readableStream = runSubscription.getReader(); let run: AnyRunShape | null = null; + let timedOut = false; - while (true) { - const { done, value } = await readableStream.read(); - if (done) { - break; + try { + while (true) { + const { done, value } = await readableStream.read(); + if (done) { + break; + } + run = value; + + if (value.isCompleted) { + break; + } } - run = value; - - if (value.isCompleted) { - break; + } catch (error) { + if (timeoutSignal.aborted) { + timedOut = true; + } else { + throw error; } } @@ -108,8 +123,12 @@ export const waitForRunToCompleteTool = { return respondWithError("Run not found"); } + const prefix = timedOut + ? `Timed out after ${input.timeoutInSeconds}s. Returning current run state:\n\n` + : ""; + return { - content: [{ type: "text", text: formatRunShape(run) }], + content: [{ type: "text", text: prefix + formatRunShape(run) }], }; }), }; From bc0d1ff59a8152b303ca7f30fa7b2be0b98646c5 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Thu, 12 Feb 2026 17:48:02 +0000 Subject: [PATCH 03/25] Metrics dashboards (#3019) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary - Implemented metrics dashboards with a built-in dashboard and custom dashboards - Added a "Big number” display type What changed - New data format for metric layouts and saving/editing layouts (editing, saving, cancel revert) - QueryWidget usable on Query page and Metrics dashboards - Time filtering, auto-reloading and timeBucket() auto-bin support - Filters added to metrics; widget popover/improved history and blank states - Side menu: - Metrics/Insights section with icons, colors, padding, collapsible behavior and reordering of custom dashboards - Move action logic into service for reuse and API querying; refactor reordering for reuse --- Open with Devin --------- Co-authored-by: James Ritchie --- .vscode/settings.json | 1 - apps/webapp/app/components/AlphaBadge.tsx | 29 + .../app/components/code/AIQueryInput.tsx | 149 +- .../app/components/code/ChartConfigPanel.tsx | 206 ++- .../app/components/code/QueryResultsChart.tsx | 359 ++-- .../webapp/app/components/code/TSQLEditor.tsx | 43 +- .../app/components/code/TSQLResultsTable.tsx | 124 +- .../webapp/app/components/code/chartColors.ts | 183 +++ .../components/code/tsql/tsqlCompletion.ts | 10 + .../app/components/layout/AppLayout.tsx | 2 +- .../app/components/logs/LogsTaskFilter.tsx | 4 +- .../app/components/metrics/QueryWidget.tsx | 496 ++++++ .../app/components/metrics/QueuesFilter.tsx | 212 +++ .../metrics/SaveToDashboardDialog.tsx | 177 ++ .../app/components/metrics/ScopeFilter.tsx | 64 + .../app/components/metrics/TitleWidget.tsx | 125 ++ .../navigation/DashboardDialogs.tsx | 255 +++ .../components/navigation/DashboardList.tsx | 123 ++ .../app/components/navigation/SideMenu.tsx | 691 ++++---- .../components/navigation/SideMenuItem.tsx | 112 +- .../components/navigation/SideMenuSection.tsx | 28 +- .../components/navigation/TreeConnectors.tsx | 29 + .../components/navigation/sideMenuTypes.ts | 7 + .../navigation/useReorderableList.ts | 129 ++ .../components/primitives/AppliedFilter.tsx | 16 +- .../app/components/primitives/ClientTabs.tsx | 3 +- .../app/components/primitives/FormButtons.tsx | 4 +- .../primitives/LoadingBarDivider.tsx | 6 +- .../app/components/primitives/Popover.tsx | 27 +- .../app/components/primitives/Resizable.tsx | 6 +- .../app/components/primitives/Tooltip.tsx | 2 +- .../primitives/charts/BigNumber.tsx | 46 - .../primitives/charts/BigNumberCard.tsx | 171 ++ .../app/components/primitives/charts/Card.tsx | 17 +- .../components/primitives/charts/ChartBar.tsx | 41 +- .../primitives/charts/ChartLegendCompound.tsx | 56 +- .../primitives/charts/ChartLine.tsx | 28 +- .../app/components/query/QueryEditor.tsx | 1457 +++++++++++++++++ .../app/components/runs/v3/SharedFilters.tsx | 103 +- .../app/components/runs/v3/TaskRunStatus.tsx | 39 + apps/webapp/app/env.server.ts | 4 + apps/webapp/app/hooks/useDashboardEditor.ts | 515 ++++++ apps/webapp/app/hooks/useElementVisibility.ts | 35 + apps/webapp/app/hooks/useInterval.ts | 63 + apps/webapp/app/hooks/useOrganizations.ts | 26 + apps/webapp/app/hooks/useRevalidateOnParam.ts | 57 + .../app/models/runtimeEnvironment.server.ts | 23 + .../presenters/v3/BuiltInDashboards.server.ts | 225 +++ .../presenters/v3/LimitsPresenter.server.ts | 45 + .../v3/MetricDashboardPresenter.server.ts | 123 ++ .../route.tsx | 24 +- .../route.tsx | 295 ++++ .../route.tsx | 772 +++++++++ .../AITabContent.tsx | 14 +- .../ExamplesContent.tsx | 13 + .../QueryHistoryPopover.tsx | 51 +- .../TRQLGuideContent.tsx | 5 + .../route.tsx | 960 +---------- .../_app.orgs.$organizationSlug/route.tsx | 51 +- apps/webapp/app/routes/resources.metric.tsx | 283 ++++ ...vParam.dashboards.$dashboardId.widgets.tsx | 492 ++++++ ...tParam.env.$envParam.dashboards.create.tsx | 84 + ...ces.orgs.$organizationSlug.select-plan.tsx | 69 +- .../routes/resources.preferences.sidemenu.tsx | 47 +- .../app/routes/storybook.charts/route.tsx | 8 +- .../app/services/clickhouseInstance.server.ts | 30 +- .../services/dashboardPreferences.server.ts | 95 +- .../app/services/queryService.server.ts | 194 ++- apps/webapp/app/tailwind.css | 40 + apps/webapp/app/utils/pathBuilder.ts | 22 +- apps/webapp/app/v3/querySchemas.ts | 5 + .../app/v3/services/aiQueryService.server.ts | 57 +- apps/webapp/package.json | 4 +- apps/webapp/tailwind.config.js | 6 + .../clickhouse/src/client/tsql.ts | 10 +- .../migration.sql | 25 + .../migration.sql | 3 + .../migration.sql | 5 + .../migration.sql | 2 + .../database/prisma/schema.prisma | 147 +- internal-packages/tsql/src/index.ts | 42 +- .../tsql/src/query/printer.test.ts | 285 +++- internal-packages/tsql/src/query/printer.ts | 117 +- .../tsql/src/query/printer_context.ts | 47 +- internal-packages/tsql/src/query/schema.ts | 18 + .../tsql/src/query/time_buckets.test.ts | 181 ++ .../tsql/src/query/time_buckets.ts | 86 + internal-packages/tsql/src/query/validator.ts | 7 +- pnpm-lock.yaml | 64 +- 89 files changed, 9408 insertions(+), 1948 deletions(-) create mode 100644 apps/webapp/app/components/code/chartColors.ts create mode 100644 apps/webapp/app/components/metrics/QueryWidget.tsx create mode 100644 apps/webapp/app/components/metrics/QueuesFilter.tsx create mode 100644 apps/webapp/app/components/metrics/SaveToDashboardDialog.tsx create mode 100644 apps/webapp/app/components/metrics/ScopeFilter.tsx create mode 100644 apps/webapp/app/components/metrics/TitleWidget.tsx create mode 100644 apps/webapp/app/components/navigation/DashboardDialogs.tsx create mode 100644 apps/webapp/app/components/navigation/DashboardList.tsx create mode 100644 apps/webapp/app/components/navigation/TreeConnectors.tsx create mode 100644 apps/webapp/app/components/navigation/sideMenuTypes.ts create mode 100644 apps/webapp/app/components/navigation/useReorderableList.ts delete mode 100644 apps/webapp/app/components/primitives/charts/BigNumber.tsx create mode 100644 apps/webapp/app/components/primitives/charts/BigNumberCard.tsx create mode 100644 apps/webapp/app/components/query/QueryEditor.tsx create mode 100644 apps/webapp/app/hooks/useDashboardEditor.ts create mode 100644 apps/webapp/app/hooks/useElementVisibility.ts create mode 100644 apps/webapp/app/hooks/useInterval.ts create mode 100644 apps/webapp/app/hooks/useRevalidateOnParam.ts create mode 100644 apps/webapp/app/presenters/v3/BuiltInDashboards.server.ts create mode 100644 apps/webapp/app/presenters/v3/MetricDashboardPresenter.server.ts create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.metrics.$dashboardKey/route.tsx create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.metrics.custom.$dashboardId/route.tsx create mode 100644 apps/webapp/app/routes/resources.metric.tsx create mode 100644 apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx create mode 100644 apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.create.tsx create mode 100644 internal-packages/database/prisma/migrations/20260201130503_metrics_dashboard_table_created/migration.sql create mode 100644 internal-packages/database/prisma/migrations/20260202044337_metrics_dashboard_description/migration.sql create mode 100644 internal-packages/database/prisma/migrations/20260202100000_add_friendlyid_to_metrics_dashboard/migration.sql create mode 100644 internal-packages/database/prisma/migrations/20260211120000_make_metrics_dashboard_owner_nullable/migration.sql create mode 100644 internal-packages/tsql/src/query/time_buckets.test.ts create mode 100644 internal-packages/tsql/src/query/time_buckets.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 382a5ae62..fd9f3dcde 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,5 @@ "packages/cli-v3/e2e": true }, "vitest.disableWorkspaceWarning": true, - "typescript.experimental.useTsgo": true, "chat.agent.maxRequests": 10000 } diff --git a/apps/webapp/app/components/AlphaBadge.tsx b/apps/webapp/app/components/AlphaBadge.tsx index 58da1a994..0a1c4a7fc 100644 --- a/apps/webapp/app/components/AlphaBadge.tsx +++ b/apps/webapp/app/components/AlphaBadge.tsx @@ -30,3 +30,32 @@ export function AlphaTitle({ children }: { children: React.ReactNode }) { ); } + +export function BetaBadge({ + inline = false, + className, +}: { + inline?: boolean; + className?: string; +}) { + return ( + + Beta + + } + content="This feature is in Beta." + disableHoverableContent + /> + ); +} + +export function BetaTitle({ children }: { children: React.ReactNode }) { + return ( + <> + {children} + + + ); +} diff --git a/apps/webapp/app/components/code/AIQueryInput.tsx b/apps/webapp/app/components/code/AIQueryInput.tsx index 38d0c9b21..0775ec2c2 100644 --- a/apps/webapp/app/components/code/AIQueryInput.tsx +++ b/apps/webapp/app/components/code/AIQueryInput.tsx @@ -1,7 +1,13 @@ -import { PencilSquareIcon, PlusIcon, SparklesIcon } from "@heroicons/react/20/solid"; +import { CheckIcon, PencilSquareIcon, PlusIcon, XMarkIcon } from "@heroicons/react/20/solid"; import { AnimatePresence, motion } from "framer-motion"; import { Suspense, lazy, useCallback, useEffect, useRef, useState } from "react"; -import { AISparkleIcon } from "~/assets/icons/AISparkleIcon"; +import { Button } from "~/components/primitives/Buttons"; +import { Spinner } from "~/components/primitives/Spinner"; +import { useEnvironment } from "~/hooks/useEnvironment"; +import { useOrganization } from "~/hooks/useOrganizations"; +import { useProject } from "~/hooks/useProject"; +import type { AITimeFilter } from "~/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/types"; +import { cn } from "~/utils/cn"; // Lazy load streamdown components to avoid SSR issues const StreamdownRenderer = lazy(() => @@ -13,13 +19,6 @@ const StreamdownRenderer = lazy(() => ), })) ); -import { Button } from "~/components/primitives/Buttons"; -import { Spinner } from "~/components/primitives/Spinner"; -import { useEnvironment } from "~/hooks/useEnvironment"; -import { useOrganization } from "~/hooks/useOrganizations"; -import { useProject } from "~/hooks/useProject"; -import type { AITimeFilter } from "~/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/types"; -import { cn } from "~/utils/cn"; type StreamEventType = | { type: "thinking"; content: string } @@ -179,21 +178,7 @@ export function AIQueryInput({ setThinking((prev) => prev + event.content); break; case "tool_call": - if (event.tool === "setTimeFilter") { - setThinking((prev) => { - if (prev.trimEnd().endsWith("Setting time filter...")) { - return prev; - } - return prev + `\nSetting time filter...\n`; - }); - } else { - setThinking((prev) => { - if (prev.trimEnd().endsWith("Validating query...")) { - return prev; - } - return prev + `\nValidating query...\n`; - }); - } + // Tool calls are handled silently — no UI text needed break; case "time_filter": // Apply time filter immediately when the AI sets it @@ -262,13 +247,13 @@ export function AIQueryInput({ }, [error]); return ( -
+
{/* Gradient border wrapper like the schedules AI input */}
-
+