@hef2024/llmasaservice-ui 0.25.1 → 0.25.3

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.js CHANGED
@@ -5533,6 +5533,10 @@ var AIChatPanel = ({
5533
5533
  const queuedToolRequestsRef = (0, import_react14.useRef)(null);
5534
5534
  const suppressAbortHistoryUpdateRef = (0, import_react14.useRef)(false);
5535
5535
  const toolReplaySummariesByKeyRef = (0, import_react14.useRef)({});
5536
+ const continuationDispatchPendingRef = (0, import_react14.useRef)(false);
5537
+ const successfulMutationSignaturesRef = (0, import_react14.useRef)(/* @__PURE__ */ new Set());
5538
+ const queuedDrainTimerRef = (0, import_react14.useRef)(null);
5539
+ const queuedDrainScheduledRef = (0, import_react14.useRef)(false);
5536
5540
  (0, import_react14.useEffect)(() => {
5537
5541
  if (!initialHistory) return;
5538
5542
  setHistory((prev) => {
@@ -6041,6 +6045,8 @@ var AIChatPanel = ({
6041
6045
  return false;
6042
6046
  }, [customerEmailCaptureMode, emailInputSet]);
6043
6047
  const pendingToolRequestsRef = (0, import_react14.useRef)(pendingToolRequests);
6048
+ const activeToolCallsRef = (0, import_react14.useRef)(activeToolCalls);
6049
+ const turnLockRef = (0, import_react14.useRef)(false);
6044
6050
  const streamIdleRef = (0, import_react14.useRef)(idle);
6045
6051
  streamIdleRef.current = idle;
6046
6052
  const waitForStreamIdle = (0, import_react14.useCallback)((timeoutMs = 2500) => __async(void 0, null, function* () {
@@ -6054,6 +6060,36 @@ var AIChatPanel = ({
6054
6060
  (0, import_react14.useEffect)(() => {
6055
6061
  pendingToolRequestsRef.current = pendingToolRequests;
6056
6062
  }, [pendingToolRequests]);
6063
+ (0, import_react14.useEffect)(() => {
6064
+ activeToolCallsRef.current = activeToolCalls;
6065
+ }, [activeToolCalls]);
6066
+ const hasUnresolvedToolRequestsInLatestResponse = (0, import_react14.useCallback)(() => {
6067
+ const latestResponse = String(responseRef.current || "");
6068
+ if (!latestResponse.trim()) return false;
6069
+ const latestRequests = extractToolRequestMatchesFromText(latestResponse);
6070
+ if (latestRequests.length === 0) return false;
6071
+ return latestRequests.some((request) => {
6072
+ const signature = getToolCallSignature(request.toolName, request.callId);
6073
+ if (!signature) return false;
6074
+ if (handledToolCallSignaturesRef.current.has(signature)) return false;
6075
+ if (inFlightToolCallSignaturesRef.current.has(signature)) return false;
6076
+ return true;
6077
+ });
6078
+ }, [getToolCallSignature]);
6079
+ const hasInFlightTurnWork = (0, import_react14.useCallback)(() => {
6080
+ return toolRequestProcessingRef.current || continuationDispatchPendingRef.current || (queuedToolRequestsRef.current || []).length > 0 || (pendingToolRequestsRef.current || []).length > 0 || (activeToolCallsRef.current || []).length > 0 || !streamIdleRef.current || hasUnresolvedToolRequestsInLatestResponse();
6081
+ }, [hasUnresolvedToolRequestsInLatestResponse]);
6082
+ const queuePromptForLater = (0, import_react14.useCallback)((promptText) => {
6083
+ const normalizedPrompt = String(promptText || "").trim();
6084
+ if (!normalizedPrompt) return;
6085
+ const nextQueuedPrompts = [...queuedPromptsRef.current, normalizedPrompt];
6086
+ queuedPromptsRef.current = nextQueuedPrompts;
6087
+ setQueuedPrompts(nextQueuedPrompts);
6088
+ }, []);
6089
+ const releaseTurnLockIfSettled = (0, import_react14.useCallback)(() => {
6090
+ if (hasInFlightTurnWork()) return;
6091
+ turnLockRef.current = false;
6092
+ }, [hasInFlightTurnWork]);
6057
6093
  const processGivenToolRequests = (0, import_react14.useCallback)(
6058
6094
  (requests) => __async(void 0, null, function* () {
6059
6095
  var _a2, _b, _c;
@@ -6080,6 +6116,7 @@ var AIChatPanel = ({
6080
6116
  return;
6081
6117
  }
6082
6118
  toolRequestProcessingRef.current = true;
6119
+ turnLockRef.current = true;
6083
6120
  try {
6084
6121
  let requestsToProcess = requests;
6085
6122
  if (!requestsToProcess || requestsToProcess.length === 0) {
@@ -6158,6 +6195,44 @@ ${traceSummary}` : traceSummary;
6158
6195
  });
6159
6196
  }
6160
6197
  }
6198
+ const normalizeToolArgsForSignature = (value) => {
6199
+ if (Array.isArray(value)) {
6200
+ return value.map((item) => normalizeToolArgsForSignature(item));
6201
+ }
6202
+ if (value && typeof value === "object") {
6203
+ const normalizedObject = {};
6204
+ Object.keys(value).sort().forEach((key) => {
6205
+ normalizedObject[key] = normalizeToolArgsForSignature(
6206
+ value[key]
6207
+ );
6208
+ });
6209
+ return normalizedObject;
6210
+ }
6211
+ return value != null ? value : null;
6212
+ };
6213
+ const getSemanticMutationSignature = (toolName, args) => {
6214
+ const normalizedName = String(toolName || "").trim().toLowerCase();
6215
+ const normalizedArgs = normalizeToolArgsForSignature(args);
6216
+ return `${normalizedName}::${JSON.stringify(normalizedArgs)}`;
6217
+ };
6218
+ const isLikelyMutatingCall = (toolName, args) => {
6219
+ const normalizedName = String(toolName || "").trim().toLowerCase();
6220
+ const action = String((args == null ? void 0 : args.action) || "").trim().toLowerCase();
6221
+ const mutatingActions = /* @__PURE__ */ new Set([
6222
+ "add",
6223
+ "create",
6224
+ "materialize_from_sourcing_result",
6225
+ "materialize",
6226
+ "update",
6227
+ "delete",
6228
+ "remove",
6229
+ "cancel",
6230
+ "complete",
6231
+ "move"
6232
+ ]);
6233
+ if (action && mutatingActions.has(action)) return true;
6234
+ return normalizedName.includes("pipeline") || normalizedName.includes("work_item") || normalizedName.includes("candidate_pipeline") || normalizedName.includes("cron");
6235
+ };
6161
6236
  const parsedToolCalls = yield Promise.all(
6162
6237
  requestsToProcess.map((req, index) => __async(void 0, null, function* () {
6163
6238
  var _a3, _b2, _c2, _d, _e, _f;
@@ -6186,27 +6261,44 @@ ${traceSummary}` : traceSummary;
6186
6261
  const serviceTag = typeof req.serviceTag === "string" && req.serviceTag || typeof req.groups[3] === "string" && req.groups[3] || typeof (parsedToolCall == null ? void 0 : parsedToolCall.service) === "string" && parsedToolCall.service || "";
6187
6262
  const callSignature = getToolCallSignature(toolName, callId);
6188
6263
  if (!callSignature) return null;
6264
+ const semanticMutationSignature = getSemanticMutationSignature(toolName, args);
6265
+ const likelyMutating = isLikelyMutatingCall(toolName, args);
6189
6266
  return {
6190
6267
  req,
6191
6268
  toolName,
6192
6269
  callId,
6193
6270
  args,
6194
6271
  serviceTag,
6195
- callSignature
6272
+ callSignature,
6273
+ semanticMutationSignature,
6274
+ likelyMutating
6196
6275
  };
6197
6276
  }))
6198
6277
  );
6199
6278
  const toolCallBatch = parsedToolCalls.filter(Boolean);
6200
6279
  const seenCallSignatures = /* @__PURE__ */ new Set();
6280
+ const seenMutationSemanticsInBatch = /* @__PURE__ */ new Set();
6201
6281
  const callsToRun = [];
6282
+ const suppressedMutationDuplicates = [];
6202
6283
  toolCallBatch.forEach((toolCall) => {
6203
6284
  if (seenCallSignatures.has(toolCall.callSignature)) return;
6204
6285
  seenCallSignatures.add(toolCall.callSignature);
6205
6286
  if (handledToolCallSignaturesRef.current.has(toolCall.callSignature)) return;
6206
6287
  if (inFlightToolCallSignaturesRef.current.has(toolCall.callSignature)) return;
6288
+ if (toolCall.likelyMutating && seenMutationSemanticsInBatch.has(toolCall.semanticMutationSignature)) {
6289
+ suppressedMutationDuplicates.push(toolCall);
6290
+ return;
6291
+ }
6292
+ if (toolCall.likelyMutating && successfulMutationSignaturesRef.current.has(toolCall.semanticMutationSignature)) {
6293
+ suppressedMutationDuplicates.push(toolCall);
6294
+ return;
6295
+ }
6296
+ if (toolCall.likelyMutating) {
6297
+ seenMutationSemanticsInBatch.add(toolCall.semanticMutationSignature);
6298
+ }
6207
6299
  callsToRun.push(toolCall);
6208
6300
  });
6209
- if (callsToRun.length === 0) {
6301
+ if (callsToRun.length === 0 && suppressedMutationDuplicates.length === 0) {
6210
6302
  setPendingToolRequests(
6211
6303
  (prev) => prev.filter((request) => {
6212
6304
  const signature = getToolCallSignature(request.toolName, request.callId);
@@ -6218,11 +6310,14 @@ ${traceSummary}` : traceSummary;
6218
6310
  setIsLoading(false);
6219
6311
  return;
6220
6312
  }
6221
- const callsToRunSignatures = new Set(callsToRun.map((toolCall) => toolCall.callSignature));
6313
+ const handledBatchSignatures = /* @__PURE__ */ new Set([
6314
+ ...callsToRun.map((toolCall) => toolCall.callSignature),
6315
+ ...suppressedMutationDuplicates.map((toolCall) => toolCall.callSignature)
6316
+ ]);
6222
6317
  setPendingToolRequests(
6223
6318
  (prev) => prev.filter((request) => {
6224
6319
  const signature = getToolCallSignature(request.toolName, request.callId);
6225
- return !signature || !callsToRunSignatures.has(signature);
6320
+ return !signature || !handledBatchSignatures.has(signature);
6226
6321
  })
6227
6322
  );
6228
6323
  callsToRun.forEach((toolCall) => {
@@ -6235,7 +6330,7 @@ ${traceSummary}` : traceSummary;
6235
6330
  args: toolCall.args
6236
6331
  }))
6237
6332
  );
6238
- const finalToolCalls = callsToRun.map((toolCall) => ({
6333
+ const finalToolCalls = [...callsToRun, ...suppressedMutationDuplicates].map((toolCall) => ({
6239
6334
  id: toolCall.callId,
6240
6335
  type: "tool_use",
6241
6336
  name: toolCall.toolName,
@@ -6366,12 +6461,30 @@ ${traceSummary}` : traceSummary;
6366
6461
  }
6367
6462
  }))
6368
6463
  );
6369
- finalToolResponses = toolResponses.filter(Boolean);
6464
+ const executedResponses = toolResponses.filter(Boolean);
6465
+ const suppressedResponses = suppressedMutationDuplicates.map((toolCall) => ({
6466
+ tool_call_id: toolCall.callId,
6467
+ tool_name: toolCall.toolName,
6468
+ result: "Duplicate mutating call suppressed: this exact action already succeeded earlier in the same turn.",
6469
+ isError: false
6470
+ }));
6471
+ finalToolResponses = [...executedResponses, ...suppressedResponses];
6472
+ callsToRun.forEach((toolCall) => {
6473
+ const matchedResponse = executedResponses.find(
6474
+ (response2) => (response2 == null ? void 0 : response2.tool_call_id) === toolCall.callId
6475
+ );
6476
+ if (!(matchedResponse == null ? void 0 : matchedResponse.isError) && toolCall.likelyMutating) {
6477
+ successfulMutationSignaturesRef.current.add(toolCall.semanticMutationSignature);
6478
+ }
6479
+ });
6370
6480
  } finally {
6371
6481
  callsToRun.forEach((toolCall) => {
6372
6482
  inFlightToolCallSignaturesRef.current.delete(toolCall.callSignature);
6373
6483
  handledToolCallSignaturesRef.current.add(toolCall.callSignature);
6374
6484
  });
6485
+ suppressedMutationDuplicates.forEach((toolCall) => {
6486
+ handledToolCallSignaturesRef.current.add(toolCall.callSignature);
6487
+ });
6375
6488
  }
6376
6489
  setActiveToolCalls([]);
6377
6490
  const currentLastKey = lastKeyRef.current;
@@ -6475,6 +6588,7 @@ ${traceSummary}` : traceSummary;
6475
6588
  if (!streamIdleRef.current) {
6476
6589
  setActiveToolCalls([]);
6477
6590
  setIsLoading(false);
6591
+ continuationDispatchPendingRef.current = false;
6478
6592
  setError({
6479
6593
  message: "Timed out waiting for the previous stream to settle before tool continuation.",
6480
6594
  code: "TOOL_CONTINUATION_WAIT_TIMEOUT"
@@ -6490,6 +6604,7 @@ ${traceSummary}` : traceSummary;
6490
6604
  } else {
6491
6605
  activeStreamAppendBaseRef.current = null;
6492
6606
  }
6607
+ continuationDispatchPendingRef.current = true;
6493
6608
  send(
6494
6609
  "",
6495
6610
  newMessages,
@@ -6507,12 +6622,14 @@ ${traceSummary}` : traceSummary;
6507
6622
  newController,
6508
6623
  void 0,
6509
6624
  (errorMsg) => {
6625
+ continuationDispatchPendingRef.current = false;
6510
6626
  setActiveToolCalls([]);
6511
6627
  setIsLoading(false);
6512
6628
  setError({
6513
6629
  message: errorMsg,
6514
6630
  code: "TOOL_ERROR"
6515
6631
  });
6632
+ releaseTurnLockIfSettled();
6516
6633
  }
6517
6634
  );
6518
6635
  } finally {
@@ -6523,6 +6640,8 @@ ${traceSummary}` : traceSummary;
6523
6640
  queueMicrotask(() => {
6524
6641
  void processGivenToolRequests(queued);
6525
6642
  });
6643
+ } else {
6644
+ releaseTurnLockIfSettled();
6526
6645
  }
6527
6646
  }
6528
6647
  }),
@@ -6541,7 +6660,8 @@ ${traceSummary}` : traceSummary;
6541
6660
  idle,
6542
6661
  stop,
6543
6662
  lastController,
6544
- waitForStreamIdle
6663
+ waitForStreamIdle,
6664
+ releaseTurnLockIfSettled
6545
6665
  ]
6546
6666
  );
6547
6667
  const handleToolApproval = (0, import_react14.useCallback)(
@@ -6566,6 +6686,8 @@ ${traceSummary}` : traceSummary;
6566
6686
  );
6567
6687
  (0, import_react14.useEffect)(() => {
6568
6688
  if (pendingToolRequests.length === 0) return;
6689
+ if (!idle) return;
6690
+ if (continuationDispatchPendingRef.current) return;
6569
6691
  const configuredAutoApproveTools = Array.isArray(autoApproveTools) ? new Set(
6570
6692
  autoApproveTools.map((toolName) => normalizeToolName(String(toolName))).filter(Boolean)
6571
6693
  ) : null;
@@ -6594,6 +6716,7 @@ ${traceSummary}` : traceSummary;
6594
6716
  }
6595
6717
  }, [
6596
6718
  autoApproveTools,
6719
+ idle,
6597
6720
  pendingToolRequests,
6598
6721
  sessionApprovedTools,
6599
6722
  alwaysApprovedTools,
@@ -6902,11 +7025,20 @@ ${traceSummary}` : traceSummary;
6902
7025
  lastScrollTimeRef.current = now;
6903
7026
  }, []);
6904
7027
  const continueChat = (0, import_react14.useCallback)((promptText) => {
7028
+ const promptToSend = String(promptText || "").trim();
7029
+ if (!promptToSend) return;
7030
+ if (turnLockRef.current || hasInFlightTurnWork()) {
7031
+ queuePromptForLater(promptToSend);
7032
+ return;
7033
+ }
7034
+ turnLockRef.current = true;
6905
7035
  handledToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6906
7036
  inFlightToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6907
7037
  toolContinuationCountRef.current = 0;
7038
+ successfulMutationSignaturesRef.current = /* @__PURE__ */ new Set();
6908
7039
  activeStreamAppendBaseRef.current = null;
6909
7040
  toolReplaySummariesByKeyRef.current = {};
7041
+ continuationDispatchPendingRef.current = false;
6910
7042
  setPendingToolRequests([]);
6911
7043
  setActiveToolCalls([]);
6912
7044
  setCollapsedBlocks((prev) => {
@@ -6926,29 +7058,17 @@ ${traceSummary}` : traceSummary;
6926
7058
  setUserHasScrolled(false);
6927
7059
  prevResponseLengthRef.current = 0;
6928
7060
  setResponse("");
6929
- if (!idle) {
6930
- stop(lastController);
6931
- setHistory((prevHistory) => __spreadProps(__spreadValues({}, prevHistory), {
6932
- [lastKey != null ? lastKey : ""]: {
6933
- content: processThinkingTags(response).cleanedText + "\n\n(response cancelled)",
6934
- callId: lastCallId || ""
6935
- }
6936
- }));
6937
- return;
6938
- }
6939
7061
  if (clearFollowOnQuestionsNextPrompt) {
6940
7062
  setFollowOnQuestionsState([]);
6941
7063
  }
6942
- const promptToSend = promptText;
6943
- if (!promptToSend || !promptToSend.trim()) return;
6944
7064
  setIsLoading(true);
6945
7065
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
6946
- const promptKey = `${timestamp}:${promptToSend.trim()}`;
7066
+ const promptKey = `${timestamp}:${promptToSend}`;
6947
7067
  setHistory((prevHistory) => __spreadProps(__spreadValues({}, prevHistory), {
6948
7068
  [promptKey]: { content: "", callId: "" }
6949
7069
  }));
6950
7070
  toolReplaySummariesByKeyRef.current[promptKey] = [];
6951
- setLastPrompt(promptToSend.trim());
7071
+ setLastPrompt(promptToSend);
6952
7072
  setLastKey(promptKey);
6953
7073
  setTimeout(() => {
6954
7074
  scrollToBottom(true);
@@ -6965,7 +7085,7 @@ ${traceSummary}` : traceSummary;
6965
7085
  messagesAndHistory.push({ role: "user", content: promptForHistory });
6966
7086
  messagesAndHistory.push({ role: "assistant", content: assistantContextContent });
6967
7087
  });
6968
- let fullPromptToSend = promptToSend.trim();
7088
+ let fullPromptToSend = promptToSend;
6969
7089
  if (messagesAndHistory.length === 0 && promptTemplate) {
6970
7090
  fullPromptToSend = promptTemplate.replace("{{prompt}}", fullPromptToSend);
6971
7091
  }
@@ -6974,7 +7094,7 @@ ${traceSummary}` : traceSummary;
6974
7094
  if (onBeforeSend) {
6975
7095
  void Promise.resolve(
6976
7096
  onBeforeSend({
6977
- prompt: promptToSend.trim(),
7097
+ prompt: promptToSend,
6978
7098
  conversationId: convId || null,
6979
7099
  agentId: agent,
6980
7100
  service,
@@ -7008,6 +7128,7 @@ ${traceSummary}` : traceSummary;
7008
7128
  if (isAbortError) {
7009
7129
  if (suppressAbortHistoryUpdateRef.current) {
7010
7130
  setIsLoading(false);
7131
+ releaseTurnLockIfSettled();
7011
7132
  return;
7012
7133
  }
7013
7134
  console.log("[AIChatPanel] Request was aborted by user");
@@ -7072,6 +7193,7 @@ ${traceSummary}` : traceSummary;
7072
7193
  }
7073
7194
  }
7074
7195
  setIsLoading(false);
7196
+ releaseTurnLockIfSettled();
7075
7197
  }
7076
7198
  );
7077
7199
  setLastMessages(messagesAndHistory);
@@ -7080,15 +7202,16 @@ ${traceSummary}` : traceSummary;
7080
7202
  onConversationCreated(convId);
7081
7203
  }, 100);
7082
7204
  }
7205
+ }).catch((error2) => {
7206
+ console.error("[AIChatPanel] Failed to send prompt:", error2);
7207
+ setError({
7208
+ message: error2 instanceof Error ? error2.message : "Failed to send prompt",
7209
+ code: "UNKNOWN_ERROR"
7210
+ });
7211
+ setIsLoading(false);
7212
+ releaseTurnLockIfSettled();
7083
7213
  });
7084
7214
  }, [
7085
- idle,
7086
- stop,
7087
- lastController,
7088
- lastKey,
7089
- response,
7090
- lastCallId,
7091
- processThinkingTags,
7092
7215
  clearFollowOnQuestionsNextPrompt,
7093
7216
  promptTemplate,
7094
7217
  send,
@@ -7102,17 +7225,16 @@ ${traceSummary}` : traceSummary;
7102
7225
  scrollToBottom,
7103
7226
  onConversationCreated,
7104
7227
  onBeforeSend,
7228
+ hasInFlightTurnWork,
7229
+ queuePromptForLater,
7230
+ releaseTurnLockIfSettled,
7105
7231
  getThinkingBlockCollapseKey,
7106
7232
  getThinkingBlockRenderKey,
7107
7233
  setResponse
7108
7234
  ]);
7109
7235
  const handleQueuePrompt = (0, import_react14.useCallback)((promptText) => {
7110
- const normalizedPrompt = String(promptText || "").trim();
7111
- if (!normalizedPrompt) return;
7112
- const nextQueuedPrompts = [...queuedPromptsRef.current, normalizedPrompt];
7113
- queuedPromptsRef.current = nextQueuedPrompts;
7114
- setQueuedPrompts(nextQueuedPrompts);
7115
- }, []);
7236
+ queuePromptForLater(promptText);
7237
+ }, [queuePromptForLater]);
7116
7238
  const handleClearQueuedPrompt = (0, import_react14.useCallback)((index) => {
7117
7239
  const nextQueuedPrompts = queuedPromptsRef.current.filter((_, queueIndex) => queueIndex !== index);
7118
7240
  queuedPromptsRef.current = nextQueuedPrompts;
@@ -7124,18 +7246,6 @@ ${traceSummary}` : traceSummary;
7124
7246
  const handleStop = (0, import_react14.useCallback)(() => {
7125
7247
  stop(lastController);
7126
7248
  }, [stop, lastController]);
7127
- (0, import_react14.useEffect)(() => {
7128
- const nextQueuedPrompt = String(queuedPromptsRef.current[0] || "").trim();
7129
- if (!nextQueuedPrompt) return;
7130
- if (!idle || isLoading) return;
7131
- if (pendingToolRequests.length > 0 || activeToolCalls.length > 0) return;
7132
- if (toolRequestProcessingRef.current) return;
7133
- if ((queuedToolRequestsRef.current || []).length > 0) return;
7134
- const remainingQueuedPrompts = queuedPromptsRef.current.slice(1);
7135
- queuedPromptsRef.current = remainingQueuedPrompts;
7136
- setQueuedPrompts(remainingQueuedPrompts);
7137
- continueChat(nextQueuedPrompt);
7138
- }, [activeToolCalls.length, continueChat, idle, isLoading, pendingToolRequests.length, queuedPrompts.length]);
7139
7249
  const handleNewConversation = (0, import_react14.useCallback)(() => {
7140
7250
  if (!newConversationConfirm) {
7141
7251
  setNewConversationConfirm(true);
@@ -7176,6 +7286,7 @@ ${traceSummary}` : traceSummary;
7176
7286
  setUserHasScrolled(false);
7177
7287
  setError(null);
7178
7288
  setActiveToolCalls([]);
7289
+ turnLockRef.current = false;
7179
7290
  setTimeout(() => {
7180
7291
  var _a2;
7181
7292
  setJustReset(false);
@@ -7195,6 +7306,7 @@ ${traceSummary}` : traceSummary;
7195
7306
  if (inFlightToolCallSignaturesRef.current.has(callSignature)) return false;
7196
7307
  return true;
7197
7308
  });
7309
+ pendingToolRequestsRef.current = unseenToolRequests;
7198
7310
  setPendingToolRequests((prev) => {
7199
7311
  if (areToolRequestListsEqual(prev, unseenToolRequests)) {
7200
7312
  return prev;
@@ -7285,9 +7397,11 @@ ${traceSummary}` : traceSummary;
7285
7397
  responseCompleteCallbackRef.current(currentLastCallId, currentLastPrompt || "", entry.content);
7286
7398
  }
7287
7399
  }
7400
+ releaseTurnLockIfSettled();
7288
7401
  }
7289
7402
  if (!isNowIdle && hasNotifiedCompletionRef.current) {
7290
7403
  hasNotifiedCompletionRef.current = false;
7404
+ continuationDispatchPendingRef.current = false;
7291
7405
  prevResponseLengthRef.current = 0;
7292
7406
  const currentLastKey = lastKeyRef.current;
7293
7407
  const existingContent = currentLastKey ? ((_a2 = latestHistoryRef.current[currentLastKey]) == null ? void 0 : _a2.content) || "" : "";
@@ -7296,7 +7410,48 @@ ${traceSummary}` : traceSummary;
7296
7410
  base: existingContent
7297
7411
  } : null;
7298
7412
  }
7299
- }, [idle]);
7413
+ }, [idle, releaseTurnLockIfSettled]);
7414
+ (0, import_react14.useEffect)(() => {
7415
+ if (!idle) return;
7416
+ releaseTurnLockIfSettled();
7417
+ }, [idle, response, pendingToolRequests.length, activeToolCalls.length, releaseTurnLockIfSettled]);
7418
+ (0, import_react14.useEffect)(() => {
7419
+ const nextQueuedPrompt = String(queuedPromptsRef.current[0] || "").trim();
7420
+ if (!nextQueuedPrompt) return;
7421
+ if (!idle || isLoading) return;
7422
+ if (pendingToolRequests.length > 0 || activeToolCalls.length > 0) return;
7423
+ if (turnLockRef.current || hasInFlightTurnWork()) return;
7424
+ if (queuedDrainScheduledRef.current) return;
7425
+ queuedDrainScheduledRef.current = true;
7426
+ queuedDrainTimerRef.current = window.setTimeout(() => {
7427
+ queuedDrainScheduledRef.current = false;
7428
+ queuedDrainTimerRef.current = null;
7429
+ const queuedPrompt = String(queuedPromptsRef.current[0] || "").trim();
7430
+ if (!queuedPrompt) return;
7431
+ if (turnLockRef.current || hasInFlightTurnWork()) return;
7432
+ const remainingQueuedPrompts = queuedPromptsRef.current.slice(1);
7433
+ queuedPromptsRef.current = remainingQueuedPrompts;
7434
+ setQueuedPrompts(remainingQueuedPrompts);
7435
+ continueChat(queuedPrompt);
7436
+ }, 0);
7437
+ }, [
7438
+ continueChat,
7439
+ hasInFlightTurnWork,
7440
+ idle,
7441
+ isLoading,
7442
+ pendingToolRequests.length,
7443
+ activeToolCalls.length,
7444
+ queuedPrompts.length
7445
+ ]);
7446
+ (0, import_react14.useEffect)(() => {
7447
+ return () => {
7448
+ if (queuedDrainTimerRef.current !== null) {
7449
+ window.clearTimeout(queuedDrainTimerRef.current);
7450
+ queuedDrainTimerRef.current = null;
7451
+ }
7452
+ queuedDrainScheduledRef.current = false;
7453
+ };
7454
+ }, []);
7300
7455
  (0, import_react14.useEffect)(() => {
7301
7456
  scrollToEndRef.current = scrollToEnd || false;
7302
7457
  }, [scrollToEnd]);
@@ -8589,7 +8744,49 @@ var parseCallMessages = (rawMessages) => {
8589
8744
  return normalized;
8590
8745
  };
8591
8746
  var shouldSkipTranscriptMessage = (message) => {
8592
- return message.role === "system" || message.role === "user" && message.content.startsWith("__system__:");
8747
+ const normalizedContent = message.content.trim().toLowerCase();
8748
+ const isInternalContinuationPrompt = message.role === "user" && normalizedContent.startsWith("original request:") && normalizedContent.includes("tool execution summary for the previous request:");
8749
+ return message.role === "system" || message.role === "user" && message.content.startsWith("__system__:") || isInternalContinuationPrompt;
8750
+ };
8751
+ var looksLikeToolStubResponse = (value) => {
8752
+ const text = value.trim();
8753
+ if (!text) return false;
8754
+ return text.includes('"type":"tool_use"') || text.includes('"type": "tool_use"') || text.includes('"type":"function"') || text.includes('"type": "function"');
8755
+ };
8756
+ var pickMoreCompleteResponse = (primaryResponse, fallbackResponse) => {
8757
+ const primary = primaryResponse.trim();
8758
+ const fallback = fallbackResponse.trim();
8759
+ if (!fallback) return primary;
8760
+ if (!primary) return fallback;
8761
+ if (primary === fallback) return primary;
8762
+ if (looksLikeToolStubResponse(primary) && !looksLikeToolStubResponse(fallback)) {
8763
+ return fallback;
8764
+ }
8765
+ if (fallback.startsWith(primary) || fallback.length > primary.length + 40) {
8766
+ return fallback;
8767
+ }
8768
+ return primary;
8769
+ };
8770
+ var mergeContinuationResponse = (baseResponse, continuationResponse) => {
8771
+ const base = baseResponse.trim();
8772
+ const continuation = continuationResponse.trim();
8773
+ if (!base) return continuation;
8774
+ if (!continuation) return base;
8775
+ if (looksLikeToolStubResponse(base) && !looksLikeToolStubResponse(continuation)) {
8776
+ return continuation;
8777
+ }
8778
+ if (base.includes(continuation)) {
8779
+ return base;
8780
+ }
8781
+ const maxOverlap = Math.min(base.length, continuation.length);
8782
+ for (let overlap = maxOverlap; overlap > 0; overlap -= 1) {
8783
+ if (base.slice(-overlap) === continuation.slice(0, overlap)) {
8784
+ return `${base}${continuation.slice(overlap)}`;
8785
+ }
8786
+ }
8787
+ return `${base}
8788
+
8789
+ ${continuation}`;
8593
8790
  };
8594
8791
  var parseTimestampMs = (value) => {
8595
8792
  const timestamp = toStringValue(value).trim();
@@ -8669,22 +8866,29 @@ var buildTurnsFromMessages = (messages, fallbackResponse, callId, timestampMs) =
8669
8866
  const prompt = message.content.trim();
8670
8867
  if (!prompt) continue;
8671
8868
  let response = "";
8869
+ let lastAssistantIndex = -1;
8672
8870
  let reachedNextUser = false;
8673
8871
  for (let lookAhead = index + 1; lookAhead < messages.length; lookAhead += 1) {
8674
8872
  const nextMessage = messages[lookAhead];
8675
8873
  if (!nextMessage) continue;
8676
8874
  if (nextMessage.role === "assistant") {
8677
- response = nextMessage.content.trim();
8678
- index = lookAhead;
8679
- break;
8875
+ const assistantResponse = nextMessage.content.trim();
8876
+ if (assistantResponse) {
8877
+ response = assistantResponse;
8878
+ lastAssistantIndex = lookAhead;
8879
+ }
8880
+ continue;
8680
8881
  }
8681
8882
  if (nextMessage.role === "user") {
8682
8883
  reachedNextUser = true;
8683
8884
  break;
8684
8885
  }
8685
8886
  }
8686
- if (!response && !reachedNextUser) {
8687
- response = fallbackResponse;
8887
+ if (lastAssistantIndex >= 0) {
8888
+ index = lastAssistantIndex;
8889
+ }
8890
+ if (!reachedNextUser) {
8891
+ response = pickMoreCompleteResponse(response, fallbackResponse);
8688
8892
  }
8689
8893
  if (!response) continue;
8690
8894
  turns.push({
@@ -8730,6 +8934,17 @@ var buildTranscriptTurnsFromCalls = (calls) => {
8730
8934
  timestampMs
8731
8935
  }
8732
8936
  ];
8937
+ } else if (fallbackResponse && mergedTurns.length > 0) {
8938
+ const previousTurn = mergedTurns[mergedTurns.length - 1];
8939
+ mergedTurns = [
8940
+ ...mergedTurns.slice(0, -1),
8941
+ __spreadProps(__spreadValues({}, previousTurn), {
8942
+ response: mergeContinuationResponse(previousTurn.response, fallbackResponse),
8943
+ callId: callId || previousTurn.callId,
8944
+ timestampMs: Math.max(previousTurn.timestampMs, timestampMs)
8945
+ })
8946
+ ];
8947
+ continue;
8733
8948
  }
8734
8949
  }
8735
8950
  mergedTurns = mergeTranscriptTurns(mergedTurns, callTurns);