@assistant-ui/react 0.4.7 → 0.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/index.mjs CHANGED
@@ -307,13 +307,17 @@ var ThreadProvider = ({
307
307
  useCallback2(
308
308
  (thread) => {
309
309
  const onThreadUpdate = () => {
310
- context.useThread.setState(
311
- Object.freeze({
312
- isRunning: context.useThreadRuntime.getState().isRunning
313
- }),
314
- true
315
- );
316
- context.useThreadMessages.setState(thread.messages, true);
310
+ if (thread.isRunning !== context.useThread.getState().isRunning) {
311
+ context.useThread.setState(
312
+ Object.freeze({
313
+ isRunning: thread.isRunning
314
+ }),
315
+ true
316
+ );
317
+ }
318
+ if (thread.messages !== context.useThreadMessages.getState()) {
319
+ context.useThreadMessages.setState(thread.messages, true);
320
+ }
317
321
  };
318
322
  onThreadUpdate();
319
323
  return thread.subscribe(onThreadUpdate);
@@ -1169,12 +1173,28 @@ import { memo as memo2 } from "react";
1169
1173
  import { useEffect as useEffect6, useState as useState4 } from "react";
1170
1174
  import { create as create10 } from "zustand";
1171
1175
  import { jsx as jsx13 } from "react/jsx-runtime";
1172
- var DONE_STATUS = { type: "done" };
1176
+ var COMPLETE_STATUS = {
1177
+ type: "complete"
1178
+ };
1179
+ var toContentPartStatus = (message, partIndex, part) => {
1180
+ if (message.role !== "assistant") return COMPLETE_STATUS;
1181
+ const isLastPart = partIndex === message.content.length - 1;
1182
+ if (part.type !== "tool-call") {
1183
+ if ("reason" in message.status && message.status.reason === "tool-calls" && isLastPart)
1184
+ throw new Error(
1185
+ "Encountered unexpected requires-action status. This is likely an internal bug in assistant-ui."
1186
+ );
1187
+ return isLastPart ? message.status : COMPLETE_STATUS;
1188
+ }
1189
+ if (!!part.result) {
1190
+ return COMPLETE_STATUS;
1191
+ }
1192
+ return message.status;
1193
+ };
1173
1194
  var syncContentPart = ({ message }, useContentPart, partIndex) => {
1174
1195
  const part = message.content[partIndex];
1175
1196
  if (!part) return;
1176
- const messageStatus = message.role === "assistant" ? message.status : DONE_STATUS;
1177
- const status = partIndex === message.content.length - 1 ? messageStatus : DONE_STATUS;
1197
+ const status = toContentPartStatus(message, partIndex, part);
1178
1198
  const currentState = useContentPart.getState();
1179
1199
  if (currentState.part === part && currentState.status === status) return;
1180
1200
  useContentPart.setState(
@@ -1327,7 +1347,7 @@ ContentPartPrimitiveDisplay.displayName = "ContentPartPrimitive.Display";
1327
1347
  // src/primitives/contentPart/ContentPartInProgress.tsx
1328
1348
  var ContentPartPrimitiveInProgress = ({ children }) => {
1329
1349
  const { useContentPart } = useContentPartContext();
1330
- const isInProgress = useContentPart((c) => c.status.type === "in_progress");
1350
+ const isInProgress = useContentPart((c) => c.status.type === "running");
1331
1351
  return isInProgress ? children : null;
1332
1352
  };
1333
1353
  ContentPartPrimitiveInProgress.displayName = "ContentPartPrimitive.InProgress";
@@ -1366,10 +1386,16 @@ var MessageContentPartComponent = ({
1366
1386
  const type = part.type;
1367
1387
  switch (type) {
1368
1388
  case "text":
1389
+ if (status.type === "requires-action")
1390
+ throw new Error("Encountered unexpected requires-action status");
1369
1391
  return /* @__PURE__ */ jsx16(Text2, { part, status });
1370
1392
  case "image":
1393
+ if (status.type === "requires-action")
1394
+ throw new Error("Encountered unexpected requires-action status");
1371
1395
  return /* @__PURE__ */ jsx16(Image2, { part, status });
1372
1396
  case "ui":
1397
+ if (status.type === "requires-action")
1398
+ throw new Error("Encountered unexpected requires-action status");
1373
1399
  return /* @__PURE__ */ jsx16(UI, { part, status });
1374
1400
  case "tool-call": {
1375
1401
  const Tool = by_name[part.toolName] || Fallback2;
@@ -1948,6 +1974,184 @@ var generateId = customAlphabet(
1948
1974
  var optimisticPrefix = "__optimistic__";
1949
1975
  var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
1950
1976
 
1977
+ // src/internal.ts
1978
+ var internal_exports = {};
1979
+ __export(internal_exports, {
1980
+ BaseAssistantRuntime: () => BaseAssistantRuntime,
1981
+ MessageRepository: () => MessageRepository,
1982
+ ProxyConfigProvider: () => ProxyConfigProvider,
1983
+ TooltipIconButton: () => TooltipIconButton,
1984
+ generateId: () => generateId,
1985
+ useSmooth: () => useSmooth
1986
+ });
1987
+
1988
+ // src/runtimes/core/BaseAssistantRuntime.tsx
1989
+ var BaseAssistantRuntime = class {
1990
+ constructor(_thread) {
1991
+ this._thread = _thread;
1992
+ this._thread = _thread;
1993
+ }
1994
+ get thread() {
1995
+ return this._thread;
1996
+ }
1997
+ set thread(thread) {
1998
+ this._thread = thread;
1999
+ this.subscriptionHandler();
2000
+ }
2001
+ _subscriptions = /* @__PURE__ */ new Set();
2002
+ subscribe(callback) {
2003
+ this._subscriptions.add(callback);
2004
+ return () => this._subscriptions.delete(callback);
2005
+ }
2006
+ subscriptionHandler = () => {
2007
+ for (const callback of this._subscriptions) callback();
2008
+ };
2009
+ };
2010
+
2011
+ // src/ui/base/tooltip-icon-button.tsx
2012
+ import { forwardRef as forwardRef17 } from "react";
2013
+
2014
+ // src/ui/base/tooltip.tsx
2015
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
2016
+
2017
+ // src/ui/utils/withDefaults.tsx
2018
+ import {
2019
+ forwardRef as forwardRef15
2020
+ } from "react";
2021
+ import classNames from "classnames";
2022
+ import { jsx as jsx25 } from "react/jsx-runtime";
2023
+ var withDefaultProps = ({
2024
+ className,
2025
+ ...defaultProps
2026
+ }) => ({ className: classNameProp, ...props }) => {
2027
+ return {
2028
+ className: classNames(className, classNameProp),
2029
+ ...defaultProps,
2030
+ ...props
2031
+ };
2032
+ };
2033
+ var withDefaults = (Component, defaultProps) => {
2034
+ const getProps = withDefaultProps(defaultProps);
2035
+ const WithDefaults = forwardRef15(
2036
+ (props, ref) => {
2037
+ const ComponentAsAny = Component;
2038
+ return /* @__PURE__ */ jsx25(ComponentAsAny, { ...getProps(props), ref });
2039
+ }
2040
+ );
2041
+ WithDefaults.displayName = "withDefaults(" + (typeof Component === "string" ? Component : Component.displayName) + ")";
2042
+ return WithDefaults;
2043
+ };
2044
+
2045
+ // src/ui/base/tooltip.tsx
2046
+ import { jsx as jsx26 } from "react/jsx-runtime";
2047
+ var Tooltip = (props) => {
2048
+ return /* @__PURE__ */ jsx26(TooltipPrimitive.Provider, { children: /* @__PURE__ */ jsx26(TooltipPrimitive.Root, { ...props }) });
2049
+ };
2050
+ Tooltip.displayName = "Tooltip";
2051
+ var TooltipTrigger = TooltipPrimitive.Trigger;
2052
+ var TooltipContent = withDefaults(TooltipPrimitive.Content, {
2053
+ sideOffset: 4,
2054
+ className: "aui-tooltip-content"
2055
+ });
2056
+ TooltipContent.displayName = "TooltipContent";
2057
+
2058
+ // src/ui/base/button.tsx
2059
+ import { cva } from "class-variance-authority";
2060
+ import { Primitive as Primitive11 } from "@radix-ui/react-primitive";
2061
+ import { forwardRef as forwardRef16 } from "react";
2062
+ import { jsx as jsx27 } from "react/jsx-runtime";
2063
+ var buttonVariants = cva("aui-button", {
2064
+ variants: {
2065
+ variant: {
2066
+ default: "aui-button-primary",
2067
+ outline: "aui-button-outline",
2068
+ ghost: "aui-button-ghost"
2069
+ },
2070
+ size: {
2071
+ default: "aui-button-medium",
2072
+ icon: "aui-button-icon"
2073
+ }
2074
+ },
2075
+ defaultVariants: {
2076
+ variant: "default",
2077
+ size: "default"
2078
+ }
2079
+ });
2080
+ var Button = forwardRef16(
2081
+ ({ className, variant, size, ...props }, ref) => {
2082
+ return /* @__PURE__ */ jsx27(
2083
+ Primitive11.button,
2084
+ {
2085
+ className: buttonVariants({ variant, size, className }),
2086
+ ...props,
2087
+ ref
2088
+ }
2089
+ );
2090
+ }
2091
+ );
2092
+ Button.displayName = "Button";
2093
+
2094
+ // src/ui/base/tooltip-icon-button.tsx
2095
+ import { jsx as jsx28, jsxs as jsxs4 } from "react/jsx-runtime";
2096
+ var TooltipIconButton = forwardRef17(({ children, tooltip, side = "bottom", ...rest }, ref) => {
2097
+ return /* @__PURE__ */ jsxs4(Tooltip, { children: [
2098
+ /* @__PURE__ */ jsx28(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs4(Button, { variant: "ghost", size: "icon", ...rest, ref, children: [
2099
+ children,
2100
+ /* @__PURE__ */ jsx28("span", { className: "aui-sr-only", children: tooltip })
2101
+ ] }) }),
2102
+ /* @__PURE__ */ jsx28(TooltipContent, { side, children: tooltip })
2103
+ ] });
2104
+ });
2105
+ TooltipIconButton.displayName = "TooltipIconButton";
2106
+
2107
+ // src/runtimes/edge/converters/fromCoreMessage.ts
2108
+ var fromCoreMessages = (message) => {
2109
+ return message.map((message2) => fromCoreMessage(message2));
2110
+ };
2111
+ var fromCoreMessage = (message, {
2112
+ id = generateId(),
2113
+ status = { type: "complete", reason: "unknown" }
2114
+ } = {}) => {
2115
+ const commonProps = {
2116
+ id,
2117
+ createdAt: /* @__PURE__ */ new Date()
2118
+ };
2119
+ const role = message.role;
2120
+ switch (role) {
2121
+ case "assistant":
2122
+ return {
2123
+ ...commonProps,
2124
+ role,
2125
+ content: message.content.map((part) => {
2126
+ if (part.type === "tool-call") {
2127
+ return {
2128
+ ...part,
2129
+ argsText: JSON.stringify(part.args)
2130
+ };
2131
+ }
2132
+ return part;
2133
+ }),
2134
+ status
2135
+ };
2136
+ case "user":
2137
+ return {
2138
+ ...commonProps,
2139
+ role,
2140
+ content: message.content
2141
+ };
2142
+ case "system":
2143
+ return {
2144
+ ...commonProps,
2145
+ role,
2146
+ content: message.content
2147
+ };
2148
+ default: {
2149
+ const unsupportedRole = role;
2150
+ throw new Error(`Unknown message role: ${unsupportedRole}`);
2151
+ }
2152
+ }
2153
+ };
2154
+
1951
2155
  // src/runtimes/utils/MessageRepository.tsx
1952
2156
  var findHead = (message) => {
1953
2157
  if (message.next) return findHead(message.next);
@@ -2038,12 +2242,13 @@ var MessageRepository = class {
2038
2242
  do {
2039
2243
  optimisticId = generateOptimisticId();
2040
2244
  } while (this.messages.has(optimisticId));
2041
- this.addOrUpdateMessage(parentId, {
2042
- ...message,
2043
- id: optimisticId,
2044
- createdAt: /* @__PURE__ */ new Date(),
2045
- ...message.role === "assistant" ? { status: "in_progress" } : void 0
2046
- });
2245
+ this.addOrUpdateMessage(
2246
+ parentId,
2247
+ fromCoreMessage(message, {
2248
+ id: optimisticId,
2249
+ status: { type: "running" }
2250
+ })
2251
+ );
2047
2252
  return optimisticId;
2048
2253
  }
2049
2254
  deleteMessage(messageId, replacementId) {
@@ -2110,136 +2315,6 @@ var MessageRepository = class {
2110
2315
  }
2111
2316
  };
2112
2317
 
2113
- // src/runtimes/core/BaseAssistantRuntime.tsx
2114
- var BaseAssistantRuntime = class {
2115
- constructor(_thread) {
2116
- this._thread = _thread;
2117
- this._thread = _thread;
2118
- }
2119
- get thread() {
2120
- return this._thread;
2121
- }
2122
- set thread(thread) {
2123
- this._thread = thread;
2124
- this.subscriptionHandler();
2125
- }
2126
- _subscriptions = /* @__PURE__ */ new Set();
2127
- subscribe(callback) {
2128
- this._subscriptions.add(callback);
2129
- return () => this._subscriptions.delete(callback);
2130
- }
2131
- subscriptionHandler = () => {
2132
- for (const callback of this._subscriptions) callback();
2133
- };
2134
- };
2135
-
2136
- // src/internal.ts
2137
- var internal_exports = {};
2138
- __export(internal_exports, {
2139
- BaseAssistantRuntime: () => BaseAssistantRuntime,
2140
- MessageRepository: () => MessageRepository,
2141
- ProxyConfigProvider: () => ProxyConfigProvider,
2142
- TooltipIconButton: () => TooltipIconButton,
2143
- generateId: () => generateId,
2144
- useSmooth: () => useSmooth
2145
- });
2146
-
2147
- // src/ui/base/tooltip-icon-button.tsx
2148
- import { forwardRef as forwardRef17 } from "react";
2149
-
2150
- // src/ui/base/tooltip.tsx
2151
- import * as TooltipPrimitive from "@radix-ui/react-tooltip";
2152
-
2153
- // src/ui/utils/withDefaults.tsx
2154
- import {
2155
- forwardRef as forwardRef15
2156
- } from "react";
2157
- import classNames from "classnames";
2158
- import { jsx as jsx25 } from "react/jsx-runtime";
2159
- var withDefaultProps = ({
2160
- className,
2161
- ...defaultProps
2162
- }) => ({ className: classNameProp, ...props }) => {
2163
- return {
2164
- className: classNames(className, classNameProp),
2165
- ...defaultProps,
2166
- ...props
2167
- };
2168
- };
2169
- var withDefaults = (Component, defaultProps) => {
2170
- const getProps = withDefaultProps(defaultProps);
2171
- const WithDefaults = forwardRef15(
2172
- (props, ref) => {
2173
- const ComponentAsAny = Component;
2174
- return /* @__PURE__ */ jsx25(ComponentAsAny, { ...getProps(props), ref });
2175
- }
2176
- );
2177
- WithDefaults.displayName = "withDefaults(" + (typeof Component === "string" ? Component : Component.displayName) + ")";
2178
- return WithDefaults;
2179
- };
2180
-
2181
- // src/ui/base/tooltip.tsx
2182
- import { jsx as jsx26 } from "react/jsx-runtime";
2183
- var Tooltip = (props) => {
2184
- return /* @__PURE__ */ jsx26(TooltipPrimitive.Provider, { children: /* @__PURE__ */ jsx26(TooltipPrimitive.Root, { ...props }) });
2185
- };
2186
- Tooltip.displayName = "Tooltip";
2187
- var TooltipTrigger = TooltipPrimitive.Trigger;
2188
- var TooltipContent = withDefaults(TooltipPrimitive.Content, {
2189
- sideOffset: 4,
2190
- className: "aui-tooltip-content"
2191
- });
2192
- TooltipContent.displayName = "TooltipContent";
2193
-
2194
- // src/ui/base/button.tsx
2195
- import { cva } from "class-variance-authority";
2196
- import { Primitive as Primitive11 } from "@radix-ui/react-primitive";
2197
- import { forwardRef as forwardRef16 } from "react";
2198
- import { jsx as jsx27 } from "react/jsx-runtime";
2199
- var buttonVariants = cva("aui-button", {
2200
- variants: {
2201
- variant: {
2202
- default: "aui-button-primary",
2203
- outline: "aui-button-outline",
2204
- ghost: "aui-button-ghost"
2205
- },
2206
- size: {
2207
- default: "aui-button-medium",
2208
- icon: "aui-button-icon"
2209
- }
2210
- },
2211
- defaultVariants: {
2212
- variant: "default",
2213
- size: "default"
2214
- }
2215
- });
2216
- var Button = forwardRef16(
2217
- ({ className, variant, size, ...props }, ref) => {
2218
- return /* @__PURE__ */ jsx27(
2219
- Primitive11.button,
2220
- {
2221
- className: buttonVariants({ variant, size, className }),
2222
- ...props,
2223
- ref
2224
- }
2225
- );
2226
- }
2227
- );
2228
- Button.displayName = "Button";
2229
-
2230
- // src/ui/base/tooltip-icon-button.tsx
2231
- import { jsx as jsx28, jsxs as jsxs4 } from "react/jsx-runtime";
2232
- var TooltipIconButton = forwardRef17(({ children, tooltip, side = "bottom", ...rest }, ref) => {
2233
- return /* @__PURE__ */ jsxs4(Tooltip, { children: [
2234
- /* @__PURE__ */ jsx28(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs4(Button, { variant: "ghost", size: "icon", ...rest, ref, children: [
2235
- children,
2236
- /* @__PURE__ */ jsx28("span", { className: "aui-sr-only", children: tooltip })
2237
- ] }) }),
2238
- /* @__PURE__ */ jsx28(TooltipContent, { side, children: tooltip })
2239
- ] });
2240
- });
2241
- TooltipIconButton.displayName = "TooltipIconButton";
2242
-
2243
2318
  // src/runtimes/edge/converters/toLanguageModelMessages.ts
2244
2319
  var assistantMessageSplitter = () => {
2245
2320
  const stash = [];
@@ -2460,35 +2535,43 @@ var fromLanguageModelMessages = (lm, { mergeRoundtrips }) => {
2460
2535
  return messages;
2461
2536
  };
2462
2537
 
2463
- // src/runtimes/edge/converters/fromCoreMessage.ts
2464
- var fromCoreMessages = (message) => {
2465
- return message.map((message2) => {
2466
- return {
2467
- id: generateId(),
2468
- createdAt: /* @__PURE__ */ new Date(),
2469
- ...message2.role === "assistant" ? {
2470
- status: { type: "done" }
2471
- } : void 0,
2472
- ...message2
2473
- };
2474
- });
2475
- };
2476
-
2477
2538
  // src/runtimes/edge/converters/toCoreMessages.ts
2478
2539
  var toCoreMessages = (message) => {
2479
- return message.map((message2) => {
2480
- return {
2481
- role: message2.role,
2482
- content: message2.content.map((part) => {
2483
- if (part.type === "ui") throw new Error("UI parts are not supported");
2484
- if (part.type === "tool-call") {
2485
- const { argsText, ...rest } = part;
2486
- return rest;
2487
- }
2488
- return part;
2489
- })
2490
- };
2491
- });
2540
+ return message.map(toCoreMessage);
2541
+ };
2542
+ var toCoreMessage = (message) => {
2543
+ const role = message.role;
2544
+ switch (role) {
2545
+ case "assistant":
2546
+ return {
2547
+ role,
2548
+ content: message.content.map((part) => {
2549
+ if (part.type === "ui") throw new Error("UI parts are not supported");
2550
+ if (part.type === "tool-call") {
2551
+ const { argsText, ...rest } = part;
2552
+ return rest;
2553
+ }
2554
+ return part;
2555
+ })
2556
+ };
2557
+ case "user":
2558
+ return {
2559
+ role,
2560
+ content: message.content.map((part) => {
2561
+ if (part.type === "ui") throw new Error("UI parts are not supported");
2562
+ return part;
2563
+ })
2564
+ };
2565
+ case "system":
2566
+ return {
2567
+ role,
2568
+ content: message.content
2569
+ };
2570
+ default: {
2571
+ const unsupportedRole = role;
2572
+ throw new Error(`Unknown message role: ${unsupportedRole}`);
2573
+ }
2574
+ }
2492
2575
  };
2493
2576
 
2494
2577
  // src/runtimes/edge/converters/fromLanguageModelTools.ts
@@ -2958,9 +3041,10 @@ var parsePartialJson = (json) => {
2958
3041
  };
2959
3042
 
2960
3043
  // src/runtimes/edge/streams/runResultStream.ts
2961
- function runResultStream(initialContent) {
3044
+ function runResultStream() {
2962
3045
  let message = {
2963
- content: initialContent
3046
+ content: [],
3047
+ status: { type: "running" }
2964
3048
  };
2965
3049
  const currentToolCall = { toolCallId: "", argsText: "" };
2966
3050
  return new TransformStream({
@@ -3008,7 +3092,13 @@ function runResultStream(initialContent) {
3008
3092
  break;
3009
3093
  }
3010
3094
  case "error": {
3011
- throw chunk.error;
3095
+ if (chunk.error instanceof Error && chunk.error.name === "AbortError") {
3096
+ message = appendOrUpdateCancel(message);
3097
+ controller.enqueue(message);
3098
+ break;
3099
+ } else {
3100
+ throw chunk.error;
3101
+ }
3012
3102
  }
3013
3103
  default: {
3014
3104
  const unhandledType = chunkType;
@@ -3082,11 +3172,42 @@ var appendOrUpdateToolResult = (message, toolCallId, toolName, result) => {
3082
3172
  };
3083
3173
  var appendOrUpdateFinish = (message, chunk) => {
3084
3174
  const { type, ...rest } = chunk;
3175
+ return {
3176
+ ...message,
3177
+ status: getStatus(chunk),
3178
+ roundtrips: [
3179
+ ...message.roundtrips ?? [],
3180
+ {
3181
+ logprobs: rest.logprobs,
3182
+ usage: rest.usage
3183
+ }
3184
+ ]
3185
+ };
3186
+ };
3187
+ var getStatus = (chunk) => {
3188
+ if (chunk.finishReason === "tool-calls") {
3189
+ return {
3190
+ type: "requires-action",
3191
+ reason: "tool-calls"
3192
+ };
3193
+ } else if (chunk.finishReason === "stop" || chunk.finishReason === "unknown") {
3194
+ return {
3195
+ type: "complete",
3196
+ reason: chunk.finishReason
3197
+ };
3198
+ } else {
3199
+ return {
3200
+ type: "incomplete",
3201
+ reason: chunk.finishReason
3202
+ };
3203
+ }
3204
+ };
3205
+ var appendOrUpdateCancel = (message) => {
3085
3206
  return {
3086
3207
  ...message,
3087
3208
  status: {
3088
- type: "done",
3089
- ...rest
3209
+ type: "incomplete",
3210
+ reason: "cancelled"
3090
3211
  }
3091
3212
  };
3092
3213
  };
@@ -3186,7 +3307,7 @@ var EdgeChatAdapter = class {
3186
3307
  constructor(options) {
3187
3308
  this.options = options;
3188
3309
  }
3189
- async roundtrip(initialContent, { messages, abortSignal, config, onUpdate }) {
3310
+ async run({ messages, abortSignal, config, onUpdate }) {
3190
3311
  const result = await fetch(this.options.api, {
3191
3312
  method: "POST",
3192
3313
  headers: {
@@ -3206,46 +3327,14 @@ var EdgeChatAdapter = class {
3206
3327
  `Edge runtime returned status ${result.status}: ${await result.text()}`
3207
3328
  );
3208
3329
  }
3209
- const stream = result.body.pipeThrough(new TextDecoderStream()).pipeThrough(chunkByLineStream()).pipeThrough(assistantDecoderStream()).pipeThrough(toolResultStream(config.tools)).pipeThrough(runResultStream(initialContent));
3210
- let message;
3330
+ const stream = result.body.pipeThrough(new TextDecoderStream()).pipeThrough(chunkByLineStream()).pipeThrough(assistantDecoderStream()).pipeThrough(toolResultStream(config.tools)).pipeThrough(runResultStream());
3211
3331
  let update;
3212
3332
  for await (update of asAsyncIterable(stream)) {
3213
- message = onUpdate(update);
3333
+ onUpdate(update);
3214
3334
  }
3215
3335
  if (update === void 0)
3216
3336
  throw new Error("No data received from Edge Runtime");
3217
- return [message, update];
3218
- }
3219
- async run({ messages, abortSignal, config, onUpdate }) {
3220
- let roundtripAllowance = this.options.maxToolRoundtrips ?? 1;
3221
- let usage = {
3222
- promptTokens: 0,
3223
- completionTokens: 0
3224
- };
3225
- let result;
3226
- let assistantMessage;
3227
- do {
3228
- [assistantMessage, result] = await this.roundtrip(result?.content ?? [], {
3229
- messages: assistantMessage ? [...messages, assistantMessage] : messages,
3230
- abortSignal,
3231
- config,
3232
- onUpdate
3233
- });
3234
- if (result.status?.type === "done") {
3235
- usage.promptTokens += result.status.usage?.promptTokens ?? 0;
3236
- usage.completionTokens += result.status.usage?.completionTokens ?? 0;
3237
- }
3238
- } while (result.status?.type === "done" && result.status.finishReason === "tool-calls" && result.content.every((c) => c.type !== "tool-call" || !!c.result) && roundtripAllowance-- > 0);
3239
- if (result.status?.type === "done" && usage.promptTokens > 0) {
3240
- result = {
3241
- ...result,
3242
- status: {
3243
- ...result.status,
3244
- usage
3245
- }
3246
- };
3247
- }
3248
- return result;
3337
+ return update;
3249
3338
  }
3250
3339
  };
3251
3340
 
@@ -3259,6 +3348,7 @@ var useEdgeRuntime = ({
3259
3348
  };
3260
3349
 
3261
3350
  // src/runtimes/local/LocalRuntime.tsx
3351
+ var shouldContinue = (result) => result.status?.type === "requires-action" && result.status.reason === "tool-calls" && result.content.every((c) => c.type !== "tool-call" || !!c.result);
3262
3352
  var LocalRuntime = class extends BaseAssistantRuntime {
3263
3353
  _proxyConfigProvider;
3264
3354
  constructor(adapter, options) {
@@ -3292,6 +3382,7 @@ var LocalThreadRuntime = class {
3292
3382
  constructor(configProvider, adapter, options) {
3293
3383
  this.configProvider = configProvider;
3294
3384
  this.adapter = adapter;
3385
+ this.options = options;
3295
3386
  if (options?.initialMessages) {
3296
3387
  let parentId = null;
3297
3388
  const messages = fromCoreMessages(options.initialMessages);
@@ -3335,27 +3426,51 @@ var LocalThreadRuntime = class {
3335
3426
  }
3336
3427
  async startRun(parentId) {
3337
3428
  this.repository.resetHead(parentId);
3338
- const messages = this.repository.getMessages();
3429
+ const id = generateId();
3339
3430
  let message = {
3340
- id: generateId(),
3431
+ id,
3341
3432
  role: "assistant",
3342
- status: { type: "in_progress" },
3433
+ status: { type: "running" },
3343
3434
  content: [{ type: "text", text: "" }],
3344
3435
  createdAt: /* @__PURE__ */ new Date()
3345
3436
  };
3437
+ do {
3438
+ message = await this.performRoundtrip(parentId, message);
3439
+ } while (shouldContinue(message));
3440
+ }
3441
+ async performRoundtrip(parentId, message) {
3442
+ const messages = this.repository.getMessages();
3346
3443
  this.abortController?.abort();
3347
3444
  this.abortController = new AbortController();
3348
- this.repository.addOrUpdateMessage(parentId, { ...message });
3349
- this.notifySubscribers();
3445
+ const initialContent = message.content;
3446
+ const initialRoundtrips = message.roundtrips;
3350
3447
  const updateMessage = (m) => {
3351
3448
  message = {
3352
3449
  ...message,
3353
- ...m
3450
+ ...m.content ? { content: [...initialContent, ...m.content ?? []] } : void 0,
3451
+ status: m.status ?? message.status,
3452
+ ...m.roundtrips?.length ? { roundtrips: [...initialRoundtrips ?? [], ...m.roundtrips] } : void 0
3354
3453
  };
3355
3454
  this.repository.addOrUpdateMessage(parentId, message);
3356
3455
  this.notifySubscribers();
3357
- return message;
3358
3456
  };
3457
+ const maxToolRoundtrips = this.options?.maxToolRoundtrips ?? 1;
3458
+ const toolRoundtrips = message.roundtrips?.length ?? 0;
3459
+ if (toolRoundtrips > maxToolRoundtrips) {
3460
+ updateMessage({
3461
+ status: {
3462
+ type: "incomplete",
3463
+ reason: "tool-calls"
3464
+ }
3465
+ });
3466
+ return message;
3467
+ } else {
3468
+ updateMessage({
3469
+ status: {
3470
+ type: "running"
3471
+ }
3472
+ });
3473
+ }
3359
3474
  try {
3360
3475
  const result = await this.adapter.run({
3361
3476
  messages,
@@ -3363,21 +3478,29 @@ var LocalThreadRuntime = class {
3363
3478
  config: this.configProvider.getModelConfig(),
3364
3479
  onUpdate: updateMessage
3365
3480
  });
3366
- if (result.status?.type === "in_progress")
3481
+ if (result.status?.type === "running")
3367
3482
  throw new Error(
3368
- "Unexpected in_progress status returned from ChatModelAdapter"
3483
+ "Unexpected running status returned from ChatModelAdapter"
3369
3484
  );
3370
3485
  this.abortController = null;
3371
- updateMessage({ status: { type: "done" }, ...result });
3372
- this.repository.addOrUpdateMessage(parentId, { ...message });
3373
- } catch (e) {
3374
- const isAbortError = e instanceof Error && e.name === "AbortError";
3375
- this.abortController = null;
3376
3486
  updateMessage({
3377
- status: isAbortError ? { type: "cancelled" } : { type: "error", error: e }
3487
+ status: { type: "complete", reason: "unknown" },
3488
+ ...result
3378
3489
  });
3379
- if (!isAbortError) throw e;
3490
+ } catch (e) {
3491
+ this.abortController = null;
3492
+ if (e instanceof Error && e.name === "AbortError") {
3493
+ updateMessage({
3494
+ status: { type: "incomplete", reason: "cancelled" }
3495
+ });
3496
+ } else {
3497
+ updateMessage({
3498
+ status: { type: "incomplete", reason: "error", error: e }
3499
+ });
3500
+ throw e;
3501
+ }
3380
3502
  }
3503
+ return message;
3381
3504
  }
3382
3505
  cancelRun() {
3383
3506
  if (!this.abortController) return;
@@ -3392,14 +3515,16 @@ var LocalThreadRuntime = class {
3392
3515
  return () => this._subscriptions.delete(callback);
3393
3516
  }
3394
3517
  addToolResult({ messageId, toolCallId, result }) {
3395
- const { parentId, message } = this.repository.getMessage(messageId);
3518
+ let { parentId, message } = this.repository.getMessage(messageId);
3396
3519
  if (message.role !== "assistant")
3397
3520
  throw new Error("Tried to add tool result to non-assistant message");
3521
+ let added = false;
3398
3522
  let found = false;
3399
3523
  const newContent = message.content.map((c) => {
3400
3524
  if (c.type !== "tool-call") return c;
3401
3525
  if (c.toolCallId !== toolCallId) return c;
3402
3526
  found = true;
3527
+ if (!c.result) added = true;
3403
3528
  return {
3404
3529
  ...c,
3405
3530
  result
@@ -3407,10 +3532,14 @@ var LocalThreadRuntime = class {
3407
3532
  });
3408
3533
  if (!found)
3409
3534
  throw new Error("Tried to add tool result to non-existing tool call");
3410
- this.repository.addOrUpdateMessage(parentId, {
3535
+ message = {
3411
3536
  ...message,
3412
3537
  content: newContent
3413
- });
3538
+ };
3539
+ this.repository.addOrUpdateMessage(parentId, message);
3540
+ if (added && shouldContinue(message)) {
3541
+ this.performRoundtrip(parentId, message);
3542
+ }
3414
3543
  }
3415
3544
  };
3416
3545
 
@@ -3605,7 +3734,7 @@ var Text = ({ status }) => {
3605
3734
  {
3606
3735
  className: classNames2(
3607
3736
  "aui-text",
3608
- status.type === "in_progress" && "aui-text-in-progress"
3737
+ status.type === "running" && "aui-text-in-progress"
3609
3738
  ),
3610
3739
  children: /* @__PURE__ */ jsx33(contentPart_exports.Text, {})
3611
3740
  }
@@ -4131,11 +4260,13 @@ export {
4131
4260
  thread_welcome_default as ThreadWelcome,
4132
4261
  user_action_bar_default as UserActionBar,
4133
4262
  user_message_default as UserMessage,
4263
+ fromCoreMessage,
4134
4264
  fromCoreMessages,
4135
4265
  fromLanguageModelMessages,
4136
4266
  fromLanguageModelTools,
4137
4267
  makeAssistantTool,
4138
4268
  makeAssistantToolUI,
4269
+ toCoreMessage,
4139
4270
  toCoreMessages,
4140
4271
  toLanguageModelMessages,
4141
4272
  toLanguageModelTools,