@assistant-ui/react 0.4.8 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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,35 @@ 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 === Math.max(0, 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
+ };
1194
+ var EMPTY_CONTENT = Object.freeze({ type: "text", text: "" });
1173
1195
  var syncContentPart = ({ message }, useContentPart, partIndex) => {
1174
- const part = message.content[partIndex];
1175
- if (!part) return;
1176
- const messageStatus = message.role === "assistant" ? message.status : DONE_STATUS;
1177
- const status = partIndex === message.content.length - 1 ? messageStatus : DONE_STATUS;
1196
+ let part = message.content[partIndex];
1197
+ if (!part) {
1198
+ if (message.content.length === 0 && partIndex === 0) {
1199
+ part = EMPTY_CONTENT;
1200
+ } else {
1201
+ return;
1202
+ }
1203
+ }
1204
+ const status = toContentPartStatus(message, partIndex, part);
1178
1205
  const currentState = useContentPart.getState();
1179
1206
  if (currentState.part === part && currentState.status === status) return;
1180
1207
  useContentPart.setState(
@@ -1299,7 +1326,7 @@ var ContentPartPrimitiveText = forwardRef7(({ smooth = true, ...rest }, forwarde
1299
1326
  part: { text }
1300
1327
  } = useContentPartText();
1301
1328
  const smoothText = useSmooth(text, smooth);
1302
- return /* @__PURE__ */ jsx14(Primitive4.span, { "data-status": status, ...rest, ref: forwardedRef, children: smoothText });
1329
+ return /* @__PURE__ */ jsx14(Primitive4.span, { "data-status": status.type, ...rest, ref: forwardedRef, children: smoothText });
1303
1330
  });
1304
1331
  ContentPartPrimitiveText.displayName = "ContentPartPrimitive.Text";
1305
1332
 
@@ -1327,7 +1354,7 @@ ContentPartPrimitiveDisplay.displayName = "ContentPartPrimitive.Display";
1327
1354
  // src/primitives/contentPart/ContentPartInProgress.tsx
1328
1355
  var ContentPartPrimitiveInProgress = ({ children }) => {
1329
1356
  const { useContentPart } = useContentPartContext();
1330
- const isInProgress = useContentPart((c) => c.status.type === "in_progress");
1357
+ const isInProgress = useContentPart((c) => c.status.type === "running");
1331
1358
  return isInProgress ? children : null;
1332
1359
  };
1333
1360
  ContentPartPrimitiveInProgress.displayName = "ContentPartPrimitive.InProgress";
@@ -1366,10 +1393,16 @@ var MessageContentPartComponent = ({
1366
1393
  const type = part.type;
1367
1394
  switch (type) {
1368
1395
  case "text":
1396
+ if (status.type === "requires-action")
1397
+ throw new Error("Encountered unexpected requires-action status");
1369
1398
  return /* @__PURE__ */ jsx16(Text2, { part, status });
1370
1399
  case "image":
1400
+ if (status.type === "requires-action")
1401
+ throw new Error("Encountered unexpected requires-action status");
1371
1402
  return /* @__PURE__ */ jsx16(Image2, { part, status });
1372
1403
  case "ui":
1404
+ if (status.type === "requires-action")
1405
+ throw new Error("Encountered unexpected requires-action status");
1373
1406
  return /* @__PURE__ */ jsx16(UI, { part, status });
1374
1407
  case "tool-call": {
1375
1408
  const Tool = by_name[part.toolName] || Fallback2;
@@ -1399,7 +1432,7 @@ var MessagePrimitiveContent = ({
1399
1432
  components
1400
1433
  }) => {
1401
1434
  const { useMessage } = useMessageContext();
1402
- const contentLength = useMessage((s) => s.message.content.length);
1435
+ const contentLength = useMessage((s) => s.message.content.length) || 1;
1403
1436
  return new Array(contentLength).fill(null).map((_, idx) => {
1404
1437
  const partIndex = idx;
1405
1438
  return /* @__PURE__ */ jsx16(
@@ -1703,7 +1736,7 @@ var useThreadViewportAutoScroll = ({
1703
1736
  const div = divRef.current;
1704
1737
  if (!div) return;
1705
1738
  const isAtBottom = useViewport.getState().isAtBottom;
1706
- const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
1739
+ const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight + 1;
1707
1740
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
1708
1741
  } else {
1709
1742
  if (newIsAtBottom) {
@@ -1939,6 +1972,40 @@ var ThreadPrimitiveSuggestion = createActionButton(
1939
1972
  // src/runtimes/local/useLocalRuntime.tsx
1940
1973
  import { useInsertionEffect as useInsertionEffect3, useState as useState8 } from "react";
1941
1974
 
1975
+ // src/runtimes/core/BaseAssistantRuntime.tsx
1976
+ var BaseAssistantRuntime = class {
1977
+ constructor(_thread) {
1978
+ this._thread = _thread;
1979
+ this._thread = _thread;
1980
+ }
1981
+ get thread() {
1982
+ return this._thread;
1983
+ }
1984
+ set thread(thread) {
1985
+ this._thread = thread;
1986
+ this.subscriptionHandler();
1987
+ }
1988
+ _subscriptions = /* @__PURE__ */ new Set();
1989
+ subscribe(callback) {
1990
+ this._subscriptions.add(callback);
1991
+ return () => this._subscriptions.delete(callback);
1992
+ }
1993
+ subscriptionHandler = () => {
1994
+ for (const callback of this._subscriptions) callback();
1995
+ };
1996
+ };
1997
+
1998
+ // src/internal.ts
1999
+ var internal_exports = {};
2000
+ __export(internal_exports, {
2001
+ BaseAssistantRuntime: () => BaseAssistantRuntime,
2002
+ MessageRepository: () => MessageRepository,
2003
+ ProxyConfigProvider: () => ProxyConfigProvider,
2004
+ TooltipIconButton: () => TooltipIconButton,
2005
+ generateId: () => generateId,
2006
+ useSmooth: () => useSmooth
2007
+ });
2008
+
1942
2009
  // src/utils/idUtils.tsx
1943
2010
  import { customAlphabet } from "nanoid/non-secure";
1944
2011
  var generateId = customAlphabet(
@@ -1948,6 +2015,54 @@ var generateId = customAlphabet(
1948
2015
  var optimisticPrefix = "__optimistic__";
1949
2016
  var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
1950
2017
 
2018
+ // src/runtimes/edge/converters/fromCoreMessage.ts
2019
+ var fromCoreMessages = (message) => {
2020
+ return message.map((message2) => fromCoreMessage(message2));
2021
+ };
2022
+ var fromCoreMessage = (message, {
2023
+ id = generateId(),
2024
+ status = { type: "complete", reason: "unknown" }
2025
+ } = {}) => {
2026
+ const commonProps = {
2027
+ id,
2028
+ createdAt: /* @__PURE__ */ new Date()
2029
+ };
2030
+ const role = message.role;
2031
+ switch (role) {
2032
+ case "assistant":
2033
+ return {
2034
+ ...commonProps,
2035
+ role,
2036
+ content: message.content.map((part) => {
2037
+ if (part.type === "tool-call") {
2038
+ return {
2039
+ ...part,
2040
+ argsText: JSON.stringify(part.args)
2041
+ };
2042
+ }
2043
+ return part;
2044
+ }),
2045
+ status
2046
+ };
2047
+ case "user":
2048
+ return {
2049
+ ...commonProps,
2050
+ role,
2051
+ content: message.content
2052
+ };
2053
+ case "system":
2054
+ return {
2055
+ ...commonProps,
2056
+ role,
2057
+ content: message.content
2058
+ };
2059
+ default: {
2060
+ const unsupportedRole = role;
2061
+ throw new Error(`Unknown message role: ${unsupportedRole}`);
2062
+ }
2063
+ }
2064
+ };
2065
+
1951
2066
  // src/runtimes/utils/MessageRepository.tsx
1952
2067
  var findHead = (message) => {
1953
2068
  if (message.next) return findHead(message.next);
@@ -2038,12 +2153,13 @@ var MessageRepository = class {
2038
2153
  do {
2039
2154
  optimisticId = generateOptimisticId();
2040
2155
  } 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
- });
2156
+ this.addOrUpdateMessage(
2157
+ parentId,
2158
+ fromCoreMessage(message, {
2159
+ id: optimisticId,
2160
+ status: { type: "running" }
2161
+ })
2162
+ );
2047
2163
  return optimisticId;
2048
2164
  }
2049
2165
  deleteMessage(messageId, replacementId) {
@@ -2110,40 +2226,6 @@ var MessageRepository = class {
2110
2226
  }
2111
2227
  };
2112
2228
 
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
2229
  // src/ui/base/tooltip-icon-button.tsx
2148
2230
  import { forwardRef as forwardRef17 } from "react";
2149
2231
 
@@ -2460,35 +2542,43 @@ var fromLanguageModelMessages = (lm, { mergeRoundtrips }) => {
2460
2542
  return messages;
2461
2543
  };
2462
2544
 
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
2545
  // src/runtimes/edge/converters/toCoreMessages.ts
2478
2546
  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
- });
2547
+ return message.map(toCoreMessage);
2548
+ };
2549
+ var toCoreMessage = (message) => {
2550
+ const role = message.role;
2551
+ switch (role) {
2552
+ case "assistant":
2553
+ return {
2554
+ role,
2555
+ content: message.content.map((part) => {
2556
+ if (part.type === "ui") throw new Error("UI parts are not supported");
2557
+ if (part.type === "tool-call") {
2558
+ const { argsText, ...rest } = part;
2559
+ return rest;
2560
+ }
2561
+ return part;
2562
+ })
2563
+ };
2564
+ case "user":
2565
+ return {
2566
+ role,
2567
+ content: message.content.map((part) => {
2568
+ if (part.type === "ui") throw new Error("UI parts are not supported");
2569
+ return part;
2570
+ })
2571
+ };
2572
+ case "system":
2573
+ return {
2574
+ role,
2575
+ content: message.content
2576
+ };
2577
+ default: {
2578
+ const unsupportedRole = role;
2579
+ throw new Error(`Unknown message role: ${unsupportedRole}`);
2580
+ }
2581
+ }
2492
2582
  };
2493
2583
 
2494
2584
  // src/runtimes/edge/converters/fromLanguageModelTools.ts
@@ -2958,9 +3048,10 @@ var parsePartialJson = (json) => {
2958
3048
  };
2959
3049
 
2960
3050
  // src/runtimes/edge/streams/runResultStream.ts
2961
- function runResultStream(initialContent) {
3051
+ function runResultStream() {
2962
3052
  let message = {
2963
- content: initialContent
3053
+ content: [],
3054
+ status: { type: "running" }
2964
3055
  };
2965
3056
  const currentToolCall = { toolCallId: "", argsText: "" };
2966
3057
  return new TransformStream({
@@ -3008,7 +3099,13 @@ function runResultStream(initialContent) {
3008
3099
  break;
3009
3100
  }
3010
3101
  case "error": {
3011
- throw chunk.error;
3102
+ if (chunk.error instanceof Error && chunk.error.name === "AbortError") {
3103
+ message = appendOrUpdateCancel(message);
3104
+ controller.enqueue(message);
3105
+ break;
3106
+ } else {
3107
+ throw chunk.error;
3108
+ }
3012
3109
  }
3013
3110
  default: {
3014
3111
  const unhandledType = chunkType;
@@ -3082,11 +3179,42 @@ var appendOrUpdateToolResult = (message, toolCallId, toolName, result) => {
3082
3179
  };
3083
3180
  var appendOrUpdateFinish = (message, chunk) => {
3084
3181
  const { type, ...rest } = chunk;
3182
+ return {
3183
+ ...message,
3184
+ status: getStatus(chunk),
3185
+ roundtrips: [
3186
+ ...message.roundtrips ?? [],
3187
+ {
3188
+ logprobs: rest.logprobs,
3189
+ usage: rest.usage
3190
+ }
3191
+ ]
3192
+ };
3193
+ };
3194
+ var getStatus = (chunk) => {
3195
+ if (chunk.finishReason === "tool-calls") {
3196
+ return {
3197
+ type: "requires-action",
3198
+ reason: "tool-calls"
3199
+ };
3200
+ } else if (chunk.finishReason === "stop" || chunk.finishReason === "unknown") {
3201
+ return {
3202
+ type: "complete",
3203
+ reason: chunk.finishReason
3204
+ };
3205
+ } else {
3206
+ return {
3207
+ type: "incomplete",
3208
+ reason: chunk.finishReason
3209
+ };
3210
+ }
3211
+ };
3212
+ var appendOrUpdateCancel = (message) => {
3085
3213
  return {
3086
3214
  ...message,
3087
3215
  status: {
3088
- type: "done",
3089
- ...rest
3216
+ type: "incomplete",
3217
+ reason: "cancelled"
3090
3218
  }
3091
3219
  };
3092
3220
  };
@@ -3186,7 +3314,7 @@ var EdgeChatAdapter = class {
3186
3314
  constructor(options) {
3187
3315
  this.options = options;
3188
3316
  }
3189
- async roundtrip(initialContent, { messages, abortSignal, config, onUpdate }) {
3317
+ async run({ messages, abortSignal, config, onUpdate }) {
3190
3318
  const result = await fetch(this.options.api, {
3191
3319
  method: "POST",
3192
3320
  headers: {
@@ -3206,46 +3334,14 @@ var EdgeChatAdapter = class {
3206
3334
  `Edge runtime returned status ${result.status}: ${await result.text()}`
3207
3335
  );
3208
3336
  }
3209
- const stream = result.body.pipeThrough(new TextDecoderStream()).pipeThrough(chunkByLineStream()).pipeThrough(assistantDecoderStream()).pipeThrough(toolResultStream(config.tools)).pipeThrough(runResultStream(initialContent));
3210
- let message;
3337
+ const stream = result.body.pipeThrough(new TextDecoderStream()).pipeThrough(chunkByLineStream()).pipeThrough(assistantDecoderStream()).pipeThrough(toolResultStream(config.tools)).pipeThrough(runResultStream());
3211
3338
  let update;
3212
3339
  for await (update of asAsyncIterable(stream)) {
3213
- message = onUpdate(update);
3340
+ onUpdate(update);
3214
3341
  }
3215
3342
  if (update === void 0)
3216
3343
  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;
3344
+ return update;
3249
3345
  }
3250
3346
  };
3251
3347
 
@@ -3258,30 +3354,10 @@ var useEdgeRuntime = ({
3258
3354
  return useLocalRuntime(adapter, { initialMessages });
3259
3355
  };
3260
3356
 
3261
- // src/runtimes/local/LocalRuntime.tsx
3262
- var LocalRuntime = class extends BaseAssistantRuntime {
3263
- _proxyConfigProvider;
3264
- constructor(adapter, options) {
3265
- const proxyConfigProvider = new ProxyConfigProvider();
3266
- super(new LocalThreadRuntime(proxyConfigProvider, adapter, options));
3267
- this._proxyConfigProvider = proxyConfigProvider;
3268
- }
3269
- set adapter(adapter) {
3270
- this.thread.adapter = adapter;
3271
- }
3272
- registerModelConfigProvider(provider) {
3273
- return this._proxyConfigProvider.registerModelConfigProvider(provider);
3274
- }
3275
- switchToThread(threadId) {
3276
- if (threadId) {
3277
- throw new Error("LocalRuntime does not yet support switching threads");
3278
- }
3279
- return this.thread = new LocalThreadRuntime(
3280
- this._proxyConfigProvider,
3281
- this.thread.adapter
3282
- );
3283
- }
3284
- };
3357
+ // src/runtimes/local/shouldContinue.tsx
3358
+ var shouldContinue = (result) => result.status?.type === "requires-action" && result.status.reason === "tool-calls" && result.content.every((c) => c.type !== "tool-call" || !!c.result);
3359
+
3360
+ // src/runtimes/local/LocalThreadRuntime.tsx
3285
3361
  var CAPABILITIES = Object.freeze({
3286
3362
  edit: true,
3287
3363
  reload: true,
@@ -3292,6 +3368,7 @@ var LocalThreadRuntime = class {
3292
3368
  constructor(configProvider, adapter, options) {
3293
3369
  this.configProvider = configProvider;
3294
3370
  this.adapter = adapter;
3371
+ this.options = options;
3295
3372
  if (options?.initialMessages) {
3296
3373
  let parentId = null;
3297
3374
  const messages = fromCoreMessages(options.initialMessages);
@@ -3335,27 +3412,51 @@ var LocalThreadRuntime = class {
3335
3412
  }
3336
3413
  async startRun(parentId) {
3337
3414
  this.repository.resetHead(parentId);
3338
- const messages = this.repository.getMessages();
3415
+ const id = generateId();
3339
3416
  let message = {
3340
- id: generateId(),
3417
+ id,
3341
3418
  role: "assistant",
3342
- status: { type: "in_progress" },
3343
- content: [{ type: "text", text: "" }],
3419
+ status: { type: "running" },
3420
+ content: [],
3344
3421
  createdAt: /* @__PURE__ */ new Date()
3345
3422
  };
3423
+ do {
3424
+ message = await this.performRoundtrip(parentId, message);
3425
+ } while (shouldContinue(message));
3426
+ }
3427
+ async performRoundtrip(parentId, message) {
3428
+ const messages = this.repository.getMessages();
3346
3429
  this.abortController?.abort();
3347
3430
  this.abortController = new AbortController();
3348
- this.repository.addOrUpdateMessage(parentId, { ...message });
3349
- this.notifySubscribers();
3431
+ const initialContent = message.content;
3432
+ const initialRoundtrips = message.roundtrips;
3350
3433
  const updateMessage = (m) => {
3351
3434
  message = {
3352
3435
  ...message,
3353
- ...m
3436
+ ...m.content ? { content: [...initialContent, ...m.content ?? []] } : void 0,
3437
+ status: m.status ?? message.status,
3438
+ ...m.roundtrips?.length ? { roundtrips: [...initialRoundtrips ?? [], ...m.roundtrips] } : void 0
3354
3439
  };
3355
3440
  this.repository.addOrUpdateMessage(parentId, message);
3356
3441
  this.notifySubscribers();
3357
- return message;
3358
3442
  };
3443
+ const maxToolRoundtrips = this.options?.maxToolRoundtrips ?? 1;
3444
+ const toolRoundtrips = message.roundtrips?.length ?? 0;
3445
+ if (toolRoundtrips > maxToolRoundtrips) {
3446
+ updateMessage({
3447
+ status: {
3448
+ type: "incomplete",
3449
+ reason: "tool-calls"
3450
+ }
3451
+ });
3452
+ return message;
3453
+ } else {
3454
+ updateMessage({
3455
+ status: {
3456
+ type: "running"
3457
+ }
3458
+ });
3459
+ }
3359
3460
  try {
3360
3461
  const result = await this.adapter.run({
3361
3462
  messages,
@@ -3363,21 +3464,29 @@ var LocalThreadRuntime = class {
3363
3464
  config: this.configProvider.getModelConfig(),
3364
3465
  onUpdate: updateMessage
3365
3466
  });
3366
- if (result.status?.type === "in_progress")
3467
+ if (result.status?.type === "running")
3367
3468
  throw new Error(
3368
- "Unexpected in_progress status returned from ChatModelAdapter"
3469
+ "Unexpected running status returned from ChatModelAdapter"
3369
3470
  );
3370
3471
  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
3472
  updateMessage({
3377
- status: isAbortError ? { type: "cancelled" } : { type: "error", error: e }
3473
+ status: { type: "complete", reason: "unknown" },
3474
+ ...result
3378
3475
  });
3379
- if (!isAbortError) throw e;
3476
+ } catch (e) {
3477
+ this.abortController = null;
3478
+ if (e instanceof Error && e.name === "AbortError") {
3479
+ updateMessage({
3480
+ status: { type: "incomplete", reason: "cancelled" }
3481
+ });
3482
+ } else {
3483
+ updateMessage({
3484
+ status: { type: "incomplete", reason: "error", error: e }
3485
+ });
3486
+ throw e;
3487
+ }
3380
3488
  }
3489
+ return message;
3381
3490
  }
3382
3491
  cancelRun() {
3383
3492
  if (!this.abortController) return;
@@ -3392,14 +3501,16 @@ var LocalThreadRuntime = class {
3392
3501
  return () => this._subscriptions.delete(callback);
3393
3502
  }
3394
3503
  addToolResult({ messageId, toolCallId, result }) {
3395
- const { parentId, message } = this.repository.getMessage(messageId);
3504
+ let { parentId, message } = this.repository.getMessage(messageId);
3396
3505
  if (message.role !== "assistant")
3397
3506
  throw new Error("Tried to add tool result to non-assistant message");
3507
+ let added = false;
3398
3508
  let found = false;
3399
3509
  const newContent = message.content.map((c) => {
3400
3510
  if (c.type !== "tool-call") return c;
3401
3511
  if (c.toolCallId !== toolCallId) return c;
3402
3512
  found = true;
3513
+ if (!c.result) added = true;
3403
3514
  return {
3404
3515
  ...c,
3405
3516
  result
@@ -3407,10 +3518,39 @@ var LocalThreadRuntime = class {
3407
3518
  });
3408
3519
  if (!found)
3409
3520
  throw new Error("Tried to add tool result to non-existing tool call");
3410
- this.repository.addOrUpdateMessage(parentId, {
3521
+ message = {
3411
3522
  ...message,
3412
3523
  content: newContent
3413
- });
3524
+ };
3525
+ this.repository.addOrUpdateMessage(parentId, message);
3526
+ if (added && shouldContinue(message)) {
3527
+ this.performRoundtrip(parentId, message);
3528
+ }
3529
+ }
3530
+ };
3531
+
3532
+ // src/runtimes/local/LocalRuntime.tsx
3533
+ var LocalRuntime = class extends BaseAssistantRuntime {
3534
+ _proxyConfigProvider;
3535
+ constructor(adapter, options) {
3536
+ const proxyConfigProvider = new ProxyConfigProvider();
3537
+ super(new LocalThreadRuntime(proxyConfigProvider, adapter, options));
3538
+ this._proxyConfigProvider = proxyConfigProvider;
3539
+ }
3540
+ set adapter(adapter) {
3541
+ this.thread.adapter = adapter;
3542
+ }
3543
+ registerModelConfigProvider(provider) {
3544
+ return this._proxyConfigProvider.registerModelConfigProvider(provider);
3545
+ }
3546
+ switchToThread(threadId) {
3547
+ if (threadId) {
3548
+ throw new Error("LocalRuntime does not yet support switching threads");
3549
+ }
3550
+ return this.thread = new LocalThreadRuntime(
3551
+ this._proxyConfigProvider,
3552
+ this.thread.adapter
3553
+ );
3414
3554
  }
3415
3555
  };
3416
3556
 
@@ -3605,7 +3745,7 @@ var Text = ({ status }) => {
3605
3745
  {
3606
3746
  className: classNames2(
3607
3747
  "aui-text",
3608
- status.type === "in_progress" && "aui-text-in-progress"
3748
+ status.type === "running" && "aui-text-in-progress"
3609
3749
  ),
3610
3750
  children: /* @__PURE__ */ jsx33(contentPart_exports.Text, {})
3611
3751
  }
@@ -4131,11 +4271,13 @@ export {
4131
4271
  thread_welcome_default as ThreadWelcome,
4132
4272
  user_action_bar_default as UserActionBar,
4133
4273
  user_message_default as UserMessage,
4274
+ fromCoreMessage,
4134
4275
  fromCoreMessages,
4135
4276
  fromLanguageModelMessages,
4136
4277
  fromLanguageModelTools,
4137
4278
  makeAssistantTool,
4138
4279
  makeAssistantToolUI,
4280
+ toCoreMessage,
4139
4281
  toCoreMessages,
4140
4282
  toLanguageModelMessages,
4141
4283
  toLanguageModelTools,