Conversation
New package that provides a custom AI SDK ChatTransport implementation bridging Vercel AI SDK's useChat hook with Trigger.dev's durable task execution and realtime streams. Key exports: - TriggerChatTransport class implementing ChatTransport<UIMessage> - createChatTransport() factory function - ChatTaskPayload type for task-side typing - TriggerChatTransportOptions type The transport triggers a Trigger.dev task with chat messages as payload, then subscribes to the task's realtime stream to receive UIMessageChunk data, which useChat processes natively. Co-authored-by: Eric Allam <eric@trigger.dev>
Tests cover: - Constructor with required and optional options - sendMessages triggering task and returning UIMessageChunk stream - Correct payload structure sent to trigger API - Custom streamKey in stream URL - Extra headers propagation - reconnectToStream with existing and non-existing sessions - createChatTransport factory function - Error handling for API failures - regenerate-message trigger type Co-authored-by: Eric Allam <eric@trigger.dev>
- Cache ApiClient instance instead of creating per-call - Add streamTimeoutSeconds option for customizable stream timeout - Clean up subscribeToStream method (remove unused variable) - Improve JSDoc with backend task example - Minor code cleanup Co-authored-by: Eric Allam <eric@trigger.dev>
Adds 3 additional test cases: - Abort signal gracefully closes the stream - Multiple independent chat sessions tracked correctly - ChatRequestOptions.body is merged into task payload Co-authored-by: Eric Allam <eric@trigger.dev>
Co-authored-by: Eric Allam <eric@trigger.dev>
ChatSessionState is an implementation detail of the transport's session tracking. Users don't need to access it since the sessions map is private. Co-authored-by: Eric Allam <eric@trigger.dev>
The accessToken option now accepts either a string or a function
returning a string. This enables dynamic token refresh patterns:
new TriggerChatTransport({
taskId: 'my-task',
accessToken: () => getLatestToken(),
})
The function is called on each sendMessages() call, allowing fresh
tokens to be used for each task trigger.
Co-authored-by: Eric Allam <eric@trigger.dev>
Use the already-resolved token when creating ApiClient instead of calling resolveAccessToken() again through getApiClient(). Co-authored-by: Eric Allam <eric@trigger.dev>
Two new subpath exports: @trigger.dev/sdk/chat (frontend, browser-safe): - TriggerChatTransport — ChatTransport implementation for useChat - createChatTransport() — factory function - TriggerChatTransportOptions type @trigger.dev/sdk/ai (backend, adds to existing ai.tool/ai.currentToolOptions): - chatTask() — pre-typed task wrapper with auto-pipe - pipeChat() — pipe StreamTextResult to realtime stream - CHAT_STREAM_KEY constant - ChatTaskPayload type - ChatTaskOptions type - PipeChatOptions type Co-authored-by: Eric Allam <eric@trigger.dev>
Move and adapt tests from packages/ai to packages/trigger-sdk. - Import from ./chat.js instead of ./transport.js - Use 'task' option instead of 'taskId' - All 17 tests passing Co-authored-by: Eric Allam <eric@trigger.dev>
All functionality now lives in: - @trigger.dev/sdk/chat (frontend transport) - @trigger.dev/sdk/ai (backend chatTask, pipeChat) Co-authored-by: Eric Allam <eric@trigger.dev>
Co-authored-by: Eric Allam <eric@trigger.dev>
1. Add null/object guard before enqueuing UIMessageChunk from SSE stream to handle heartbeat or malformed events safely 2. Use incrementing counter instead of Date.now() in test message factories to avoid duplicate IDs 3. Add test covering publicAccessToken from trigger response being used for stream subscription auth Co-authored-by: Eric Allam <eric@trigger.dev>
Comprehensive guide covering: - Quick start with chatTask + TriggerChatTransport - Backend patterns: simple (return streamText), complex (pipeChat), and manual (task + ChatTaskPayload) - Frontend options: dynamic tokens, extra data, self-hosting - ChatTaskPayload reference - Added to Writing tasks navigation near Streams Co-authored-by: Eric Allam <eric@trigger.dev>
Minimal example showcasing the new chatTask + TriggerChatTransport APIs: - Backend: chatTask with streamText auto-pipe (src/trigger/chat.ts) - Frontend: TriggerChatTransport with useChat (src/components/chat.tsx) - Token generation via auth.createTriggerPublicToken (src/app/page.tsx) - Tailwind v4 styling Co-authored-by: Eric Allam <eric@trigger.dev>
…delMessages @ai-sdk/openai v3 and @ai-sdk/react v3 are needed for ai v6 compatibility. convertToModelMessages is async in newer AI SDK versions. Co-authored-by: Eric Allam <eric@trigger.dev>
…r mesages from the transport
…and documenting it all
…endingMessages hook
Replace triggerAndWait with triggerAndSubscribe in ai.tool to fix: - Parallel tool calls (no more preventMultipleWaits errors) - Stop signal while suspended (parent stays alive, child gets cancelled) New task.triggerAndSubscribe() method: trigger + subscribeToRun in a single span, with abort signal support and cancelOnAbort option. Convert deepResearch to a schemaTask + ai.tool in the reference app. refs TRI-7986
…at.defer improvements - chat.inject(): queue model messages from background work for injection at the next prepareStep boundary or before the next turn's run() - Deferred work from onTurnComplete no longer blocks waiting for next message - Background queue persists across turns (not reset) so deferred work from onTurnComplete can inject into the next turn - Reference app: self-review pattern using generateObject + chat.inject() - Hide transient data-turn-status and data-background-context-injected parts in UI
… ai.tool - Assert toolFromTask return as AI SDK ToolSet-compatible; import ToolSet from ai - Add changesets for @trigger.dev/sdk - ai-chat reference: chat-tools module, registry language model helper, streamText cleanup - Prisma migration removing user tool demo; demo docs and next config tweaks
- Pass TaskRunContext through chat lifecycle events, CompactedEvent, and ChatTaskRunPayload; use ctx.run.id for chat access tokens - Export TaskRunContext from @trigger.dev/sdk - ai-chat reference: executeCode via E2B, code-sandbox module, warm on onTurnStart, dispose on token onWait and onComplete; chat.local run id - Docs: database persistence + code sandbox pattern pages; reference and backend updates for ctx; chat.defer anchor; navigation
…, chat.withClientData, and ChatBuilder
plus playground support, including playground conversations, and a new agent list
- Upgrade streamdown from v1.4.0 to v2.5.0 with @streamdown/code plugin - Custom Shiki theme matching the Trigger.dev VS Code dark theme colors - Consolidate duplicated lazy StreamdownRenderer into shared component - Patch streamdown to inline highlighted body (fixes Arc browser) - Add streamdown storybook page for visual testing - Handle AGENT triggerSource in TestTaskPresenter exhaustive switch - Update Tailwind config to scan streamdown dist for utility classes
…d preloaded conversations on first message
🦋 Changeset detectedLatest commit: a67ee16 The changes in this PR will be included in the next version bump. This PR includes changesets to release 30 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Hi @deepshekhardas, thanks for your interest in contributing! This project requires that pull request authors are vouched, and you are not in the list of vouched users. This PR will be closed automatically. See https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md for more details. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (38)
📒 Files selected for processing (109)
WalkthroughThis change introduces AI chat agent functionality to the Trigger.dev platform. It includes new workspace routes and components for listing and interacting with deployed chat agents, new SDK exports for chat transport and client implementations, database schema extensions for conversation persistence, task trigger source filtering, Streamdown theme updates, and comprehensive streaming/WebSocket integration for bidirectional agent communication. The changes span from database migrations and service layers through UI components to core SDK type definitions and stream management. Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes ✨ Finishing Touches🧪 Generate unit tests (beta)
|
| return json({ error: "payload and chatId are required" }, { status: 400 }); | ||
| } | ||
|
|
||
| const payload = JSON.parse(payloadStr) as Record<string, any>; |
There was a problem hiding this comment.
🟡 Unhandled JSON.parse on user-supplied payloadStr causes 500 instead of 400
The JSON.parse(payloadStr) on line 100 is called without a try-catch on user-supplied form data. If the client sends malformed JSON (e.g., network corruption, a tampered request, or a client-side serialization bug), the action throws an unhandled exception, producing a 500 error with an HTML error boundary response. The client at playground.$agentParam/route.tsx:237 then calls response.json() on the HTML body, which fails with a confusing parse error. Note that the clientData parse on line 132-136 IS correctly wrapped in try-catch, showing this pattern was known but not applied consistently here.
| const payload = JSON.parse(payloadStr) as Record<string, any>; | |
| let payload: Record<string, any>; | |
| try { | |
| payload = JSON.parse(payloadStr) as Record<string, any>; | |
| } catch { | |
| return json({ error: "Invalid payload JSON" }, { status: 400 }); | |
| } |
Was this helpful? React with 👍 or 👎 to provide feedback.
| return json({ error: "chatId is required" }, { status: 400 }); | ||
| } | ||
|
|
||
| const messagesData = messagesStr ? JSON.parse(messagesStr) : undefined; |
There was a problem hiding this comment.
🟡 Unhandled JSON.parse on user-supplied messagesStr causes 500 instead of 400
The JSON.parse(messagesStr) on line 193 is called without a try-catch on user-supplied form data. If the messages string is malformed, the action throws an unhandled exception producing a 500. The client's fire-and-forget fetch call at playground.$agentParam/route.tsx:335 silently swallows the error (.catch(() => {})), so message persistence silently fails without any feedback. Wrapping in try-catch would allow returning a proper 400 error.
| const messagesData = messagesStr ? JSON.parse(messagesStr) : undefined; | |
| let messagesData: unknown; | |
| try { | |
| messagesData = messagesStr ? JSON.parse(messagesStr) : undefined; | |
| } catch { | |
| return json({ error: "Invalid messages JSON" }, { status: 400 }); | |
| } |
Was this helpful? React with 👍 or 👎 to provide feedback.
| const tasks = await this._replica.backgroundWorkerTask.findMany({ | ||
| where: { | ||
| workerId: currentWorker.id, | ||
| triggerSource: { not: "AGENT" }, |
There was a problem hiding this comment.
🚩 TaskListPresenter now excludes AGENT tasks, may affect existing API consumers
The TaskListPresenter at line 64 now filters out tasks with triggerSource: 'AGENT' from the task list. Similarly, TestPresenter excludes agents at line 55 and 59-61. This means agents won't appear in the Tasks page or the Test page, which is the stated intent. However, any external API consumers or integrations that rely on the task list endpoint returning ALL tasks (including agents) would see a behavioral change. The agents are instead surfaced via the new dedicated Agents page.
Was this helpful? React with 👍 or 👎 to provide feedback.
Closes #
✅ Checklist
Testing
[Describe the steps you took to test this change]
Changelog
[Short description of what has changed]
Screenshots
[Screenshots]
💯