You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(sdk)!: chat.agent actions are no longer turns
Action turns previously fell through to the regular turn machinery,
calling onTurnStart, run(), onTurnComplete, etc. — meaning every action
fired an LLM call by default. Customers worked around this with a
chat.store-based skipModelCall flag pattern (Graham at Arena).
Now actions fire hydrateMessages and onAction only. No onTurnStart,
prepareMessages, onBeforeTurnComplete, onTurnComplete; no run()
invocation; no turn-counter increment. The trace span is named
"chat action" instead of "chat turn N".
onAction widens to accept the same return shapes as run(): void
(side-effect-only, default), StreamTextResult (auto-piped as the
response), string, or UIMessage. Customers who want a model response
from an action return streamText(...) directly from onAction.
If an action arrives but no onAction handler is configured, console.warn
fires once and the action is ignored (vs. silently triggering run()
on a stale wire payload).
Closes TRI-9118.
BREAKING: customers who relied on actions auto-invoking run() must
move that logic into onAction. See the changeset for the migration
snippet.
`chat.agent` actions are no longer treated as turns. They fire `hydrateMessages` and `onAction` only — no `onTurnStart` / `prepareMessages` / `onBeforeTurnComplete` / `onTurnComplete`, no `run()`, no turn-counter increment. The trace span is named `chat action` instead of `chat turn N`.
6
+
7
+
`onAction` can now return a `StreamTextResult`, `string`, or `UIMessage` to produce a model response from the action; returning `void` (the previous and now default) is side-effect-only.
8
+
9
+
**Migration**: if you previously had `run()` branching on `payload.trigger === "action"`, return your `streamText(...)` from `onAction` instead. If you persisted in `onTurnComplete`, do that work inside `onAction`. For any other state-only action, just remove your skip-the-model workaround — the default is now correct.
10
+
11
+
```ts
12
+
// before
13
+
onAction: async ({ action }) => {
14
+
if (action.type==="regenerate") {
15
+
chat.store.set({ skipModelCall: false });
16
+
chat.history.slice(0, -1);
17
+
}
18
+
},
19
+
run: async ({ messages, signal }) => {
20
+
if (chat.store.get()?.skipModelCall) return;
21
+
returnstreamText({ model, messages, abortSignal: signal });
22
+
},
23
+
24
+
// after
25
+
onAction: async ({ action, messages, signal }) => {
26
+
if (action.type==="regenerate") {
27
+
chat.history.slice(0, -1);
28
+
returnstreamText({ model, messages, abortSignal: signal });
29
+
}
30
+
},
31
+
run: async ({ messages, signal }) =>
32
+
streamText({ model, messages, abortSignal: signal }),
0 commit comments