Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .server-changes/sessions-test-column.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
area: webapp
type: feature
---

Agent sessions started from the Test playground are now flagged with a real `Session.isTest` boolean instead of a `"playground"` tag, surfaced as a dedicated "Test" column (check icon) in the Sessions table on both the Sessions and Agent pages, plus a matching property on the session detail page. The legacy `"playground"` tag is hidden from the Tags display on pre-existing sessions.
19 changes: 17 additions & 2 deletions apps/webapp/app/components/sessions/v1/SessionsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ArrowRightIcon } from "@heroicons/react/20/solid";
import { CheckIcon } from "@heroicons/react/24/solid";
import { useLocation, useNavigation } from "@remix-run/react";
import { formatDuration } from "@trigger.dev/core/v3/utils/durations";
import { RunsIconExtraSmall } from "~/assets/icons/RunsIcon";
Expand Down Expand Up @@ -87,6 +88,7 @@ export function SessionsTable({
</TableHeaderCell>
<TableHeaderCell>Type</TableHeaderCell>
<TableHeaderCell>Agent ID</TableHeaderCell>
<TableHeaderCell>Test</TableHeaderCell>
<TableHeaderCell>Tags</TableHeaderCell>
<TableHeaderCell>Created</TableHeaderCell>
<TableHeaderCell>Duration</TableHeaderCell>
Expand All @@ -97,7 +99,7 @@ export function SessionsTable({
</TableHeader>
<TableBody>
{sessions.length === 0 ? (
<TableBlankRow colSpan={8}>
<TableBlankRow colSpan={9}>
<div className="flex items-center justify-center">
<Paragraph className="w-auto">
{hasFilters
Expand Down Expand Up @@ -144,6 +146,19 @@ export function SessionsTable({
<MiddleTruncate text={session.taskIdentifier} className="font-mono text-xs" />
</div>
</TableCell>
<TableCell to={sessionPath}>
<span className="sr-only">{session.isTest ? "Yes" : "No"}</span>
{session.isTest ? (
<CheckIcon
aria-hidden
className="size-4 text-charcoal-400 group-hover/table-row:text-text-bright"
/>
) : (
<span aria-hidden className="text-text-dimmed">
</span>
)}
</TableCell>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<TableCell to={sessionPath}>
{session.tags.length > 0 ? (
<div className="flex flex-wrap gap-1">
Expand All @@ -168,7 +183,7 @@ export function SessionsTable({
)}
{isLoading && (
<TableBlankRow
colSpan={8}
colSpan={9}
className="absolute left-0 top-0 flex h-full w-full items-center justify-center gap-2 bg-charcoal-900/90"
>
<Spinner /> <span className="text-text-dimmed">Loading…</span>
Expand Down
9 changes: 8 additions & 1 deletion apps/webapp/app/presenters/v3/SessionListPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { findDisplayableEnvironment } from "~/models/runtimeEnvironment.server";
import {
type SessionStatus,
SessionsRepository,
LEGACY_PLAYGROUND_TAG,
} from "~/services/sessionsRepository/sessionsRepository.server";
import { ServiceValidationError } from "~/v3/services/baseService.server";
import { findCurrentWorkerFromEnvironment } from "~/v3/models/workerDeployment.server";
Expand Down Expand Up @@ -225,7 +226,13 @@ export class SessionListPresenter {
externalId: session.externalId,
type: session.type,
taskIdentifier: session.taskIdentifier,
tags: session.tags ? [...session.tags].sort((a, b) => a.localeCompare(b)) : [],
isTest: session.isTest,
// Hide the legacy "playground" tag (pre-isTest sessions) from display.
tags: session.tags
? [...session.tags]
.filter((t) => t !== LEGACY_PLAYGROUND_TAG)
.sort((a, b) => a.localeCompare(b))
: [],
status,
closedAt: session.closedAt ? session.closedAt.toISOString() : undefined,
closedReason: session.closedReason ?? undefined,
Expand Down
9 changes: 8 additions & 1 deletion apps/webapp/app/presenters/v3/SessionPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { env } from "~/env.server";
import { findDisplayableEnvironment } from "~/models/runtimeEnvironment.server";
import { chatSnapshotStorageKey } from "~/services/realtime/chatSnapshot.server";
import { resolveSessionByIdOrExternalId } from "~/services/realtime/sessions.server";
import { LEGACY_PLAYGROUND_TAG } from "~/services/sessionsRepository/sessionsRepository.server";
import { logger } from "~/services/logger.server";
import { generatePresignedUrl } from "~/v3/objectStore.server";
import { runStore } from "~/v3/runStore.server";
Expand Down Expand Up @@ -177,7 +178,13 @@ export class SessionPresenter {
externalId: session.externalId,
type: session.type,
taskIdentifier: session.taskIdentifier,
tags: session.tags ? [...session.tags].sort((a, b) => a.localeCompare(b)) : [],
isTest: session.isTest,
// Hide the legacy "playground" tag (pre-isTest sessions) from display.
tags: session.tags
? [...session.tags]
.filter((t) => t !== LEGACY_PLAYGROUND_TAG)
.sort((a, b) => a.localeCompare(b))
: [],
metadata: session.metadata,
triggerConfig: session.triggerConfig,
streamBasinName: session.streamBasinName,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BoltIcon, BoltSlashIcon } from "@heroicons/react/20/solid";
import { BookOpenIcon } from "@heroicons/react/24/solid";
import { BookOpenIcon, CheckIcon } from "@heroicons/react/24/solid";
import { type MetaFunction } from "@remix-run/react";
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
import { useVirtualizer } from "@tanstack/react-virtual";
Expand Down Expand Up @@ -818,6 +818,19 @@ function OverviewTab({ session, status }: { session: LoadedSession; status: Sess
<span className="font-mono text-sm">{session.taskIdentifier}</span>
</Property.Value>
</Property.Item>
<Property.Item>
<Property.Label>Test</Property.Label>
<Property.Value>
<span className="sr-only">{session.isTest ? "Yes" : "No"}</span>
{session.isTest ? (
<CheckIcon aria-hidden className="size-4 text-text-dimmed" />
) : (
<span aria-hidden className="text-text-dimmed">
</span>
)}
</Property.Value>
</Property.Item>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
{session.currentRun ? (
<Property.Item>
<Property.Label>Current run</Property.Label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,10 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
type: "chat.agent",
taskIdentifier: agentSlug,
triggerConfig: triggerConfig as unknown as Prisma.InputJsonValue,
tags: ["playground"],
// Mark as a Test session — surfaced via the Test column in the
// Sessions table. Session tags stay empty; the triggered run still
// carries "playground:true" via triggerConfig.tags.
isTest: true,
projectId: project.id,
runtimeEnvironmentId: environment.id,
environmentType: environment.type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ function toSessionInsertArray(
session.expiresAt ? session.expiresAt.getTime() : null,
session.createdAt.getTime(),
session.updatedAt.getTime(),
session.isTest ?? false,
version.toString(),
isDeleted ? 1 : 0,
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export class ClickHouseSessionsRepository implements ISessionsRepository {
externalId: true,
type: true,
taskIdentifier: true,
isTest: true,
tags: true,
metadata: true,
closedAt: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ export type SessionsRepositoryOptions = {
export const SessionStatus = z.enum(["ACTIVE", "CLOSED", "EXPIRED"]);
export type SessionStatus = z.infer<typeof SessionStatus>;

/**
* Legacy marker tag for sessions created from the Test/playground before the
* `Session.isTest` boolean existed. New sessions set `isTest` instead; this tag
* is hidden from the Tags display so it doesn't surface on pre-isTest rows.
*/
export const LEGACY_PLAYGROUND_TAG = "playground";

const SessionListInputOptionsSchema = z.object({
organizationId: z.string(),
projectId: z.string(),
Expand Down Expand Up @@ -87,6 +94,7 @@ export type ListedSession = Prisma.SessionGetPayload<{
externalId: true;
type: true;
taskIdentifier: true;
isTest: true;
tags: true;
metadata: true;
closedAt: true;
Expand Down
2 changes: 2 additions & 0 deletions apps/webapp/test/sessionsReplicationService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ describe("SessionsReplicationService", () => {
},
tags: ["user:42", "plan:pro"],
metadata: { plan: "pro", seats: 3 },
isTest: true,
},
});

Expand Down Expand Up @@ -108,6 +109,7 @@ describe("SessionsReplicationService", () => {
environment_type: "DEVELOPMENT",
task_identifier: "my-agent",
tags: ["user:42", "plan:pro"],
is_test: 1,
_is_deleted: 0,
})
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- +goose Up
-- Existing rows default to 0 and are intentionally NOT backfilled: the Sessions
-- list reads isTest from Postgres (ClickHouse only supplies session IDs), so the
-- UI is correct without it. A backfill is only needed if a ClickHouse-side
-- isTest filter/aggregate over sessions_v1 is added later.
ALTER TABLE trigger_dev.sessions_v1
ADD COLUMN IF NOT EXISTS is_test UInt8 DEFAULT 0;

-- +goose Down
ALTER TABLE trigger_dev.sessions_v1
DROP COLUMN IF EXISTS is_test;
4 changes: 4 additions & 0 deletions internal-packages/clickhouse/src/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const SessionV1 = z.object({
expires_at: z.number().int().nullish(),
created_at: z.number().int(),
updated_at: z.number().int(),
is_test: z.boolean().default(false),
_version: z.string(),
_is_deleted: z.number().int().default(0),
});
Expand All @@ -43,6 +44,7 @@ export const SESSION_COLUMNS = [
"expires_at",
"created_at",
"updated_at",
"is_test",
"_version",
"_is_deleted",
] as const;
Expand Down Expand Up @@ -70,6 +72,7 @@ export type SessionFieldTypes = {
expires_at: number | null;
created_at: number;
updated_at: number;
is_test: boolean;
_version: string;
_is_deleted: number;
};
Expand All @@ -95,6 +98,7 @@ export type SessionInsertArray = [
expires_at: number | null,
created_at: number,
updated_at: number,
is_test: boolean,
_version: string,
_is_deleted: number,
];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Session" ADD COLUMN "isTest" BOOLEAN NOT NULL DEFAULT false;
4 changes: 4 additions & 0 deletions internal-packages/database/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,10 @@ model Session {
/// (chatId, messages, trigger) are merged at trigger time.
triggerConfig Json

/// Whether this session was created from the Test/playground UI rather
/// than by a real chat.agent() trigger. Mirrors TaskRun.isTest.
isTest Boolean @default(false)

tags String[] @default([])
metadata Json?

Expand Down
Loading