feat(webapp): agent-view dashboard for chat.agent runs (3/4)#3545
Conversation
WalkthroughThis pull request introduces agent view functionality for Trigger.dev, enabling users to inspect agent conversations in run details and test agents interactively via a new playground. It adds session-backed realtime message streaming, a comprehensive dashboard for agent analytics, and supporting infrastructure for session and conversation management. The changes span agent message rendering, session persistence, playground chat interface, ClickHouse-backed sparkline analytics, and realtime SSE streaming endpoints. Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
a60187c to
172b7c3
Compare
ecfac76 to
7ee523e
Compare
172b7c3 to
a63e60a
Compare
7ee523e to
fdc61c6
Compare
a63e60a to
ce861fb
Compare
fdc61c6 to
96700b1
Compare
ce861fb to
dc686d8
Compare
96700b1 to
482d752
Compare
dc686d8 to
6c3490c
Compare
482d752 to
920e876
Compare
6c3490c to
9d26e60
Compare
920e876 to
d96e2f7
Compare
9d26e60 to
b971910
Compare
d96e2f7 to
3256f42
Compare
b971910 to
fa95095
Compare
3256f42 to
220b33c
Compare
fa95095 to
dc95b98
Compare
220b33c to
067109f
Compare
dc95b98 to
abb2cab
Compare
067109f to
c1f6db7
Compare
abb2cab to
3233503
Compare
c1f6db7 to
f75bcd8
Compare
3233503 to
3b68877
Compare
f75bcd8 to
40a3dff
Compare
3b68877 to
cac4fc5
Compare
c8b5507 to
8b6fcfd
Compare
5c937df to
f5e3067
Compare
8b6fcfd to
353fbf1
Compare
f5e3067 to
6a1e9af
Compare
353fbf1 to
9a8b726
Compare
6a1e9af to
04e1747
Compare
9a8b726 to
7690836
Compare
04e1747 to
2a7d030
Compare
…3542) ## Summary A `/sessions` dashboard for inspecting durable Sessions, an `AGENT` / `SCHEDULED` task-kind filter for the runs list, and the server-side hardening (rate-limit exemption for packets, retry-with-backoff on stream appends, typed too-large-chunk error) that the `chat.agent` runtime in #3543 needs. Builds on the Sessions primitive shipped in #3417. ## Design The Sessions list + detail routes mirror the run inspector pattern. `TaskTriggerSource` gains `AGENT` and `SCHEDULED` values, persisted on `BackgroundWorker.taskKind` and `TaskRun.taskKind` (plus a matching Clickhouse column), so the runs list can filter by kind. New `@trigger.dev/core` modules — `sessionStreams`, `inputStreams`, a `sessionStreamInstance` for realtime streams, and the `realtime-streams-api` / `session-streams-api` surfaces — expose the typed shapes that chat.agent will use to drive `session.out`. `ChatChunkTooLargeError` lets the runtime drop oversized chunks with a typed surface instead of failing the run. `s2Append` retries transient failures with exponential backoff. `/api/v[12]/packets/*` is exempt from customer rate limits so chat snapshot reads and writes don't get throttled under load. ## Stack Part of a 4-PR stack. Merge bottom-up. 1. **This PR** (#3542) → `main` 2. #3543 → #3542 — `chat.agent` runtime + browser transport 3. #3545 → #3543 — agent-view dashboard 4. #3546 → #3545 — ai-chat reference + MCP tooling Replaces #3173 (closed). <!-- GitButler Footer Boundary Top --> --- This is **part 5 of 5 in a stack** made with GitButler: - <kbd> 5 </kbd> #3612 - <kbd> 4 </kbd> #3546 - <kbd> 3 </kbd> #3545 - <kbd> 2 </kbd> #3543 - <kbd> 1 </kbd> #3542 👈 <!-- GitButler Footer Boundary Bottom -->
7690836 to
535245f
Compare
2a7d030 to
5fa5d3d
Compare
535245f to
f8acef8
Compare
5fa5d3d to
f8d5198
Compare
f8acef8 to
16720a5
Compare
f8d5198 to
a08c3a4
Compare
a08c3a4 to
dc82bb6
Compare
dc82bb6 to
12b4525
Compare
Dashboard surfaces for inspecting and debugging chat.agent runs. Depends on the Sessions primitive (L1) and chat.agent runtime (L2+L3). Run inspector — chat-aware: - AgentView + AgentMessageView (run inspector tab for chat.agent runs) - AIChatMessages + AISpanDetails + types.ts (per-span chat message rendering, tool-call/tool-output handling) - PromptSpanDetails (gen_ai.* span detail panel) - StreamdownRenderer + shikiTheme (markdown renderer with shiki highlighting and v2 patch) - useAutoScrollToBottom hook Playground UI (interactive chat.agent debugger): - /playground index + /playground/$agentParam routes - /agents route + AgentListPresenter - PlaygroundPresenter (per-org basin variants, clientData wiring) - realtime session routes for playground + run inspector chat - AI-generate-payload + AIPayloadTabContent for the test panel Navigation + theming: - SideMenu links for Agents and Playground - BlankStatePanels copy updates - tailwind config + tailwind.css storybook hooks - streamdown@2 dep in apps/webapp/package.json Includes agent-view-sessions, playground-trigger-config-fields, run-agent-view, and streamdown-v2-upgrade .server-changes.
12b4525 to
23ff763
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts (2)
73-79: ⚡ Quick winUse Zod for
Timeout-Secondsas well.The manual
Number(...)path accepts inputs like1e2and0x10, and it bypasses the repo’s validation convention. A small header schema keeps params and headers on the same validation path and tightens the accepted input shape.♻️ Proposed fix
+const TimeoutSecondsHeaderSchema = z + .string() + .regex(/^[1-9]\d{0,2}$/, "Invalid timeout") + .transform(Number) + .refine((value) => value <= 600, "Invalid timeout"); + export async function loader({ request, params }: LoaderFunctionArgs) { const userId = await requireUserId(request); const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params); const { session: sessionParam, io } = ParamsSchema.parse(params); @@ const lastEventId = request.headers.get("Last-Event-ID") || undefined; - const timeoutInSecondsRaw = request.headers.get("Timeout-Seconds"); - let timeoutInSeconds: number | undefined; - if (timeoutInSecondsRaw !== null) { - timeoutInSeconds = Number(timeoutInSecondsRaw); - if (!Number.isInteger(timeoutInSeconds) || timeoutInSeconds < 1 || timeoutInSeconds > 600) { - return new Response("Invalid timeout", { status: 400 }); - } - } + const timeoutInSecondsResult = TimeoutSecondsHeaderSchema.optional().safeParse( + request.headers.get("Timeout-Seconds") ?? undefined + ); + if (!timeoutInSecondsResult.success) { + return new Response("Invalid timeout", { status: 400 }); + } + const timeoutInSeconds = timeoutInSecondsResult.data;As per coding guidelines, "Use zod for validation in packages/core and apps/webapp".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts around lines 73 - 79, The code currently parses Timeout-Seconds manually (timeoutInSecondsRaw → timeoutInSeconds) which accepts weird numeric formats; replace this with a Zod header schema that preprocesses the string header into a number and validates it as an integer between 1 and 600 (use z.preprocess to parse the string then z.number().int().min(1).max(600)), run validation against request.headers.get("Timeout-Seconds"), set timeoutInSeconds from the parsed result only on success, and return the 400 Response when the Zod parse fails; update references to timeoutInSecondsRaw/timeoutInSeconds in the surrounding logic to use the validated value.
29-90: ⚡ Quick winAdd
@crumbsaround the new auth/lookup/streaming branches.This route introduces several failure branches and a long-lived stream setup, but it doesn’t carry any branch-local crumbs yet. Please add them before the stack lands so this path is easier to debug in follow-up PRs.
As per coding guidelines, "Add crumbs as you write code — not just when debugging. Mark lines with //
@crumbsor wrap blocks in //#region@crumbs."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts around lines 29 - 90, Wrap the new auth/lookup/streaming branches in crumb markers so each failure or long-lived-stream path is traceable: add // `@crumbs` (or // `#region` `@crumbs`) before and after the requireUserId call, the findProjectBySlug project-not-found branch, the findEnvironmentBySlug environment-not-found branch, the resolveSessionByIdOrExternalId session-not-found branch, the getRealtimeStreamInstance / S2RealtimeStreams backend-check branch, and the final streamResponseFromSessionStream invocation (including the HEAD and timeout validation branches) so each branch has a local crumb comment to surface in logs and stack traces for the loader function.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts:
- Line 3: The fresh-session subscribe path currently uses $replica for the
initial session lookup which can 404 under replica lag; change the lookup to use
the primary DB client instead (replace usages of $replica in the fresh-session
SSE subscribe handler with the primary client, e.g., $db or $primary), or
implement a short fallback: attempt the lookup with $replica and if it returns
no row/404 immediately retry against the primary client; update the import to
remove $replica usage for that path and reference the same primary client used
elsewhere in this module (fix occurrences around the fresh-session subscribe
code and the section noted at lines ~44-48).
In `@apps/webapp/app/routes/storybook.streamdown/route.tsx`:
- Around line 1-117: The Story route lacks crumb annotations around the two
markdown preview render blocks; add inline crumb markers (either // `@crumbs`
above/below or wrap with // `#region` `@crumbs` ... // `#endregion`) around the JSX
sections that render StreamdownRenderer for sampleMarkdown and codeOnlyMarkdown
inside the Story component (identify the blocks using the Story function and the
StreamdownRenderer usage wrapped in Suspense with Header2). Ensure both preview
containers (the "Full Markdown" and "Code Highlighting Theme" sections) are
annotated so branch-time tracing picks them up.
---
Nitpick comments:
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts:
- Around line 73-79: The code currently parses Timeout-Seconds manually
(timeoutInSecondsRaw → timeoutInSeconds) which accepts weird numeric formats;
replace this with a Zod header schema that preprocesses the string header into a
number and validates it as an integer between 1 and 600 (use z.preprocess to
parse the string then z.number().int().min(1).max(600)), run validation against
request.headers.get("Timeout-Seconds"), set timeoutInSeconds from the parsed
result only on success, and return the 400 Response when the Zod parse fails;
update references to timeoutInSecondsRaw/timeoutInSeconds in the surrounding
logic to use the validated value.
- Around line 29-90: Wrap the new auth/lookup/streaming branches in crumb
markers so each failure or long-lived-stream path is traceable: add // `@crumbs`
(or // `#region` `@crumbs`) before and after the requireUserId call, the
findProjectBySlug project-not-found branch, the findEnvironmentBySlug
environment-not-found branch, the resolveSessionByIdOrExternalId
session-not-found branch, the getRealtimeStreamInstance / S2RealtimeStreams
backend-check branch, and the final streamResponseFromSessionStream invocation
(including the HEAD and timeout validation branches) so each branch has a local
crumb comment to surface in logs and stack traces for the loader function.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 1f3e6136-3661-43eb-8353-8deb9985ff9f
📒 Files selected for processing (31)
.server-changes/agent-view-sessions.md.server-changes/playground-trigger-config-fields.md.server-changes/run-agent-view.md.server-changes/streamdown-v2-upgrade.mdapps/webapp/app/components/navigation/SideMenu.tsxapps/webapp/app/components/runs/v3/agent/AgentMessageView.tsxapps/webapp/app/components/runs/v3/agent/AgentView.tsxapps/webapp/app/hooks/useAutoScrollToBottom.tsapps/webapp/app/presenters/v3/AgentListPresenter.server.tsapps/webapp/app/presenters/v3/PlaygroundPresenter.server.tsapps/webapp/app/presenters/v3/SessionPresenter.server.tsapps/webapp/app/presenters/v3/SpanPresenter.server.tsapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.agents/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.$agentParam/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.sessions.$sessionParam/route.tsxapps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/SchemaTabContent.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.action.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.append.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.realtime.v1.sessions.$sessionId.$io.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.realtime.v1.streams.$runId.$streamId.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.realtime.v1.streams.$runId.input.$streamId.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.streams.$streamKey/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.sessions.$sessionParam.realtime.v1.$io.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.ai-generate-payload.tsxapps/webapp/app/routes/storybook.streamdown/route.tsxapps/webapp/app/routes/storybook/route.tsxapps/webapp/app/tailwind.cssapps/webapp/tailwind.config.js
✅ Files skipped from review due to trivial changes (5)
- .server-changes/playground-trigger-config-fields.md
- .server-changes/run-agent-view.md
- .server-changes/streamdown-v2-upgrade.md
- apps/webapp/app/routes/storybook/route.tsx
- .server-changes/agent-view-sessions.md
🚧 Files skipped from review as they are similar to previous changes (24)
- apps/webapp/tailwind.config.js
- apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.realtime.v1.streams.$runId.input.$streamId.ts
- apps/webapp/app/presenters/v3/PlaygroundPresenter.server.ts
- apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/SchemaTabContent.tsx
- apps/webapp/app/presenters/v3/SpanPresenter.server.ts
- apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
- apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.append.ts
- apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground/route.tsx
- apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.streams.$streamKey/route.tsx
- apps/webapp/app/components/runs/v3/agent/AgentMessageView.tsx
- apps/webapp/app/components/navigation/SideMenu.tsx
- apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.realtime.v1.streams.$runId.$streamId.ts
- apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.realtime.v1.sessions.$sessionId.$io.ts
- apps/webapp/app/presenters/v3/SessionPresenter.server.ts
- apps/webapp/app/components/runs/v3/agent/AgentView.tsx
- apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.sessions.$sessionParam.realtime.v1.$io.ts
- apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.agents/route.tsx
- apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.ai-generate-payload.tsx
- apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.sessions.$sessionParam/route.tsx
- apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.action.tsx
- apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.$agentParam/route.tsx
- apps/webapp/app/hooks/useAutoScrollToBottom.ts
- apps/webapp/app/tailwind.css
- apps/webapp/app/presenters/v3/AgentListPresenter.server.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: typecheck / typecheck
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
**/*.{ts,tsx}: Import from@trigger.dev/coresubpaths only, never from the root. Subpath imports must be used to maintain proper module boundaries.
When writing Trigger.dev tasks, always import from@trigger.dev/sdk. Never use@trigger.dev/sdk/v3or deprecatedclient.defineJob.
Prisma is version 6.14.0. Use the Prisma client frominternal-packages/databasefor all database operations.
For ClickHouse client, schema migrations, and analytics queries, useinternal-packages/clickhouse.
Files:
apps/webapp/app/routes/storybook.streamdown/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/routes/storybook.streamdown/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Add crumbs as you write code — not just when debugging. Mark lines with
//@Crumbsor wrap blocks in `// `#region` `@crumbs. They stay on the branch throughout development and are stripped byagentcrumbs stripbefore merge.
Files:
apps/webapp/app/routes/storybook.streamdown/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: Access environment variables through theenvexport ofenv.server.tsinstead of directly accessingprocess.env
Use subpath exports from@trigger.dev/corepackage instead of importing from the root@trigger.dev/corepathUse named constants for sentinel/placeholder values (e.g.
const UNSET_VALUE = '__unset__') instead of raw string literals scattered across comparisons
Files:
apps/webapp/app/routes/storybook.streamdown/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts
apps/webapp/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Only use
useCallback/useMemofor context provider values, expensive derived data that is a dependency elsewhere, or stable refs required by a dependency array. Don't wrap ordinary event handlers or trivial computations
Files:
apps/webapp/app/routes/storybook.streamdown/route.tsx
**/*.{ts,tsx,js,jsx,json,md,css,scss}
📄 CodeRabbit inference engine (AGENTS.md)
Code formatting is enforced using Prettier. Run
pnpm run formatbefore committing
Files:
apps/webapp/app/routes/storybook.streamdown/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts
apps/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
When modifying only server components (
apps/webapp/,apps/supervisor/, etc.) with no package changes, add a.server-changes/file instead of a changeset. See.server-changes/README.mdfor format and documentation.
Files:
apps/webapp/app/routes/storybook.streamdown/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries
Files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts
🧠 Learnings (8)
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.
Applied to files:
apps/webapp/app/routes/storybook.streamdown/route.tsx
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.
Applied to files:
apps/webapp/app/routes/storybook.streamdown/route.tsx
📚 Learning: 2026-03-22T13:26:12.060Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/code/TextEditor.tsx:81-86
Timestamp: 2026-03-22T13:26:12.060Z
Learning: In the triggerdotdev/trigger.dev codebase, do not flag `navigator.clipboard.writeText(...)` calls for `missing-await`/`unhandled-promise` issues. These clipboard writes are intentionally invoked without `await` and without `catch` handlers across the project; keep that behavior consistent when reviewing TypeScript/TSX files (e.g., usages like in `apps/webapp/app/components/code/TextEditor.tsx`).
Applied to files:
apps/webapp/app/routes/storybook.streamdown/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts
📚 Learning: 2026-03-22T19:24:14.403Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3187
File: apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts:200-204
Timestamp: 2026-03-22T19:24:14.403Z
Learning: In the triggerdotdev/trigger.dev codebase, webhook URLs are not expected to contain embedded credentials/secrets (e.g., fields like `ProjectAlertWebhookProperties` should only hold credential-free webhook endpoints). During code review, if you see logging or inclusion of raw webhook URLs in error messages, do not automatically treat it as a credential-leak/secrets-in-logs issue by default—first verify the URL does not contain embedded credentials (for example, no username/password in the URL, no obvious secret/token query params or fragments). If the URL is credential-free per this project’s conventions, allow the logging.
Applied to files:
apps/webapp/app/routes/storybook.streamdown/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts
📚 Learning: 2026-04-02T19:18:26.255Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 3319
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.bulk-actions/route.tsx:179-189
Timestamp: 2026-04-02T19:18:26.255Z
Learning: In this repo’s route components that render the Inspector `ResizablePanelGroup` panels, it’s acceptable to pass `collapsed={!isShowingInspector}` together with a no-op `onCollapseChange={() => {}}` when panel visibility is intentionally controlled only by route parameters (e.g., `*Param` search/route params) rather than user drag/collapse interactions. Do not flag an empty/no-op `onCollapseChange` as “missing wiring” in these cases; only flag it when collapse state is expected to change based on user interaction.
Applied to files:
apps/webapp/app/routes/storybook.streamdown/route.tsx
📚 Learning: 2026-05-12T21:04:00.184Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3542
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.sessions._index/route.tsx:40-42
Timestamp: 2026-05-12T21:04:00.184Z
Learning: In triggerdotdev/trigger.dev route loader implementations (Remix `route.tsx` files under `apps/webapp/app/routes/**`), follow the existing convention for missing/unauthorized environment lookups: when `findEnvironmentBySlug` (or the equivalent env resolver) returns a falsy value, handle it by throwing `new Error("Environment not found")` rather than returning a `404` `Response` (i.e., do not flag this as “missing 404 response”). Changing the error-to-404 convention is a cross-cutting refactor and should be left out of individual PRs unless the PR explicitly addresses that broader migration.
Applied to files:
apps/webapp/app/routes/storybook.streamdown/route.tsx
📚 Learning: 2026-05-08T21:00:20.973Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 3538
File: apps/webapp/app/components/primitives/Resizable.tsx:60-78
Timestamp: 2026-05-08T21:00:20.973Z
Learning: In the triggerdotdev/trigger.dev codebase, treat Zod as a boundary validation tool (API handlers, request/response validation, and storage/DB read/write validation), not as inline render-time validation inside React components/primitive UI code. For render-time guards, prefer small manual type-narrowing checks (e.g., a short predicate like ~10–20 lines) over importing Zod into UI primitives, to avoid per-render schema-parse overhead and unnecessary abstraction. Use the manual guard approach unless you truly need schema validation at a boundary; only then introduce Zod.
Applied to files:
apps/webapp/app/routes/storybook.streamdown/route.tsx
📚 Learning: 2026-05-12T21:04:05.815Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3542
File: apps/webapp/app/components/sessions/v1/SessionStatus.tsx:1-3
Timestamp: 2026-05-12T21:04:05.815Z
Learning: In this Remix + TypeScript codebase, do not flag a server/client boundary violation when a file imports only types from a module matching `*.server`.
Specifically, it’s safe to import types using `import type { Foo } from "*.server"` or `import { type Foo } from "*.server"` because TypeScript erases type-only imports at compile time and they emit no JavaScript, so they won’t cross the Remix server/client bundle boundary.
Only raise the boundary concern for value imports (e.g., `import { Foo }` without `type`, or `import Foo`), since those produce JavaScript output.
Applied to files:
apps/webapp/app/routes/storybook.streamdown/route.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.realtime.v1.sessions.$session.$io.ts
Summary
A chat-aware run inspector and a
/playgroundUI for testingchat.agenttasks interactively. Builds on #3543's runtime.Design
The run inspector grows a new tab that renders the conversation chain for any
chat.agent-kind run. It subscribes to the run's session streams, threads chat parts through a per-message renderer, and uses a shared markdown + Shiki component for code highlighting (also used by the test-payload panel).The playground is a standalone
/playgroundroute that lets you drive a deployed chat agent from the dashboard — pick a task, send messages, watch tool calls render, and see span detail on every turn. The matching/agentslist view shows all deployed agents in the project.Test plan
chat.agentrun, verify the new chat tab renders the conversation/playground, pick an agent, send a message, watch it stream/agents, verify the list reflects deployed agents in the projectStack
Part of a 4-PR stack. Merge bottom-up.
main— Sessions dashboard + chat-ready hardeningchat.agentruntime + browser transportThis is part 3 of 5 in a stack made with GitButler: