Commit Graph

7144 Commits

Author SHA1 Message Date
Eric Allam 5a7fb627e5 docs: add mermaid architecture diagrams for ai-chat system 2026-04-17 16:19:23 +01:00
Eric Allam ad291c44d2 feat(chat): add preload support, dynamic tools, and preload-specific timeouts
- Add onPreload hook and preloaded field to all lifecycle events
- Add transport.preload(chatId) for eagerly starting runs before first message
- Add preloadWarmTimeoutInSeconds and preloadTimeout task options
- Add preload:true run tag and chat.preloaded span attributes
- Add UserTool model for per-user dynamic tools loaded from DB
- Load dynamic tools in onPreload/onChatStart via chat.local
- Build dynamicTool() instances in run and spread into streamText tools
- Reference project: preload on new chat, dynamic company-info and user-preferences tools
2026-04-17 16:19:23 +01:00
Eric Allam 8363b4462a feat(ai): pass chat context and toolCallId to subtasks, add typed ai.chatContext helpers
- Store chat turn context (chatId, turn, continuation, clientData) in locals for auto-detection
- toolFromTask now auto-detects chat context and passes it to subtask metadata
- Skip serializing messages array (can be large, rarely needed by subtasks)
- Tag subtask runs with toolCallId for dashboard visibility
- Add ai.toolCallId() convenience helper
- Add ai.chatContext<typeof myChat>() with typed clientData inference
- Add ai.chatContextOrThrow<typeof myChat>() that throws if not in a chat context
- Update deepResearch example to use ai.chatContextOrThrow
- Document all helpers in ai-chat guide
2026-04-17 16:19:23 +01:00
Eric Allam 2101829404 feat(chat): expose typed chat.stream, add deepResearch subtask example, per-chat model persistence, debug panel
- Export chat.stream (typed RealtimeDefinedStream<UIMessageChunk>) for writing custom data to the chat stream
- Add deepResearch subtask using data-* chunks to stream progress back to parent chat via target: root
- Use AI SDK data-research-progress chunk protocol with id-based updates for live progress
- Add ResearchProgress component and generic data-* fallback renderer in frontend
- Persist model per chat in DB (schema + onChatStart), model selector only on new chats
- Add collapsible debug panel showing run ID (with dashboard link), chat ID, model, status, session info
- Document chat.stream API, data-* chunks, and subtask streaming pattern in docs
2026-04-17 16:19:23 +01:00
Eric Allam cb6d99decf Some improvements to the example ai-chat 2026-04-17 16:19:23 +01:00
Eric Allam 5b527a5439 feat(chat): add stop handling, abort cleanup, continuation support, and reference project enhancements
- Fix onFinish race condition: await onFinishPromise so capturedResponseMessage is set before accumulation
- Add chat.isStopped() helper accessible from anywhere during a turn
- Add chat.cleanupAbortedParts() to remove incomplete tool/reasoning/text parts on stop
- Auto-cleanup aborted parts before passing to onTurnComplete
- Clean incoming messages from frontend to prevent tool_use without tool_result API errors
- Add stopped and rawResponseMessage fields to TurnCompleteEvent
- Add continuation and previousRunId fields to all lifecycle hooks and run payload
- Add span attributes (chat.id, chat.turn, chat.stopped, chat.continuation, chat.previous_run_id, etc.)
- Add webFetch tool and reasoning model support to ai-chat reference project
- Render reasoning parts in frontend chat component
- Document all new fields in ai-chat guide
2026-04-17 16:19:23 +01:00
Eric Allam 2369297145 feat: add chat.local for per-run typed data with Proxy access and dirty tracking 2026-04-17 16:19:23 +01:00
Eric Allam 610067f29a Make clientData typesafe and pass to all chat.task hooks 2026-04-17 16:19:23 +01:00
Eric Allam 4a1a0f1701 Add triggerOptions to the transport, auto-tag with the chat ID 2026-04-17 16:19:23 +01:00
Eric Allam 600069ffa1 use locals for the chat pipe counter instead of a module global 2026-04-17 16:19:23 +01:00
Eric Allam 3ae26683a6 fixed chat tests 2026-04-17 16:19:23 +01:00
Eric Allam fb03c199a3 update pnpm link file 2026-04-17 16:19:23 +01:00
Eric Allam be405bbe28 Minor fixes around reconnecting streams 2026-04-17 16:19:23 +01:00
Eric Allam a59e37c07e feat: add onTurnStart hook, lastEventId support, and stream resume deduplication 2026-04-17 16:19:23 +01:00
Eric Allam a7b439f42e remove postinstall hook 2026-04-17 16:19:23 +01:00
Eric Allam c3d9378283 Add ai chat to the sidebar for now 2026-04-17 16:19:23 +01:00
Eric Allam 081c569401 build full example with persisting messages, adding necessary hooks, and documenting it all 2026-04-17 16:19:23 +01:00
Eric Allam ef3940d666 Accumulate messages in the task, allowing us to only have to send user mesages from the transport 2026-04-17 16:19:23 +01:00
Eric Allam 88ed29740e Add some pending message support in the example 2026-04-17 16:19:23 +01:00
Eric Allam 7f0f0ad1d1 Fix double looping when resuming from an input stream waitpoint 2026-04-17 16:19:23 +01:00
Eric Allam 3558dfa236 Added better telemetry support to view turns 2026-04-17 16:19:23 +01:00
Eric Allam beb89976cd provide already converted UIMessages to the run function for better dx 2026-04-17 16:19:23 +01:00
Eric Allam 8f242b3e21 Add clientData support 2026-04-17 16:19:23 +01:00
Eric Allam 80ce669327 Add warmTimeoutInSeconds option 2026-04-17 16:19:23 +01:00
Eric Allam 885af35c4b add stopping support and fix issue with the OpenAI responses API and stopped streams 2026-04-17 16:19:23 +01:00
Eric Allam a0bc91a21d use input streams and rename chatTask and chatState to chat.task and chat.state 2026-04-17 16:19:23 +01:00
Eric Allam bf229d6faa expose a useTriggerChatTransport hook 2026-04-17 16:19:23 +01:00
Eric Allam b8d0483729 Added tool example 2026-04-17 16:19:23 +01:00
Eric Allam 1467591123 Use a single run with iterative waitpoint token completions 2026-04-17 16:19:23 +01:00
Cursor Agent d14c30bc5f fix(reference): use compatible @ai-sdk v3 packages, await convertToModelMessages
@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>
2026-04-17 16:19:23 +01:00
Cursor Agent 72557e7c56 feat(reference): add ai-chat Next.js reference project
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>
2026-04-17 16:19:23 +01:00
Cursor Agent 2161f19895 docs(ai): add AI Chat with useChat guide
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>
2026-04-17 16:19:23 +01:00
Cursor Agent e517281d94 fix: address CodeRabbit review feedback
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>
2026-04-17 16:19:23 +01:00
Cursor Agent 428c7aed4d chore: update changeset to target @trigger.dev/sdk
Co-authored-by: Eric Allam <eric@trigger.dev>
2026-04-17 16:19:23 +01:00
Cursor Agent 1e0fe3573f refactor: delete packages/ai/ — moved to @trigger.dev/sdk subpaths
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>
2026-04-17 16:19:23 +01:00
Cursor Agent 26a7ed719e test: move chat transport tests to @trigger.dev/sdk
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>
2026-04-17 16:19:23 +01:00
Cursor Agent c3d95d749d feat: add chat transport and AI chat helpers to @trigger.dev/sdk
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>
2026-04-17 16:19:23 +01:00
Cursor Agent 1886e25c38 refactor: avoid double-resolving accessToken in sendMessages
Use the already-resolved token when creating ApiClient instead of
calling resolveAccessToken() again through getApiClient().

Co-authored-by: Eric Allam <eric@trigger.dev>
2026-04-17 16:19:23 +01:00
Cursor Agent a65268b45f feat: support dynamic accessToken function for token refresh
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>
2026-04-17 16:19:23 +01:00
Cursor Agent fc72884466 refactor: remove internal ChatSessionState from public exports
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>
2026-04-17 16:19:23 +01:00
Cursor Agent e3c3b5090f chore: add changeset for @trigger.dev/ai package
Co-authored-by: Eric Allam <eric@trigger.dev>
2026-04-17 16:19:23 +01:00
Cursor Agent ae44d7312f test: add abort signal, multiple sessions, and body merging tests
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>
2026-04-17 16:19:23 +01:00
Cursor Agent 4fc9b0ff53 refactor: polish TriggerChatTransport implementation
- 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>
2026-04-17 16:19:23 +01:00
Cursor Agent 358f75e7e8 test: add comprehensive unit tests for TriggerChatTransport
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>
2026-04-17 16:19:23 +01:00
Cursor Agent 87fd2d885d feat: add @trigger.dev/ai package with TriggerChatTransport
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>
2026-04-17 16:19:23 +01:00
Iss 7d7ebdde52 feat: Increase default project limit per org from 10 to 25 (#3409) 2026-04-17 11:05:57 -04:00
nicktrn 9a988ab885 chore(webapp): clarify admin feature flags are global (#3408)
global flags are global.
2026-04-17 13:49:30 +01:00
nicktrn 581db83f64 feat(webapp): highlight microVM regions on the regions page (#3407)
Adds a `MicroVM` badge next to the region name on the regions page. Uses
the existing `small` badge variant for visual consistency with the
`Default` badge already on this page.
2026-04-17 13:40:05 +01:00
Eric Allam 69acdc2b32 fix(core): truncate large error stacks and messages to prevent OOM (#3405)
## Summary

Large error stacks and messages can OOM the worker process when
serialized into OTel spans or `TaskRunError` objects. This was reported
when throwing an error with a massive `.stack` property from a chat
agent hook.

This adds frame-based stack truncation (similar to Sentry's approach)
plus message length limits, applied consistently across all error
serialization paths.

### What changed

**`packages/core/src/v3/errors.ts`**
- `truncateStack()` — parses `error.stack` into message lines + frame
lines, caps at 50 frames (keep top 5 closest to throw + bottom 45 entry
points, with "... N frames omitted ..." in between). Individual lines
capped at 1024 chars.
- `truncateMessage()` — caps error messages at 1000 chars
- Applied in `parseError()` and `sanitizeError()`

**`packages/core/src/v3/otel/utils.ts`**
- `sanitizeSpanError()` now uses `truncateStack` and `truncateMessage`
from `errors.ts` instead of duplicating truncation logic
- Non-Error values (strings, JSON) capped at 5000 chars

**`packages/core/src/v3/tracer.ts`**
- `startActiveSpan` catch block now delegates to `recordSpanException()`
instead of calling `span.recordException()` directly

### Limits

| What | Limit | Rationale |
|------|-------|-----------|
| Stack frames | 50 | Matches Sentry's `STACKTRACE_FRAME_LIMIT` |
| Top frames kept | 5 | Closest to throw site |
| Bottom frames kept | 45 | Entry points / framework frames |
| Per-line length | 1024 | Matches Sentry, prevents regex DoS |
| Message length | 1000 | Bounded but generous |
| Generic string (non-Error) | 5000 | Fallback for JSON/string errors in
spans |

## Test plan

- [x] 17 unit tests in `packages/core/test/errors.test.ts`
- [x] E2E: threw a 300-frame / 5000-char-message error in the ai-chat
reference app, verified truncated stack and message in span via
`get_span_details`
- [x] Verified the run survived the error (no OOM, continued waiting for
next message)
2026-04-17 13:25:01 +01:00
Matt Aitken 45ba398c80 Error page graph: for a time bucket don't fill zeros for a version with no errors (#3402)
This caused performance issues with large numbers of versions, and bad
UX when hovering the graph (showing irrelevant versions)
2026-04-17 10:12:54 +01:00