@trigger.dev/sdk 4.5.0-rc.7 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/v3/ai-shared.js +1 -4
- package/dist/commonjs/v3/ai-shared.js.map +1 -1
- package/dist/commonjs/v3/ai.d.ts +13 -10
- package/dist/commonjs/v3/ai.js +121 -66
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/auth.d.ts +1 -1
- package/dist/commonjs/v3/auth.js +3 -4
- package/dist/commonjs/v3/auth.js.map +1 -1
- package/dist/commonjs/v3/batch.d.ts +1 -1
- package/dist/commonjs/v3/batch.js.map +1 -1
- package/dist/commonjs/v3/chat-client.js +18 -14
- package/dist/commonjs/v3/chat-client.js.map +1 -1
- package/dist/commonjs/v3/chat-react.js +2 -4
- package/dist/commonjs/v3/chat-react.js.map +1 -1
- package/dist/commonjs/v3/chat-server.d.ts +69 -1
- package/dist/commonjs/v3/chat-server.js +162 -14
- package/dist/commonjs/v3/chat-server.js.map +1 -1
- package/dist/commonjs/v3/chat-server.test.js +156 -12
- package/dist/commonjs/v3/chat-server.test.js.map +1 -1
- package/dist/commonjs/v3/chat.js +4 -3
- package/dist/commonjs/v3/chat.js.map +1 -1
- package/dist/commonjs/v3/chat.test.js +9 -48
- package/dist/commonjs/v3/chat.test.js.map +1 -1
- package/dist/commonjs/v3/deployments.d.ts +2 -3
- package/dist/commonjs/v3/deployments.js.map +1 -1
- package/dist/commonjs/v3/envvars.js.map +1 -1
- package/dist/commonjs/v3/idempotencyKeys.js.map +1 -1
- package/dist/commonjs/v3/metadata.d.ts +2 -2
- package/dist/commonjs/v3/prompt.js +2 -6
- package/dist/commonjs/v3/prompt.js.map +1 -1
- package/dist/commonjs/v3/promptManagement.js.map +1 -1
- package/dist/commonjs/v3/query.js.map +1 -1
- package/dist/commonjs/v3/queues.d.ts +1 -1
- package/dist/commonjs/v3/queues.js.map +1 -1
- package/dist/commonjs/v3/retry.d.ts +1 -1
- package/dist/commonjs/v3/retry.js +1 -1
- package/dist/commonjs/v3/retry.js.map +1 -1
- package/dist/commonjs/v3/runs.d.ts +3 -4
- package/dist/commonjs/v3/runs.js.map +1 -1
- package/dist/commonjs/v3/schedules/index.d.ts +3 -3
- package/dist/commonjs/v3/schedules/index.js.map +1 -1
- package/dist/commonjs/v3/sessions.d.ts +3 -4
- package/dist/commonjs/v3/sessions.js +2 -1
- package/dist/commonjs/v3/sessions.js.map +1 -1
- package/dist/commonjs/v3/shared.d.ts +3 -4
- package/dist/commonjs/v3/shared.js +0 -12
- package/dist/commonjs/v3/shared.js.map +1 -1
- package/dist/commonjs/v3/skill.js.map +1 -1
- package/dist/commonjs/v3/streams.d.ts +1 -1
- package/dist/commonjs/v3/streams.js +4 -5
- package/dist/commonjs/v3/streams.js.map +1 -1
- package/dist/commonjs/v3/streams.test.js +3 -3
- package/dist/commonjs/v3/streams.test.js.map +1 -1
- package/dist/commonjs/v3/test/mock-chat-agent.js +1 -4
- package/dist/commonjs/v3/test/mock-chat-agent.js.map +1 -1
- package/dist/commonjs/v3/test/test-session-handle.d.ts +2 -1
- package/dist/commonjs/v3/test/test-session-handle.js.map +1 -1
- package/dist/commonjs/v3/triggerClient.test.js +1 -1
- package/dist/commonjs/v3/triggerClient.test.js.map +1 -1
- package/dist/commonjs/v3/triggerClient.types.test.js +11 -11
- package/dist/commonjs/v3/triggerClient.types.test.js.map +1 -1
- package/dist/commonjs/v3/wait.d.ts +2 -1
- package/dist/commonjs/v3/wait.js.map +1 -1
- package/dist/commonjs/v3/webhooks.js +1 -1
- package/dist/commonjs/v3/webhooks.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/ai-shared.js +1 -4
- package/dist/esm/v3/ai-shared.js.map +1 -1
- package/dist/esm/v3/ai.d.ts +13 -10
- package/dist/esm/v3/ai.js +119 -54
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/auth.d.ts +1 -1
- package/dist/esm/v3/auth.js +1 -2
- package/dist/esm/v3/auth.js.map +1 -1
- package/dist/esm/v3/batch.d.ts +1 -1
- package/dist/esm/v3/batch.js +1 -1
- package/dist/esm/v3/batch.js.map +1 -1
- package/dist/esm/v3/chat-client.js +18 -14
- package/dist/esm/v3/chat-client.js.map +1 -1
- package/dist/esm/v3/chat-react.js +2 -4
- package/dist/esm/v3/chat-react.js.map +1 -1
- package/dist/esm/v3/chat-server.d.ts +69 -1
- package/dist/esm/v3/chat-server.js +162 -14
- package/dist/esm/v3/chat-server.js.map +1 -1
- package/dist/esm/v3/chat-server.test.js +156 -12
- package/dist/esm/v3/chat-server.test.js.map +1 -1
- package/dist/esm/v3/chat.js +4 -3
- package/dist/esm/v3/chat.js.map +1 -1
- package/dist/esm/v3/chat.test.js +9 -48
- package/dist/esm/v3/chat.test.js.map +1 -1
- package/dist/esm/v3/deployments.d.ts +2 -3
- package/dist/esm/v3/deployments.js +1 -1
- package/dist/esm/v3/deployments.js.map +1 -1
- package/dist/esm/v3/envvars.js.map +1 -1
- package/dist/esm/v3/idempotencyKeys.js +1 -1
- package/dist/esm/v3/idempotencyKeys.js.map +1 -1
- package/dist/esm/v3/metadata.d.ts +2 -2
- package/dist/esm/v3/prompt.js +2 -6
- package/dist/esm/v3/prompt.js.map +1 -1
- package/dist/esm/v3/promptManagement.js.map +1 -1
- package/dist/esm/v3/query.js.map +1 -1
- package/dist/esm/v3/queues.d.ts +1 -1
- package/dist/esm/v3/queues.js.map +1 -1
- package/dist/esm/v3/retry.d.ts +1 -1
- package/dist/esm/v3/retry.js +1 -1
- package/dist/esm/v3/retry.js.map +1 -1
- package/dist/esm/v3/runs.d.ts +2 -3
- package/dist/esm/v3/runs.js.map +1 -1
- package/dist/esm/v3/schedules/index.d.ts +3 -3
- package/dist/esm/v3/schedules/index.js.map +1 -1
- package/dist/esm/v3/sessions.d.ts +3 -4
- package/dist/esm/v3/sessions.js +2 -1
- package/dist/esm/v3/sessions.js.map +1 -1
- package/dist/esm/v3/shared.d.ts +3 -4
- package/dist/esm/v3/shared.js +1 -13
- package/dist/esm/v3/shared.js.map +1 -1
- package/dist/esm/v3/skill.js.map +1 -1
- package/dist/esm/v3/streams.d.ts +1 -1
- package/dist/esm/v3/streams.js +4 -5
- package/dist/esm/v3/streams.js.map +1 -1
- package/dist/esm/v3/streams.test.js +4 -4
- package/dist/esm/v3/streams.test.js.map +1 -1
- package/dist/esm/v3/test/mock-chat-agent.js +4 -7
- package/dist/esm/v3/test/mock-chat-agent.js.map +1 -1
- package/dist/esm/v3/test/test-session-handle.d.ts +2 -1
- package/dist/esm/v3/test/test-session-handle.js +1 -1
- package/dist/esm/v3/test/test-session-handle.js.map +1 -1
- package/dist/esm/v3/triggerClient.test.js +1 -1
- package/dist/esm/v3/triggerClient.test.js.map +1 -1
- package/dist/esm/v3/triggerClient.types.test.js +11 -11
- package/dist/esm/v3/triggerClient.types.test.js.map +1 -1
- package/dist/esm/v3/wait.d.ts +2 -1
- package/dist/esm/v3/wait.js.map +1 -1
- package/dist/esm/v3/webhooks.js +1 -1
- package/dist/esm/v3/webhooks.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/docs/ai/prompts.mdx +0 -4
- package/docs/ai-chat/actions.mdx +0 -4
- package/docs/ai-chat/anatomy.mdx +0 -4
- package/docs/ai-chat/backend.mdx +0 -4
- package/docs/ai-chat/background-injection.mdx +0 -4
- package/docs/ai-chat/changelog.mdx +109 -1
- package/docs/ai-chat/chat-local.mdx +0 -4
- package/docs/ai-chat/client-protocol.mdx +0 -4
- package/docs/ai-chat/compaction.mdx +0 -4
- package/docs/ai-chat/custom-agents.mdx +32 -4
- package/docs/ai-chat/error-handling.mdx +0 -4
- package/docs/ai-chat/fast-starts.mdx +86 -4
- package/docs/ai-chat/frontend.mdx +0 -4
- package/docs/ai-chat/how-it-works.mdx +0 -4
- package/docs/ai-chat/lifecycle-hooks.mdx +0 -4
- package/docs/ai-chat/mcp.mdx +0 -4
- package/docs/ai-chat/overview.mdx +0 -4
- package/docs/ai-chat/patterns/branching-conversations.mdx +0 -4
- package/docs/ai-chat/patterns/code-sandbox.mdx +0 -4
- package/docs/ai-chat/patterns/database-persistence.mdx +0 -4
- package/docs/ai-chat/patterns/human-in-the-loop.mdx +9 -5
- package/docs/ai-chat/patterns/large-payloads.mdx +0 -4
- package/docs/ai-chat/patterns/oom-resilience.mdx +0 -4
- package/docs/ai-chat/patterns/persistence-and-replay.mdx +0 -4
- package/docs/ai-chat/patterns/recovery-boot.mdx +0 -4
- package/docs/ai-chat/patterns/skills.mdx +0 -4
- package/docs/ai-chat/patterns/sub-agents.mdx +0 -4
- package/docs/ai-chat/patterns/tool-result-auditing.mdx +0 -4
- package/docs/ai-chat/patterns/trusted-edge-signals.mdx +0 -4
- package/docs/ai-chat/patterns/version-upgrades.mdx +0 -4
- package/docs/ai-chat/pending-messages.mdx +0 -4
- package/docs/ai-chat/prompt-caching.mdx +4 -4
- package/docs/ai-chat/quick-start.mdx +0 -4
- package/docs/ai-chat/reference.mdx +1 -5
- package/docs/ai-chat/server-chat.mdx +0 -4
- package/docs/ai-chat/sessions.mdx +0 -4
- package/docs/ai-chat/testing.mdx +0 -4
- package/docs/ai-chat/tools.mdx +0 -4
- package/docs/ai-chat/types.mdx +0 -4
- package/docs/ai-chat/upgrade-guide.mdx +0 -4
- package/docs/building-with-ai.mdx +1 -1
- package/docs/deploy-environment-variables.mdx +1 -1
- package/docs/deployment/dev-branches.mdx +92 -0
- package/docs/deployment/overview.mdx +1 -1
- package/docs/help-slack.mdx +1 -0
- package/docs/how-to-reduce-your-spend.mdx +14 -7
- package/docs/introduction.mdx +13 -13
- package/docs/mcp-agent-rules.mdx +1 -1
- package/docs/open-source-self-hosting.mdx +1 -1
- package/docs/self-hosting/kubernetes.mdx +8 -8
- package/docs/self-hosting/overview.mdx +2 -1
- package/docs/tasks/scheduled.mdx +2 -2
- package/docs/troubleshooting-debugging-in-vscode.mdx +1 -0
- package/docs/troubleshooting-github-issues.mdx +1 -1
- package/docs/troubleshooting-uptime-status.mdx +1 -0
- package/docs/video-walkthrough.mdx +1 -1
- package/docs/wait-for.mdx +1 -1
- package/package.json +2 -2
- package/docs/cli-dev.mdx +0 -8
|
@@ -4,10 +4,6 @@ sidebarTitle: "Background injection"
|
|
|
4
4
|
description: "Inject context from background work into the agent's conversation — self-review, RAG augmentation, or any async analysis."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
## Overview
|
|
12
8
|
|
|
13
9
|
`chat.inject()` queues model messages for injection into the conversation. Messages are picked up at the start of the next turn or at the next `prepareStep` boundary (between tool-call steps).
|
|
@@ -1,9 +1,117 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Changelog"
|
|
3
3
|
sidebarTitle: "Changelog"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Changelog for the AI Agents SDK."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
<Update label="July 2, 2026" description="4.5.0" tags={["SDK", "Bug fix"]}>
|
|
8
|
+
|
|
9
|
+
## Point chat sessions at a different project or environment
|
|
10
|
+
|
|
11
|
+
[`chat.headStart`](/ai-chat/fast-starts#head-start) and [`chat.createStartSessionAction`](/ai-chat/sessions) now take an `apiClient` option (`baseURL` + `accessToken`). The server that creates the session and triggers the run can target a different project or environment than its ambient Trigger config, without setting a global `TRIGGER_SECRET_KEY`. Useful when your `chat.agent` lives in a separate project from the app serving the route, or when one server starts chats across more than one environment. Your LLM provider keys stay in the `run` callback and are unaffected. ([#4018](https://github.com/triggerdotdev/trigger.dev/pull/4018))
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
export const POST = chat.headStart({
|
|
15
|
+
agentId: "my-agent",
|
|
16
|
+
apiClient: { baseURL, accessToken },
|
|
17
|
+
run: async ({ chat }) =>
|
|
18
|
+
streamText({ ...chat.toStreamTextOptions({ tools }), model: anthropic("claude-sonnet-4-6") }),
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const startSession = chat.createStartSessionAction("my-chat", {
|
|
24
|
+
apiClient: { baseURL, accessToken },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await startSession({ chatId, clientData });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Fix: chat agents on preview branches
|
|
31
|
+
|
|
32
|
+
Messaging a `chat.agent` (or `AgentChat`) deployed to a preview branch failed with `x-trigger-branch header required for preview env`. The realtime message-append and stream-subscribe calls now send the `x-trigger-branch` header, resolved the same way `sessions.start` resolves it, so preview-branch chat agents work. ([#4018](https://github.com/triggerdotdev/trigger.dev/pull/4018))
|
|
33
|
+
|
|
34
|
+
## Fix: Head Start handovers with a prepareMessages hook
|
|
35
|
+
|
|
36
|
+
A [Head Start](/ai-chat/fast-starts#head-start) handover passes the first turn's pending tool call to the agent as a tool-approval round whose trailing tool message must reach the model untouched. A `prepareMessages` hook that rewrites the last message (for example the recommended [prompt-caching](/ai-chat/prompt-caching) breakpoint) could disturb that tail, so the turn failed with `tool_use ids were found without tool_result`. The agent now preserves the approval tail across `prepareMessages`, so prompt caching and Head Start compose cleanly. ([#4018](https://github.com/triggerdotdev/trigger.dev/pull/4018))
|
|
37
|
+
|
|
38
|
+
</Update>
|
|
39
|
+
|
|
40
|
+
<Update label="June 17, 2026" description="4.5.0-rc.7" tags={["SDK", "CLI", "Bug fix"]}>
|
|
41
|
+
|
|
42
|
+
## chat.headStart now works for custom agents and sessions
|
|
43
|
+
|
|
44
|
+
[Head Start](/ai-chat/fast-starts#head-start) — running the first `streamText` step in your warm server while the agent boots in parallel — now works with the `chat.customAgent` and `chat.createSession` backends, not just the managed `chat.agent`. The warm step-1 response hands over to your loop the same way it does for a managed agent. ([#3963](https://github.com/triggerdotdev/trigger.dev/pull/3963))
|
|
45
|
+
|
|
46
|
+
In a `chat.customAgent` loop, consume the handover on turn 0:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
const conversation = new chat.MessageAccumulator();
|
|
50
|
+
const { isFinal, skipped } = await conversation.consumeHandover({ payload });
|
|
51
|
+
if (skipped) return; // warm handler aborted, so exit without a turn
|
|
52
|
+
if (isFinal) {
|
|
53
|
+
await chat.writeTurnComplete(); // step 1 is the response, no streamText
|
|
54
|
+
} else {
|
|
55
|
+
const result = streamText({ model, messages: conversation.modelMessages, tools });
|
|
56
|
+
// Pass originalMessages so the handed-over tool round merges into the
|
|
57
|
+
// step-1 assistant instead of starting a new message.
|
|
58
|
+
const response = await chat.pipeAndCapture(result, {
|
|
59
|
+
originalMessages: conversation.uiMessages,
|
|
60
|
+
});
|
|
61
|
+
if (response) await conversation.addResponse(response);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
With `chat.createSession`, the iterator surfaces it as `turn.handover`; call `turn.complete()` with no argument on a final handover. The lower-level `chat.waitForHandover()` and `accumulator.applyHandover()` are also exported for hand-rolled loops. See [Handover with custom agents](/ai-chat/fast-starts#handover-with-custom-agents).
|
|
66
|
+
|
|
67
|
+
## Cache your system prompt with Anthropic prompt caching
|
|
68
|
+
|
|
69
|
+
`chat.toStreamTextOptions()` can now emit the system prompt as a cacheable message when you opt in, so a large, stable system block is billed at cache-read rates on every turn instead of full price. Without an option, `system` stays a plain string. ([#3952](https://github.com/triggerdotdev/trigger.dev/pull/3952))
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
// at the streamText call site (Anthropic sugar)
|
|
73
|
+
streamText({
|
|
74
|
+
...chat.toStreamTextOptions({ cacheControl: { type: "ephemeral" } }),
|
|
75
|
+
messages,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// provider-agnostic equivalent
|
|
79
|
+
chat.toStreamTextOptions({
|
|
80
|
+
systemProviderOptions: { anthropic: { cacheControl: { type: "ephemeral" } } },
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// or where the prompt is defined
|
|
84
|
+
chat.prompt.set(SYSTEM_PROMPT, {
|
|
85
|
+
providerOptions: { anthropic: { cacheControl: { type: "ephemeral" } } },
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Pairs with a `prepareMessages` cache breakpoint to cache the conversation prefix across turns too. See the [Prompt caching](/ai-chat/prompt-caching) guide.
|
|
90
|
+
|
|
91
|
+
## Custom agent loop fixes
|
|
92
|
+
|
|
93
|
+
Three fixes for custom agent loops (`chat.customAgent`, `chat.createSession`, and hand-rolled `MessageAccumulator` loops): ([#3936](https://github.com/triggerdotdev/trigger.dev/pull/3936))
|
|
94
|
+
|
|
95
|
+
- **Continuations no longer replay answered messages.** The `.in` resume cursor is now seeded before any listener attaches (the same boot logic `chat.agent` uses), so a chat that continues after a cancel, crash, or upgrade only sees genuinely new messages.
|
|
96
|
+
- **Steering mid-stream keeps the in-flight response.** `chat.pipeAndCapture` now stamps a server-generated message id on the stream, so a `prepareStep` injection keeps the partial text instead of replacing the message.
|
|
97
|
+
- **Task-backed tools work from custom loops.** `ai.toolExecute` now threads the parent's session to the child run, so child tasks can stream progress into the chat with `chat.stream.writer({ target: "root" })` instead of failing with "session handle is not initialized".
|
|
98
|
+
|
|
99
|
+
See [Custom agents](/ai-chat/custom-agents).
|
|
100
|
+
|
|
101
|
+
## trigger skills: namespacing, docs bundling, and a cost-savings skill
|
|
102
|
+
|
|
103
|
+
Follow-ups to the [`trigger skills`](/ai-chat/patterns/skills) command shipped in rc.6:
|
|
104
|
+
|
|
105
|
+
- The skills installed into your coding assistant are now namespaced with a `trigger-` prefix (e.g. `trigger-authoring-tasks`, `trigger-getting-started`) so they don't collide with unrelated skills in your skills directory. ([#3970](https://github.com/triggerdotdev/trigger.dev/pull/3970))
|
|
106
|
+
- `@trigger.dev/sdk` now bundles the Trigger.dev agent skills and the full Trigger.dev documentation those skills reference. The installed skills read this content from `node_modules`, so the guidance your AI assistant follows is pinned to the SDK version in your project and stays current across upgrades instead of going stale until the next reinstall. ([#3937](https://github.com/triggerdotdev/trigger.dev/pull/3937), [#3970](https://github.com/triggerdotdev/trigger.dev/pull/3970))
|
|
107
|
+
- New `trigger-cost-savings` skill for auditing and reducing compute spend — right-sizing machines, `maxDuration`, batching, and debounce. ([#3970](https://github.com/triggerdotdev/trigger.dev/pull/3970))
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npx trigger.dev@4.5.0-rc.7 skills
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
</Update>
|
|
114
|
+
|
|
7
115
|
<Update label="June 12, 2026" description="4.5.0-rc.6" tags={["SDK", "CLI", "Bug fix"]}>
|
|
8
116
|
|
|
9
117
|
## chat.agent reliability fixes
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "chat.local"
|
|
|
4
4
|
description: "Typed, run-scoped data accessible from hooks, run(), tools, and subtasks. Survives across turns, auto-cleared between runs, auto-hydrated into subtasks."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
Use `chat.local` to create typed, run-scoped data that persists across turns and is accessible from anywhere — the run function, tools, nested helpers. Each run gets its own isolated copy, and locals are automatically cleared between runs.
|
|
12
8
|
|
|
13
9
|
Lifecycle hooks and **`run`** also receive **`ctx`** ([`TaskRunContext`](/ai-chat/reference#task-context-ctx)) — the same object as on a standard `task()` — for tags, metadata, and cleanup that needs the full run record.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Client Protocol"
|
|
|
4
4
|
description: "The wire protocol for building custom chat transports — how clients communicate with chat agents over Sessions and SSE."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
This page documents the protocol that chat clients use to communicate with `chat.agent()` tasks. Use this if you're building a custom transport (e.g., for a Slack bot, CLI tool, or native app) instead of using the built-in `TriggerChatTransport` or `AgentChat`.
|
|
12
8
|
|
|
13
9
|
<Note>
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Compaction"
|
|
|
4
4
|
description: "Automatic context compaction to keep long conversations within token limits."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
## Overview
|
|
12
8
|
|
|
13
9
|
Long conversations accumulate tokens across turns. Eventually the context window fills up, causing errors or degraded responses. Compaction solves this by automatically summarizing the conversation when token usage exceeds a threshold, then using that summary as the context for future turns.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Custom agents"
|
|
|
4
4
|
description: "Build chat agents without chat.agent()'s managed lifecycle: register with chat.customAgent(), then drive turns with the createSession iterator or a hand-rolled loop."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
**A custom agent is a task you register with `chat.customAgent()` and drive yourself — either with the managed turn iterator from `chat.createSession()`, or with a fully hand-rolled loop over the raw chat primitives.** You give up `chat.agent()`'s lifecycle hooks and automatic continuation recovery; you gain inline control over every turn, and (at the lowest level) full control over the stream conversion.
|
|
12
8
|
|
|
13
9
|
See the [comparison table](/ai-chat/backend) before dropping down. The frontend is unchanged either way: all levels speak the same wire protocol, so [`useTriggerChatTransport`](/ai-chat/frontend) points at a custom agent exactly like a `chat.agent()`.
|
|
@@ -179,6 +175,38 @@ for await (const turn of session) {
|
|
|
179
175
|
}
|
|
180
176
|
```
|
|
181
177
|
|
|
178
|
+
## Stopping generation
|
|
179
|
+
|
|
180
|
+
The frontend stops a turn with [`transport.stopGeneration(chatId)`](/ai-chat/frontend#stop-generation), which writes a stop signal to the session's input stream. It aborts the current turn's generation but keeps the run alive, so the next message continues on the same session.
|
|
181
|
+
|
|
182
|
+
`turn.signal` is a combined stop-and-cancel `AbortSignal`, fresh each turn. Pass it to `streamText` so the stop reaches the model, then let `turn.complete()` finish the turn:
|
|
183
|
+
|
|
184
|
+
```ts trigger/my-chat.ts
|
|
185
|
+
for await (const turn of session) {
|
|
186
|
+
const result = streamText({
|
|
187
|
+
model: anthropic("claude-sonnet-4-5"),
|
|
188
|
+
messages: turn.messages,
|
|
189
|
+
abortSignal: turn.signal, // fires on a user stop OR a run cancel
|
|
190
|
+
stopWhen: stepCountIs(15),
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
await turn.complete(result);
|
|
194
|
+
|
|
195
|
+
if (turn.stopped) {
|
|
196
|
+
// user stopped this turn — the partial response is already accumulated
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
On a stop, `turn.complete()` cleans up the aborted parts of the partial response, accumulates it as its own assistant message, and writes turn-complete. The run does not end — the loop continues to the next turn.
|
|
202
|
+
|
|
203
|
+
Read `turn.stopped` to tell a user stop from a full run cancel:
|
|
204
|
+
|
|
205
|
+
- **User stop** (`transport.stopGeneration`): `turn.signal` aborts, `turn.stopped` is `true`, the partial response is accumulated, and the run stays alive for the next message.
|
|
206
|
+
- **Run cancel** (cancelled, expired, or `maxDuration` exceeded): `turn.signal` aborts, `turn.stopped` is `false`, and `turn.complete()` returns without accumulating because the run is ending.
|
|
207
|
+
|
|
208
|
+
A hand-rolled loop wires this itself with `chat.createStopSignal()` and `chat.cleanupAbortedParts()`. Two things `createSession` handles for you are easy to get wrong there — see the [hand-rolled loop checklist](#hand-rolled-loop-checklist).
|
|
209
|
+
|
|
182
210
|
## Hand-rolled loop with primitives
|
|
183
211
|
|
|
184
212
|
For full control, skip `createSession` and compose the primitives directly:
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Error handling"
|
|
|
4
4
|
description: "How errors flow through chat.agent — stream errors, hook errors, run failures — and how to recover."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
`chat.agent` errors fall into four layers, each with different recovery semantics. The default behavior is **conversation-preserving**: a thrown error in a hook or `run()` does not kill the chat. The current turn ends with an error chunk, and the agent waits for the user's next message.
|
|
12
8
|
|
|
13
9
|
## Error layers at a glance
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Fast starts"
|
|
|
4
4
|
description: "Two ways to cut first-turn TTFC: Preload eagerly triggers the run before the first message; Head Start runs step 1 in your warm server while the agent boots in parallel."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
The first turn of a brand-new conversation pays for the chat.agent run's cold start: dequeue, process boot, `onPreload` / `onChatStart` hooks, and only then the LLM call. Two features address this from different angles.
|
|
12
8
|
|
|
13
9
|
## Picking an approach
|
|
@@ -112,6 +108,8 @@ Head Start runs step 1's LLM call in your warm server process while the agent ru
|
|
|
112
108
|
|
|
113
109
|
`chat.headStart` returns a standard [Web Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) handler — `(req: Request) => Promise<Response>` — so it slots into any runtime that speaks Web Fetch.
|
|
114
110
|
|
|
111
|
+
Drive it one of two ways: wire the handler into the transport's [`headStart` option](#the-transport-option) so the browser's first message POSTs to it (the setup below), or call [`chat.startHeadStart`](#detached-head-start) from a backend that creates the chat and triggers the run in one request, then resumes on a separate page.
|
|
112
|
+
|
|
115
113
|
**Verified runtimes:** Node 18+, Bun, Deno, Cloudflare Workers, Vercel (Node and Edge), Netlify (Functions and Edge). The handler uses only `fetch` and Web `ReadableStream` / `TransformStream` (no `node:*` imports), and the S2 streaming dependency picks the right transport for each runtime automatically (HTTP/2 on Node/Deno, HTTP/1.1 on Bun/Workers/browsers).
|
|
116
114
|
|
|
117
115
|
**Compatible frameworks (native Web Fetch):** Next.js App Router, Hono, SvelteKit, Remix, React Router v7, TanStack Start, Astro, Nitro/Nuxt, Elysia. Mount the handler directly.
|
|
@@ -545,6 +543,10 @@ Head Start composes with [`hydrateMessages`](/ai-chat/lifecycle-hooks#hydratemes
|
|
|
545
543
|
|
|
546
544
|
Your hydrate hook shapes **model context**, not the transcript — dropping reasoning-only entries or unresolved tool rows from the returned chain is fine and does not affect what `onTurnComplete` persists or what the UI renders.
|
|
547
545
|
|
|
546
|
+
#### With `prepareMessages`
|
|
547
|
+
|
|
548
|
+
When the first turn's handover carries a pending tool call, the runtime reshapes it into a tool-approval round: the partial assistant gets a `tool-approval-request` and the chain ends with a `tool` message holding the matching `tool-approval-response`. The agent's `streamText` reads that trailing row to execute the handed-over call before step 2. `chat.agent` keeps that tail intact across your [`prepareMessages`](/ai-chat/reference#chatagentoptions) hook, so the common [prompt-caching](/ai-chat/prompt-caching) pattern of rolling a cache breakpoint onto the last message is safe on a resume turn (the breakpoint lands on the next user or assistant message instead). If you hand-roll a backend with [`chat.customAgent`](#chatcustomagent) or [`chat.createSession`](#chatcreatesession), preserve that trailing approval row yourself.
|
|
549
|
+
|
|
548
550
|
### Handover with custom agents
|
|
549
551
|
|
|
550
552
|
The route handler is backend-agnostic: `agentId` can point at a `chat.agent`, a [`chat.customAgent`](/ai-chat/custom-agents), or a [`chat.createSession`](/ai-chat/custom-agents#managed-loop-chatcreatesession) loop. With `chat.agent` the handover is consumed for you (the steps above). The two hand-rolled backends consume it explicitly on turn 0.
|
|
@@ -655,6 +657,86 @@ Optional. When set, the FIRST message of a brand-new chat (no existing session s
|
|
|
655
657
|
|
|
656
658
|
This is **not** a stock `useChat` `endpoint` — it's not the canonical request URL for every turn, just the first-turn shortcut.
|
|
657
659
|
|
|
660
|
+
### Detached head start
|
|
661
|
+
|
|
662
|
+
`chat.startHeadStart` runs the same head start as the [`chat.headStart` handler above](#setup); the difference is how step 1 reaches the browser. `chat.headStart` streams step 1 back over the live connection the browser opens when it sends the first message. `chat.startHeadStart` has no open browser connection to stream to, so it drains step 1 into the durable session stream and the browser **resumes** it when the chat page loads.
|
|
663
|
+
|
|
664
|
+
Use it when there's no open connection at first-turn time, because the first message is captured outside the chat UI and the conversation renders on a separate page. A typical flow: a "new chat" form posts the prompt to your backend, which creates the chat row, starts the run with `chat.startHeadStart`, and returns a `chatId`; the browser then navigates to `/chats/{chatId}` and resumes. You still get the first-turn TTFC win; the browser picks step 1 up on resume instead of live.
|
|
665
|
+
|
|
666
|
+
**1. Start the head start in your create endpoint.** Call `chat.startHeadStart`, keep `completion` alive past the response (`waitUntil` / Next.js `after`), and return the `chatId`.
|
|
667
|
+
|
|
668
|
+
```ts app/api/chat/create/route.ts (your backend)
|
|
669
|
+
import { chat } from "@trigger.dev/sdk/chat-server";
|
|
670
|
+
import { streamText } from "ai";
|
|
671
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
672
|
+
import { after } from "next/server";
|
|
673
|
+
// Schema-only tools; same bundle-isolation rule as chat.headStart.
|
|
674
|
+
import { headStartTools } from "@/lib/chat-tools/schemas";
|
|
675
|
+
|
|
676
|
+
export async function POST(req: Request) {
|
|
677
|
+
const { chatId, messages } = await req.json();
|
|
678
|
+
// Persist your own chat row + the first user message here.
|
|
679
|
+
|
|
680
|
+
const { completion } = await chat.startHeadStart({
|
|
681
|
+
agentId: "my-chat",
|
|
682
|
+
chatId, // session externalId; reuse it on the destination page
|
|
683
|
+
messages, // first-turn user history
|
|
684
|
+
run: async ({ chat: helper }) =>
|
|
685
|
+
streamText({
|
|
686
|
+
...helper.toStreamTextOptions({ tools: headStartTools }),
|
|
687
|
+
model: anthropic("claude-sonnet-4-6"),
|
|
688
|
+
system: "You are a helpful assistant.",
|
|
689
|
+
}),
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// Keep the function warm until step 1 drains and the handover dispatches.
|
|
693
|
+
after(completion);
|
|
694
|
+
|
|
695
|
+
return Response.json({ chatId });
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**2. Resume the chat on the destination page.** Set no `headStart` and no `startSession`: the run is already in flight, so the transport resumes `session.out` and replays step 1 (warm) then step 2+ (agent) under one assistant message.
|
|
700
|
+
|
|
701
|
+
```tsx app/chats/[chatId]/page.tsx
|
|
702
|
+
import { useTriggerChatTransport } from "@trigger.dev/sdk/chat/react";
|
|
703
|
+
import { useChat } from "@ai-sdk/react";
|
|
704
|
+
|
|
705
|
+
const transport = useTriggerChatTransport({
|
|
706
|
+
task: "my-chat",
|
|
707
|
+
accessToken: ({ chatId }) => mintChatAccessToken(chatId),
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
const { messages } = useChat({ id: chatId, transport, resume: true });
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
<Warning>
|
|
714
|
+
`completion` must run to completion, or step 1 never finishes draining and the turn stalls. On serverless, hand it to the platform's run-after-response primitive (`waitUntil`, Next.js `after`). On a long-lived server you can ignore it; it runs in the background.
|
|
715
|
+
</Warning>
|
|
716
|
+
|
|
717
|
+
<Warning>
|
|
718
|
+
If you hydrate the transport's session state yourself (the `sessions` option, e.g. so the same page can also resume already-stored chats from your session store), a head-started session is still mid-turn, so mark it `isStreaming: true`. A session hydrated as not streaming is treated as settled: the transport skips reconnecting to `session.out`, so the browser never sees the turn even though the run completed. The minimal example above sidesteps this by not hydrating a session at all (`resume: true` alone reconnects).
|
|
719
|
+
</Warning>
|
|
720
|
+
|
|
721
|
+
The `run` callback and bundle-isolation rule are the same as `chat.headStart`. Pass `metadata` to attach auth tokens or context to the run; it never reaches the browser. The cost over the transport-routed handler is one extra round trip: the create request, then the resume.
|
|
722
|
+
|
|
723
|
+
#### The `chat.startHeadStart` API
|
|
724
|
+
|
|
725
|
+
```ts
|
|
726
|
+
chat.startHeadStart<TTools>({
|
|
727
|
+
agentId: string, // chat.agent / chat.customAgent / chat.createSession id
|
|
728
|
+
chatId: string, // session externalId; reuse on the destination page
|
|
729
|
+
messages: UIMessage[], // first-turn user history
|
|
730
|
+
run: (args: HeadStartRunArgs<TTools>) => Promise<StreamTextResult<any, any>>,
|
|
731
|
+
idleTimeoutInSeconds?: number, // how long the agent waits for the handover signal. Default: 60
|
|
732
|
+
triggerConfig?: Partial<SessionTriggerConfig>, // tags, queue, machine, …
|
|
733
|
+
apiClient?: ApiClientConfiguration, // when the agent lives in another project/env
|
|
734
|
+
metadata?: Record<string, unknown>, // merged into the run payload; never sent to the browser
|
|
735
|
+
}): Promise<{ chatId: string; completion: Promise<void> }>
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
`completion` resolves once the head start finishes; `await` it or hand it to `waitUntil`. It rejects if the warm step or the dispatch fails.
|
|
739
|
+
|
|
658
740
|
### Limitations
|
|
659
741
|
|
|
660
742
|
- **First turn only.** Step 2+ and turn 2+ run on the trigger side. There's no per-turn "head start every turn" mode — the win comes from amortizing agent boot across the LLM call once.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Frontend"
|
|
|
4
4
|
description: "Transport setup, session management, client data, and frontend patterns for AI Chat."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
## How the transport works
|
|
12
8
|
|
|
13
9
|
Vanilla `useChat` expects an `api` URL — it POSTs the conversation to your own Next.js route handler, which terminates the stream. `useTriggerChatTransport` replaces that round-trip: instead of an `api` URL, you pass a custom [`ChatTransport`](https://ai-sdk.dev/docs/ai-sdk-ui/transport) that talks directly to the Trigger.dev cloud (or your self-hosted webapp) on behalf of `useChat`.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "How it works"
|
|
|
4
4
|
description: "End-to-end mechanics of a chat.agent turn: the two durable channels per session, the long-lived task that reads and writes them, and how a chat survives refreshes, deploys, and idle gaps."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
This page explains how `chat.agent` is put together, what each piece does on a single turn, and how a chat survives across turns. It is not an API tour — for that, see [Backend](/ai-chat/backend), [Frontend](/ai-chat/frontend), and the [Reference](/ai-chat/reference). For the byte-level wire format, see [Client Protocol](/ai-chat/client-protocol).
|
|
12
8
|
|
|
13
9
|
<Note>
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Lifecycle hooks"
|
|
|
4
4
|
description: "Hook into every stage of a chat agent's run: preload, turn start, turn complete, suspend, resume, and more."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
`chat.agent({ ... })` accepts a set of lifecycle hooks for persisting state, validating input, transforming messages, and reacting to suspension and resumption. They fire at well-defined points in the chat agent's lifetime.
|
|
12
8
|
|
|
13
9
|
**Once per worker process (every fresh run boot):** `onBoot` → `onPreload` (preloaded runs only).
|
package/docs/ai-chat/mcp.mdx
CHANGED
|
@@ -4,10 +4,6 @@ sidebarTitle: "MCP Server"
|
|
|
4
4
|
description: "Chat with your agents from any AI coding tool using the Trigger.dev MCP server."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
The Trigger.dev MCP server includes tools for having conversations with your chat agents directly from AI coding tools like Claude Code, Cursor, Windsurf, and others. This lets your AI assistant interact with your agents without writing any code.
|
|
12
8
|
|
|
13
9
|
## Available tools
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Overview"
|
|
|
4
4
|
description: "Durable multi-turn AI chats — one Trigger.dev task per conversation, surviving refreshes, deploys, and crashes."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
An AI chat isn't a request — it's a session. `chat.agent` runs every conversation as a single long-lived Trigger.dev task: you write the loop, it wakes up when a message arrives, freezes when none do, and the same in-memory state and on-disk workspace survive across page refreshes, deploys, idle gaps, and crashes. The substrate handles the parts most teams stitch together by hand — turn lifecycle, mid-stream resume, recovery from cancel/crash/OOM, HITL approvals, deploy upgrades — so your code is the loop you'd write anyway: messages in, `streamText` out.
|
|
12
8
|
|
|
13
9
|
## A minimal example
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Branching conversations"
|
|
|
4
4
|
description: "Build ChatGPT-style conversation trees with edit, regenerate, undo, and branch switching using hydrateMessages, chat.history, and actions."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
Most chat UIs treat conversations as linear sequences. But real conversations branch — users edit previous messages, regenerate responses, undo exchanges, and explore alternative paths. This pattern shows how to build a branching conversation system using `hydrateMessages`, `chat.history`, and custom actions.
|
|
12
8
|
|
|
13
9
|
## Data model
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Code sandbox"
|
|
|
4
4
|
description: "Warm an isolated sandbox on each chat turn, run an AI SDK executeCode tool, and tear down right before the run suspends — using chat.agent hooks and chat.local."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
Use a **hosted code sandbox** (for example [E2B](https://e2b.dev)) when the model should run short scripts to analyze tool output (PostHog queries, CSV-like data, math) without executing arbitrary code on the Trigger worker host.
|
|
12
8
|
|
|
13
9
|
This page describes a **durable chat** pattern that fits `chat.agent()`:
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Database persistence"
|
|
|
4
4
|
description: "Split conversation state and live session metadata across hooks — preload, turn start, turn complete — without tying the pattern to a specific ORM or schema."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
Durable chat runs can span **hours** and **many turns**. You usually want:
|
|
12
8
|
|
|
13
9
|
1. **Conversation state** — full **`UIMessage[]`** (or equivalent) keyed by **`chatId`**, so reloads and history views work.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Human-in-the-loop"
|
|
|
4
4
|
description: "Pause the agent mid-response to ask the user a clarifying question, then resume with their answer."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
Some turns need to stop and ask the user something before they can finish — picking between options, confirming a destructive action, or clarifying an ambiguous request. The AI SDK calls this **human-in-the-loop** (HITL), and the building block is a tool with no `execute` function.
|
|
12
8
|
|
|
13
9
|
When the LLM calls a tool that has no `execute`, `streamText` ends with the tool call still pending. The turn completes cleanly, the frontend renders UI to collect the answer, and when the user responds, a new turn resumes with the answer merged into the same assistant message.
|
|
@@ -20,7 +16,7 @@ Turn N:
|
|
|
20
16
|
LLM streams text → calls askUser tool (no execute)
|
|
21
17
|
streamText ends with tool-call in `input-available` state
|
|
22
18
|
onTurnComplete fires (finishReason = "tool-calls")
|
|
23
|
-
Agent
|
|
19
|
+
Agent suspends (compute freed) — maxDuration does not tick while paused
|
|
24
20
|
|
|
25
21
|
Frontend:
|
|
26
22
|
Renders question + option buttons from tool input
|
|
@@ -36,6 +32,14 @@ Turn N+1:
|
|
|
36
32
|
|
|
37
33
|
The AI SDK's `toUIMessageStream` automatically reuses the assistant message ID across the pause (we pass `originalMessages` internally), so `responseMessage` in the post-resume `onTurnComplete` is the **full merged message** — the original text, the completed tool call, and any follow-up content — not just the new parts.
|
|
38
34
|
|
|
35
|
+
## Duration and cost while paused
|
|
36
|
+
|
|
37
|
+
A pause doesn't hold compute. After the model calls a no-execute tool, the turn finishes and the run stays warm for `idleTimeoutInSeconds` (default 30s), then **suspends** and frees its compute, the same way [`wait.for`](/wait-for) does. The user's `addToolOutput` wakes it back up.
|
|
38
|
+
|
|
39
|
+
Because the run is suspended while it waits, the human's thinking time is not billed and does **not** count against [`maxDuration`](/runs/max-duration). `maxDuration` measures active CPU time and excludes suspended waitpoint time, exactly like `wait.for`, so a user can take minutes, hours, or days to answer without the run hitting `maxDuration`. The only time that counts is each turn's actual compute plus the short warm window before each suspend.
|
|
40
|
+
|
|
41
|
+
You don't need to raise `maxDuration` or end the run to support long human waits. How long a single suspended pause stays open is governed by the run's suspend timeout, not `maxDuration`; if a wait outlives it the run ends, and the next `addToolOutput` boots a fresh continuation that picks up the resolved tool result.
|
|
42
|
+
|
|
39
43
|
## Backend: define the tool
|
|
40
44
|
|
|
41
45
|
A HITL tool has an `inputSchema` describing what the model can ask, but **no `execute` function**. When the LLM calls it, `streamText` returns control to your agent.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Large payloads"
|
|
|
4
4
|
description: "Why a single chunk on the chat stream is capped at ~1 MiB, what error you'll see, and how to work around it with ID references."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
The realtime stream that backs `chat.agent` enforces a **per-record cap of ~1 MiB** (`1048576` bytes minus a small envelope reserve). Anything written through the chat output — auto-piped LLM chunks, `chat.response.write`, custom `writer.write` parts — counts as one record per chunk and is rejected if it crosses the cap.
|
|
12
8
|
|
|
13
9
|
This is a platform-level limit and cannot be raised per project or per stream.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "OOM resilience"
|
|
|
4
4
|
description: "Recover from out-of-memory errors mid-turn by automatically retrying the failed turn on a larger machine — without losing the in-flight user message or re-processing completed turns."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
When a `chat.agent` turn runs out of memory, the worker process dies and everything in it is gone: the in-flight LLM call, the accumulator, any tool execution mid-flight. By default, Trigger.dev surfaces the OOM as a run failure.
|
|
12
8
|
|
|
13
9
|
Setting `oomMachine` opts the agent into automatic recovery: the failed turn re-runs on a larger machine, picks up the user message that triggered the OOM (without re-processing earlier completed turns), and produces a normal response.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Persistence and replay"
|
|
|
4
4
|
description: "How chat.agent rebuilds conversation history at run boot — durable JSON snapshot in object storage plus session.out replay, with a hydrateMessages short-circuit for backend-owned history."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
`chat.agent` runs are processes — they boot, stream a turn, and either suspend (waiting for the next message) or exit. When the next message arrives at a session whose previous run already exited, a **fresh** run boots with no in-memory state. Something has to rebuild the conversation history before that turn can produce a coherent response.
|
|
12
8
|
|
|
13
9
|
This page walks through the **snapshot + replay** model the runtime uses by default, and the [`hydrateMessages`](/ai-chat/lifecycle-hooks#hydratemessages) short-circuit that turns the whole thing off when the customer owns history.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Recovery boot"
|
|
|
4
4
|
description: "Recover from cancel-mid-stream, crashes, and OOM kills with full conversational context. The smart default Just Works; the onRecoveryBoot hook is the override path for advanced policies."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
When a `chat.agent` run dies in the middle of streaming a response — the user cancels, the worker OOMs, or an unhandled exception kills the process — the durable streams hold what was in flight. The next run boots as a continuation, reads both stream tails, and reconstructs a chain that preserves the partial response so any follow-up (`keep going`, `actually do X instead`, a new question) has full context.
|
|
12
8
|
|
|
13
9
|
The behavior is automatic. The `onRecoveryBoot` hook is opt-in for policies that need something different.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Agent Skills"
|
|
|
4
4
|
description: "Ship reusable capabilities (folders with SKILL.md + scripts) that a chat agent discovers and invokes on demand."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
Agent skills are reusable capabilities you ship as folders — a `SKILL.md` describing when and how to use them, plus optional scripts, references, and assets. The chat agent sees a short description of each skill in its system prompt, loads the full instructions on demand via a `loadSkill` tool, and invokes the bundled scripts via `bash` — all without you wiring anything up manually.
|
|
12
8
|
|
|
13
9
|
Built on the [AI SDK cookbook pattern](https://ai-sdk.dev/cookbook/guides/agent-skills). Works with any provider (OpenAI, Anthropic, Gemini, etc.) — not tied to Anthropic's server-side skills.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Sub-Agents"
|
|
|
4
4
|
description: "Delegate work to durable sub-agents from within a parent agent's tool calls, with streaming preliminary results."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
Sub-agents let a parent agent delegate work to other agents running as durable Trigger.dev tasks. The sub-agent's response streams back through the parent as preliminary tool results, so the frontend sees the sub-agent working inside the parent's tool call card.
|
|
12
8
|
|
|
13
9
|
This builds on the AI SDK's [async generator tool pattern](https://ai-sdk.dev/docs/agents/subagents) and Trigger.dev's [AgentChat](/ai-chat/server-chat) for server-side agent interaction.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Tool result auditing"
|
|
|
4
4
|
description: "Fire side effects exactly once per resolved tool call — audit logs, billing, notifications — using extractNewToolResults inside hydrateMessages or onTurnComplete."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
When a chat agent uses [tools](/ai-chat/tools) (especially [human-in-the-loop](/ai-chat/patterns/human-in-the-loop) tools that wait on `addToolOutput` from the frontend), you often need to fire side effects exactly once per resolved tool call:
|
|
12
8
|
|
|
13
9
|
- **Audit logs** — record every tool result for compliance.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Trusted edge signals"
|
|
|
4
4
|
description: "How to safely deliver server-trusted signals (bot scores, JA4, ASN, ReCAPTCHA verdicts) to a chat.agent run via an edge proxy."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
A common need for chat-style endpoints is to drive agent behavior from **server-trusted signals** that the browser cannot be allowed to declare itself — bot management scores, JA4 fingerprints, ASN, ReCAPTCHA verdicts, or any other anti-abuse data only the edge can see. The agent's [`clientData`](/ai-chat/reference#withclientdata) channel is the right delivery mechanism, but `clientData` set in the browser is by definition spoofable. The fix is to move the value population out of the browser and into a trusted edge proxy.
|
|
12
8
|
|
|
13
9
|
This page documents the pattern using Cloudflare Workers as the proxy. The same shape applies to any edge layer (custom reverse proxy, Vercel Edge Middleware, AWS Lambda@Edge) — the trust comes from the deployment topology, not from Trigger.dev validating the source.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Version upgrades"
|
|
|
4
4
|
description: "Gracefully migrate suspended chat agents to a new deployment using chat.requestUpgrade() and the continuation mechanism."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
Chat agent runs are pinned to the worker version they started on. When you deploy a new version, suspended runs resume on the **old** code. If your deploy includes breaking changes (new tools, changed schemas, updated API contracts), this can cause issues.
|
|
12
8
|
|
|
13
9
|
`chat.requestUpgrade()` lets the agent opt out of the current run so the transport triggers a new one on the latest version.
|
|
@@ -4,10 +4,6 @@ sidebarTitle: "Pending Messages"
|
|
|
4
4
|
description: "Inject user messages mid-execution to steer agents between tool-call steps."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
import RcBanner from "/snippets/ai-chat-rc-banner.mdx";
|
|
8
|
-
|
|
9
|
-
<RcBanner />
|
|
10
|
-
|
|
11
7
|
## Overview
|
|
12
8
|
|
|
13
9
|
When an AI agent is executing tool calls, users may want to send a message that **steers the agent mid-execution** — adding context, correcting course, or refining the request without waiting for the response to finish.
|