@trigger.dev/sdk 4.5.0-rc.3 → 4.5.0-rc.5

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.
Files changed (45) hide show
  1. package/dist/commonjs/imports/ai-runtime-cjs.cjs.map +1 -0
  2. package/dist/commonjs/imports/ai-runtime.d.ts +1 -0
  3. package/dist/commonjs/imports/ai-runtime.js +27 -0
  4. package/dist/commonjs/v3/ai-shared.d.ts +16 -1
  5. package/dist/commonjs/v3/ai-shared.js.map +1 -1
  6. package/dist/commonjs/v3/ai.d.ts +81 -8
  7. package/dist/commonjs/v3/ai.js +138 -34
  8. package/dist/commonjs/v3/ai.js.map +1 -1
  9. package/dist/commonjs/v3/aiAutoTelemetry.d.ts +2 -0
  10. package/dist/commonjs/v3/aiAutoTelemetry.js +81 -0
  11. package/dist/commonjs/v3/aiAutoTelemetry.js.map +1 -0
  12. package/dist/commonjs/v3/chat-client.js +5 -3
  13. package/dist/commonjs/v3/chat-client.js.map +1 -1
  14. package/dist/commonjs/v3/chat-server.d.ts +29 -6
  15. package/dist/commonjs/v3/chat-server.js +6 -4
  16. package/dist/commonjs/v3/chat-server.js.map +1 -1
  17. package/dist/commonjs/v3/chat.d.ts +11 -0
  18. package/dist/commonjs/v3/chat.js +61 -1
  19. package/dist/commonjs/v3/chat.js.map +1 -1
  20. package/dist/commonjs/v3/shared.js +17 -9
  21. package/dist/commonjs/v3/shared.js.map +1 -1
  22. package/dist/commonjs/version.js +1 -1
  23. package/dist/esm/imports/ai-runtime.d.ts +2 -0
  24. package/dist/esm/imports/ai-runtime.js +16 -0
  25. package/dist/esm/imports/ai-runtime.js.map +1 -0
  26. package/dist/esm/v3/ai-shared.d.ts +16 -1
  27. package/dist/esm/v3/ai-shared.js.map +1 -1
  28. package/dist/esm/v3/ai.d.ts +81 -8
  29. package/dist/esm/v3/ai.js +109 -5
  30. package/dist/esm/v3/ai.js.map +1 -1
  31. package/dist/esm/v3/aiAutoTelemetry.d.ts +2 -0
  32. package/dist/esm/v3/aiAutoTelemetry.js +78 -0
  33. package/dist/esm/v3/aiAutoTelemetry.js.map +1 -0
  34. package/dist/esm/v3/chat-client.js +3 -1
  35. package/dist/esm/v3/chat-client.js.map +1 -1
  36. package/dist/esm/v3/chat-server.d.ts +29 -6
  37. package/dist/esm/v3/chat-server.js +3 -1
  38. package/dist/esm/v3/chat-server.js.map +1 -1
  39. package/dist/esm/v3/chat.d.ts +11 -0
  40. package/dist/esm/v3/chat.js +61 -1
  41. package/dist/esm/v3/chat.js.map +1 -1
  42. package/dist/esm/v3/shared.js +18 -10
  43. package/dist/esm/v3/shared.js.map +1 -1
  44. package/dist/esm/version.js +1 -1
  45. package/package.json +11 -6
@@ -12,7 +12,9 @@ exports.__setReplaySessionInTailImplForTests = __setReplaySessionInTailImplForTe
12
12
  exports.__replaySessionInTailProductionPathForTests = __replaySessionInTailProductionPathForTests;
13
13
  exports.buildSkillTools = buildSkillTools;
14
14
  const v3_1 = require("@trigger.dev/core/v3");
15
- const ai_1 = require("ai");
15
+ // Runtime VALUES go through the ESM/CJS shim so the CJS build can `require`
16
+ // ESM-only `ai@7` (see ../imports/ai-runtime.ts).
17
+ const ai_runtime_js_1 = require("../imports/ai-runtime.js");
16
18
  const api_1 = require("@opentelemetry/api");
17
19
  const auth_js_1 = require("./auth.js");
18
20
  const locals_js_1 = require("./locals.js");
@@ -29,6 +31,7 @@ const agentSkillsRuntime_js_1 = require("./agentSkillsRuntime.js");
29
31
  const streams_js_1 = require("./streams.js");
30
32
  const sessions_js_1 = require("./sessions.js");
31
33
  const shared_js_1 = require("./shared.js");
34
+ const aiAutoTelemetry_js_1 = require("./aiAutoTelemetry.js");
32
35
  const v3_2 = require("@trigger.dev/core/v3");
33
36
  const tracer_js_1 = require("./tracer.js");
34
37
  const METADATA_KEY = "tool.execute.options";
@@ -38,7 +41,16 @@ const METADATA_KEY = "tool.execute.options";
38
41
  * stopped/aborted conversations with partial tool parts.
39
42
  */
40
43
  function toModelMessages(messages) {
41
- return (0, ai_1.convertToModelMessages)(messages, { ignoreIncompleteToolCalls: true });
44
+ // Pass the resolved per-turn `tools` (if any) so the AI SDK can look up each
45
+ // tool's `toModelOutput` and re-apply it to prior-turn tool results. Without
46
+ // `tools` it falls back to JSON-stringifying the raw output (TRI-10149). The
47
+ // conditional spread keeps the options object byte-identical to the no-tools
48
+ // path when nothing was declared.
49
+ const tools = locals_js_1.locals.get(chatResolvedToolsKey);
50
+ return (0, ai_runtime_js_1.convertToModelMessages)(messages, {
51
+ ignoreIncompleteToolCalls: true,
52
+ ...(tools ? { tools } : {}),
53
+ });
42
54
  }
43
55
  const chatTurnContextKey = locals_js_1.locals.create("chat.turnContext");
44
56
  /**
@@ -442,7 +454,7 @@ async function replaySessionOutTail(sessionId, options) {
442
454
  });
443
455
  let last;
444
456
  try {
445
- for await (const snapshot of (0, ai_1.readUIMessageStream)({ stream: segmentStream })) {
457
+ for await (const snapshot of (0, ai_runtime_js_1.readUIMessageStream)({ stream: segmentStream })) {
446
458
  last = snapshot;
447
459
  }
448
460
  }
@@ -645,9 +657,14 @@ function createTaskToolExecuteHandler(task) {
645
657
  const toolMeta = {
646
658
  toolCallId: toolOpts?.toolCallId ?? "",
647
659
  };
648
- if (toolOpts?.experimental_context !== undefined) {
660
+ // v6 passes user context as `experimental_context`, v7 as `context`. Read
661
+ // whichever is set and stamp both so subtasks reading either name work.
662
+ const toolContext = toolOpts?.context ?? toolOpts?.experimental_context;
663
+ if (toolContext !== undefined) {
649
664
  try {
650
- toolMeta.experimental_context = JSON.parse(JSON.stringify(toolOpts.experimental_context));
665
+ const serialized = JSON.parse(JSON.stringify(toolContext));
666
+ toolMeta.experimental_context = serialized;
667
+ toolMeta.context = serialized;
651
668
  }
652
669
  catch {
653
670
  /* non-serializable */
@@ -690,9 +707,9 @@ function toolFromTask(task, options) {
690
707
  // Zod-backed tasks: use static `tool()` so runtime shape matches `ToolSet`. Generic task context
691
708
  // prevents `tool()` overloads from inferring input; `as any` is localized to this call only.
692
709
  if ("schema" in task && task.schema && (0, v3_1.isSchemaZodEsque)(task.schema)) {
693
- const staticTool = (0, ai_1.tool)({
710
+ const staticTool = (0, ai_runtime_js_1.tool)({
694
711
  description: task.description ?? "",
695
- inputSchema: (0, ai_1.zodSchema)(task.schema),
712
+ inputSchema: (0, ai_runtime_js_1.zodSchema)(task.schema),
696
713
  execute: async (input, toolOpts) => executeFromTaskInput(input, toolOpts),
697
714
  ...(options?.experimental_toToolResultContent !== undefined
698
715
  ? { experimental_toToolResultContent: options.experimental_toToolResultContent }
@@ -700,7 +717,7 @@ function toolFromTask(task, options) {
700
717
  });
701
718
  return staticTool;
702
719
  }
703
- const toolDefinition = (0, ai_1.dynamicTool)({
720
+ const toolDefinition = (0, ai_runtime_js_1.dynamicTool)({
704
721
  description: task.description,
705
722
  inputSchema: convertTaskSchemaToToolParameters(task),
706
723
  ...(options?.experimental_toToolResultContent !== undefined
@@ -768,15 +785,15 @@ function convertTaskSchemaToToolParameters(task) {
768
785
  if ("schema" in task) {
769
786
  // If TaskSchema is ArkTypeEsque, use ai.jsonSchema to convert it to a Schema
770
787
  if ("toJsonSchema" in task.schema && typeof task.schema.toJsonSchema === "function") {
771
- return (0, ai_1.jsonSchema)(task.schema.toJsonSchema());
788
+ return (0, ai_runtime_js_1.jsonSchema)(task.schema.toJsonSchema());
772
789
  }
773
790
  // If TaskSchema is ZodEsque, use ai.zodSchema to convert it to a Schema
774
791
  if ((0, v3_1.isSchemaZodEsque)(task.schema)) {
775
- return (0, ai_1.zodSchema)(task.schema);
792
+ return (0, ai_runtime_js_1.zodSchema)(task.schema);
776
793
  }
777
794
  }
778
795
  if ("jsonSchema" in task) {
779
- return (0, ai_1.jsonSchema)(task.jsonSchema);
796
+ return (0, ai_runtime_js_1.jsonSchema)(task.jsonSchema);
780
797
  }
781
798
  throw new Error("Cannot convert task to a tool. Make sure to use a task with a schema or jsonSchema.");
782
799
  }
@@ -1260,7 +1277,7 @@ function synthesizeHandoverUIMessage(partial, messageId) {
1260
1277
  // browser). Fall back to a fresh id only if the handover signal
1261
1278
  // didn't carry one.
1262
1279
  return {
1263
- id: messageId ?? (0, ai_1.generateId)(),
1280
+ id: messageId ?? (0, ai_runtime_js_1.generateId)(),
1264
1281
  role: "assistant",
1265
1282
  parts,
1266
1283
  };
@@ -1416,7 +1433,7 @@ function* iterateToolParts(message) {
1416
1433
  if (message.role !== "assistant")
1417
1434
  return;
1418
1435
  for (const part of (message.parts ?? [])) {
1419
- if (!(0, ai_1.isToolUIPart)(part))
1436
+ if (!(0, ai_runtime_js_1.isToolUIPart)(part))
1420
1437
  continue;
1421
1438
  const toolCallId = part.toolCallId;
1422
1439
  if (typeof toolCallId !== "string" || toolCallId.length === 0)
@@ -1424,7 +1441,7 @@ function* iterateToolParts(message) {
1424
1441
  yield {
1425
1442
  part,
1426
1443
  toolCallId,
1427
- toolName: (0, ai_1.getToolName)(part),
1444
+ toolName: (0, ai_runtime_js_1.getToolName)(part),
1428
1445
  state: part.state,
1429
1446
  };
1430
1447
  }
@@ -1448,7 +1465,7 @@ function extractPendingToolCallsFromPartial(partial) {
1448
1465
  const parts = (partial.parts ?? []);
1449
1466
  for (let i = 0; i < parts.length; i++) {
1450
1467
  const part = parts[i];
1451
- if (!(0, ai_1.isToolUIPart)(part))
1468
+ if (!(0, ai_runtime_js_1.isToolUIPart)(part))
1452
1469
  continue;
1453
1470
  if (!isPendingToolState(part.state))
1454
1471
  continue;
@@ -1457,7 +1474,7 @@ function extractPendingToolCallsFromPartial(partial) {
1457
1474
  continue;
1458
1475
  out.push({
1459
1476
  toolCallId,
1460
- toolName: (0, ai_1.getToolName)(part),
1477
+ toolName: (0, ai_runtime_js_1.getToolName)(part),
1461
1478
  input: part.input,
1462
1479
  partIndex: i,
1463
1480
  });
@@ -1560,7 +1577,7 @@ function extractNewToolResultsFromHistory(message, messages) {
1560
1577
  function mergeIncomingIntoHydrated(hydrated, incoming) {
1561
1578
  const incomingAdvancedByCallId = new Map();
1562
1579
  for (const part of (incoming.parts ?? [])) {
1563
- if (!(0, ai_1.isToolUIPart)(part))
1580
+ if (!(0, ai_runtime_js_1.isToolUIPart)(part))
1564
1581
  continue;
1565
1582
  const toolCallId = part.toolCallId;
1566
1583
  if (typeof toolCallId !== "string" || toolCallId.length === 0)
@@ -1574,7 +1591,7 @@ function mergeIncomingIntoHydrated(hydrated, incoming) {
1574
1591
  let mutated = false;
1575
1592
  const hydratedParts = (hydrated.parts ?? []);
1576
1593
  const mergedParts = hydratedParts.map((part) => {
1577
- if (!(0, ai_1.isToolUIPart)(part))
1594
+ if (!(0, ai_runtime_js_1.isToolUIPart)(part))
1578
1595
  return part;
1579
1596
  const toolCallId = part.toolCallId;
1580
1597
  if (typeof toolCallId !== "string" || toolCallId.length === 0)
@@ -1743,6 +1760,18 @@ const chatOnCompactedKey = locals_js_1.locals.create("chat.onCompacted");
1743
1760
  /** @internal Full task `ctx` for the active `chat.agent` run (for hooks invoked from nested compaction). */
1744
1761
  const chatAgentRunContextKey = locals_js_1.locals.create("chat.agentRunContext");
1745
1762
  const chatPrepareMessagesKey = locals_js_1.locals.create("chat.prepareMessages");
1763
+ /**
1764
+ * @internal The raw `tools` option from `chat.agent({ tools })`, either a
1765
+ * static `ToolSet` or a per-turn function. Set once at boot.
1766
+ */
1767
+ const chatToolsOptionKey = locals_js_1.locals.create("chat.toolsOption");
1768
+ /**
1769
+ * @internal The concrete `ToolSet` resolved for the current turn. Read by
1770
+ * `toModelMessages` so `convertToModelMessages` can re-run `toModelOutput` on
1771
+ * prior-turn tool results. Unset when no `tools` were declared (preserves the
1772
+ * exact pre-feature conversion behavior).
1773
+ */
1774
+ const chatResolvedToolsKey = locals_js_1.locals.create("chat.resolvedTools");
1746
1775
  /** @internal Flag set by `chat.requestUpgrade()` to exit the loop after the current turn. */
1747
1776
  const chatUpgradeRequestedKey = locals_js_1.locals.create("chat.upgradeRequested");
1748
1777
  /**
@@ -1836,6 +1865,37 @@ async function applyPrepareMessages(messages, reason) {
1836
1865
  },
1837
1866
  });
1838
1867
  }
1868
+ /**
1869
+ * Resolve the `tools` option into a concrete `ToolSet` and cache it in locals so
1870
+ * `toModelMessages` can pass it to `convertToModelMessages`. For the function
1871
+ * form, invokes the user function with the given context (or the current turn
1872
+ * context when no override is passed). Pass an `override` for the boot-time
1873
+ * history conversion, which runs before the per-turn context exists and uses
1874
+ * the run/continuation payload's `clientData`.
1875
+ *
1876
+ * Fails closed: a throwing resolver propagates rather than carrying a prior
1877
+ * turn's set forward. The function form can gate capabilities by user or flag,
1878
+ * so reusing stale tools would leak capabilities. No-op when no `tools` were
1879
+ * declared.
1880
+ * @internal
1881
+ */
1882
+ async function resolveTurnTools(override) {
1883
+ const option = locals_js_1.locals.get(chatToolsOptionKey);
1884
+ if (!option)
1885
+ return;
1886
+ if (typeof option !== "function") {
1887
+ locals_js_1.locals.set(chatResolvedToolsKey, option);
1888
+ return;
1889
+ }
1890
+ const ctx = override ?? locals_js_1.locals.get(chatTurnContextKey);
1891
+ const resolved = await option({
1892
+ chatId: ctx?.chatId ?? "",
1893
+ turn: ctx?.turn ?? 0,
1894
+ continuation: ctx?.continuation ?? false,
1895
+ clientData: ctx?.clientData,
1896
+ });
1897
+ locals_js_1.locals.set(chatResolvedToolsKey, resolved);
1898
+ }
1839
1899
  /**
1840
1900
  * Read the current compaction state. Returns the summary and base message count
1841
1901
  * if compaction has occurred in this turn, or `undefined` if not.
@@ -1921,7 +1981,7 @@ async function chatCompact(messages, steps, options) {
1921
1981
  return { type: "skipped" };
1922
1982
  }
1923
1983
  const result = await tracer_js_1.tracer.startActiveSpan("context compaction", async (span) => {
1924
- const compactionId = (0, ai_1.generateId)();
1984
+ const compactionId = (0, ai_runtime_js_1.generateId)();
1925
1985
  let summary;
1926
1986
  const { waitUntilComplete } = chatStream.writer({
1927
1987
  spanName: "stream compaction chunks",
@@ -2096,7 +2156,7 @@ async function drainSteeringQueue(config, messages, steps, queueOverride) {
2096
2156
  execute: ({ write }) => {
2097
2157
  write({
2098
2158
  type: ai_shared_js_2.PENDING_MESSAGE_INJECTED_TYPE,
2099
- id: (0, ai_1.generateId)(),
2159
+ id: (0, ai_runtime_js_1.generateId)(),
2100
2160
  data: {
2101
2161
  messageIds: uiMessages.map((m) => m.id),
2102
2162
  messages: uiMessages.map((m, idx) => ({
@@ -2250,9 +2310,9 @@ function findSkillByName(skills, name) {
2250
2310
  * (e.g. in a `chat.createSession` loop with custom streamText).
2251
2311
  */
2252
2312
  function buildSkillTools(skills) {
2253
- const loadSkill = (0, ai_1.tool)({
2313
+ const loadSkill = (0, ai_runtime_js_1.tool)({
2254
2314
  description: "Load the full instructions for a skill by its name. Call this first before using a skill.",
2255
- inputSchema: (0, ai_1.jsonSchema)({
2315
+ inputSchema: (0, ai_runtime_js_1.jsonSchema)({
2256
2316
  type: "object",
2257
2317
  properties: {
2258
2318
  name: {
@@ -2280,9 +2340,9 @@ function buildSkillTools(skills) {
2280
2340
  };
2281
2341
  },
2282
2342
  });
2283
- const readFile = (0, ai_1.tool)({
2343
+ const readFile = (0, ai_runtime_js_1.tool)({
2284
2344
  description: "Read a file from a skill's bundled folder. Paths must be relative to the skill's root.",
2285
- inputSchema: (0, ai_1.jsonSchema)({
2345
+ inputSchema: (0, ai_runtime_js_1.jsonSchema)({
2286
2346
  type: "object",
2287
2347
  properties: {
2288
2348
  skill: { type: "string", description: "The skill's name (from frontmatter)." },
@@ -2310,9 +2370,9 @@ function buildSkillTools(skills) {
2310
2370
  }
2311
2371
  },
2312
2372
  });
2313
- const bash = (0, ai_1.tool)({
2373
+ const bash = (0, ai_runtime_js_1.tool)({
2314
2374
  description: "Run a bash command inside a skill's bundled folder. Use this to invoke the skill's scripts. The working directory is the skill's root.",
2315
- inputSchema: (0, ai_1.jsonSchema)({
2375
+ inputSchema: (0, ai_runtime_js_1.jsonSchema)({
2316
2376
  type: "object",
2317
2377
  properties: {
2318
2378
  skill: { type: "string", description: "The skill's name (from frontmatter)." },
@@ -2561,7 +2621,7 @@ function chatCustomAgent(options) {
2561
2621
  return task;
2562
2622
  }
2563
2623
  function chatAgent(options) {
2564
- const { run: userRun, clientDataSchema, onBoot, onRecoveryBoot, onPreload, onChatStart, onValidateMessages, hydrateMessages, actionSchema, onAction, onTurnStart, onBeforeTurnComplete, onCompacted, compaction, pendingMessages: pendingMessagesConfig, prepareMessages, onTurnComplete, maxTurns = 100, turnTimeout = "1h", idleTimeoutInSeconds = 30, chatAccessTokenTTL = "1h", preloadIdleTimeoutInSeconds, preloadTimeout, uiMessageStreamOptions, onChatSuspend, onChatResume, exitAfterPreloadIdle = false, oomMachine, ...restOptions } = options;
2624
+ const { run: userRun, clientDataSchema, onBoot, onRecoveryBoot, onPreload, onChatStart, onValidateMessages, hydrateMessages, actionSchema, onAction, onTurnStart, onBeforeTurnComplete, onCompacted, compaction, pendingMessages: pendingMessagesConfig, prepareMessages, tools: toolsOption, onTurnComplete, maxTurns = 100, turnTimeout = "1h", idleTimeoutInSeconds = 30, chatAccessTokenTTL = "1h", preloadIdleTimeoutInSeconds, preloadTimeout, uiMessageStreamOptions, onChatSuspend, onChatResume, exitAfterPreloadIdle = false, oomMachine, ...restOptions } = options;
2565
2625
  const parseClientData = clientDataSchema ? (0, v3_1.getSchemaParseFn)(clientDataSchema) : undefined;
2566
2626
  const parseAction = actionSchema ? (0, v3_1.getSchemaParseFn)(actionSchema) : undefined;
2567
2627
  // chat.agent does not expose generic retry options (see docstring on
@@ -2578,6 +2638,11 @@ function chatAgent(options) {
2578
2638
  agentConfig: { type: "ai-sdk-chat" },
2579
2639
  run: async (payload, { signal: runSignal, ctx }) => {
2580
2640
  locals_js_1.locals.set(chatAgentRunContextKey, ctx);
2641
+ // On AI SDK 7, register the `@ai-sdk/otel` integration (once per process)
2642
+ // so `experimental_telemetry` spans flow into the run trace. Awaited here
2643
+ // at run boot — before any `streamText` — and a no-op on v5/v6 or when the
2644
+ // optional `@ai-sdk/otel` peer isn't installed. See ./aiAutoTelemetry.ts.
2645
+ await (0, aiAutoTelemetry_js_1.ensureAiSdkTelemetry)();
2581
2646
  // Bind the run to its backing Session so every module-level helper
2582
2647
  // (chat.stream, chat.messages, chat.stopSignal) resolves to this
2583
2648
  // chat's `.in` / `.out` channels.
@@ -2610,6 +2675,19 @@ function chatAgent(options) {
2610
2675
  if (prepareMessages) {
2611
2676
  locals_js_1.locals.set(chatPrepareMessagesKey, prepareMessages);
2612
2677
  }
2678
+ if (toolsOption) {
2679
+ // Cast: the option's function form is typed against the parsed
2680
+ // `clientData` (`ResolveToolsEvent<inferSchemaOut<...>>`), but the
2681
+ // locals key uses the erased `ResolveToolsEvent<unknown>`. The runtime
2682
+ // value is identical; this mirrors how `prepareMessages` is stored.
2683
+ locals_js_1.locals.set(chatToolsOptionKey, toolsOption);
2684
+ // Static tools are usable immediately. The function form is resolved
2685
+ // just before the boot history conversion (with the payload's
2686
+ // clientData) and again per-turn (see resolveTurnTools).
2687
+ if (typeof toolsOption !== "function") {
2688
+ locals_js_1.locals.set(chatResolvedToolsKey, toolsOption);
2689
+ }
2690
+ }
2613
2691
  if (compaction) {
2614
2692
  locals_js_1.locals.set(chatAgentCompactionKey, compaction);
2615
2693
  }
@@ -2940,6 +3018,27 @@ function chatAgent(options) {
2940
3018
  accumulatedUIMessages = [...payload.headStartMessages];
2941
3019
  }
2942
3020
  if (accumulatedUIMessages.length > 0) {
3021
+ // Resolve a function-form `tools` with the run/continuation payload's
3022
+ // clientData so this conversion of the restored history applies each
3023
+ // tool's toModelOutput (static tools were already seeded above). This
3024
+ // only re-renders saved history, so it fails open: a resolver hiccup
3025
+ // logs and converts without tools rather than blocking the resume.
3026
+ // Per-turn resolveTurnTools still fails closed for live turns.
3027
+ if (typeof toolsOption === "function") {
3028
+ try {
3029
+ await resolveTurnTools({
3030
+ chatId: payload.chatId,
3031
+ turn: 0,
3032
+ continuation: payload.continuation ?? false,
3033
+ clientData: parseClientData
3034
+ ? await parseClientData(payload.metadata)
3035
+ : payload.metadata,
3036
+ });
3037
+ }
3038
+ catch (error) {
3039
+ v3_1.logger.warn("chat.agent: tools() resolver threw at boot; restored history converted without toModelOutput", { error: error instanceof Error ? error.message : String(error) });
3040
+ }
3041
+ }
2943
3042
  try {
2944
3043
  accumulatedMessages = await toModelMessages(accumulatedUIMessages);
2945
3044
  }
@@ -3361,6 +3460,10 @@ function chatAgent(options) {
3361
3460
  continuation,
3362
3461
  clientData,
3363
3462
  });
3463
+ // Resolve the per-turn `tools` set now that turn context
3464
+ // (incl. parsed clientData) exists, so every toModelMessages
3465
+ // call this turn can re-apply tool `toModelOutput`.
3466
+ await resolveTurnTools();
3364
3467
  // Per-turn stop controller (reset each turn)
3365
3468
  const stopController = new AbortController();
3366
3469
  currentStopController = stopController;
@@ -3736,7 +3839,7 @@ function chatAgent(options) {
3736
3839
  const resolvedOptions = resolveUIMessageStreamOptions();
3737
3840
  const uiStream = actionStreamResult.toUIMessageStream({
3738
3841
  ...resolvedOptions,
3739
- generateMessageId: resolvedOptions.generateMessageId ?? ai_1.generateId,
3842
+ generateMessageId: resolvedOptions.generateMessageId ?? ai_runtime_js_1.generateId,
3740
3843
  });
3741
3844
  await pipeChat(uiStream, {
3742
3845
  signal: combinedSignal,
@@ -3930,6 +4033,7 @@ function chatAgent(options) {
3930
4033
  previousTurnUsage,
3931
4034
  totalUsage: cumulativeUsage,
3932
4035
  ctx,
4036
+ tools: locals_js_1.locals.get(chatResolvedToolsKey) ?? {},
3933
4037
  signal: combinedSignal,
3934
4038
  cancelSignal,
3935
4039
  stopSignal,
@@ -3959,7 +4063,7 @@ function chatAgent(options) {
3959
4063
  // Always provide generateMessageId so the start chunk carries a
3960
4064
  // messageId. Without this, the frontend and backend generate IDs
3961
4065
  // independently and they won't match for ID-based dedup.
3962
- generateMessageId: resolvedOptions.generateMessageId ?? ai_1.generateId,
4066
+ generateMessageId: resolvedOptions.generateMessageId ?? ai_runtime_js_1.generateId,
3963
4067
  onFinish: ({ responseMessage, finishReason, }) => {
3964
4068
  capturedResponseMessage = responseMessage;
3965
4069
  capturedFinishReason = finishReason;
@@ -4087,7 +4191,7 @@ function chatAgent(options) {
4087
4191
  // may produce a message with an empty ID since IDs are normally
4088
4192
  // assigned by the frontend's useChat).
4089
4193
  if (!capturedResponseMessage.id) {
4090
- capturedResponseMessage = { ...capturedResponseMessage, id: (0, ai_1.generateId)() };
4194
+ capturedResponseMessage = { ...capturedResponseMessage, id: (0, ai_runtime_js_1.generateId)() };
4091
4195
  }
4092
4196
  // Append any non-transient data parts queued via chat.response or writer.write()
4093
4197
  const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
@@ -4143,7 +4247,7 @@ function chatAgent(options) {
4143
4247
  const remainingParts = locals_js_1.locals.get(chatResponsePartsKey);
4144
4248
  if (remainingParts && remainingParts.length > 0) {
4145
4249
  capturedResponseMessage = {
4146
- id: (0, ai_1.generateId)(),
4250
+ id: (0, ai_runtime_js_1.generateId)(),
4147
4251
  role: "assistant",
4148
4252
  parts: [...remainingParts],
4149
4253
  };
@@ -4185,7 +4289,7 @@ function chatAgent(options) {
4185
4289
  });
4186
4290
  if (shouldTrigger) {
4187
4291
  await tracer_js_1.tracer.startActiveSpan("context compaction (outer loop)", async (compactionSpan) => {
4188
- const compactionId = (0, ai_1.generateId)();
4292
+ const compactionId = (0, ai_runtime_js_1.generateId)();
4189
4293
  const { waitUntilComplete } = chatStream.writer({
4190
4294
  spanName: "stream compaction chunks",
4191
4295
  collapsed: true,
@@ -5259,7 +5363,7 @@ class ChatMessageAccumulator {
5259
5363
  }
5260
5364
  async addResponse(response) {
5261
5365
  if (!response.id) {
5262
- response = { ...response, id: (0, ai_1.generateId)() };
5366
+ response = { ...response, id: (0, ai_runtime_js_1.generateId)() };
5263
5367
  }
5264
5368
  this.uiMessages.push(response);
5265
5369
  try {
@@ -5588,7 +5692,7 @@ function createChatSession(payload, options) {
5588
5692
  const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
5589
5693
  if (queuedParts && queuedParts.length > 0) {
5590
5694
  await accumulator.addResponse({
5591
- id: (0, ai_1.generateId)(),
5695
+ id: (0, ai_runtime_js_1.generateId)(),
5592
5696
  role: "assistant",
5593
5697
  parts: queuedParts,
5594
5698
  });