-
chore: release v4.5.0-rc.0 (#3563)
发布于
2026-05-21 13:39:02 +00:00 | 825 次提交 在此版本后已推送到 mainSummary
44 improvements, 1 bug fix.
Improvements
- AI Prompts — define prompt templates as code alongside your tasks,
version them on deploy, and override the text or model from the
dashboard without redeploying. Prompts integrate with the Vercel AI SDK
viatoAISDKTelemetry()(links every generation span back to the
prompt) and withchat.agentviachat.prompt.set()+
chat.toStreamTextOptions().
(#3629) - Code-defined, deploy-versioned templates — define with
prompts.define({ id, model, config, variables, content }). Every
deploy creates a new version visible in the dashboard. Mustache-style
placeholders ({{var}},{{#cond}}...{{/cond}}) with Zod / ArkType /
Valibot-typed variables. - Dashboard overrides — change a prompt's text or model from the
dashboard without redeploying. Overrides take priority over the deployed
"current" version and are environment-scoped (dev / staging / production
independent). - Resolve API —
prompt.resolve(vars, { version?, label? })returns
the compiledtext, resolvedmodel,version, and labels. Standalone
prompts.resolve<typeof handle>(slug, vars)for cross-file resolution
with full type inference on slug and variable shape. - AI SDK integration — spread
resolved.toAISDKTelemetry({ ...extra })into anygenerateText/streamTextcall and every generation
span links to the prompt in the dashboard alongside its input variables,
model, tokens, and cost. chat.agentintegration —chat.prompt.set(resolved)stores the
resolved prompt run-scoped;chat.toStreamTextOptions({ registry })
pullssystem,model(resolved via the AI SDK provider registry),
temperature/maxTokens/ etc., and telemetry into a single spread
forstreamText.- Management SDK —
prompts.list(),prompts.versions(slug),
prompts.promote(slug, version),prompts.createOverride(slug, body),
prompts.updateOverride(slug, body),prompts.removeOverride(slug),
prompts.reactivateOverride(slug, version). - Dashboard — prompts list with per-prompt usage sparklines;
per-prompt detail with Template / Details / Versions / Generations /
Metrics tabs. AI generation spans get a custom inspector showing the
linked prompt's metadata, input variables, and template content
alongside model, tokens, cost, and the message thread. - Adds
onBoottochat.agent— a lifecycle hook that fires once per
worker process picking up the chat. Runs for the initial run, preloaded
runs, AND reactive continuation runs (post-cancel, crash,endRun,
requestUpgrade, OOM retry), before any other hook. Use it to
initializechat.local, open per-process resources, or re-hydrate state
from your DB on continuation — anywhere the SAME run picking up after
suspend/resume isn't enough.
(#3543) - AI SDK
useChatintegration — a custom
ChatTransport
(useTriggerChatTransport) plugs straight into Vercel AI SDK's
useChathook. Text streaming, tool calls, reasoning, anddata-*
parts all work natively over Trigger.dev's realtime streams. No custom
API routes needed. - First-turn fast path (
chat.headStart) — opt-in handler that runs
the first turn'sstreamTextstep in your warm server process while the
agent run boots in parallel, cutting cold-start TTFC by roughly half
(measured 2801ms → 1218ms onclaude-sonnet-4-6). The agent owns step
2+ (tool execution, persistence, hooks) so heavy deps stay where they
belong. Web Fetch handler works natively in Next.js, Hono, SvelteKit,
Remix, Workers, etc.; bridge to Express/Fastify/Koa via
chat.toNodeListener. New@trigger.dev/sdk/chat-serversubpath. - Multi-turn durability via Sessions — every chat is backed by a
durable Session that outlives any individual run. Conversations resume
across page refreshes, idle timeout, crashes, and deploys;resume: truereconnects vialastEventIdso clients only see new chunks.
sessions.listenumerates chats for inbox-style UIs. - Auto-accumulated history, delta-only wire — the backend
accumulates the full conversation across turns; clients only ship the
new message each turn. Long chats never hit the 512 KiB body cap.
RegisterhydrateMessagesto be the source of truth yourself. - Lifecycle hooks —
onPreload,onChatStart,
onValidateMessages,hydrateMessages,onTurnStart,
onBeforeTurnComplete,onTurnComplete,onChatSuspend,
onChatResume— for persistence, validation, and post-turn work. - Stop generation — client-driven
transport.stopGeneration(chatId)
aborts mid-stream; the run stays alive for the next message, partial
response is captured, and aborted parts (stuckpartial-calltools,
in-progress reasoning) are auto-cleaned. - Tool approvals (HITL) — tools with
needsApproval: truepause
until the user approves or denies viaaddToolApprovalResponse. The
runtime reconciles the updated assistant message by ID and continues
streamText. - Steering and background injection —
pendingMessagesinjects user
messages between tool-call steps so users can steer the agent
mid-execution;chat.inject()+chat.defer()adds context from
background work (self-review, RAG, safety checks) between turns. - Actions — non-turn frontend commands (undo, rollback, regenerate,
edit) sent viatransport.sendAction. FirehydrateMessages+
onActiononly — no turn hooks, norun().onActioncan return a
StreamTextResultfor a model response, orvoidfor side-effect-only. - Typed state primitives —
chat.local<T>for per-run state
accessible from hooks,run(), tools, and subtasks (auto-serialized
throughai.toolExecute);chat.storefor typed shared data between
agent and client;chat.historyfor reading and mutating the message
chain;clientDataSchemafor typedclientDatain every hook. chat.toStreamTextOptions()— one spread intostreamTextwires
up versioned system Prompts,
model resolution, telemetry metadata, compaction, steering, and
background injection.- Multi-tab coordination —
multiTab: true+useMultiTabChat
prevents duplicate sends and syncs state across browser tabs via
BroadcastChannel. Non-active tabs go read-only with live updates. - Network resilience — built-in indefinite retry with bounded
backoff, reconnect ononline/ tab refocus / bfcache restore,
Last-Event-IDmid-stream resume. No app code needed. - Sessions — a durable, run-aware stream channel keyed on a stable
externalId. A Session is the unit of state that owns a multi-run
conversation: messages flow through.in, responses through.out,
both survive run boundaries. Sessions back the newchat.agentruntime,
and you can build on them directly for any pattern that needs durable
bi-directional streaming across runs.
(#3542) - Add
ai.toolExecute(task)so you can wire a Trigger subtask in as the
executehandler of an AI SDKtool()while definingdescriptionand
inputSchemayourself — useful when you want full control over the tool
surface and just need Trigger's subtask machinery for the body.
(#3546) - Type
chat.createStartSessionActionagainst your chat agent so
clientDatais typed end-to-end on the first turn:
(#3684) - Add
regionto the runs list / retrieve API: filter runs by region
(runs.list({ region: "..." })/filter[region]=<masterQueue>) and
read each run's executing region from the newregionfield on the
response.
(#3612) - Add
TRIGGER_BUILD_SKIP_REWRITE_TIMESTAMP=1escape hatch for local
self-hosted builds whose buildx driver doesn't support
rewrite-timestampalongside push (e.g. orbstack's defaultdocker
driver).
(#3618) - Reject overlong
idempotencyKeyvalues at the API boundary so they no
longer trip an internal size limit on the underlying unique index and
surface as a generic 500. Inputs are capped at 2048 characters — well
above whatidempotencyKeys.create()produces (a 64-character hash) and
above any realistic raw key. Applies totasks.trigger,
tasks.batchTrigger,batch.create(Phase 1 streaming batches),
wait.createToken,wait.forDuration, and the input/session stream
waitpoint endpoints. Over-limit requests now return a structured 400
instead.
(#3560) - AI SDK
useChatintegration — a custom
ChatTransport
(useTriggerChatTransport) plugs straight into Vercel AI SDK's
useChathook. Text streaming, tool calls, reasoning, anddata-*
parts all work natively over Trigger.dev's realtime streams. No custom
API routes needed. - First-turn fast path (
chat.headStart) — opt-in handler that runs
the first turn'sstreamTextstep in your warm server process while the
agent run boots in parallel, cutting cold-start TTFC by roughly half
(measured 2801ms → 1218ms onclaude-sonnet-4-6). The agent owns step
2+ (tool execution, persistence, hooks) so heavy deps stay where they
belong. Web Fetch handler works natively in Next.js, Hono, SvelteKit,
Remix, Workers, etc.; bridge to Express/Fastify/Koa via
chat.toNodeListener. New@trigger.dev/sdk/chat-serversubpath. - Multi-turn durability via Sessions — every chat is backed by a
durable Session that outlives any individual run. Conversations resume
across page refreshes, idle timeout, crashes, and deploys;resume: truereconnects vialastEventIdso clients only see new chunks.
sessions.listenumerates chats for inbox-style UIs. - Auto-accumulated history, delta-only wire — the backend
accumulates the full conversation across turns; clients only ship the
new message each turn. Long chats never hit the 512 KiB body cap.
RegisterhydrateMessagesto be the source of truth yourself. - Lifecycle hooks —
onPreload,onChatStart,
onValidateMessages,hydrateMessages,onTurnStart,
onBeforeTurnComplete,onTurnComplete,onChatSuspend,
onChatResume— for persistence, validation, and post-turn work. - Stop generation — client-driven
transport.stopGeneration(chatId)
aborts mid-stream; the run stays alive for the next message, partial
response is captured, and aborted parts (stuckpartial-calltools,
in-progress reasoning) are auto-cleaned. - Tool approvals (HITL) — tools with
needsApproval: truepause
until the user approves or denies viaaddToolApprovalResponse. The
runtime reconciles the updated assistant message by ID and continues
streamText. - Steering and background injection —
pendingMessagesinjects user
messages between tool-call steps so users can steer the agent
mid-execution;chat.inject()+chat.defer()adds context from
background work (self-review, RAG, safety checks) between turns. - Actions — non-turn frontend commands (undo, rollback, regenerate,
edit) sent viatransport.sendAction. FirehydrateMessages+
onActiononly — no turn hooks, norun().onActioncan return a
StreamTextResultfor a model response, orvoidfor side-effect-only. - Typed state primitives —
chat.local<T>for per-run state
accessible from hooks,run(), tools, and subtasks (auto-serialized
throughai.toolExecute);chat.storefor typed shared data between
agent and client;chat.historyfor reading and mutating the message
chain;clientDataSchemafor typedclientDatain every hook. chat.toStreamTextOptions()— one spread intostreamTextwires
up versioned system Prompts,
model resolution, telemetry metadata, compaction, steering, and
background injection.- Multi-tab coordination —
multiTab: true+useMultiTabChat
prevents duplicate sends and syncs state across browser tabs via
BroadcastChannel. Non-active tabs go read-only with live updates. - Network resilience — built-in indefinite retry with bounded
backoff, reconnect ononline/ tab refocus / bfcache restore,
Last-Event-IDmid-stream resume. No app code needed. - Retry
TASK_PROCESS_SIGSEGVtask crashes under the user's retry
policy instead of failing the run on the first segfault. SIGSEGV in Node
tasks is frequently non-deterministic (native addon races, JIT/GC
interaction, near-OOM in native code, host issues), so retrying on a
fresh process often succeeds. The retry is gated by the task's existing
retryconfig +maxAttempts— same pathTASK_PROCESS_SIGTERMand
uncaught exceptions already use — so tasks without a retry policy still
fail fast.
(#3552) - The public interfaces for a plugin system. Initially consolidated
authentication and authorization interfaces.
(#3499) - Add MollifierBuffer and MollifierDrainer primitives for trigger burst
smoothing.
(#3614)
Bug fixes
- Fix
LocalsKey<T>type incompatibility across dual-package builds.
The phantom value-type brand no longer uses a module-levelunique symbol, so a single TypeScript compilation that resolves the type from
both the ESM and CJS outputs (which can happen under certain pnpm
hoisting layouts) no longer sees two structurally-incompatible variants
of the same type.
(#3626)
Raw changeset output
⚠️⚠️⚠️⚠️⚠️⚠️
mainis currently in pre mode so this branch has prereleases
rather than normal releases. If you want to exit prereleases, run
changeset pre exitonmain.⚠️⚠️⚠️⚠️⚠️⚠️
Releases
@trigger.dev/sdk@4.5.0-rc.0
Minor Changes
-
AI Prompts — define prompt templates as code alongside your tasks,
version them on deploy, and override the text or model from the
dashboard without redeploying. Prompts integrate with the Vercel AI SDK
viatoAISDKTelemetry()(links every generation span back to the
prompt) and withchat.agentviachat.prompt.set()+
chat.toStreamTextOptions().
(#3629)import { prompts } from "@trigger.dev/sdk"; import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; import { z } from "zod"; export const supportPrompt = prompts.define({ id: "customer-support", model: "gpt-4o", config: { temperature: 0.7 }, variables: z.object({ customerName: z.string(), plan: z.string(), issue: z.string(), }), content: `You are a support agent for Acme. Customer: {{customerName}} ({{plan}} plan) Issue: {{issue}}`, }); const resolved = await supportPrompt.resolve({ customerName: "Alice", plan: "Pro", issue: "Can't access billing", }); const result = await generateText({ model: openai(resolved.model ?? "gpt-4o"), system: resolved.text, prompt: "Can't access billing", ...resolved.toAISDKTelemetry(), });What you get:
-
Code-defined, deploy-versioned templates — define with
prompts.define({ id, model, config, variables, content }). Every
deploy creates a new version visible in the dashboard. Mustache-style
placeholders ({{var}},{{#cond}}...{{/cond}}) with Zod / ArkType /
Valibot-typed variables. -
Dashboard overrides — change a prompt's text or model from the
dashboard without redeploying. Overrides take priority over the deployed
"current" version and are environment-scoped (dev / staging / production
independent). -
Resolve API —
prompt.resolve(vars, { version?, label? })returns
the compiledtext, resolvedmodel,version, and labels. Standalone
prompts.resolve<typeof handle>(slug, vars)for cross-file resolution
with full type inference on slug and variable shape. -
AI SDK integration — spread
resolved.toAISDKTelemetry({ ...extra })into anygenerateText/streamTextcall and every generation
span links to the prompt in the dashboard alongside its input variables,
model, tokens, and cost. -
chat.agentintegration —chat.prompt.set(resolved)stores the
resolved prompt run-scoped;chat.toStreamTextOptions({ registry })
pullssystem,model(resolved via the AI SDK provider registry),
temperature/maxTokens/ etc., and telemetry into a single spread
forstreamText. -
Management SDK —
prompts.list(),prompts.versions(slug),
prompts.promote(slug, version),prompts.createOverride(slug, body),
prompts.updateOverride(slug, body),prompts.removeOverride(slug),
prompts.reactivateOverride(slug, version). -
Dashboard — prompts list with per-prompt usage sparklines;
per-prompt detail with Template / Details / Versions / Generations /
Metrics tabs. AI generation spans get a custom inspector showing the
linked prompt's metadata, input variables, and template content
alongside model, tokens, cost, and the message thread.
See /docs/ai/prompts for the full
reference — template syntax, version resolution order, override
workflow, and type utilities (PromptHandle,PromptIdentifier,
PromptVariables).-
Adds
onBoottochat.agent— a lifecycle hook that fires once per
worker process picking up the chat. Runs for the initial run, preloaded
runs, AND reactive continuation runs (post-cancel, crash,endRun,
requestUpgrade, OOM retry), before any other hook. Use it to
initializechat.local, open per-process resources, or re-hydrate state
from your DB on continuation — anywhere the SAME run picking up after
suspend/resume isn't enough.
(#3543)
const userContext = chat.local<{ name: string; plan: string }>({ id:
"userContext" });export const myChat = chat.agent({ id: "my-chat", onBoot: async ({ clientData, continuation }) => {const user = await db.user.findUnique({ where: { id: clientData.userId }
});
userContext.init({ name: user.name, plan: user.plan });
},
run: async ({ messages, signal }) =>
streamText({ model: openai("gpt-4o"), messages, abortSignal: signal }),
});
```Use
onBoot(notonChatStart) for state setup that must run every
time a worker picks up the chat —onChatStartfires once per chat and
won't run on continuation, leavingchat.localuninitialized when
run()tries to use it.-
AI Agents — run AI SDK chat completions as durable Trigger.dev
agents instead of fragile API routes. Define an agent in one function,
pointuseChatat it from React, and the conversation survives page
refreshes, network blips, and process restarts.
(#3543)import { chat } from "@trigger.dev/sdk/ai"; import { streamText } from "ai"; import { openai } from "@ai-sdk/openai"; export const myChat = chat.agent({ id: "my-chat", run: async ({ messages, signal }) =>
streamText({ model: openai("gpt-4o"), messages, abortSignal: signal }),
});
``````tsx import { useChat } from "@ai-sdk/react";import { useTriggerChatTransport } from "@trigger.dev/sdk/chat/react";
const transport = useTriggerChatTransport({ task: "my-chat",
accessToken, startSession });
const { messages, sendMessage } = useChat({ transport });
```**What you get:**- AI SDK
useChatintegration — a custom
ChatTransport
(useTriggerChatTransport) plugs straight into Vercel AI SDK's
useChathook. Text streaming, tool calls, reasoning, anddata-*
parts all work natively over Trigger.dev's realtime streams. No custom
API routes needed. - First-turn fast path (
chat.headStart) — opt-in handler that runs
the first turn'sstreamTextstep in your warm server process while the
agent run boots in parallel, cutting cold-start TTFC by roughly half
(measured 2801ms → 1218ms onclaude-sonnet-4-6). The agent owns step
2+ (tool execution, persistence, hooks) so heavy deps stay where they
belong. Web Fetch handler works natively in Next.js, Hono, SvelteKit,
Remix, Workers, etc.; bridge to Express/Fastify/Koa via
chat.toNodeListener. New@trigger.dev/sdk/chat-serversubpath. - Multi-turn durability via Sessions — every chat is backed by a
durable Session that outlives any individual run. Conversations resume
across page refreshes, idle timeout, crashes, and deploys;resume: truereconnects vialastEventIdso clients only see new chunks.
sessions.listenumerates chats for inbox-style UIs. - Auto-accumulated history, delta-only wire — the backend
accumulates the full conversation across turns; clients only ship the
new message each turn. Long chats never hit the 512 KiB body cap.
RegisterhydrateMessagesto be the source of truth yourself. - Lifecycle hooks —
onPreload,onChatStart,
onValidateMessages,hydrateMessages,onTurnStart,
onBeforeTurnComplete,onTurnComplete,onChatSuspend,
onChatResume— for persistence, validation, and post-turn work. - Stop generation — client-driven
transport.stopGeneration(chatId)
aborts mid-stream; the run stays alive for the next message, partial
response is captured, and aborted parts (stuckpartial-calltools,
in-progress reasoning) are auto-cleaned. - Tool approvals (HITL) — tools with
needsApproval: truepause
until the user approves or denies viaaddToolApprovalResponse. The
runtime reconciles the updated assistant message by ID and continues
streamText. - Steering and background injection —
pendingMessagesinjects user
messages between tool-call steps so users can steer the agent
mid-execution;chat.inject()+chat.defer()adds context from
background work (self-review, RAG, safety checks) between turns. - Actions — non-turn frontend commands (undo, rollback, regenerate,
edit) sent viatransport.sendAction. FirehydrateMessages+
onActiononly — no turn hooks, norun().onActioncan return a
StreamTextResultfor a model response, orvoidfor side-effect-only. - Typed state primitives —
chat.local<T>for per-run state
accessible from hooks,run(), tools, and subtasks (auto-serialized
throughai.toolExecute);chat.storefor typed shared data between
agent and client;chat.historyfor reading and mutating the message
chain;clientDataSchemafor typedclientDatain every hook. chat.toStreamTextOptions()— one spread intostreamTextwires
up versioned system Prompts,
model resolution, telemetry metadata, compaction, steering, and
background injection.- Multi-tab coordination —
multiTab: true+useMultiTabChat
prevents duplicate sends and syncs state across browser tabs via
BroadcastChannel. Non-active tabs go read-only with live updates. - Network resilience — built-in indefinite retry with bounded
backoff, reconnect ononline/ tab refocus / bfcache restore,
Last-Event-IDmid-stream resume. No app code needed.
See /docs/ai-chat for the
full surface — quick start, three backend approaches (chat.agent,
chat.createSession, raw task), persistence and code-sandbox patterns,
type-level guides, and API reference.- Add read primitives to
chat.historyfor HITL flows:
getPendingToolCalls(),getResolvedToolCalls(),
extractNewToolResults(message),getChain(), and
findMessage(messageId). These lift the accumulator-walking logic that
customers building human-in-the-loop tools were re-implementing into the
SDK. (#3543)
Use
getPendingToolCalls()to gate fresh user turns while a tool call
is awaiting an answer. UseextractNewToolResults(message)to dedup
tool results when persisting to your own store — the helper returns only
the parts whosetoolCallIdis not already resolved on the chain.```ts const pending = chat.history.getPendingToolCalls(); if (pending.length > 0) { // an addToolOutput is expected before a new user message } onTurnComplete: async ({ responseMessage }) => {const newResults = chat.history.extractNewToolResults(responseMessage);
for (const r of newResults) {
await db.toolResults.upsert({ id: r.toolCallId, output: r.output,
errorText: r.errorText });
}
};
```-
Sessions — a durable, run-aware stream channel keyed on a stable
externalId. A Session is the unit of state that owns a multi-run
conversation: messages flow through.in, responses through.out,
both survive run boundaries. Sessions back the newchat.agentruntime,
and you can build on them directly for any pattern that needs durable
bi-directional streaming across runs.
(#3542)import { sessions, tasks } from "@trigger.dev/sdk"; // Trigger a task and subscribe to its session output in one call
const { runId, stream } = await tasks.triggerAndSubscribe("my-task",
payload, {
externalId: "user-456",
});for await (const chunk of stream) { // ... }// Enumerate existing sessions (powers inbox-style UIs without a
separate index)
for await (const s of sessions.list({ type: "chat.agent", tag:
"user:user-456" })) {
console.log(s.id, s.externalId, s.createdAt, s.closedAt);
}
```See /docs/ai-chat/overview
for the full surface — Sessions powers the durable, resumable chat
runtime described there.Patch Changes
-
Add Agent Skills for
chat.agent. Drop a folder with aSKILL.mdand
any helper scripts/references next to your task code, register it with
skills.define({ id, path }), and the CLI bundles it into the deploy
image automatically — notrigger.config.tschanges. The agent gets a
one-line summary in its system prompt and discovers full instructions on
demand vialoadSkill, withbashandreadFiletools scoped
per-skill (path-traversal guards, output caps, abort-signal
propagation).
(#3543)
const pdfSkill = skills.define({ id: "pdf-extract", path:
"./skills/pdf-extract" });chat.skills.set([await pdfSkill.local()]); ```Built on the AI SDK cookbook
pattern — portable
across providers. SDK + CLI only for now; dashboard-editableSKILL.md
text is on the roadmap.-
Add
ai.toolExecute(task)so you can wire a Trigger subtask in as the
executehandler of an AI SDKtool()while definingdescriptionand
inputSchemayourself — useful when you want full control over the tool
surface and just need Trigger's subtask machinery for the body.
(#3546)const myTool = tool({ description: "...", inputSchema: z.object({ ... }), execute: ai.toolExecute(mySubtask), });
ai.tool(task)(toolFromTask) keeps doing the all-in-one wrap and now
aligns its return type with AI SDK'sToolSet. Minimumaipeer raised
to^6.0.116to avoid cross-versionToolSetmismatches in monorepos.-
Stamp
gen_ai.conversation.id(the chat id) on every span and metric
emitted from inside achat.taskorchat.agentrun. Lets you filter
dashboard spans, runs, and metrics by the chat conversation that
produced them — independent of the run boundary, so multi-run chats
correlate cleanly. No code changes required on the user side.
(#3543) -
Type
chat.createStartSessionActionagainst your chat agent so
clientDatais typed end-to-end on the first turn:
(#3684)import { chat } from "@trigger.dev/sdk/ai"; import type { myChat } from "@/trigger/chat";
export const startChatSession = chat.createStartSessionAction("my-chat");
// In the browser, threaded from the transport's typed startSession
callback:
const transport = useTriggerChatTransport({
task: "my-chat",
startSession: ({ chatId, clientData }) => startChatSession({ chatId,
clientData }),
// ...
});
```ChatStartSessionParamsgains a typedclientDatafield — folded into
the first run'spayload.metadatasoonPreload/onChatStartsee
the same shape per-turnmetadatacarries via the transport. The opaque
session-levelmetadatafield is unchanged.- Unit-test
chat.agentdefinitions offline withmockChatAgentfrom
@trigger.dev/sdk/ai/test. Drives a real agent's turn loop in-process —
no network, no task runtime — so you can send messages, actions, and
stop signals via driver methods, inspect captured output chunks, and
verify hooks fire. Pairs withMockLanguageModelV3fromai/testfor
model mocking.setupLocalslets you pre-seedlocals(DB clients,
service stubs) beforerun()starts.
(#3543)
The broader
runInMockTaskContextharness it's built on lives at
@trigger.dev/core/v3/test— useful for unit-testing any task code, not
just chat.-
Add
regionto the runs list / retrieve API: filter runs by region
(runs.list({ region: "..." })/filter[region]=<masterQueue>) and
read each run's executing region from the newregionfield on the
response.
(#3612) -
Updated dependencies:
@trigger.dev/core@4.5.0-rc.0
@trigger.dev/build@4.5.0-rc.0
Patch Changes
-
Add Agent Skills for
chat.agent. Drop a folder with aSKILL.mdand
any helper scripts/references next to your task code, register it with
skills.define({ id, path }), and the CLI bundles it into the deploy
image automatically — notrigger.config.tschanges. The agent gets a
one-line summary in its system prompt and discovers full instructions on
demand vialoadSkill, withbashandreadFiletools scoped
per-skill (path-traversal guards, output caps, abort-signal
propagation).
(#3543)
const pdfSkill = skills.define({ id: "pdf-extract", path:
"./skills/pdf-extract" });chat.skills.set([await pdfSkill.local()]); ```Built on the AI SDK cookbook
pattern — portable
across providers. SDK + CLI only for now; dashboard-editableSKILL.md
text is on the roadmap.- Updated dependencies:
@trigger.dev/core@4.5.0-rc.0
trigger.dev@4.5.0-rc.0
Patch Changes
-
Add Agent Skills for
chat.agent. Drop a folder with aSKILL.mdand
any helper scripts/references next to your task code, register it with
skills.define({ id, path }), and the CLI bundles it into the deploy
image automatically — notrigger.config.tschanges. The agent gets a
one-line summary in its system prompt and discovers full instructions on
demand vialoadSkill, withbashandreadFiletools scoped
per-skill (path-traversal guards, output caps, abort-signal
propagation).
(#3543)
const pdfSkill = skills.define({ id: "pdf-extract", path:
"./skills/pdf-extract" });chat.skills.set([await pdfSkill.local()]); ```Built on the AI SDK cookbook
pattern — portable
across providers. SDK + CLI only for now; dashboard-editableSKILL.md
text is on the roadmap.-
Add
TRIGGER_BUILD_SKIP_REWRITE_TIMESTAMP=1escape hatch for local
self-hosted builds whose buildx driver doesn't support
rewrite-timestampalongside push (e.g. orbstack's defaultdocker
driver).
(#3618) -
The CLI MCP server's agent-chat tools (
start_agent_chat,
send_agent_message,close_agent_chat) now run on the new Sessions
primitive, so AI assistants driving achat.agentget the same
idempotent-by-chatId, durable-across-runs behavior the browser
transport gets. Required PAT scopes go fromwrite:inputStreamsto
read:sessions+write:sessions.
(#3546) -
MCP
list_runstool: add aregionfilter input and surface each
run's executing region in the formatted summary.
(#3612) -
Updated dependencies:
@trigger.dev/core@4.5.0-rc.0@trigger.dev/build@4.5.0-rc.0@trigger.dev/schema-to-json@4.5.0-rc.0
@trigger.dev/core@4.5.0-rc.0
Patch Changes
-
Add Agent Skills for
chat.agent. Drop a folder with aSKILL.mdand
any helper scripts/references next to your task code, register it with
skills.define({ id, path }), and the CLI bundles it into the deploy
image automatically — notrigger.config.tschanges. The agent gets a
one-line summary in its system prompt and discovers full instructions on
demand vialoadSkill, withbashandreadFiletools scoped
per-skill (path-traversal guards, output caps, abort-signal
propagation).
(#3543)
const pdfSkill = skills.define({ id: "pdf-extract", path:
"./skills/pdf-extract" });chat.skills.set([await pdfSkill.local()]); ```Built on the AI SDK cookbook
pattern — portable
across providers. SDK + CLI only for now; dashboard-editableSKILL.md
text is on the roadmap.-
Reject overlong
idempotencyKeyvalues at the API boundary so they no
longer trip an internal size limit on the underlying unique index and
surface as a generic 500. Inputs are capped at 2048 characters — well
above whatidempotencyKeys.create()produces (a 64-character hash) and
above any realistic raw key. Applies totasks.trigger,
tasks.batchTrigger,batch.create(Phase 1 streaming batches),
wait.createToken,wait.forDuration, and the input/session stream
waitpoint endpoints. Over-limit requests now return a structured 400
instead.
(#3560) -
AI Agents — run AI SDK chat completions as durable Trigger.dev
agents instead of fragile API routes. Define an agent in one function,
pointuseChatat it from React, and the conversation survives page
refreshes, network blips, and process restarts.
(#3543)import { chat } from "@trigger.dev/sdk/ai"; import { streamText } from "ai"; import { openai } from "@ai-sdk/openai"; export const myChat = chat.agent({ id: "my-chat", run: async ({ messages, signal }) =>
streamText({ model: openai("gpt-4o"), messages, abortSignal: signal }),
});
``````tsx import { useChat } from "@ai-sdk/react";import { useTriggerChatTransport } from "@trigger.dev/sdk/chat/react";
const transport = useTriggerChatTransport({ task: "my-chat",
accessToken, startSession });
const { messages, sendMessage } = useChat({ transport });
```**What you get:**- AI SDK
useChatintegration — a custom
ChatTransport
(useTriggerChatTransport) plugs straight into Vercel AI SDK's
useChathook. Text streaming, tool calls, reasoning, anddata-*
parts all work natively over Trigger.dev's realtime streams. No custom
API routes needed. - First-turn fast path (
chat.headStart) — opt-in handler that runs
the first turn'sstreamTextstep in your warm server process while the
agent run boots in parallel, cutting cold-start TTFC by roughly half
(measured 2801ms → 1218ms onclaude-sonnet-4-6). The agent owns step
2+ (tool execution, persistence, hooks) so heavy deps stay where they
belong. Web Fetch handler works natively in Next.js, Hono, SvelteKit,
Remix, Workers, etc.; bridge to Express/Fastify/Koa via
chat.toNodeListener. New@trigger.dev/sdk/chat-serversubpath. - Multi-turn durability via Sessions — every chat is backed by a
durable Session that outlives any individual run. Conversations resume
across page refreshes, idle timeout, crashes, and deploys;resume: truereconnects vialastEventIdso clients only see new chunks.
sessions.listenumerates chats for inbox-style UIs. - Auto-accumulated history, delta-only wire — the backend
accumulates the full conversation across turns; clients only ship the
new message each turn. Long chats never hit the 512 KiB body cap.
RegisterhydrateMessagesto be the source of truth yourself. - Lifecycle hooks —
onPreload,onChatStart,
onValidateMessages,hydrateMessages,onTurnStart,
onBeforeTurnComplete,onTurnComplete,onChatSuspend,
onChatResume— for persistence, validation, and post-turn work. - Stop generation — client-driven
transport.stopGeneration(chatId)
aborts mid-stream; the run stays alive for the next message, partial
response is captured, and aborted parts (stuckpartial-calltools,
in-progress reasoning) are auto-cleaned. - Tool approvals (HITL) — tools with
needsApproval: truepause
until the user approves or denies viaaddToolApprovalResponse. The
runtime reconciles the updated assistant message by ID and continues
streamText. - Steering and background injection —
pendingMessagesinjects user
messages between tool-call steps so users can steer the agent
mid-execution;chat.inject()+chat.defer()adds context from
background work (self-review, RAG, safety checks) between turns. - Actions — non-turn frontend commands (undo, rollback, regenerate,
edit) sent viatransport.sendAction. FirehydrateMessages+
onActiononly — no turn hooks, norun().onActioncan return a
StreamTextResultfor a model response, orvoidfor side-effect-only. - Typed state primitives —
chat.local<T>for per-run state
accessible from hooks,run(), tools, and subtasks (auto-serialized
throughai.toolExecute);chat.storefor typed shared data between
agent and client;chat.historyfor reading and mutating the message
chain;clientDataSchemafor typedclientDatain every hook. chat.toStreamTextOptions()— one spread intostreamTextwires
up versioned system Prompts,
model resolution, telemetry metadata, compaction, steering, and
background injection.- Multi-tab coordination —
multiTab: true+useMultiTabChat
prevents duplicate sends and syncs state across browser tabs via
BroadcastChannel. Non-active tabs go read-only with live updates. - Network resilience — built-in indefinite retry with bounded
backoff, reconnect ononline/ tab refocus / bfcache restore,
Last-Event-IDmid-stream resume. No app code needed.
See /docs/ai-chat for the
full surface — quick start, three backend approaches (chat.agent,
chat.createSession, raw task), persistence and code-sandbox patterns,
type-level guides, and API reference.-
Stamp
gen_ai.conversation.id(the chat id) on every span and metric
emitted from inside achat.taskorchat.agentrun. Lets you filter
dashboard spans, runs, and metrics by the chat conversation that
produced them — independent of the run boundary, so multi-run chats
correlate cleanly. No code changes required on the user side.
(#3543) -
Fix
LocalsKey<T>type incompatibility across dual-package builds.
The phantom value-type brand no longer uses a module-levelunique symbol, so a single TypeScript compilation that resolves the type from
both the ESM and CJS outputs (which can happen under certain pnpm
hoisting layouts) no longer sees two structurally-incompatible variants
of the same type.
(#3626) -
Unit-test
chat.agentdefinitions offline withmockChatAgentfrom
@trigger.dev/sdk/ai/test. Drives a real agent's turn loop in-process —
no network, no task runtime — so you can send messages, actions, and
stop signals via driver methods, inspect captured output chunks, and
verify hooks fire. Pairs withMockLanguageModelV3fromai/testfor
model mocking.setupLocalslets you pre-seedlocals(DB clients,
service stubs) beforerun()starts.
(#3543)
The broader
runInMockTaskContextharness it's built on lives at
@trigger.dev/core/v3/test— useful for unit-testing any task code, not
just chat.-
Retry
TASK_PROCESS_SIGSEGVtask crashes under the user's retry
policy instead of failing the run on the first segfault. SIGSEGV in Node
tasks is frequently non-deterministic (native addon races, JIT/GC
interaction, near-OOM in native code, host issues), so retrying on a
fresh process often succeeds. The retry is gated by the task's existing
retryconfig +maxAttempts— same pathTASK_PROCESS_SIGTERMand
uncaught exceptions already use — so tasks without a retry policy still
fail fast.
(#3552) -
Add
regionto the runs list / retrieve API: filter runs by region
(runs.list({ region: "..." })/filter[region]=<masterQueue>) and
read each run's executing region from the newregionfield on the
response.
(#3612) -
Sessions — a durable, run-aware stream channel keyed on a stable
externalId. A Session is the unit of state that owns a multi-run
conversation: messages flow through.in, responses through.out,
both survive run boundaries. Sessions back the newchat.agentruntime,
and you can build on them directly for any pattern that needs durable
bi-directional streaming across runs.
(#3542)import { sessions, tasks } from "@trigger.dev/sdk"; // Trigger a task and subscribe to its session output in one call
const { runId, stream } = await tasks.triggerAndSubscribe("my-task",
payload, {
externalId: "user-456",
});for await (const chunk of stream) { // ... }// Enumerate existing sessions (powers inbox-style UIs without a
separate index)
for await (const s of sessions.list({ type: "chat.agent", tag:
"user:user-456" })) {
console.log(s.id, s.externalId, s.createdAt, s.closedAt);
}
```See /docs/ai-chat/overview
for the full surface — Sessions powers the durable, resumable chat
runtime described there.@trigger.dev/plugins@4.5.0-rc.0
Patch Changes
- The public interfaces for a plugin system. Initially consolidated
authentication and authorization interfaces.
(#3499) - Updated dependencies:
@trigger.dev/core@4.5.0-rc.0
@trigger.dev/python@4.5.0-rc.0
Patch Changes
- Updated dependencies:
@trigger.dev/sdk@4.5.0-rc.0@trigger.dev/core@4.5.0-rc.0@trigger.dev/build@4.5.0-rc.0
@trigger.dev/react-hooks@4.5.0-rc.0
Patch Changes
- Updated dependencies:
@trigger.dev/core@4.5.0-rc.0
@trigger.dev/redis-worker@4.5.0-rc.0
Patch Changes
- Add MollifierBuffer and MollifierDrainer primitives for trigger burst
smoothing.
(#3614)
MollifierBuffer (
accept,pop,ack,requeue,fail,
evaluateTrip) is a per-env FIFO over Redis with atomic Lua transitions
for status tracking.evaluateTripis a sliding-window trip evaluator
the webapp gate uses to detect per-env trigger bursts.MollifierDrainer pops entries through a polling loop with a
user-supplied handler. The loop survives transient Redis errors via
capped exponential backoff (up to 5s), and per-env pop failures don't
poison the rest of the batch — one env's blip is logged and counted as
failed for that tick. Rotation is two-level: orgs at the top, envs
within each org. The buffer maintainsmollifier:orgsand
mollifier:org-envs:${orgId}atomically with per-env queues, so the
drainer walks orgs → envs directly without an in-memory cache. The
maxOrgsPerTickoption (default 500) caps how many orgs are scheduled
per tick; for each picked org, one env is popped (rotating round-robin
within the org). An org with N envs gets the same per-tick scheduling
slot as an org with 1 env, so tenant-level drainage throughput is
determined by org count rather than env count.- Updated dependencies:
@trigger.dev/core@4.5.0-rc.0
@trigger.dev/rsc@4.5.0-rc.0
Patch Changes
- Updated dependencies:
@trigger.dev/core@4.5.0-rc.0
@trigger.dev/schema-to-json@4.5.0-rc.0
Patch Changes
- Updated dependencies:
@trigger.dev/core@4.5.0-rc.0
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
下载附件
- AI Prompts — define prompt templates as code alongside your tasks,