Skip to content

Commit 4f8587c

Browse files
ericallamCopilotgithub-actions[bot]
authored
docs(sdk): describe chat.turn() in the advanced chat agent skill (#4902)
## Summary The bundled `trigger-chat-agent-advanced` skill still told agents to answer from an action by returning a value, an API #4816 removed. Code generated from it fails at runtime with the `chat.turn()` error. Before, the skill said: ```ts onAction: async ({ action, streamText }) => { if (action.type === "regenerate") { chat.history.slice(0, -1); return streamText({ model, messages }); } }, ``` After: ```ts onAction: async ({ action }) => { if (action.type === "undo") chat.history.slice(0, -2); // edit only if (action.type === "regenerate") { chat.history.slice(0, -1); return chat.turn(); // answer the edited history } }, ``` The section now covers edit-only actions, `chat.turn()` and the `action-turn` trigger, persistence for both the platform-managed and `hydrateMessages` models, and sending actions through `useChat` (`body.action` or `useChatActions`) so the answer renders, with `transport.sendAction` noted as the raw-stream path. Docs-only change to an SDK-bundled skill; no changeset, since the `chat.turn()` release note from #4816 already covers the behavior. Raised by Devin on #4884 after merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01AxuSksX18bj1yhnLpkcQ6a --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ericallam <534+ericallam@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Eric Allam <ericallam@users.noreply.github.com>
1 parent c719f84 commit 4f8587c

1 file changed

Lines changed: 31 additions & 15 deletions

File tree

  • packages/trigger-sdk/skills/trigger-chat-agent-advanced

packages/trigger-sdk/skills/trigger-chat-agent-advanced/SKILL.md

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -185,37 +185,53 @@ compaction: {
185185
},
186186
```
187187

188-
### 5. Actions: mutate state without a turn
189-
190-
`actionSchema` validates; `onAction` mutates via `chat.history` (`slice`, `replace`, `rollbackTo`,
191-
`remove`, `getPendingToolCalls`, `extractNewToolResults`). Actions fire `hydrateMessages` and
192-
`onAction` only, never `run()` or the turn hooks. Return a `StreamTextResult`, string, or `UIMessage`
193-
to also emit a model response, built with the `streamText` from `onAction`'s own argument so it
194-
carries the agent's prompt and tools like any other turn.
195-
196-
Persistence goes through the agent's transcript storage (`storage` on `chat.agent`; the platform
197-
snapshot by default). After an action that changed the conversation the runtime hands the storage a
198-
changeset: an undo is one `truncateAfter`, a regenerate is a `truncateAfter` plus the new answer's
199-
`put`. With the deprecated `hydrateMessages` your store is the source of truth and the runtime does
200-
not write, so mirror every mutation yourself: a regenerate is a delete and an insert.
188+
### 5. Actions: edit state, and optionally answer
189+
190+
`actionSchema` validates; `onAction` edits via `chat.history` (`slice`, `replace`, `rollbackTo`,
191+
`remove`, `getPendingToolCalls`, `extractNewToolResults`). An action fires `hydrateMessages` and
192+
`onAction` only. Return nothing for an edit-only action: no model call, and the turn counter does not
193+
advance. Return `chat.turn()` to answer after the edit: a turn runs on the edited history with
194+
everything a turn has (the agent's system prompt and tools, steering, compaction, injected
195+
instructions, `onTurnStart` and `onTurnComplete`, persistence), and `run()` receives it with
196+
`trigger: "action-turn"`. `onAction` has no `streamText` argument, and returning a
197+
`StreamTextResult`, string or `UIMessage` from it throws.
198+
199+
Persistence splits by model. With transcript storage (`storage` on `chat.agent`; the platform
200+
snapshot by default), the runtime hands storage a changeset with `reason: "action"` after an action
201+
that changed the conversation: an undo is one `truncateAfter`, a regenerate is a `truncateAfter`
202+
followed by the new answer's `put` when the turn completes, and an edit is a `put` for the edited
203+
id. With the deprecated `hydrateMessages` your store is the source of truth and the runtime does
204+
not write, so mirror every mutation yourself: a regenerate is a delete and an insert, and the
205+
answer that follows `chat.turn()` arrives through `onTurnComplete` like any turn's answer.
201206

202207
```ts
203208
export const myChat = chat.agent({
204209
id: "my-chat",
205210
actionSchema: z.discriminatedUnion("type", [
206211
z.object({ type: z.literal("undo") }),
212+
z.object({ type: z.literal("regenerate") }),
207213
z.object({ type: z.literal("rollback"), targetMessageId: z.string() }),
208214
]),
209215
onAction: async ({ action }) => {
210-
if (action.type === "undo") chat.history.slice(0, -2);
216+
if (action.type === "undo") chat.history.slice(0, -2); // edit only
211217
if (action.type === "rollback") chat.history.rollbackTo(action.targetMessageId);
218+
if (action.type === "regenerate") {
219+
chat.history.slice(0, -1);
220+
return chat.turn(); // answer the edited history
221+
}
212222
},
213223
run: async ({ messages, signal, streamText }) =>
214224
streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal }),
215225
});
216226
```
217227

218-
Send from the browser with `transport.sendAction(chatId, { type: "undo" })`, or server-side with
228+
Send from the browser through `useChat`, so the answer a turn produces renders like any turn:
229+
`sendMessage(undefined, { body: { action: { type: "regenerate" } } })`, `regenerate({ body: { action } })`,
230+
or `useChatActions({ sendMessage })` from `@trigger.dev/sdk/chat/react`. For regeneration, use
231+
`regenerate()` from `useChat` (Vercel AI SDK) which removes the last assistant message before
232+
streaming the new one; calling `sendMessage` with a regenerate action appends without removal,
233+
leaving both answers visible. `transport.sendAction(chatId, action)` returns a raw stream the
234+
caller must read and apply itself. Server-side, use
219235
`agentChat.sendAction({ type: "rollback", targetMessageId: "msg-3" })`.
220236

221237
### 6. Fast starts: Head Start

0 commit comments

Comments
 (0)