@hef2024/llmasaservice-ui 0.25.0 → 0.25.2

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
@@ -5498,6 +5498,10 @@ var AIChatPanel = ({
5498
5498
  const queuedToolRequestsRef = useRef6(null);
5499
5499
  const suppressAbortHistoryUpdateRef = useRef6(false);
5500
5500
  const toolReplaySummariesByKeyRef = useRef6({});
5501
+ const continuationDispatchPendingRef = useRef6(false);
5502
+ const successfulMutationSignaturesRef = useRef6(/* @__PURE__ */ new Set());
5503
+ const queuedDrainTimerRef = useRef6(null);
5504
+ const queuedDrainScheduledRef = useRef6(false);
5501
5505
  useEffect8(() => {
5502
5506
  if (!initialHistory) return;
5503
5507
  setHistory((prev) => {
@@ -6006,6 +6010,8 @@ var AIChatPanel = ({
6006
6010
  return false;
6007
6011
  }, [customerEmailCaptureMode, emailInputSet]);
6008
6012
  const pendingToolRequestsRef = useRef6(pendingToolRequests);
6013
+ const activeToolCallsRef = useRef6(activeToolCalls);
6014
+ const turnLockRef = useRef6(false);
6009
6015
  const streamIdleRef = useRef6(idle);
6010
6016
  streamIdleRef.current = idle;
6011
6017
  const waitForStreamIdle = useCallback2((timeoutMs = 2500) => __async(void 0, null, function* () {
@@ -6019,6 +6025,36 @@ var AIChatPanel = ({
6019
6025
  useEffect8(() => {
6020
6026
  pendingToolRequestsRef.current = pendingToolRequests;
6021
6027
  }, [pendingToolRequests]);
6028
+ useEffect8(() => {
6029
+ activeToolCallsRef.current = activeToolCalls;
6030
+ }, [activeToolCalls]);
6031
+ const hasUnresolvedToolRequestsInLatestResponse = useCallback2(() => {
6032
+ const latestResponse = String(responseRef.current || "");
6033
+ if (!latestResponse.trim()) return false;
6034
+ const latestRequests = extractToolRequestMatchesFromText(latestResponse);
6035
+ if (latestRequests.length === 0) return false;
6036
+ return latestRequests.some((request) => {
6037
+ const signature = getToolCallSignature(request.toolName, request.callId);
6038
+ if (!signature) return false;
6039
+ if (handledToolCallSignaturesRef.current.has(signature)) return false;
6040
+ if (inFlightToolCallSignaturesRef.current.has(signature)) return false;
6041
+ return true;
6042
+ });
6043
+ }, [getToolCallSignature]);
6044
+ const hasInFlightTurnWork = useCallback2(() => {
6045
+ return toolRequestProcessingRef.current || continuationDispatchPendingRef.current || (queuedToolRequestsRef.current || []).length > 0 || (pendingToolRequestsRef.current || []).length > 0 || (activeToolCallsRef.current || []).length > 0 || !streamIdleRef.current || hasUnresolvedToolRequestsInLatestResponse();
6046
+ }, [hasUnresolvedToolRequestsInLatestResponse]);
6047
+ const queuePromptForLater = useCallback2((promptText) => {
6048
+ const normalizedPrompt = String(promptText || "").trim();
6049
+ if (!normalizedPrompt) return;
6050
+ const nextQueuedPrompts = [...queuedPromptsRef.current, normalizedPrompt];
6051
+ queuedPromptsRef.current = nextQueuedPrompts;
6052
+ setQueuedPrompts(nextQueuedPrompts);
6053
+ }, []);
6054
+ const releaseTurnLockIfSettled = useCallback2(() => {
6055
+ if (hasInFlightTurnWork()) return;
6056
+ turnLockRef.current = false;
6057
+ }, [hasInFlightTurnWork]);
6022
6058
  const processGivenToolRequests = useCallback2(
6023
6059
  (requests) => __async(void 0, null, function* () {
6024
6060
  var _a2, _b, _c;
@@ -6045,6 +6081,7 @@ var AIChatPanel = ({
6045
6081
  return;
6046
6082
  }
6047
6083
  toolRequestProcessingRef.current = true;
6084
+ turnLockRef.current = true;
6048
6085
  try {
6049
6086
  let requestsToProcess = requests;
6050
6087
  if (!requestsToProcess || requestsToProcess.length === 0) {
@@ -6123,6 +6160,44 @@ ${traceSummary}` : traceSummary;
6123
6160
  });
6124
6161
  }
6125
6162
  }
6163
+ const normalizeToolArgsForSignature = (value) => {
6164
+ if (Array.isArray(value)) {
6165
+ return value.map((item) => normalizeToolArgsForSignature(item));
6166
+ }
6167
+ if (value && typeof value === "object") {
6168
+ const normalizedObject = {};
6169
+ Object.keys(value).sort().forEach((key) => {
6170
+ normalizedObject[key] = normalizeToolArgsForSignature(
6171
+ value[key]
6172
+ );
6173
+ });
6174
+ return normalizedObject;
6175
+ }
6176
+ return value != null ? value : null;
6177
+ };
6178
+ const getSemanticMutationSignature = (toolName, args) => {
6179
+ const normalizedName = String(toolName || "").trim().toLowerCase();
6180
+ const normalizedArgs = normalizeToolArgsForSignature(args);
6181
+ return `${normalizedName}::${JSON.stringify(normalizedArgs)}`;
6182
+ };
6183
+ const isLikelyMutatingCall = (toolName, args) => {
6184
+ const normalizedName = String(toolName || "").trim().toLowerCase();
6185
+ const action = String((args == null ? void 0 : args.action) || "").trim().toLowerCase();
6186
+ const mutatingActions = /* @__PURE__ */ new Set([
6187
+ "add",
6188
+ "create",
6189
+ "materialize_from_sourcing_result",
6190
+ "materialize",
6191
+ "update",
6192
+ "delete",
6193
+ "remove",
6194
+ "cancel",
6195
+ "complete",
6196
+ "move"
6197
+ ]);
6198
+ if (action && mutatingActions.has(action)) return true;
6199
+ return normalizedName.includes("pipeline") || normalizedName.includes("work_item") || normalizedName.includes("candidate_pipeline") || normalizedName.includes("cron");
6200
+ };
6126
6201
  const parsedToolCalls = yield Promise.all(
6127
6202
  requestsToProcess.map((req, index) => __async(void 0, null, function* () {
6128
6203
  var _a3, _b2, _c2, _d, _e, _f;
@@ -6151,27 +6226,44 @@ ${traceSummary}` : traceSummary;
6151
6226
  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 || "";
6152
6227
  const callSignature = getToolCallSignature(toolName, callId);
6153
6228
  if (!callSignature) return null;
6229
+ const semanticMutationSignature = getSemanticMutationSignature(toolName, args);
6230
+ const likelyMutating = isLikelyMutatingCall(toolName, args);
6154
6231
  return {
6155
6232
  req,
6156
6233
  toolName,
6157
6234
  callId,
6158
6235
  args,
6159
6236
  serviceTag,
6160
- callSignature
6237
+ callSignature,
6238
+ semanticMutationSignature,
6239
+ likelyMutating
6161
6240
  };
6162
6241
  }))
6163
6242
  );
6164
6243
  const toolCallBatch = parsedToolCalls.filter(Boolean);
6165
6244
  const seenCallSignatures = /* @__PURE__ */ new Set();
6245
+ const seenMutationSemanticsInBatch = /* @__PURE__ */ new Set();
6166
6246
  const callsToRun = [];
6247
+ const suppressedMutationDuplicates = [];
6167
6248
  toolCallBatch.forEach((toolCall) => {
6168
6249
  if (seenCallSignatures.has(toolCall.callSignature)) return;
6169
6250
  seenCallSignatures.add(toolCall.callSignature);
6170
6251
  if (handledToolCallSignaturesRef.current.has(toolCall.callSignature)) return;
6171
6252
  if (inFlightToolCallSignaturesRef.current.has(toolCall.callSignature)) return;
6253
+ if (toolCall.likelyMutating && seenMutationSemanticsInBatch.has(toolCall.semanticMutationSignature)) {
6254
+ suppressedMutationDuplicates.push(toolCall);
6255
+ return;
6256
+ }
6257
+ if (toolCall.likelyMutating && successfulMutationSignaturesRef.current.has(toolCall.semanticMutationSignature)) {
6258
+ suppressedMutationDuplicates.push(toolCall);
6259
+ return;
6260
+ }
6261
+ if (toolCall.likelyMutating) {
6262
+ seenMutationSemanticsInBatch.add(toolCall.semanticMutationSignature);
6263
+ }
6172
6264
  callsToRun.push(toolCall);
6173
6265
  });
6174
- if (callsToRun.length === 0) {
6266
+ if (callsToRun.length === 0 && suppressedMutationDuplicates.length === 0) {
6175
6267
  setPendingToolRequests(
6176
6268
  (prev) => prev.filter((request) => {
6177
6269
  const signature = getToolCallSignature(request.toolName, request.callId);
@@ -6183,11 +6275,14 @@ ${traceSummary}` : traceSummary;
6183
6275
  setIsLoading(false);
6184
6276
  return;
6185
6277
  }
6186
- const callsToRunSignatures = new Set(callsToRun.map((toolCall) => toolCall.callSignature));
6278
+ const handledBatchSignatures = /* @__PURE__ */ new Set([
6279
+ ...callsToRun.map((toolCall) => toolCall.callSignature),
6280
+ ...suppressedMutationDuplicates.map((toolCall) => toolCall.callSignature)
6281
+ ]);
6187
6282
  setPendingToolRequests(
6188
6283
  (prev) => prev.filter((request) => {
6189
6284
  const signature = getToolCallSignature(request.toolName, request.callId);
6190
- return !signature || !callsToRunSignatures.has(signature);
6285
+ return !signature || !handledBatchSignatures.has(signature);
6191
6286
  })
6192
6287
  );
6193
6288
  callsToRun.forEach((toolCall) => {
@@ -6200,7 +6295,7 @@ ${traceSummary}` : traceSummary;
6200
6295
  args: toolCall.args
6201
6296
  }))
6202
6297
  );
6203
- const finalToolCalls = callsToRun.map((toolCall) => ({
6298
+ const finalToolCalls = [...callsToRun, ...suppressedMutationDuplicates].map((toolCall) => ({
6204
6299
  id: toolCall.callId,
6205
6300
  type: "tool_use",
6206
6301
  name: toolCall.toolName,
@@ -6331,12 +6426,30 @@ ${traceSummary}` : traceSummary;
6331
6426
  }
6332
6427
  }))
6333
6428
  );
6334
- finalToolResponses = toolResponses.filter(Boolean);
6429
+ const executedResponses = toolResponses.filter(Boolean);
6430
+ const suppressedResponses = suppressedMutationDuplicates.map((toolCall) => ({
6431
+ tool_call_id: toolCall.callId,
6432
+ tool_name: toolCall.toolName,
6433
+ result: "Duplicate mutating call suppressed: this exact action already succeeded earlier in the same turn.",
6434
+ isError: false
6435
+ }));
6436
+ finalToolResponses = [...executedResponses, ...suppressedResponses];
6437
+ callsToRun.forEach((toolCall) => {
6438
+ const matchedResponse = executedResponses.find(
6439
+ (response2) => (response2 == null ? void 0 : response2.tool_call_id) === toolCall.callId
6440
+ );
6441
+ if (!(matchedResponse == null ? void 0 : matchedResponse.isError) && toolCall.likelyMutating) {
6442
+ successfulMutationSignaturesRef.current.add(toolCall.semanticMutationSignature);
6443
+ }
6444
+ });
6335
6445
  } finally {
6336
6446
  callsToRun.forEach((toolCall) => {
6337
6447
  inFlightToolCallSignaturesRef.current.delete(toolCall.callSignature);
6338
6448
  handledToolCallSignaturesRef.current.add(toolCall.callSignature);
6339
6449
  });
6450
+ suppressedMutationDuplicates.forEach((toolCall) => {
6451
+ handledToolCallSignaturesRef.current.add(toolCall.callSignature);
6452
+ });
6340
6453
  }
6341
6454
  setActiveToolCalls([]);
6342
6455
  const currentLastKey = lastKeyRef.current;
@@ -6440,6 +6553,7 @@ ${traceSummary}` : traceSummary;
6440
6553
  if (!streamIdleRef.current) {
6441
6554
  setActiveToolCalls([]);
6442
6555
  setIsLoading(false);
6556
+ continuationDispatchPendingRef.current = false;
6443
6557
  setError({
6444
6558
  message: "Timed out waiting for the previous stream to settle before tool continuation.",
6445
6559
  code: "TOOL_CONTINUATION_WAIT_TIMEOUT"
@@ -6455,6 +6569,7 @@ ${traceSummary}` : traceSummary;
6455
6569
  } else {
6456
6570
  activeStreamAppendBaseRef.current = null;
6457
6571
  }
6572
+ continuationDispatchPendingRef.current = true;
6458
6573
  send(
6459
6574
  "",
6460
6575
  newMessages,
@@ -6472,12 +6587,14 @@ ${traceSummary}` : traceSummary;
6472
6587
  newController,
6473
6588
  void 0,
6474
6589
  (errorMsg) => {
6590
+ continuationDispatchPendingRef.current = false;
6475
6591
  setActiveToolCalls([]);
6476
6592
  setIsLoading(false);
6477
6593
  setError({
6478
6594
  message: errorMsg,
6479
6595
  code: "TOOL_ERROR"
6480
6596
  });
6597
+ releaseTurnLockIfSettled();
6481
6598
  }
6482
6599
  );
6483
6600
  } finally {
@@ -6488,6 +6605,8 @@ ${traceSummary}` : traceSummary;
6488
6605
  queueMicrotask(() => {
6489
6606
  void processGivenToolRequests(queued);
6490
6607
  });
6608
+ } else {
6609
+ releaseTurnLockIfSettled();
6491
6610
  }
6492
6611
  }
6493
6612
  }),
@@ -6506,7 +6625,8 @@ ${traceSummary}` : traceSummary;
6506
6625
  idle,
6507
6626
  stop,
6508
6627
  lastController,
6509
- waitForStreamIdle
6628
+ waitForStreamIdle,
6629
+ releaseTurnLockIfSettled
6510
6630
  ]
6511
6631
  );
6512
6632
  const handleToolApproval = useCallback2(
@@ -6531,6 +6651,8 @@ ${traceSummary}` : traceSummary;
6531
6651
  );
6532
6652
  useEffect8(() => {
6533
6653
  if (pendingToolRequests.length === 0) return;
6654
+ if (!idle) return;
6655
+ if (continuationDispatchPendingRef.current) return;
6534
6656
  const configuredAutoApproveTools = Array.isArray(autoApproveTools) ? new Set(
6535
6657
  autoApproveTools.map((toolName) => normalizeToolName(String(toolName))).filter(Boolean)
6536
6658
  ) : null;
@@ -6559,6 +6681,7 @@ ${traceSummary}` : traceSummary;
6559
6681
  }
6560
6682
  }, [
6561
6683
  autoApproveTools,
6684
+ idle,
6562
6685
  pendingToolRequests,
6563
6686
  sessionApprovedTools,
6564
6687
  alwaysApprovedTools,
@@ -6867,11 +6990,20 @@ ${traceSummary}` : traceSummary;
6867
6990
  lastScrollTimeRef.current = now;
6868
6991
  }, []);
6869
6992
  const continueChat = useCallback2((promptText) => {
6993
+ const promptToSend = String(promptText || "").trim();
6994
+ if (!promptToSend) return;
6995
+ if (turnLockRef.current || hasInFlightTurnWork()) {
6996
+ queuePromptForLater(promptToSend);
6997
+ return;
6998
+ }
6999
+ turnLockRef.current = true;
6870
7000
  handledToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6871
7001
  inFlightToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6872
7002
  toolContinuationCountRef.current = 0;
7003
+ successfulMutationSignaturesRef.current = /* @__PURE__ */ new Set();
6873
7004
  activeStreamAppendBaseRef.current = null;
6874
7005
  toolReplaySummariesByKeyRef.current = {};
7006
+ continuationDispatchPendingRef.current = false;
6875
7007
  setPendingToolRequests([]);
6876
7008
  setActiveToolCalls([]);
6877
7009
  setCollapsedBlocks((prev) => {
@@ -6891,29 +7023,17 @@ ${traceSummary}` : traceSummary;
6891
7023
  setUserHasScrolled(false);
6892
7024
  prevResponseLengthRef.current = 0;
6893
7025
  setResponse("");
6894
- if (!idle) {
6895
- stop(lastController);
6896
- setHistory((prevHistory) => __spreadProps(__spreadValues({}, prevHistory), {
6897
- [lastKey != null ? lastKey : ""]: {
6898
- content: processThinkingTags(response).cleanedText + "\n\n(response cancelled)",
6899
- callId: lastCallId || ""
6900
- }
6901
- }));
6902
- return;
6903
- }
6904
7026
  if (clearFollowOnQuestionsNextPrompt) {
6905
7027
  setFollowOnQuestionsState([]);
6906
7028
  }
6907
- const promptToSend = promptText;
6908
- if (!promptToSend || !promptToSend.trim()) return;
6909
7029
  setIsLoading(true);
6910
7030
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
6911
- const promptKey = `${timestamp}:${promptToSend.trim()}`;
7031
+ const promptKey = `${timestamp}:${promptToSend}`;
6912
7032
  setHistory((prevHistory) => __spreadProps(__spreadValues({}, prevHistory), {
6913
7033
  [promptKey]: { content: "", callId: "" }
6914
7034
  }));
6915
7035
  toolReplaySummariesByKeyRef.current[promptKey] = [];
6916
- setLastPrompt(promptToSend.trim());
7036
+ setLastPrompt(promptToSend);
6917
7037
  setLastKey(promptKey);
6918
7038
  setTimeout(() => {
6919
7039
  scrollToBottom(true);
@@ -6930,7 +7050,7 @@ ${traceSummary}` : traceSummary;
6930
7050
  messagesAndHistory.push({ role: "user", content: promptForHistory });
6931
7051
  messagesAndHistory.push({ role: "assistant", content: assistantContextContent });
6932
7052
  });
6933
- let fullPromptToSend = promptToSend.trim();
7053
+ let fullPromptToSend = promptToSend;
6934
7054
  if (messagesAndHistory.length === 0 && promptTemplate) {
6935
7055
  fullPromptToSend = promptTemplate.replace("{{prompt}}", fullPromptToSend);
6936
7056
  }
@@ -6939,7 +7059,7 @@ ${traceSummary}` : traceSummary;
6939
7059
  if (onBeforeSend) {
6940
7060
  void Promise.resolve(
6941
7061
  onBeforeSend({
6942
- prompt: promptToSend.trim(),
7062
+ prompt: promptToSend,
6943
7063
  conversationId: convId || null,
6944
7064
  agentId: agent,
6945
7065
  service,
@@ -6973,6 +7093,7 @@ ${traceSummary}` : traceSummary;
6973
7093
  if (isAbortError) {
6974
7094
  if (suppressAbortHistoryUpdateRef.current) {
6975
7095
  setIsLoading(false);
7096
+ releaseTurnLockIfSettled();
6976
7097
  return;
6977
7098
  }
6978
7099
  console.log("[AIChatPanel] Request was aborted by user");
@@ -7037,6 +7158,7 @@ ${traceSummary}` : traceSummary;
7037
7158
  }
7038
7159
  }
7039
7160
  setIsLoading(false);
7161
+ releaseTurnLockIfSettled();
7040
7162
  }
7041
7163
  );
7042
7164
  setLastMessages(messagesAndHistory);
@@ -7045,15 +7167,16 @@ ${traceSummary}` : traceSummary;
7045
7167
  onConversationCreated(convId);
7046
7168
  }, 100);
7047
7169
  }
7170
+ }).catch((error2) => {
7171
+ console.error("[AIChatPanel] Failed to send prompt:", error2);
7172
+ setError({
7173
+ message: error2 instanceof Error ? error2.message : "Failed to send prompt",
7174
+ code: "UNKNOWN_ERROR"
7175
+ });
7176
+ setIsLoading(false);
7177
+ releaseTurnLockIfSettled();
7048
7178
  });
7049
7179
  }, [
7050
- idle,
7051
- stop,
7052
- lastController,
7053
- lastKey,
7054
- response,
7055
- lastCallId,
7056
- processThinkingTags,
7057
7180
  clearFollowOnQuestionsNextPrompt,
7058
7181
  promptTemplate,
7059
7182
  send,
@@ -7067,17 +7190,16 @@ ${traceSummary}` : traceSummary;
7067
7190
  scrollToBottom,
7068
7191
  onConversationCreated,
7069
7192
  onBeforeSend,
7193
+ hasInFlightTurnWork,
7194
+ queuePromptForLater,
7195
+ releaseTurnLockIfSettled,
7070
7196
  getThinkingBlockCollapseKey,
7071
7197
  getThinkingBlockRenderKey,
7072
7198
  setResponse
7073
7199
  ]);
7074
7200
  const handleQueuePrompt = useCallback2((promptText) => {
7075
- const normalizedPrompt = String(promptText || "").trim();
7076
- if (!normalizedPrompt) return;
7077
- const nextQueuedPrompts = [...queuedPromptsRef.current, normalizedPrompt];
7078
- queuedPromptsRef.current = nextQueuedPrompts;
7079
- setQueuedPrompts(nextQueuedPrompts);
7080
- }, []);
7201
+ queuePromptForLater(promptText);
7202
+ }, [queuePromptForLater]);
7081
7203
  const handleClearQueuedPrompt = useCallback2((index) => {
7082
7204
  const nextQueuedPrompts = queuedPromptsRef.current.filter((_, queueIndex) => queueIndex !== index);
7083
7205
  queuedPromptsRef.current = nextQueuedPrompts;
@@ -7089,18 +7211,6 @@ ${traceSummary}` : traceSummary;
7089
7211
  const handleStop = useCallback2(() => {
7090
7212
  stop(lastController);
7091
7213
  }, [stop, lastController]);
7092
- useEffect8(() => {
7093
- const nextQueuedPrompt = String(queuedPromptsRef.current[0] || "").trim();
7094
- if (!nextQueuedPrompt) return;
7095
- if (!idle || isLoading) return;
7096
- if (pendingToolRequests.length > 0 || activeToolCalls.length > 0) return;
7097
- if (toolRequestProcessingRef.current) return;
7098
- if ((queuedToolRequestsRef.current || []).length > 0) return;
7099
- const remainingQueuedPrompts = queuedPromptsRef.current.slice(1);
7100
- queuedPromptsRef.current = remainingQueuedPrompts;
7101
- setQueuedPrompts(remainingQueuedPrompts);
7102
- continueChat(nextQueuedPrompt);
7103
- }, [activeToolCalls.length, continueChat, idle, isLoading, pendingToolRequests.length, queuedPrompts.length]);
7104
7214
  const handleNewConversation = useCallback2(() => {
7105
7215
  if (!newConversationConfirm) {
7106
7216
  setNewConversationConfirm(true);
@@ -7141,6 +7251,7 @@ ${traceSummary}` : traceSummary;
7141
7251
  setUserHasScrolled(false);
7142
7252
  setError(null);
7143
7253
  setActiveToolCalls([]);
7254
+ turnLockRef.current = false;
7144
7255
  setTimeout(() => {
7145
7256
  var _a2;
7146
7257
  setJustReset(false);
@@ -7160,6 +7271,7 @@ ${traceSummary}` : traceSummary;
7160
7271
  if (inFlightToolCallSignaturesRef.current.has(callSignature)) return false;
7161
7272
  return true;
7162
7273
  });
7274
+ pendingToolRequestsRef.current = unseenToolRequests;
7163
7275
  setPendingToolRequests((prev) => {
7164
7276
  if (areToolRequestListsEqual(prev, unseenToolRequests)) {
7165
7277
  return prev;
@@ -7250,9 +7362,11 @@ ${traceSummary}` : traceSummary;
7250
7362
  responseCompleteCallbackRef.current(currentLastCallId, currentLastPrompt || "", entry.content);
7251
7363
  }
7252
7364
  }
7365
+ releaseTurnLockIfSettled();
7253
7366
  }
7254
7367
  if (!isNowIdle && hasNotifiedCompletionRef.current) {
7255
7368
  hasNotifiedCompletionRef.current = false;
7369
+ continuationDispatchPendingRef.current = false;
7256
7370
  prevResponseLengthRef.current = 0;
7257
7371
  const currentLastKey = lastKeyRef.current;
7258
7372
  const existingContent = currentLastKey ? ((_a2 = latestHistoryRef.current[currentLastKey]) == null ? void 0 : _a2.content) || "" : "";
@@ -7261,7 +7375,48 @@ ${traceSummary}` : traceSummary;
7261
7375
  base: existingContent
7262
7376
  } : null;
7263
7377
  }
7264
- }, [idle]);
7378
+ }, [idle, releaseTurnLockIfSettled]);
7379
+ useEffect8(() => {
7380
+ if (!idle) return;
7381
+ releaseTurnLockIfSettled();
7382
+ }, [idle, response, pendingToolRequests.length, activeToolCalls.length, releaseTurnLockIfSettled]);
7383
+ useEffect8(() => {
7384
+ const nextQueuedPrompt = String(queuedPromptsRef.current[0] || "").trim();
7385
+ if (!nextQueuedPrompt) return;
7386
+ if (!idle || isLoading) return;
7387
+ if (pendingToolRequests.length > 0 || activeToolCalls.length > 0) return;
7388
+ if (turnLockRef.current || hasInFlightTurnWork()) return;
7389
+ if (queuedDrainScheduledRef.current) return;
7390
+ queuedDrainScheduledRef.current = true;
7391
+ queuedDrainTimerRef.current = window.setTimeout(() => {
7392
+ queuedDrainScheduledRef.current = false;
7393
+ queuedDrainTimerRef.current = null;
7394
+ const queuedPrompt = String(queuedPromptsRef.current[0] || "").trim();
7395
+ if (!queuedPrompt) return;
7396
+ if (turnLockRef.current || hasInFlightTurnWork()) return;
7397
+ const remainingQueuedPrompts = queuedPromptsRef.current.slice(1);
7398
+ queuedPromptsRef.current = remainingQueuedPrompts;
7399
+ setQueuedPrompts(remainingQueuedPrompts);
7400
+ continueChat(queuedPrompt);
7401
+ }, 0);
7402
+ }, [
7403
+ continueChat,
7404
+ hasInFlightTurnWork,
7405
+ idle,
7406
+ isLoading,
7407
+ pendingToolRequests.length,
7408
+ activeToolCalls.length,
7409
+ queuedPrompts.length
7410
+ ]);
7411
+ useEffect8(() => {
7412
+ return () => {
7413
+ if (queuedDrainTimerRef.current !== null) {
7414
+ window.clearTimeout(queuedDrainTimerRef.current);
7415
+ queuedDrainTimerRef.current = null;
7416
+ }
7417
+ queuedDrainScheduledRef.current = false;
7418
+ };
7419
+ }, []);
7265
7420
  useEffect8(() => {
7266
7421
  scrollToEndRef.current = scrollToEnd || false;
7267
7422
  }, [scrollToEnd]);
@@ -8554,7 +8709,49 @@ var parseCallMessages = (rawMessages) => {
8554
8709
  return normalized;
8555
8710
  };
8556
8711
  var shouldSkipTranscriptMessage = (message) => {
8557
- return message.role === "system" || message.role === "user" && message.content.startsWith("__system__:");
8712
+ const normalizedContent = message.content.trim().toLowerCase();
8713
+ const isInternalContinuationPrompt = message.role === "user" && normalizedContent.startsWith("original request:") && normalizedContent.includes("tool execution summary for the previous request:");
8714
+ return message.role === "system" || message.role === "user" && message.content.startsWith("__system__:") || isInternalContinuationPrompt;
8715
+ };
8716
+ var looksLikeToolStubResponse = (value) => {
8717
+ const text = value.trim();
8718
+ if (!text) return false;
8719
+ return text.includes('"type":"tool_use"') || text.includes('"type": "tool_use"') || text.includes('"type":"function"') || text.includes('"type": "function"');
8720
+ };
8721
+ var pickMoreCompleteResponse = (primaryResponse, fallbackResponse) => {
8722
+ const primary = primaryResponse.trim();
8723
+ const fallback = fallbackResponse.trim();
8724
+ if (!fallback) return primary;
8725
+ if (!primary) return fallback;
8726
+ if (primary === fallback) return primary;
8727
+ if (looksLikeToolStubResponse(primary) && !looksLikeToolStubResponse(fallback)) {
8728
+ return fallback;
8729
+ }
8730
+ if (fallback.startsWith(primary) || fallback.length > primary.length + 40) {
8731
+ return fallback;
8732
+ }
8733
+ return primary;
8734
+ };
8735
+ var mergeContinuationResponse = (baseResponse, continuationResponse) => {
8736
+ const base = baseResponse.trim();
8737
+ const continuation = continuationResponse.trim();
8738
+ if (!base) return continuation;
8739
+ if (!continuation) return base;
8740
+ if (looksLikeToolStubResponse(base) && !looksLikeToolStubResponse(continuation)) {
8741
+ return continuation;
8742
+ }
8743
+ if (base.includes(continuation)) {
8744
+ return base;
8745
+ }
8746
+ const maxOverlap = Math.min(base.length, continuation.length);
8747
+ for (let overlap = maxOverlap; overlap > 0; overlap -= 1) {
8748
+ if (base.slice(-overlap) === continuation.slice(0, overlap)) {
8749
+ return `${base}${continuation.slice(overlap)}`;
8750
+ }
8751
+ }
8752
+ return `${base}
8753
+
8754
+ ${continuation}`;
8558
8755
  };
8559
8756
  var parseTimestampMs = (value) => {
8560
8757
  const timestamp = toStringValue(value).trim();
@@ -8634,22 +8831,29 @@ var buildTurnsFromMessages = (messages, fallbackResponse, callId, timestampMs) =
8634
8831
  const prompt = message.content.trim();
8635
8832
  if (!prompt) continue;
8636
8833
  let response = "";
8834
+ let lastAssistantIndex = -1;
8637
8835
  let reachedNextUser = false;
8638
8836
  for (let lookAhead = index + 1; lookAhead < messages.length; lookAhead += 1) {
8639
8837
  const nextMessage = messages[lookAhead];
8640
8838
  if (!nextMessage) continue;
8641
8839
  if (nextMessage.role === "assistant") {
8642
- response = nextMessage.content.trim();
8643
- index = lookAhead;
8644
- break;
8840
+ const assistantResponse = nextMessage.content.trim();
8841
+ if (assistantResponse) {
8842
+ response = assistantResponse;
8843
+ lastAssistantIndex = lookAhead;
8844
+ }
8845
+ continue;
8645
8846
  }
8646
8847
  if (nextMessage.role === "user") {
8647
8848
  reachedNextUser = true;
8648
8849
  break;
8649
8850
  }
8650
8851
  }
8651
- if (!response && !reachedNextUser) {
8652
- response = fallbackResponse;
8852
+ if (lastAssistantIndex >= 0) {
8853
+ index = lastAssistantIndex;
8854
+ }
8855
+ if (!reachedNextUser) {
8856
+ response = pickMoreCompleteResponse(response, fallbackResponse);
8653
8857
  }
8654
8858
  if (!response) continue;
8655
8859
  turns.push({
@@ -8695,6 +8899,17 @@ var buildTranscriptTurnsFromCalls = (calls) => {
8695
8899
  timestampMs
8696
8900
  }
8697
8901
  ];
8902
+ } else if (fallbackResponse && mergedTurns.length > 0) {
8903
+ const previousTurn = mergedTurns[mergedTurns.length - 1];
8904
+ mergedTurns = [
8905
+ ...mergedTurns.slice(0, -1),
8906
+ __spreadProps(__spreadValues({}, previousTurn), {
8907
+ response: mergeContinuationResponse(previousTurn.response, fallbackResponse),
8908
+ callId: callId || previousTurn.callId,
8909
+ timestampMs: Math.max(previousTurn.timestampMs, timestampMs)
8910
+ })
8911
+ ];
8912
+ continue;
8698
8913
  }
8699
8914
  }
8700
8915
  mergedTurns = mergeTranscriptTurns(mergedTurns, callTurns);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hef2024/llmasaservice-ui",
3
- "version": "0.25.0",
3
+ "version": "0.25.2",
4
4
  "description": "Prebuilt UI components for LLMAsAService.io",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",