feat(sdk): AI SDK custom useChat transport & chat.task harness#3173
feat(sdk): AI SDK custom useChat transport & chat.task harness#3173
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>
🦋 Changeset detectedLatest commit: d7817e0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 29 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 |
WalkthroughThis PR introduces AI chat integration capabilities via new subpath exports ( Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). 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 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
packages/trigger-sdk/src/v3/chat.test.ts (1)
22-23: Consider resetting messageIdCounter in beforeEach.The
messageIdCounteris a module-level variable that accumulates across all tests. While this doesn't cause functional issues (IDs just need to be unique within each test), resetting it inbeforeEachwould make tests more deterministic and easier to debug.♻️ Suggested fix
beforeEach(() => { originalFetch = global.fetch; + messageIdCounter = 0; });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/trigger-sdk/src/v3/chat.test.ts` around lines 22 - 23, The module-level variable messageIdCounter in chat.test.ts accumulates across tests making IDs non-deterministic; add a beforeEach hook in the test file that resets messageIdCounter = 0 so each test starts with a fresh counter (locate the variable by name messageIdCounter and place the reset inside the existing or new beforeEach block in the test suite).packages/trigger-sdk/src/v3/chat-react.ts (1)
76-84: Good memoization pattern, but options are captured only on first render.The
useRefpattern correctly preserves the transport instance across re-renders. Note that ifbaseURL,headers,streamKey, or other options change after the initial render, the transport won't pick up those changes. This is likely intentional (and documented via "created once"), but worth keeping in mind if callers expect reactive updates to options other thanaccessToken.The
accessTokenfunction pattern works correctly for dynamic tokens since it's called per-request.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/trigger-sdk/src/v3/chat-react.ts` around lines 76 - 84, The hook useTriggerChatTransport currently creates a single TriggerChatTransport with the options captured only on first render, so changes to baseURL, headers, streamKey, etc. after mount are ignored; modify the hook to either (A) expose or call an update method on the transport (e.g., ref.current.updateOptions or ref.current.setConfig) and update relevant fields when options.baseURL/headers/streamKey change, or (B) recreate the transport when those non-dynamic options change by tracking them in a useEffect and replacing ref.current = new TriggerChatTransport(options) (preserving any needed cleanup), while keeping the accessToken-as-function behavior unchanged so per-request tokens remain dynamic. Ensure you reference useTriggerChatTransport and TriggerChatTransport and only treat accessToken as dynamically invoked per request.packages/trigger-sdk/src/v3/chat.ts (1)
198-212: Consider logging the error for debugging.The silent catch when sending to an existing run's input stream makes debugging harder when things go wrong unexpectedly. While the fallthrough to trigger a new run is correct, a debug-level log could help diagnose issues.
🔧 Optional: Add debug logging
} catch { - // If sending fails (run died, etc.), fall through to trigger a new run. + // If sending fails (run died, etc.), fall through to trigger a new run. + // Note: Consider adding debug logging here if debugging becomes difficult this.sessions.delete(chatId); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/trigger-sdk/src/v3/chat.ts` around lines 198 - 212, The catch block swallowing errors after ApiClient.sendInputStream makes failures hard to trace; update the catch to log the caught error (including context like session.runId and chatId) before deleting the session so you still fall through to start a new run — use the class logger (e.g., this.logger.debug) if available or console.debug as a fallback, referencing ApiClient.sendInputStream, subscribeToStream, and this.sessions.delete to locate the code to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.scratch/plan-graceful-oversized-batch-items.md:
- Around line 216-238: The fenced code block that starts with "NDJSON bytes
arrive" is missing a language identifier which triggers markdownlint MD040;
update the opening fence to include a language (e.g., change ``` to ```text) so
the block is explicitly marked as plain text (ensure the closing fence remains
```), preserving the existing block contents and indentation.
In `@packages/trigger-sdk/src/v3/ai.ts`:
- Around line 560-563: The handler currently captures multiple messages into
pendingMessages via messagesInput.on (msgSub) but later only consumes the first
entry, discarding the rest; change the logic that extracts from pendingMessages
(where it currently uses a single element) to drain and preserve the entire
backlog — e.g., move all items from pendingMessages into the processing queue
(or append them to the existing messages array) before proceeding, then clean up
the subscription (msgSub) as before so no buffered messages are lost; update
references to pendingMessages, msgSub, and messagesInput.on to reflect this
full-drain behavior.
- Around line 268-269: The global _chatPipeCount is race-prone; make the pipe
counter scoped to each chat run/turn instead. Remove the module-global
_chatPipeCount and initialize a run-scoped counter (for example add a numeric
property like run.__chatPipeCount or turn.chatPipeCount on the ChatRun/ChatTurn
object when a run/turn is created or at the start of the function that currently
uses _chatPipeCount), then replace every reference to _chatPipeCount (including
uses around lines 360-361 and 548-583) with the run/turn-scoped property and
update increment/decrement logic to use that property so concurrent runs don’t
share state. Ensure the counter is initialized to 0 at run start and cleaned up
or left on the run object when finished.
---
Nitpick comments:
In `@packages/trigger-sdk/src/v3/chat-react.ts`:
- Around line 76-84: The hook useTriggerChatTransport currently creates a single
TriggerChatTransport with the options captured only on first render, so changes
to baseURL, headers, streamKey, etc. after mount are ignored; modify the hook to
either (A) expose or call an update method on the transport (e.g.,
ref.current.updateOptions or ref.current.setConfig) and update relevant fields
when options.baseURL/headers/streamKey change, or (B) recreate the transport
when those non-dynamic options change by tracking them in a useEffect and
replacing ref.current = new TriggerChatTransport(options) (preserving any needed
cleanup), while keeping the accessToken-as-function behavior unchanged so
per-request tokens remain dynamic. Ensure you reference useTriggerChatTransport
and TriggerChatTransport and only treat accessToken as dynamically invoked per
request.
In `@packages/trigger-sdk/src/v3/chat.test.ts`:
- Around line 22-23: The module-level variable messageIdCounter in chat.test.ts
accumulates across tests making IDs non-deterministic; add a beforeEach hook in
the test file that resets messageIdCounter = 0 so each test starts with a fresh
counter (locate the variable by name messageIdCounter and place the reset inside
the existing or new beforeEach block in the test suite).
In `@packages/trigger-sdk/src/v3/chat.ts`:
- Around line 198-212: The catch block swallowing errors after
ApiClient.sendInputStream makes failures hard to trace; update the catch to log
the caught error (including context like session.runId and chatId) before
deleting the session so you still fall through to start a new run — use the
class logger (e.g., this.logger.debug) if available or console.debug as a
fallback, referencing ApiClient.sendInputStream, subscribeToStream, and
this.sessions.delete to locate the code to change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: ffd316a7-f5af-4416-88b4-2e7c865da47c
⛔ Files ignored due to path filters (13)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlreferences/ai-chat/next-env.d.tsis excluded by!references/**references/ai-chat/next.config.tsis excluded by!references/**references/ai-chat/package.jsonis excluded by!references/**references/ai-chat/postcss.config.mjsis excluded by!references/**references/ai-chat/src/app/actions.tsis excluded by!references/**references/ai-chat/src/app/globals.cssis excluded by!references/**references/ai-chat/src/app/layout.tsxis excluded by!references/**references/ai-chat/src/app/page.tsxis excluded by!references/**references/ai-chat/src/components/chat.tsxis excluded by!references/**references/ai-chat/src/trigger/chat.tsis excluded by!references/**references/ai-chat/trigger.config.tsis excluded by!references/**references/ai-chat/tsconfig.jsonis excluded by!references/**
📒 Files selected for processing (18)
.changeset/ai-sdk-chat-transport.md.claude/rules/package-installation.md.scratch/plan-graceful-oversized-batch-items.mdCLAUDE.mddocs/docs.jsondocs/guides/ai-chat.mdxpackages/core/src/v3/inputStreams/index.tspackages/core/src/v3/inputStreams/manager.tspackages/core/src/v3/inputStreams/noopManager.tspackages/core/src/v3/inputStreams/types.tspackages/core/src/v3/realtimeStreams/types.tspackages/trigger-sdk/package.jsonpackages/trigger-sdk/src/v3/ai.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/streams.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). (27)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
- GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
- GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: typecheck / typecheck
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{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}: In TypeScript SDK usage, always import from@trigger.dev/sdk, never from@trigger.dev/sdk/v3or use deprecated client.defineJob
Import from@trigger.dev/coresubpaths only, never from the root
Use the Run Engine 2.0 (@internal/run-engine) and redis-worker for all new work, not legacy V1 MarQS queue or deprecated V1 functions
Files:
packages/core/src/v3/inputStreams/manager.tspackages/core/src/v3/inputStreams/index.tspackages/core/src/v3/inputStreams/noopManager.tspackages/trigger-sdk/src/v3/chat.tspackages/core/src/v3/inputStreams/types.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/streams.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat-react.tspackages/core/src/v3/realtimeStreams/types.tspackages/trigger-sdk/src/v3/ai.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:
packages/core/src/v3/inputStreams/manager.tspackages/core/src/v3/inputStreams/index.tspackages/core/src/v3/inputStreams/noopManager.tspackages/core/src/v3/inputStreams/types.tspackages/core/src/v3/realtimeStreams/types.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Files:
packages/core/src/v3/inputStreams/manager.tspackages/core/src/v3/inputStreams/index.tspackages/core/src/v3/inputStreams/noopManager.tspackages/trigger-sdk/src/v3/chat.tspackages/core/src/v3/inputStreams/types.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/streams.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat-react.tspackages/core/src/v3/realtimeStreams/types.tspackages/trigger-sdk/src/v3/ai.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:
packages/core/src/v3/inputStreams/manager.tspackages/core/src/v3/inputStreams/index.tspackages/core/src/v3/inputStreams/noopManager.tspackages/trigger-sdk/src/v3/chat.tspackages/core/src/v3/inputStreams/types.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/streams.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat-react.tspackages/core/src/v3/realtimeStreams/types.tspackages/trigger-sdk/src/v3/ai.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
packages/core/src/v3/inputStreams/manager.tspackages/core/src/v3/inputStreams/index.tspackages/core/src/v3/inputStreams/noopManager.tsdocs/docs.jsonpackages/trigger-sdk/src/v3/chat.tsCLAUDE.mdpackages/core/src/v3/inputStreams/types.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/streams.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/package.jsonpackages/core/src/v3/realtimeStreams/types.tspackages/trigger-sdk/src/v3/ai.ts
packages/core/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (packages/core/CLAUDE.md)
Never import the root package (
@trigger.dev/core). Always use subpath imports such as@trigger.dev/core/v3,@trigger.dev/core/v3/utils,@trigger.dev/core/logger, or@trigger.dev/core/schemas
Files:
packages/core/src/v3/inputStreams/manager.tspackages/core/src/v3/inputStreams/index.tspackages/core/src/v3/inputStreams/noopManager.tspackages/core/src/v3/inputStreams/types.tspackages/core/src/v3/realtimeStreams/types.ts
docs/**/*.{md,mdx}
📄 CodeRabbit inference engine (CLAUDE.md)
Docs in docs/ directory should use Mintlify MDX format following conventions in docs/CLAUDE.md
Files:
docs/guides/ai-chat.mdx
docs/**/*.mdx
📄 CodeRabbit inference engine (docs/CLAUDE.md)
docs/**/*.mdx: MDX documentation pages must include frontmatter with title (required), description (required), and sidebarTitle (optional) in YAML format
Use Mintlify components for structured content: , , , , , , /, /
Always import from@trigger.dev/sdkin code examples (never from@trigger.dev/sdk/v3)
Code examples must be complete and runnable where possible
Use language tags in code fences:typescript,bash,json
Files:
docs/guides/ai-chat.mdx
docs/**/docs.json
📄 CodeRabbit inference engine (docs/CLAUDE.md)
docs/**/docs.json: Main documentation config must be defined indocs.jsonwhich includes navigation structure, theme, and metadata
Navigation structure indocs.jsonshould be organized usingnavigation.dropdownswith groups and pages
Files:
docs/docs.json
packages/trigger-sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code
Files:
packages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/streams.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/src/v3/ai.ts
packages/trigger-sdk/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (packages/trigger-sdk/CLAUDE.md)
Always import from
@trigger.dev/sdk. Never use@trigger.dev/sdk/v3(deprecated path alias)
Files:
packages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/streams.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/src/v3/ai.ts
**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use vitest for all tests in the Trigger.dev repository
Files:
packages/trigger-sdk/src/v3/chat.test.ts
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.{ts,tsx,js,jsx}: Test files should live beside the files under test and use descriptivedescribeanditblocks
Tests should avoid mocks or stubs and use the helpers from@internal/testcontainerswhen Redis or Postgres are needed
Use vitest for running unit tests
Files:
packages/trigger-sdk/src/v3/chat.test.ts
**/*.test.{ts,tsx,js}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx,js}: Use vitest exclusively for testing - never mock anything, use testcontainers instead
Place test files next to source files with .test.ts naming convention (e.g., MyService.ts -> MyService.test.ts)
Test files using Redis or PostgreSQL should use testcontainers helpers (redisTest, postgresTest, containerTest) instead of mocks
Files:
packages/trigger-sdk/src/v3/chat.test.ts
🧠 Learnings (51)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `.withStreams()` to subscribe to realtime streams from task metadata in addition to run changes
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: The SDK at packages/trigger-sdk is an isomorphic TypeScript SDK
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
📚 Learning: 2026-03-02T12:43:34.140Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/cli-v3/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:34.140Z
Learning: Keep SDK documentation in `rules/` and `.claude/skills/trigger-dev-tasks/` synchronized when features are added or changed
Applied to files:
docs/guides/ai-chat.mdxCLAUDE.md.claude/rules/package-installation.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks
Applied to files:
docs/guides/ai-chat.mdxpackages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/package.json.changeset/ai-sdk-chat-transport.mdpackages/trigger-sdk/src/v3/ai.ts
📚 Learning: 2026-03-02T12:43:37.906Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/core/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:37.906Z
Learning: Exercise caution with changes to trigger.dev/core as they affect both the customer-facing SDK and server-side webapp - breaking changes can impact deployed user tasks and the platform simultaneously
Applied to files:
docs/guides/ai-chat.mdx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `.withStreams()` to subscribe to realtime streams from task metadata in addition to run changes
Applied to files:
docs/guides/ai-chat.mdxpackages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/streams.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat-react.ts.changeset/ai-sdk-chat-transport.mdpackages/core/src/v3/realtimeStreams/types.tspackages/trigger-sdk/src/v3/ai.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties
Applied to files:
docs/guides/ai-chat.mdxpackages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-react.ts.changeset/ai-sdk-chat-transport.mdpackages/trigger-sdk/src/v3/ai.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.trigger()` to trigger a task from inside another task with specified payload
Applied to files:
docs/guides/ai-chat.mdx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation
Applied to files:
docs/guides/ai-chat.mdxpackages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/package.json.changeset/ai-sdk-chat-transport.mdpackages/trigger-sdk/src/v3/ai.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.trigger()` with type-only imports to trigger tasks from backend code without importing the task implementation
Applied to files:
docs/guides/ai-chat.mdxpackages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/package.json.changeset/ai-sdk-chat-transport.mdpackages/trigger-sdk/src/v3/ai.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Use `TriggerAuthContext` provider to supply Public Access Token to Trigger.dev React hooks
Applied to files:
docs/guides/ai-chat.mdxpackages/trigger-sdk/src/v3/chat-react.ts
📚 Learning: 2026-02-25T17:28:20.456Z
Learnt from: isshaddad
Repo: triggerdotdev/trigger.dev PR: 3130
File: docs/v3-openapi.yaml:3134-3135
Timestamp: 2026-02-25T17:28:20.456Z
Learning: In the Trigger.dev codebase, the `publicAccessToken` returned by the SDK's `wait.createToken()` method is not part of the HTTP response body from `POST /api/v1/waitpoints/tokens`. The server returns only `{ id, isCached, url }`. The SDK's `prepareData` hook generates the JWT client-side from the `x-trigger-jwt-claims` response header after the HTTP call completes. The OpenAPI spec correctly documents only the HTTP response body, not SDK transformations.
<!-- [/add_learning]
Applied to files:
docs/guides/ai-chat.mdx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: The SDK at packages/trigger-sdk is an isomorphic TypeScript SDK
Applied to files:
docs/guides/ai-chat.mdxpackages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/package.json.changeset/ai-sdk-chat-transport.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.batchTrigger()` to trigger multiple runs of a single task with different payloads
Applied to files:
docs/guides/ai-chat.mdxpackages/trigger-sdk/src/v3/chat.test.ts.scratch/plan-graceful-oversized-batch-items.md
📚 Learning: 2026-03-02T12:43:02.539Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: docs/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:02.539Z
Learning: Organize documentation across appropriate directories: `documentation/` for core conceptual docs, `guides/` for how-to guides, `config/` for configuration reference, `deployment/` for deployment guides, `tasks/` for task documentation, `realtime/` for real-time features, `runs/` for run management, and `images/` for assets
Applied to files:
docs/docs.json
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code
Applied to files:
packages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/package.json.changeset/ai-sdk-chat-transport.md
📚 Learning: 2026-03-02T12:43:48.124Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/trigger-sdk/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:48.124Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx,js,jsx} : Always import from `trigger.dev/sdk`. Never use `trigger.dev/sdk/v3` (deprecated path alias)
Applied to files:
packages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-constants.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/package.json.changeset/ai-sdk-chat-transport.mdpackages/trigger-sdk/src/v3/ai.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution
Applied to files:
packages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/package.jsonpackages/trigger-sdk/src/v3/ai.ts
📚 Learning: 2026-03-02T12:42:41.110Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:41.110Z
Learning: Applies to **/*.{ts,tsx} : In TypeScript SDK usage, always import from trigger.dev/sdk, never from trigger.dev/sdk/v3 or use deprecated client.defineJob
Applied to files:
packages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/chat-react.tspackages/trigger-sdk/package.json.changeset/ai-sdk-chat-transport.md
📚 Learning: 2026-03-02T12:42:41.110Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:41.110Z
Learning: Applies to packages/**/*.{ts,tsx,js},integrations/**/*.{ts,tsx,js} : When modifying public packages (packages/* or integrations/*), add a changeset via pnpm run changeset:add
Applied to files:
CLAUDE.md.claude/rules/package-installation.mdpackages/trigger-sdk/package.json
📚 Learning: 2026-03-02T12:43:48.124Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/trigger-sdk/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:48.124Z
Learning: Do NOT update `.claude/skills/trigger-dev-tasks/` directory files unless explicitly asked - these are maintained in separate dedicated passes
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-02T12:43:34.140Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/cli-v3/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:34.140Z
Learning: Applies to packages/cli-v3/.claude/skills/trigger-dev-tasks/**/* : Update `.claude/skills/trigger-dev-tasks/` in parallel with `rules/` when SDK features change
Applied to files:
CLAUDE.md.claude/rules/package-installation.md
📚 Learning: 2026-03-02T12:42:41.110Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:41.110Z
Learning: Applies to docs/**/*.{md,mdx} : Docs in docs/ directory should use Mintlify MDX format following conventions in docs/CLAUDE.md
Applied to files:
CLAUDE.md.claude/rules/package-installation.md
📚 Learning: 2026-03-02T12:43:34.140Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/cli-v3/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:34.140Z
Learning: Applies to packages/cli-v3/rules/**/* : Update `rules/` directory with versioned SDK documentation when SDK features change
Applied to files:
CLAUDE.md.claude/rules/package-installation.md
📚 Learning: 2026-03-02T12:42:41.110Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:41.110Z
Learning: This is a pnpm 10.23.0 monorepo using Turborepo - run commands from root with pnpm run
Applied to files:
CLAUDE.md.claude/rules/package-installation.md
📚 Learning: 2025-11-27T16:26:44.496Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/executing-commands.mdc:0-0
Timestamp: 2025-11-27T16:26:44.496Z
Learning: Execute most monorepo commands using `pnpm run` from the root directory, with `--filter` flag for specific packages (e.g., `pnpm run dev --filter webapp`)
Applied to files:
CLAUDE.md.claude/rules/package-installation.md
📚 Learning: 2025-11-27T16:26:44.496Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/executing-commands.mdc:0-0
Timestamp: 2025-11-27T16:26:44.496Z
Learning: For running tests, navigate into the package directory and run `pnpm run test --run` to enable single-file test execution (e.g., `pnpm run test ./src/engine/tests/ttl.test.ts --run`)
Applied to files:
CLAUDE.md.claude/rules/package-installation.md
📚 Learning: 2026-01-15T10:48:02.687Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-15T10:48:02.687Z
Learning: Use pnpm as the package manager (version 10.23.0 or later) and Node.js 20.20.0
Applied to files:
CLAUDE.md.claude/rules/package-installation.md
📚 Learning: 2025-11-27T16:26:47.602Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/repo.mdc:0-0
Timestamp: 2025-11-27T16:26:47.602Z
Learning: Refer to the monorepo structure documentation at repo.md before making changes or adding new files
Applied to files:
CLAUDE.md.claude/rules/package-installation.md
📚 Learning: 2026-03-02T12:43:17.177Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: internal-packages/database/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:17.177Z
Learning: Edit Prisma schema at `prisma/schema.prisma` and generate migrations using `pnpm run db:migrate:dev:create --name "descriptive_name"` from the `internal-packages/database` directory
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-02T12:43:37.906Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/core/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:37.906Z
Learning: Applies to packages/core/**/*.{ts,tsx,js,jsx} : Never import the root package (trigger.dev/core). Always use subpath imports such as trigger.dev/core/v3, trigger.dev/core/v3/utils, trigger.dev/core/logger, or trigger.dev/core/schemas
Applied to files:
.claude/rules/package-installation.mdpackages/trigger-sdk/package.json
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Use vitest for all tests in the Trigger.dev repository
Applied to files:
packages/trigger-sdk/src/v3/chat.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.triggerAndWait()` to batch trigger multiple different tasks and wait for results
Applied to files:
packages/trigger-sdk/src/v3/chat.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.triggerByTaskAndWait()` to batch trigger tasks by passing task instances and wait for results
Applied to files:
packages/trigger-sdk/src/v3/chat.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.batchTrigger()` to trigger multiple runs of a task from inside another task
Applied to files:
packages/trigger-sdk/src/v3/chat.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.triggerAndWait()` to trigger a task and wait for its result from a parent task
Applied to files:
packages/trigger-sdk/src/v3/chat.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.batchTriggerAndWait()` to batch trigger tasks and wait for all results from a parent task
Applied to files:
packages/trigger-sdk/src/v3/chat.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Use `trigger.dev/react-hooks` package for realtime subscriptions in React components
Applied to files:
packages/trigger-sdk/src/v3/chat-react.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Use `useRun`, `useRealtimeRun` and other SWR/realtime hooks from `trigger.dev/react-hooks` for data fetching
Applied to files:
packages/trigger-sdk/src/v3/chat-react.ts
📚 Learning: 2026-03-03T13:07:33.177Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3166
File: internal-packages/run-engine/src/batch-queue/tests/index.test.ts:711-713
Timestamp: 2026-03-03T13:07:33.177Z
Learning: In `internal-packages/run-engine/src/batch-queue/tests/index.test.ts`, test assertions for rate limiter stubs can use `toBeGreaterThanOrEqual` rather than exact equality (`toBe`) because the consumer loop may call the rate limiter during empty pops in addition to actual item processing, and this over-calling is acceptable in integration tests.
Applied to files:
.scratch/plan-graceful-oversized-batch-items.md
📚 Learning: 2026-03-03T13:08:03.862Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3166
File: packages/redis-worker/src/fair-queue/index.ts:1114-1121
Timestamp: 2026-03-03T13:08:03.862Z
Learning: In packages/redis-worker/src/fair-queue/index.ts, it's acceptable for the worker queue depth cap check to allow overshooting by up to batchClaimSize messages per iteration, as the next iteration will recheck and prevent sustained growth beyond the limit.
Applied to files:
.scratch/plan-graceful-oversized-batch-items.md
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path
Applied to files:
packages/trigger-sdk/package.json.changeset/ai-sdk-chat-transport.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure build process in trigger.config.ts using `build` object with external packages, extensions, and JSX settings
Applied to files:
packages/trigger-sdk/package.json
📚 Learning: 2026-03-02T12:43:34.140Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/cli-v3/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:34.140Z
Learning: Applies to packages/cli-v3/src/build/**/* : Build system in `src/build/` should use configuration from `trigger.config.ts` in user projects to determine bundling, build extensions, and output structure
Applied to files:
packages/trigger-sdk/package.json
📚 Learning: 2026-03-02T12:42:41.110Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:41.110Z
Learning: Applies to **/*.{ts,tsx} : Import from trigger.dev/core subpaths only, never from the root
Applied to files:
packages/trigger-sdk/package.json
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Specify task locations in trigger.config.ts using the `dirs` array, with automatic exclusion of .test and .spec files
Applied to files:
packages/trigger-sdk/package.json
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Applied to files:
packages/trigger-sdk/package.json
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build
Applied to files:
packages/trigger-sdk/package.json
📚 Learning: 2025-11-26T14:40:07.146Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2710
File: packages/schema-to-json/package.json:0-0
Timestamp: 2025-11-26T14:40:07.146Z
Learning: Node.js 24+ has native TypeScript support and can execute .ts files directly without tsx or ts-node for scripts that use only erasable TypeScript syntax (type annotations, interfaces, etc.). The trigger.dev repository uses Node.js 24.11.1+ and scripts like updateVersion.ts can be run with `node` instead of `tsx`.
Applied to files:
packages/trigger-sdk/package.json
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions
Applied to files:
packages/trigger-sdk/src/v3/ai.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use metadata methods (set, del, replace, append, remove, increment, decrement, stream, flush) to update metadata during task execution
Applied to files:
packages/trigger-sdk/src/v3/ai.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks
Applied to files:
packages/trigger-sdk/src/v3/ai.ts
🪛 markdownlint-cli2 (0.21.0)
.scratch/plan-graceful-oversized-batch-items.md
[warning] 216-216: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (24)
.claude/rules/package-installation.md (1)
10-22: Clear and actionable monorepo dependency install workflow.The new guidance is explicit and practical for this repo setup.
CLAUDE.md (1)
9-10: Good top-level pointer to the package-installation rule.This keeps root instructions concise while linking to the detailed process.
packages/core/src/v3/realtimeStreams/types.ts (1)
74-77: Nice API surface extension for tracing customization.The new optional fields are additive and maintain compatibility.
Also applies to: 206-207, 244-245
packages/trigger-sdk/src/v3/chat-constants.ts (1)
1-13: Good centralization of chat stream identifiers.This avoids string duplication across the chat transport stack.
packages/trigger-sdk/src/v3/streams.ts (1)
142-143: Trace/span and input-wait lifecycle updates look consistent.The new options and waitpoint flow changes integrate cleanly with the stream abstractions.
Also applies to: 170-171, 644-645, 717-717, 754-804, 819-820
packages/trigger-sdk/package.json (1)
27-30: Export map and peer/type wiring for chat subpaths look solid.
./chatand./chat/reactare consistently mapped across build, runtime, and type resolution.Also applies to: 42-48, 77-77, 90-100, 139-161
docs/docs.json (1)
77-77: Navigation update is correctly placed.Adding
guides/ai-chatunder “Writing tasks” is consistent with the existing docs structure.packages/core/src/v3/inputStreams/manager.ts (2)
43-61: LGTM! New stream management methods are well-implemented.The
setLastSeqNumcorrectly guards against backward sequence number movement,shiftBufferproperly handles cleanup when the buffer becomes empty, and both methods align with the documented behavior intypes.ts.
181-188: LGTM! Clean stream disconnection implementation.The
disconnectStreammethod properly aborts the tail's AbortController and cleans up both the tail and buffer entries for the stream.packages/core/src/v3/inputStreams/index.ts (1)
54-64: LGTM! API wrappers follow established patterns.The new methods correctly delegate to the underlying manager and maintain consistency with the existing API surface.
packages/core/src/v3/inputStreams/noopManager.ts (1)
25-29: LGTM! Correct no-op implementations.The no-op methods properly implement the
InputStreamManagerinterface with appropriate default behavior:shiftBufferreturnsfalse(nothing to shift), and the others are silent no-ops.docs/guides/ai-chat.mdx (3)
1-5: LGTM! Well-structured documentation with proper Mintlify frontmatter.The documentation follows the MDX guidelines with required frontmatter fields (title, description) and optional sidebarTitle.
69-112: Frontend example is clear and practical.The React component example demonstrates the integration pattern well. The code is complete and follows React conventions.
19-21: Version requirement callout is clear and accurate. The@ai-sdk/reactimport path referenced in the file (line 72) is the correct path for AI SDK v5.0.0 and later. No issues found.packages/core/src/v3/inputStreams/types.ts (1)
73-93: LGTM! Excellent documentation for the new interface methods.The JSDoc comments clearly explain the purpose of each method in the context of waitpoint handling and SSE tail management. The documentation will help consumers understand when and why to use each method.
.changeset/ai-sdk-chat-transport.md (1)
1-42: LGTM! Well-documented changeset with clear examples.The minor version bump is appropriate for adding new features. The examples clearly demonstrate the usage of both frontend and backend exports.
packages/trigger-sdk/src/v3/chat.test.ts (3)
49-59: LGTM! Good test setup/teardown pattern.The
beforeEach/afterEachcorrectly saves and restoresglobal.fetch, andvi.restoreAllMocks()ensures clean state between tests.
919-1022: Excellent coverage of lastEventId tracking and SSE reconnection.The tests thoroughly verify that
Last-Event-IDis passed correctly on subsequent stream subscriptions, which is critical for proper SSE resumption behavior.
1264-1614: Comprehensive waitpoint/single-run mode test coverage.The tests cover important edge cases: storing waitpoint tokens, completing waitpoints on subsequent messages, fallback behavior when streams close unexpectedly, and fallback when waitpoint completion fails. This is crucial for the durable chat session feature.
packages/trigger-sdk/src/v3/chat.ts (5)
1-31: LGTM!Clear module documentation with a practical usage example. Imports are appropriate for a browser-safe module, using the
aipackage types and@trigger.dev/core/v3utilities that are isomorphic.
38-116: LGTM!Type definitions are well-structured with comprehensive JSDoc. The
accessTokenflexibility (supporting sync/async functions) is a nice pattern for dynamic token refresh and Next.js server actions.
308-389: Well-structured ReadableStream implementation.The streaming logic correctly handles:
- Turn completion via
__trigger_turn_completecontrol chunks- Skip-to-turn-complete state for abort/resume scenarios
lastEventIdtracking for stream resumption- Defensive cleanup with try-catch on
controller.close()The error handling properly distinguishes between AbortError (expected on stop) and other errors.
243-254: LGTM!The
reconnectToStreammethod correctly handles missing sessions by returningnull. ThecreateChatTransportfactory function provides a clean functional alternative with good documentation.Also applies to: 392-412
274-277:⚠️ Potential issue | 🔴 CriticalUpdate minimum Node.js engine requirement to match AbortSignal.any() availability.
The SDK declares
engines.node >= 18.20.0in package.json, but usesAbortSignal.any()(line 276) which requires Node 20.3.0+. This mismatch will cause runtime errors for Node 18.x and Node 20.0–20.2.x users.Either:
- Update
engines.nodeto>= 20.3.0in package.json, or- Replace
AbortSignal.any()with manual signal composition for backward compatibility⛔ Skipped due to learnings
Learnt from: nicktrn Repo: triggerdotdev/trigger.dev PR: 2593 File: packages/core/src/v3/workers/warmStartClient.ts:168-170 Timestamp: 2025-10-08T11:48:12.327Z Learning: The trigger.dev runners execute only in Node 21 and 22 environments, so modern Node.js APIs like AbortSignal.any (introduced in v20.3.0) are supported.Learnt from: CR Repo: triggerdotdev/trigger.dev PR: 0 File: .github/copilot-instructions.md:0-0 Timestamp: 2025-11-27T16:26:37.432Z Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific codeLearnt from: CR Repo: triggerdotdev/trigger.dev PR: 0 File: AGENTS.md:0-0 Timestamp: 2026-01-15T10:48:02.687Z Learning: Use pnpm as the package manager (version 10.23.0 or later) and Node.js 20.20.0
| ``` | ||
| NDJSON bytes arrive | ||
| | | ||
| createNdjsonParserStream | ||
| |-- Line <= limit --> parse JSON --> enqueue object | ||
| `-- Line > limit --> extractIndexAndTask(bytes) --> enqueue OversizedItemMarker | ||
| | | ||
| StreamBatchItemsService for-await loop | ||
| |-- OversizedItemMarker --> engine.enqueueBatchItem() with __error in options | ||
| `-- Normal item --> validate --> engine.enqueueBatchItem() | ||
| | | ||
| FairQueue consumer (#handleMessage) | ||
| |-- __error in options --> processItemCallback detects it | ||
| | --> TriggerFailedTaskService.call() | ||
| | --> Creates pre-failed TaskRun with SYSTEM_FAILURE status | ||
| | --> Proper waitpoint + TaskRunWaitpoint connections created | ||
| | --> Returns { success: true, runId: failedRunFriendlyId } | ||
| `-- Normal item --> TriggerTaskService.call() --> creates normal run | ||
| | | ||
| Batch sealing: enqueuedCount === runCount (all items go through enqueueBatchItem) | ||
| Batch completion: all items have runs (real or pre-failed), waitpoints resolve normally | ||
| Parent run: batchTriggerAndWait resolves with per-item results | ||
| ``` |
There was a problem hiding this comment.
Add a language identifier to the fenced block (Line 216).
This fence is missing a language tag, which trips markdownlint MD040.
📝 Proposed fix
-```
+```text
NDJSON bytes arrive
|
createNdjsonParserStream
|-- Line <= limit --> parse JSON --> enqueue object
`-- Line > limit --> extractIndexAndTask(bytes) --> enqueue OversizedItemMarker
|
StreamBatchItemsService for-await loop
|-- OversizedItemMarker --> engine.enqueueBatchItem() with __error in options
`-- Normal item --> validate --> engine.enqueueBatchItem()
|
FairQueue consumer (`#handleMessage`)
|-- __error in options --> processItemCallback detects it
| --> TriggerFailedTaskService.call()
| --> Creates pre-failed TaskRun with SYSTEM_FAILURE status
| --> Proper waitpoint + TaskRunWaitpoint connections created
| --> Returns { success: true, runId: failedRunFriendlyId }
`-- Normal item --> TriggerTaskService.call() --> creates normal run
|
Batch sealing: enqueuedCount === runCount (all items go through enqueueBatchItem)
Batch completion: all items have runs (real or pre-failed), waitpoints resolve normally
Parent run: batchTriggerAndWait resolves with per-item results</details>
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 216-216: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.scratch/plan-graceful-oversized-batch-items.md around lines 216 - 238, The
fenced code block that starts with "NDJSON bytes arrive" is missing a language
identifier which triggers markdownlint MD040; update the opening fence to
include a language (e.g., change ``` to ```text) so the block is explicitly
marked as plain text (ensure the closing fence remains ```), preserving the
existing block contents and indentation.
| let _chatPipeCount = 0; | ||
|
|
There was a problem hiding this comment.
Scope manual-pipe tracking per run/turn; global _chatPipeCount is race-prone.
_chatPipeCount is module-global, so concurrent chat runs can mutate the same counter and incorrectly suppress or trigger auto-piping in other runs.
💡 Suggested fix (run-scoped counter)
-import {
+import {
accessoryAttributes,
AnyTask,
isSchemaZodEsque,
SemanticInternalAttributes,
Task,
+ taskContext,
type inferSchemaIn,
type PipeStreamOptions,
type TaskIdentifier,
type TaskOptions,
type TaskSchema,
type TaskWithSchema,
} from "@trigger.dev/core/v3";
-let _chatPipeCount = 0;
+const _chatPipeCountByRun = new Map<string, number>();
async function pipeChat(
source: UIMessageStreamable | AsyncIterable<unknown> | ReadableStream<unknown>,
options?: PipeChatOptions
): Promise<void> {
- _chatPipeCount++;
+ const runId = taskContext.ctx?.run.id;
+ if (runId) {
+ _chatPipeCountByRun.set(runId, (_chatPipeCountByRun.get(runId) ?? 0) + 1);
+ }
const streamKey = options?.streamKey ?? CHAT_STREAM_KEY;
// ...
}
// inside chatTask turn:
- _chatPipeCount = 0;
+ const activeRunId = taskContext.ctx?.run.id;
+ if (activeRunId) _chatPipeCountByRun.set(activeRunId, 0);
- if (_chatPipeCount === 0 && isUIMessageStreamable(result)) {
+ if ((activeRunId ? _chatPipeCountByRun.get(activeRunId) ?? 0 : 0) === 0 && isUIMessageStreamable(result)) {
await pipeChat(result, { signal: combinedSignal, spanName: "stream response" });
}Also applies to: 360-361, 548-583
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/trigger-sdk/src/v3/ai.ts` around lines 268 - 269, The global
_chatPipeCount is race-prone; make the pipe counter scoped to each chat run/turn
instead. Remove the module-global _chatPipeCount and initialize a run-scoped
counter (for example add a numeric property like run.__chatPipeCount or
turn.chatPipeCount on the ChatRun/ChatTurn object when a run/turn is created or
at the start of the function that currently uses _chatPipeCount), then replace
every reference to _chatPipeCount (including uses around lines 360-361 and
548-583) with the run/turn-scoped property and update increment/decrement logic
to use that property so concurrent runs don’t share state. Ensure the counter is
initialized to 0 at run start and cleaned up or left on the run object when
finished.
| const pendingMessages: ChatTaskWirePayload[] = []; | ||
| const msgSub = messagesInput.on((msg) => { | ||
| pendingMessages.push(msg); | ||
| }); |
There was a problem hiding this comment.
Do not drop extra buffered messages captured during a turn.
At Line 605 only the first queued message is used, while additional messages collected at Line 561 are discarded. Since these were already consumed by the handler, they are lost.
💡 Suggested fix (preserve backlog across turns)
- let currentWirePayload = payload;
+ let currentWirePayload = payload;
+ const queuedMessages: ChatTaskWirePayload[] = [];
// ...
const pendingMessages: ChatTaskWirePayload[] = [];
const msgSub = messagesInput.on((msg) => {
pendingMessages.push(msg);
});
// ...
if (pendingMessages.length > 0) {
- currentWirePayload = pendingMessages[0]!;
+ currentWirePayload = pendingMessages.shift()!;
+ if (pendingMessages.length > 0) {
+ queuedMessages.push(...pendingMessages);
+ }
return "continue";
}
+ if (queuedMessages.length > 0) {
+ currentWirePayload = queuedMessages.shift()!;
+ return "continue";
+ }Also applies to: 605-607
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/trigger-sdk/src/v3/ai.ts` around lines 560 - 563, The handler
currently captures multiple messages into pendingMessages via messagesInput.on
(msgSub) but later only consumes the first entry, discarding the rest; change
the logic that extracts from pendingMessages (where it currently uses a single
element) to drain and preserve the entire backlog — e.g., move all items from
pendingMessages into the processing queue (or append them to the existing
messages array) before proceeding, then clean up the subscription (msgSub) as
before so no buffered messages are lost; update references to pendingMessages,
msgSub, and messagesInput.on to reflect this full-drain behavior.
No description provided.